Compare commits

...

864 Commits

Author SHA1 Message Date
tastybento fc9b00233b
Sends messages only once to all players on the island (#2389)
Was sending to visitors and all players so visitors saw it twice.
2024-05-26 17:56:46 -07:00
tastybento 1fd4a9043f
Protect pumpkins from being sheared (#2388)
Requires Paper
2024-05-26 17:45:45 -07:00
tastybento ee3b0bfcc2
Update en-US.yml (#2387) 2024-05-26 17:36:45 -07:00
tastybento 35704b3fd3
Update zh-CN.yml (#2386) 2024-05-26 17:34:01 -07:00
tastybento 60fa60372d Fix tests 2024-05-26 17:27:41 -07:00
tastybento 8b0a5a3d0b Do not load all players just to get a name. 2024-05-26 17:01:02 -07:00
tastybento ca15740a8c
Fixes a bunch of items related to making the cache smaller (#2383)
* Fixes a bunch of items related to making the cache smaller

* Fix test
2024-05-25 22:38:53 -07:00
tastybento 5e5707f2a2
Fixes #2377, was not accounting for unknown UUIDs (#2380) 2024-05-24 16:43:55 -07:00
tastybento ef58838c41
Fixes #2378 where an unknown UUID was yielding null instead of "" (#2379) 2024-05-24 15:57:46 -07:00
BONNe 885d2449d9
Fixes creeper ignation by visitors (#2375)
This fixes a long-standing bug which was introduced with a code that prevented hostile entities from targeting visitors.  

As player was not a target for creeper it allowed it to explode.

This code change prevents visitors from igniting creepers as I do not see a reason why we should allow them to ignite them, while still protecting from griefing. 

Addresses issue reported in #2372
2024-05-24 07:22:20 -07:00
tastybento 888b485f82 Fixes #2376 2024-05-24 07:18:39 -07:00
tastybento e2d9c2ce34
Avoids loading islands into cache unless they are needed. (#2373)
* Avoids loading islands into cache unless they are needed.

* Adjust methods that were calling all islands

When we cached all island, this was an inexpensive call but not now. The
methods remain but pull from the database directly. The use of them were
changed to be player specific.
2024-05-23 21:42:14 -07:00
tastybento 8aba736383
Fixes breaking rooted dirt exploit (#2371) 2024-05-19 15:35:38 -07:00
tastybento 50276cb8e5 Abstract out getting and setting islands by ID
This is preparation for potentially making the cache smaller and pulling
from the database instead when required. However, there are issues with
this because some calls can result in loading the whole database anyway.
2024-05-18 22:49:20 -07:00
tastybento d701b7e43c
Uses Bukkit version method instead of class names (#2370)
* Uses Bukkit version method instead of class names

See https://forums.papermc.io/threads/important-dev-psa-future-removal-of-cb-package-relocation.1106/

* Fix tests

* Fix server compatibility reporting issue with Paper

* Remove unused import
2024-05-18 21:47:09 -07:00
tastybento 2fc3396a8f Remove debug code that slipped in. 2024-05-18 20:08:21 -07:00
tastybento 99717f5b60
Reduces storage of Island objects in the cache #2360 (#2369) 2024-05-18 19:48:42 -07:00
tastybento 290158e6ef
Allow NPC's to hit players (#2368)
* WIP for debug only

* Allow attacks from NPC's
2024-05-18 11:07:59 -07:00
tastybento 7126e837ed
Admin command updates (#2367)
* Enables tp'ing to specific islands of a player

* Admin delete command. Fixes to admin tp command.
2024-05-16 21:02:45 -07:00
tastybento 45e5621d4c
Update README.md 2024-05-16 17:22:07 -07:00
tastybento f536a13c59 Back to 1.20.6 2024-05-12 17:20:14 -07:00
tastybento f4780659e3 Downgrade to 1.20.5 to get tests to pass.
Tech debt - need to reword tests for 1.20.6
2024-05-11 13:21:00 -07:00
tastybento aad50eab38 Just use null. 2024-05-11 13:03:50 -07:00
tastybento 24d81da907 Update to latest 1.20.6 API for PlayerDeathEvent 2024-05-11 12:55:14 -07:00
tastybento d288528a17
2356 better deletion (#2364)
* Fix 1.20.4 backwards compatibility

* Improve deletion speed and memory usage
2024-05-11 11:07:47 -07:00
tastybento 83698c267f
Purges based on team members all being offline for too long (#2362)
* Purges based on team members all being offline for too long

* Fix to riff off real team members not trusts and banned as well
2024-05-11 08:50:28 -07:00
tastybento 52a280dc0b
Remove an unused Map in cache. (#2361) 2024-05-11 08:50:03 -07:00
tastybento 4a0d44c035
Implement new API for ItemsAdder item deletion (#2353) 2024-05-11 08:49:47 -07:00
tastybento b8e1f33800
Fixes #2352 obsidian scooping NPE (#2358) 2024-05-07 21:25:58 -07:00
tastybento d8891796cd JavaDoc fixes and fix for Particle enums 2024-05-05 21:28:04 -07:00
tastybento 61e7c22bbc
Add a hook for Multipaper (#2354) 2024-05-05 21:11:16 -07:00
tastybento b1fe76c45d
Multipaper (#2343)
* Switch to use database for team invites.

* WIP multipaper

* Fixes teams. Test still need to be fixed.

* Islands are now updated correctly across servers.

This build has a lot of debug in it!

* Fix tests

* Remove debug

* Remove primary island listing

* Version id

* Fix team management and ranks

* Removed debug

* Handle island deletion better

* Island deletion across servers.

* Fix bug with MythicMobs changes #2340

* 2.4.0

* Load of debug - trying to solve the settings slowness

* Debug debug

* Bug found - addPlayer being called instead of getPlayer

* Uncomment code after debug

* Fix tests
2024-05-04 22:27:58 -07:00
tastybento 5afd454fb3 Update Slimefun 2024-05-04 21:19:21 -07:00
tastybento a55c51412d Shift Github Build Script to Java 21 2024-05-04 21:05:19 -07:00
tastybento 01a8055379
Merge pull request #2348 from BentoBoxWorld/1.20.5_compatibility
1.20.6 compatibility
2024-05-04 13:29:15 -07:00
tastybento 744665a16e
Merge branch 'develop' into 1.20.5_compatibility 2024-05-01 18:01:51 -07:00
tastybento 3e4ff33d30
Merge pull request #2349 from BentoBoxWorld/Blueprint_Bundle_Info
Write the Blueprint bundle meta data to admin info
2024-05-01 17:58:34 -07:00
tastybento 09ede87971 Write the Blueprint bundle meta data to admin info 2024-05-01 17:58:05 -07:00
tastybento b1418c144f
Update README.md 2024-05-01 08:05:11 -07:00
tastybento 15335eb992 Add NMS for latest 2024-04-29 22:26:09 -07:00
tastybento e33823d0c0 Fix tests 2024-04-29 21:37:08 -07:00
tastybento 63cc0a01d9 Fix compatibility for 1.20.6 2024-04-29 19:46:06 -07:00
tastybento 6949432cb6 1.20.5 compatibility. Not all backward compatibility done
Won't run on 1.20.4 yet without stopping due to enum incompatibility
2024-04-28 17:56:02 -07:00
tastybento 69a22e917e Fix MythicMobs test 2024-04-14 22:11:41 -07:00
tastybento ffb955b22b Fix bug with MythicMobs changes #2340 2024-04-14 22:00:37 -07:00
tastybento 3de0ff236e Fix tests 2024-04-08 23:01:45 -07:00
tastybento c86eb6a19c Make sure it's the user's island that the target is being kicked from 2024-04-08 22:11:52 -07:00
tastybento e7055c6cba Remove player from island, not all islands when kicked. 2024-04-08 21:53:18 -07:00
tastybento 5834dcbb59 Fix placeholders manager test 2024-04-08 20:15:34 -07:00
tastybento 2c75939bc3 JavaDoc and import changes 2024-04-06 09:08:11 -07:00
tastybento e570401912
Merge pull request #2337 from BentoBoxWorld/2329_placeholders_for_islands
Added placeholders for island names and member lists #2329
2024-04-04 16:30:20 -07:00
tastybento f6f4da1c89 Added placeholders for island names and member lists #2329 2024-04-04 12:59:20 -07:00
tastybento 6106b661e9
Merge pull request #2335 from BentoBoxWorld/2328_team_members_can_have_islands
Remove restrictions on having multiple islands for team members.
2024-04-04 10:23:12 -07:00
tastybento e1536fcae0 Remove superfluous null check 2024-04-04 09:12:45 -07:00
tastybento 1c19703f44 Merge if statement 2024-04-04 09:11:49 -07:00
tastybento 24b7d26fbe NPE avoid. 2024-04-04 09:09:46 -07:00
tastybento ec60991aeb Merge branch 'develop' into 2328_team_members_can_have_islands 2024-04-01 10:59:30 -07:00
tastybento 33000f9371
Merge pull request #2336 from BentoBoxWorld/2333_bundle_limits
Add feature to limit blueprint availability.
2024-03-31 18:17:59 -07:00
tastybento 83eaa50b49 Refactor to improve code quality 2024-03-31 18:16:45 -07:00
tastybento 1215a43766 Add feature to limit blueprint availability. 2024-03-31 17:29:18 -07:00
tastybento 81f765df36 Fix invites and accepts. 2024-03-30 20:57:20 -07:00
tastybento 0e6a25d74b WIP - needs work on team invites. 2024-03-29 20:26:07 -07:00
tastybento 2b19d43c85 Remove restrictions on having multiple islands for team members.
Added API to enable checking for teams on islands easier.
2024-03-29 19:38:44 -07:00
tastybento 1bce4ec1b9 Limit blueprint pasting to world y coords. Addresses #2334 2024-03-28 21:58:01 -07:00
tastybento ea8562f351 Remove more debug 2024-03-28 21:42:33 -07:00
tastybento d8f2c12fe5 Remove pasting blueprints as stone debug. 2024-03-28 21:27:48 -07:00
tastybento b734d579a1
Merge pull request #2332 from BentoBoxWorld/admin_range_multi_island
Adds support for multi islands to the admin range command
2024-03-24 18:47:39 -07:00
tastybento 44454f5854 Adds support for multi islands to the admin range command 2024-03-24 18:44:24 -07:00
tastybento da590ce319
Merge pull request #2331 from BentoBoxWorld/sort_islands
Sort player's islands by age so they are always in the same order.
2024-03-21 19:30:42 -07:00
tastybento 6599e3de80 Sort player's islands by age so they are always in the same order. 2024-03-21 19:20:31 -07:00
tastybento 3c6e3d1286
Merge pull request #2327 from BentoBoxWorld/2320_hide_flags_in_other_world
Fix #2320. Enables hiding of flags when in another world
2024-03-16 10:26:27 -07:00
tastybento d77c94c30c Fix #2320. Enables hiding of flags when in another world
The world was being taken from the user's location not the panel's world
2024-03-16 10:04:28 -07:00
tastybento d240e9c8d8 Version 2.2.1 2024-03-16 09:15:46 -07:00
tastybento 75412a4674 Fix bug 2024-03-15 19:24:41 -07:00
tastybento 40e96b9169 Remove useeless eq 2024-03-15 18:53:08 -07:00
tastybento db2b97d2fc Remove ; after record definition 2024-03-15 18:52:37 -07:00
tastybento 5ad2ba1cd9 Remove useless eq()'s 2024-03-15 18:52:24 -07:00
tastybento 6127cdced1 Reduce complexity. 2024-03-15 18:49:20 -07:00
tastybento 91998b4e24 Use method reference 2024-03-15 18:47:14 -07:00
tastybento 06ca7a311a Simplify code. Fix code smells. 2024-03-15 18:46:07 -07:00
tastybento e4e92b9634 Fix code smell 2024-03-15 18:45:55 -07:00
tastybento 0c4a4ba862 Fix tests. Added coops and trusted to team GUI view. 2024-03-15 07:50:50 -07:00
tastybento 3907cba08f
Merge branch 'master' into develop 2024-03-14 21:17:03 -07:00
tastybento 4064c9a241
Merge pull request #2324 from BentoBoxWorld/team_gui_rewrite
Team gui rewrite
2024-03-14 21:13:35 -07:00
tastybento b45c842c2f
Merge pull request #2323 from BentoBoxWorld/team_fixes
Fix promote and demote #2322
2024-03-14 21:13:21 -07:00
tastybento e2a4233f69 Fix for IslandPromoteCommandTest 2024-03-14 21:11:22 -07:00
tastybento 977c82015b Rewrite. Tested with players. 2024-03-14 20:59:20 -07:00
tastybento 6db04f872b Fix promote and demote #2322 2024-03-14 18:16:09 -07:00
tastybento 6fccf80477
Merge pull request #2321 from BentoBoxWorld/max_islands_per_island
Allow the maxhomes to apply per island.
2024-03-13 18:01:18 -07:00
tastybento eef3dcbc46 Allow the maxhomes to apply per island. 2024-03-13 17:58:28 -07:00
tastybento 253e5d7101 Use method references. 2024-03-10 22:36:42 -07:00
tastybento 1c4be17690
Merge pull request #2319 from BentoBoxWorld/better_blueprint_unzip_security
Uses path normalization to prevent directory traversal attacks.
2024-03-10 11:50:16 -07:00
tastybento dc42f51168 Uses path normalization to prevent directory traversal attacks. 2024-03-10 11:46:44 -07:00
tastybento 1fb6a8a27c
Merge pull request #2318 from BentoBoxWorld/team_owner_bug
Fixes bug where non-members could be made island owners.
2024-03-10 11:45:20 -07:00
tastybento 4170616e47 Fixes bug where non-members could be made island owners. 2024-03-10 11:29:51 -07:00
tastybento 7f532b1257 Resolve JavaDoc issues 2024-03-10 11:21:03 -07:00
tastybento c39bd75837
Merge pull request #2317 from BentoBoxWorld/mythic_mobs
Mythic mobs
2024-03-10 10:41:36 -07:00
tastybento 4810c4c4ad Adds the ability to include MythicMobs in Blueprints. Fixes #2316 2024-03-10 10:40:26 -07:00
tastybento cb7c63a520 Version 2.1.0 2024-03-09 10:29:17 -08:00
tastybento dcc3992762
Merge pull request #2315 from BentoBoxWorld/2313_Crafting_protection 2024-03-04 12:04:25 -08:00
tastybento 57164dd846 Fixes #2313 2024-03-04 07:01:47 -08:00
tastybento 69017860a0
Merge pull request #2314 from BentoBoxWorld/2309_admin_setowner
2309 admin setowner
2024-03-03 16:08:17 -08:00
tastybento 24c68a0d95 Rewrite of admin setowner command #2309 2024-03-03 16:07:49 -08:00
tastybento 994019836a Merge branch 'develop' into admin_setowner 2024-03-03 14:35:18 -08:00
tastybento bbafa6d340
Merge pull request #2312 from BentoBoxWorld/2311_team_kick_gui_fix
Fix for Island team kick requires confirmation when using GUI #2311
2024-03-03 14:32:23 -08:00
tastybento 19d81c70c6 Fix for Island team kick requires confirmation when using GUI #2311 2024-03-02 19:29:26 -08:00
tastybento 7d52325196 Version 2.1.2 2024-03-02 19:23:31 -08:00
tastybento 2d08365afc
Merge pull request #2308 from BentoBoxWorld/develop
Release 2.1.1
2024-03-01 22:16:29 -08:00
tastybento a3a4a70921 Fix NPE #2310 2024-03-01 21:51:10 -08:00
tastybento bb9ed87175 WIP 2024-03-01 21:39:18 -08:00
tastybento 0e833de22a
Update pom.xml Version 2.1.1 2024-02-26 13:47:56 -08:00
tastybento 95d89c56bc
Merge pull request #2291 from BentoBoxWorld/develop
Version 2.1.0
2024-02-25 14:58:12 -08:00
tastybento 4ddb204fcf
Merge pull request #2307 from BentoBoxWorld/2305_respawn_to_home
Change respawn point to the default home on the island. #2305
2024-02-24 16:08:32 -08:00
tastybento 562b515bfe Change respawn point to the default home on the island. #2305 2024-02-24 16:06:56 -08:00
tastybento 0a9da71c65 Fix admin commands to avoid removing players from more than one island 2024-02-24 15:51:24 -08:00
tastybento 631a15563d Remove deprecated methods from IslandsManager and IslandCache 2024-02-24 15:07:05 -08:00
tastybento f93c9ba556
Merge pull request #2306 from BentoBoxWorld/admin_disband_update
Better enable disbanding of teams via admin command
2024-02-24 14:48:56 -08:00
tastybento f568ccd2d6
Merge branch 'develop' into admin_disband_update 2024-02-24 14:48:35 -08:00
tastybento f11a56cc6b Better enable disbanding of teams via admin command 2024-02-24 14:47:22 -08:00
tastybento ad009156e5
Merge pull request #2304 from BentoBoxWorld/bukkit_paste
Bukkit paste
2024-02-23 23:08:27 -08:00
tastybento 78f84de3cb Use Bukkit pasting if user is too close to new island.
This prevents players experiencing invisible blocks at the expense of
lag.
2024-02-23 23:04:47 -08:00
tastybento 070c1102db Use Bukkit for Admin BP pasting. 2024-02-23 22:05:32 -08:00
tastybento a1d19fae74 WIP - use Bukkit for bp admin pasting. 2024-02-23 21:56:32 -08:00
tastybento 59124cfa8a Fix NPE with unregister command 2024-02-23 21:51:17 -08:00
tastybento 887f09c41b
Merge pull request #2302 from BentoBoxWorld/2296_unregister_register_fix
Adds multi-island support for the Admin Register and Unregister commands.
2024-02-19 16:52:20 -08:00
tastybento e8acd05dbe Adjust null check. 2024-02-19 16:51:08 -08:00
tastybento 3784d5d28d Added tests. Added check for world. Removed y ignore for closest island. 2024-02-19 16:27:38 -08:00
tastybento 95b7ed137f Added tests 2024-02-19 15:45:47 -08:00
tastybento 87b147ace4 Allow multi-island registrations 2024-02-19 12:03:53 -08:00
tastybento 1846ed8088 Added tests 2024-02-19 11:36:47 -08:00
tastybento af843aeb35 Unregister coded. 2024-02-19 10:55:15 -08:00
tastybento b43197d54f
Merge pull request #2300 from BentoBoxWorld/2299_placeholder_counts
Placeholder counts
2024-02-18 14:44:42 -08:00
tastybento 38551a138a Fix tests 2024-02-18 14:43:29 -08:00
tastybento 83479ac8a4 Added placeholders for team online member counts #2299 2024-02-18 14:36:42 -08:00
tastybento 5e4634400a Version 2.1.0 2024-02-18 14:36:18 -08:00
tastybento f81354fa3e Fix for MySQL https://github.com/BentoBoxWorld/BentoBox/issues/2298 2024-02-13 18:55:45 -08:00
tastybento 24025fd9ae Ignore test 2024-02-12 20:02:49 -08:00
tastybento 87054687f0 Add new DamageSource parameter 'null' to test events 2024-02-12 19:56:10 -08:00
tastybento 01f51f4d29 Temporarily not do IA custom block deletion. #2297 2024-02-12 19:27:56 -08:00
tastybento 59c18e26f5 Reduce duplications 2024-02-09 22:21:22 -08:00
tastybento b63aef5589 Add support for older 1.20.x versions 2024-02-09 17:40:58 -08:00
tastybento 2d0d9ac1c2 Add defensive code to log an warning on YAML config issue
Relates to https://github.com/BentoBoxWorld/SkyGrid/issues/67
2024-02-07 19:02:45 -08:00
tastybento 8217b375d5 Fix test. 2024-02-03 12:11:57 -08:00
tastybento 43db8d346c Added ability to hide command rank commands by Ops. 2024-02-03 11:32:28 -08:00
tastybento 9acb79fc60 Added extra info to version command on version used. 2024-02-03 08:00:17 -08:00
tastybento 6316ff4a93 Add some defensive code to try and load addons if they are not pladdons 2024-02-01 22:46:00 -08:00
tastybento 471e8d681b Version 2.0.1 2024-01-30 23:16:49 -08:00
tastybento 196e90bf4e
Merge pull request #2204 from BentoBoxWorld/develop
Release 2.0.0
2024-01-30 22:38:04 -08:00
tastybento c100fe0047 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2024-01-30 22:31:01 -08:00
tastybento b2550882ba Add optimization for Slimefun deletion. #2289 2024-01-30 22:30:51 -08:00
BONNe 6bf12d5e20
Fixes EXP reset for players onJoin and onLeave (#2286)
The total experience does not reset player collected exp. This fixes that, as it will set level to 0 and progress in towards next level to 0
2024-01-30 19:32:13 -08:00
tastybento c9b7074e79 Fix test 2024-01-27 20:31:51 -08:00
tastybento b25c65b7b2
Center default home (#2287)
* Center default home

* Remove debug
2024-01-27 17:06:31 -08:00
tastybento 787424b999 Check for SHORT_GRASS before using it. #2283 2024-01-23 12:50:42 -08:00
tastybento 13bd1bb8f3
Fix for #2281 (#2282) 2024-01-22 14:50:24 -08:00
tastybento 5d3821094c
Fix for #2279 on-island placeholder for nether and end (#2280) 2024-01-21 10:58:53 -08:00
tastybento 90a53e9fd8
Fixes #2274 hanging signs protection (#2278) 2024-01-20 08:17:50 -08:00
tastybento ffcda52912 Use latest form of icon format for TIPPED ARROWS in panels 2024-01-19 22:16:12 -08:00
tastybento b672755ef9
Use a different method to grab the DataObject classes. (#2277)
Previously, when all Addons were Addons and men were men, the
DataObjects could be tracked using the BentoBox custom ClassLoader.
However, as Addons now can be and usually are loaded using the Bukkit
Classloader, this is no longer possible. However, we can track them when
a Database class is instantiated and use that. This relies on Addons
declaring their database objects. If they have not when the migration
command is run, then obviously they cannot be transfered.
2024-01-19 21:56:19 -08:00
tastybento 7c3056b0e8 Add ability to place glowberry cave vines if planting allowed. 2024-01-15 14:48:45 -08:00
tastybento 2d44c5dbca Adds protection for berries #2272 2024-01-15 14:22:37 -08:00
tastybento ce4d8a377e JavaDoc fixes 2024-01-14 18:57:37 -08:00
tastybento cc7b1eba4c Require panel to have a world. 2024-01-13 08:01:49 -08:00
tastybento 4bfbe41956
World context settings (#2271)
* WIP to having admin setting world based on the command

and not the location of the user.

* Make world settings depend on the world of the command

Previous code based world on the location of the user when they ran the
command.

* Remove unused import

* Fix JavaDoc

* Remove unused import.
2024-01-12 20:28:07 -08:00
tastybento 76a36e685e
Enable template to determine what is shown/clickable. (#2269) 2024-01-11 19:53:27 -08:00
tastybento f1001b1d05 Do not allow invite clicks if they have been removed from the panel 2024-01-11 18:13:43 -08:00
tastybento 98d336faf0 Fix admin settings NPE. 2024-01-11 15:59:53 +09:00
tastybento edd7bcfbd2 Fix settings panel bug NPE.
The parent needs to be set before it can be used.
2024-01-11 14:38:45 +09:00
tastybento be9b429308
Faster teleports (#2267)
* Fixed bug with teleporting where home name would not be used.

* Remove debug
2024-01-10 21:09:53 -08:00
tastybento 26e5d750a0 Clarify creeper damage. Active means creepers can damage. 2024-01-09 11:57:57 +09:00
tastybento ca2976cc4b Test clean up. 2024-01-08 22:21:31 +09:00
tastybento 54a7b8b0f2 Fix to avoid admins becoming subowners when registering an island. 2024-01-08 22:21:20 +09:00
tastybento 0e0c502e1d
Team gui (#2251)
* WIP - add GUI for teams.

* WIP teams GUI

* Moe WIP on team GUI

* Modified file to avoid an NPE.

* Minor refactor to prevent int to Integer warning.

* Use latest dependencies for tests to pass.

* Move to singleton pattern for RanksManager,

* Team GUI WIP

* Added support for kick, setowner, and leave.

* Added support for accepting and rejecting an invite.

* Fixed bugs with text and operations.

* WIP for team invites.

* Adds inviting to the GUI.

* Fix tests

* Fixed minor bugs reported by SonarCloud

* Up Minecraft version and paper to 1.20.4 in POM

* Added features based on Discord feedback.

1. added some lore to the invite button to explain the players shown are
from the game world
2. removed the team state in chat
3. added a search to the invite panel

* Added back button.

Refactored some code and templates.

* Fixed bugs with teams and uncoop untrust etc of Ops.

* Return to name search GUI if name not found.

* Added return to GUI for player search

* Use Path.of
2024-01-08 20:50:26 +09:00
Rafał Chomczyk 68da898e79
Optimize CopyWorldRegenerator#copyChunkDataToChunk to reduce performance impact in case of multiple resets simultaneously (#2261) 2024-01-08 19:09:41 +09:00
tastybento c62d4f603f
Add island object to Panel for context. (#2263)
Enable Tab object to reference parent TabbedPanel in the builder. It is
late assigned after building. This enables tabs to get the parent, and
therefore get the Island object.

default methods were used to support backward compatibility.
2024-01-08 07:07:30 +02:00
tastybento 29a6a51f30 Up to 1.20.4 in POM 2024-01-05 13:22:03 +09:00
BONNe fc658ca073
Fixes warning about already existing file saving. (#2260)
Spigot JavaPlugin#saveResources either replaces or complains that replacement is disabled. So it is necessary to check if file does not exists before saving it.

Fixes #2259
2024-01-05 06:18:41 +02:00
BONNe caade1a71c
Fixes ItemParser. (#2257)
* Fixes ItemParser.

Implements new Potion and Skull parsing.
Fixes custom model data parsing.
Implements ItemFactory#createItemStack parsing that parses item using `/give` syntax.

Solves some issues mentioned in #2198

* Fixes failing BentoBoxLocaleTest

In runTime ItemFactory#createItemStack from invalid text would throw IllegalArgumentException.
2024-01-04 21:30:16 +09:00
BONNe 31fb10629a Fixes merge conflicts. 2024-01-04 10:18:25 +02:00
BONNe 38d845d2e9
Fixes issue with file exporting in Windows system. (#2256)
JAR files does not store files inside it with filesystem separator. Only spot where it makes sense to transform "/" into file separator is in output file saving.
2024-01-04 17:15:13 +09:00
BONNe bfb487342a
Customizable Island Creation Panel (#2255)
* Customizable Island Creation Panel

This implements customizable Island Creation Panel. By default, panel is generated in `/plugins/bentobox/panels` folder, however, if GameModeAddon has a specific panel defined in their folder, then that panel is taken.

* Create UnitTests for new panels

* Add island creation panel saving on loading

* Remove old IslandCreationPanel and move to the new one.

* Fixes some issues with locale and panel

---------

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2024-01-04 17:14:23 +09:00
BONNe 3be034bfa9
Customizable Language Selection Panel (#2254)
* Customizable Language Selection Panel

This implements customizable Language Selection Panel. By default, panel is generated in `/plugins/bentobox/panels` folder, however, if GameModeAddon has a specific panel defined in their folder, then that panel is taken.

* Migrate LanguagePanelTest to customizable panel.

* Remove original LanguagePanel

* Save language_panel.yml on server starting.

* Fixes some bugs in LanguagePanel.
2024-01-04 17:09:44 +09:00
tastybento e8e1d6184e
Material Type adapter (#2253)
* Fix powermockito test dependencies.

* Add Material gson type adapter to handle old Material enums

In this case GRASS -> SHORT_GRASS
2024-01-02 12:54:50 +09:00
tastybento 0352cfdd38 Lower Spigot version back to 1.20.3 because 1.20.4 doesn't exist? 2024-01-02 09:01:45 +09:00
tastybento cc5c8aa9b6
Adds an ItemAdder hook to delete any blocks when island is deleted. (#2250)
* Adds an ItemAdder hook to delete any blocks when island is deleted.

Also includes a flag for explosions.

* Make the error reporting method non-abstract.

This is not a mandatory method for many hooks.

* Delete this class as it is not used any more and just duplicate.

* Added test class.

* Minor issues resolved.
2023-12-28 10:30:20 +09:00
tastybento 86d8d147d1
Delete slimefun chunks/blocks when island is deleted. (#2247) 2023-12-24 21:24:21 +09:00
BONNe 96499f3ad6
Improves obsidian platform generation (#2246)
The obsidian platform was not generating constantly in the same spot. It was moving depending on the entrance point (from the sides).
This code changes it, as it will move through relative blocks. 
Also, this change will sync portal and platform center points, which was not done previously. 

Fixes #2239
2023-12-24 13:19:45 +09:00
tastybento b260cf1f4f Add backward compatibility handleing for GRASS 2023-12-23 08:42:46 +09:00
tastybento f6e26aa5bd Fix admin register command to handle worlds correctly
The register command was failing if a player had more that one island,
even if the island was in a different game mode.
2023-12-19 04:51:10 -08:00
tastybento 1ef55a2b1e Fix issue where seed worlds lost generators with Multiverse on restart 2023-12-19 04:36:00 -08:00
tastybento b2f5a441e5 Added 1.20.4 compatibility. 2023-12-10 09:23:10 -08:00
tastybento 8dce036d70 Workaround to avoid massive test failures.
The addition of a null check in the Enchantment Bukkit code causes a lot
of test failures. While we work out how to mock that particular area,
this code avoids running Enchantment code when under test.
2023-12-10 09:18:54 -08:00
tastybento 55e94b4c9f Revert "Rewrote PanelItemTest to avoid null check errors in Bukkit class"
This reverts commit 5a193cda1c.
2023-12-10 09:10:48 -08:00
tastybento 5a193cda1c Rewrote PanelItemTest to avoid null check errors in Bukkit class 2023-12-09 09:25:07 -08:00
tastybento 1ab2ff278d Revert "Revert to 1.20.2 for now until 1.20.3 is more stable"
This reverts commit 1942f5f1df.
2023-12-06 19:03:41 -08:00
tastybento 1942f5f1df Revert to 1.20.2 for now until 1.20.3 is more stable 2023-12-06 17:00:38 -08:00
tastybento 79b95af8bd Try to avoid test error with Material class (which is now bigger) 2023-12-06 14:06:41 -08:00
tastybento af861f2a34 Update to 1.20.3 2023-12-06 13:45:44 -08:00
tastybento 6964f8c61a Fix MV compatibility. Fixes #2244
Provides the correct generator for the seed worlds.
2023-12-06 13:38:59 -08:00
tastybento 12bf37d2c2
No tabs (#2243)
* Remove all tabs from source and replace with 4 spaces.
2023-12-04 20:55:40 -08:00
tastybento 22f398fe53 Fix deletion of islands when chunks are not deleted. Fixes #2241 2023-12-03 19:50:51 -08:00
tastybento f256c3af8d Fixes #2240 Makes locations default to island center in IslandBaseEvent 2023-12-02 21:41:35 -08:00
tastybento 54ebf2dfd2 Fix issue where players with more than one island couldn't leave a team 2023-12-02 12:06:57 -08:00
tastybento 26e6310fba Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2023-12-02 11:51:58 -08:00
BONNe f80cf238f0
Fixes end portal finding issue when entering from side (#2238)
There was a bug in the code that prevented to finding of a portal location if the player entered from the side. 
It can happen only if the portal frame is removed, but as in vanilla it is possible, then it needs to be addressed.

The fix itself is simple: instead of checking just up from the starting location, the code will check all blocks in 10x10x10 square from the starting location and find the "closest" portal location.

This will fix #2237
2023-11-28 13:04:52 -08:00
gitlocalize-app[bot] 187ae1c61a
Add Ukrainian locale (#2235)
* Translate uk.yml via GitLocalize

* Translate uk.yml via GitLocalize

* Translate uk.yml via GitLocalize

* Update uk.yml

---------

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: GIGABAIT <freebitcoin93@gmail.com>
Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2023-11-26 10:34:27 -08:00
gitlocalize-app[bot] ea6b5dd257
Add Ukrainian locale (#2236)
* Translate uk.yml via GitLocalize

* Translate uk.yml via GitLocalize

---------

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: BONNe <bonne@bonne.id.lv>
2023-11-26 09:59:46 -08:00
tastybento 9fe5212c3c Added AddonEvent test class. 2023-11-25 18:43:18 -08:00
tastybento 625b70de30 Simplify fillBorder 2023-11-25 18:22:37 -08:00
tastybento 4547232ac9 Better constants 2023-11-25 14:57:39 -08:00
tastybento 90751224d7 Better constants 2023-11-25 14:54:07 -08:00
tastybento 59c84990e5
Adds a method that can return a different default value than 0 (#2234) 2023-11-25 08:39:28 -08:00
Baterka 13c339ef4f
- Fixed separators to also work on Windows (#2233)
- Added missing `--add-opens=java.base/java.security=ALL-UNNAMED`
 - Added proper closing of AddonClassLoader
2023-11-25 07:45:56 -08:00
Huynh Tien 5de7302469
Switch to Spigot's PlayerProfile on HeadGetter (#2231)
* Switch to Spigot's PlayerProfile on HeadGetter

Remove AuthLib

* forgot to set meta back

* should check if the texture is not empty
2023-11-24 08:15:20 -08:00
tastybento 1cf7ccbb99 Fix bug where players couldn't invite other players.
The IslandsManager#inTeam method was returning true even if the team was
just 1 player.
2023-11-19 12:38:54 -08:00
tastybento defb1c7a6e
Better French, maybe... (#2228) 2023-11-18 18:28:34 -08:00
tastybento 0ecbd9150b Even more French fixing... 2023-11-18 15:38:06 -08:00
tastybento 2818ac8f33 Correct French 2023-11-18 15:35:39 -08:00
tastybento 70e4ae15b7 Fix gender 2023-11-18 15:34:24 -08:00
tastybento d95727fbdf Fix French - coffre, not poitrine. Chest not human chest! 2023-11-18 15:32:51 -08:00
tastybento 26dc5c5949 IslandsManager#isOwner correction and JavaDoc update 2023-11-18 13:49:33 -08:00
BONNe a35353a802
Fixes an issue with entity teleportation if nether/end worlds are disabled (#2227)
There was a bug that used old code (environment switching) for teleportation out of dimension. 
The issue should be fixed with calling just calling teleportation with portal environment.
2023-11-15 23:34:13 +02:00
tastybento 63d092db93
Multi island api update (#2224)
* Fixes bug with island team joining and deletion. Include debug.

* Remove ambiguous API with mutli-island.

* Add back deprecated methods so developers can be wartned not to use them

* Added back in deprecated methods.

* Fix bugs and NPEs.

* Fix some code smells

* Remove debug logging.
2023-11-12 12:22:12 -08:00
tastybento 19d7e2fe0a Fix island resetting. #2223
Islands were being deleted in all worlds, and all islands were being
deleted from the player instead of just the one island.
2023-11-10 17:06:46 -08:00
tastybento c63de278fe Revert "This changes the loading to not be on STARTUP (#2214)"
This reverts commit 4a6bf31dff.
2023-11-10 10:54:30 -08:00
tastybento 24148f30ba
Rank Management (#2220)
* Put ranks in the database. Prework for #1798

* Prevent coop and trust commands from being registered

if those ranks don't exist.

* Add /bbox rank command for ranks management

* Rename RanksObject to Ranks

* Strip / on commands if it exists. Fixes #2221

* Fix test failure
2023-11-10 10:53:28 -08:00
tastybento 4481881b9e Fix tests 2023-11-10 07:34:52 -08:00
BONNe 285205fe3f
Fixes a bug with EntityTeleportListener (#2222)
There was incorrect teleportation type detection, as target world were set to NORMAL. This prevented to detect that portal in opposite side exists, and should be linked to the correct position.
2023-11-10 07:23:38 -08:00
tastybento 5503ce0d90 Fixes #2221 2023-11-05 15:17:56 -08:00
tastybento 6d09a5a359 Fixes #2219. Island homes were set incorrectly with a new island. 2023-11-04 10:29:28 -07:00
BONNe 4200fe4abb
Fixes a bug that removed old owner from island (#2218)
* Fixes a bug that removed old owner from island

These changes fix a bug that was introduced by clearing the island cache from the old owner for 2.0 verison.

This will set the previous owner as sub-owner on the island.

* Fixes rank on event fired.

Changes from Visitor to Sub owner rank

* Update IslandsManager.java

Removes line that removes player from cache. 

They should remain in the cache.

---------

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2023-10-30 09:17:26 +02:00
tastybento 4a6bf31dff
This changes the loading to not be on STARTUP (#2214)
Worlds are then created in onEnable and not one-tick later.
2023-10-28 21:16:36 -07:00
tastybento d903e57ad6
Remove the deprecated classes for 2.0 (#2216) 2023-10-28 21:16:04 -07:00
tastybento fd99da68d9 Fix double trapped (redstone) chest protection. #2215 2023-10-23 18:31:57 -07:00
gitlocalize-app[bot] 7f50073ad1
Croatian translation WIP (#2212)
* Translate hr.yml via GitLocalize
---------

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2023-10-21 16:00:25 -07:00
gitlocalize-app[bot] c4fa6cf26e
French translation WIP (#2211)
* Translate fr.yml via GitLocalize

---------

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2023-10-21 15:33:26 -07:00
gitlocalize-app[bot] 39882cb7bc
German Translation WIP (#2210)
* Translate de.yml via GitLocalize

---------

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2023-10-21 15:01:32 -07:00
gitlocalize-app[bot] 4e0e3d2005
Portuguese translation (#2209)
* Translate pt.yml via GitLocalize

---------

Co-authored-by: dollyXtoddy <dollyxtoddy@gmail.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: SrVictor079 <cruz.joaovictor22@gmail.com>
2023-10-21 13:58:22 -07:00
gitlocalize-app[bot] c195baf66d
Czech translation (#2208)
* Translate cs.yml via GitLocalize

---------

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
2023-10-21 12:01:15 -07:00
gitlocalize-app[bot] b3808334ad
Translate ja.yml via GitLocalize (#2207)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2023-10-21 12:00:49 -07:00
gitlocalize-app[bot] 9e8df5ce80
Chinese translation (#2206)
* Translate zh-CN.yml via GitLocalize

---------

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: Jeansou <bettertreebot@gmail.com>
Co-authored-by: CuteLittleSky <2173204318@qq.com>
2023-10-21 12:00:30 -07:00
gitlocalize-app[bot] 2c49e6d2f4
Hungarian translationrian (#2205)
* Translate hu.yml via GitLocalize

* Update hu.yml

Fixed placeholders.

---------

Co-authored-by: s3babyy <s3bastian540@gmail.com>
Co-authored-by: balazzrthd <balazzrt@gmail.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: SilkyFalcon <gerolytibor2007@gmail.com>
Co-authored-by: RayenHUN <huszarikd@gmail.com>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2023-10-21 11:09:33 -07:00
tastybento 5ccaadb2ae Fix tests. 2023-10-21 08:19:46 -07:00
tastybento 8d0dbcdc36 Fix erroneous code that got pasted in... 2023-10-21 08:00:19 -07:00
tastybento e4836b9cef Remove island number check for owner transfer.
This is not needed anymore (we think).
2023-10-21 07:55:03 -07:00
BONNe af3b0bba2d
Fixes missing locale text
The message was linked to a non-existing path.
2023-10-20 20:01:41 +03:00
tastybento ea5529f077
Enables permissions to set how many islands a play can create. (#2201)
Multiple Island Permissions #2199
2023-10-15 19:01:02 -07:00
tastybento 489a1e332d Remove unregistration of MV worlds workaround.
This is no longer needed as with
https://github.com/Multiverse/Multiverse-Core/releases/tag/4.3.12 (or
maybe earlier) as the issue is fixed where the generator was not
remembered across reboots.
2023-10-15 13:57:48 -07:00
tastybento c8b2e1d801 Changed some tests for ItemParsing.
Potions are no longer extended or upgraded using the deprecated
PotionData. They have explict names, like "long_night_vision" or
similar. So these tests don't work any more.
2023-10-11 22:48:39 -07:00
tastybento a2f1054bbe PotionTypes have changed and now are explicitly named
For example, extended are now called
2023-10-11 22:46:51 -07:00
tastybento 9f8a6bcded Remove players from the island and cache. 2023-10-11 21:43:30 -07:00
tastybento dc51bd87b1 Fix code to have a default entry on the switch, which is now required. 2023-10-11 21:42:45 -07:00
tastybento 2b65543935 Adds InvincibleVisitorFlagDamageRemovalEvent and tests.
Resolves #2196
2023-10-08 08:56:13 -07:00
tastybento 7fbd041be9 Check passengers of jockeys when spawned. #2195 2023-10-07 11:15:23 -07:00
tastybento 6d2f79881b Fixes getOwner and getIslands to properly return islands in the world
The world was not being used for the getOwner return so if a player had
an island in any world then it was returned. This caused an NPE if the
island was then requested by getIsland because it would not be there.
2023-10-07 10:30:16 -07:00
tastybento e6ccce4a27 Added flags for Loom, stonecutter, cartography, smithing, grinding
Fixes #2194 and #2193
2023-10-07 09:52:24 -07:00
tastybento 8b78affc9b Update NMS to 1.20.2 2023-10-07 09:01:01 -07:00
tastybento 5c3ebc800c Add 1.20.2 compatibility. 2023-10-07 08:54:59 -07:00
tastybento 2bc82dd3cf
Add checking for enchantment table inventory (#2191)
Fixes #2190
2023-09-19 21:07:28 -07:00
tastybento a4bef159be
2 0 0 multi island (#2185)
* Multi world WIP - stashing

* Initial work on supporting multiple islands per player

The default allowed number is 5 for now, but will be set to 1 by
default.

Lots more work to do on this!

* More work on multi island. Fixed tests so clean compile.

* Remove unused imports

* Updated island go and homes command to multi island

Updated tests.

* Do not reload addons anymore.

* Add island name when entering or leaving own island

* Remove unused import

* Adds island names to /island go command.

* Enables more homes to be set if player has more than one island

* Switch to using a set for islands and explicit primary boolean in Island

* WIP

* Fix bugs with the go command.

* Be able to delete multiple islands, e.g. when joining a team

This is not fully tested.

* Do not remove all islands when a player does reset.

Players can reset just the island they are on.

* More fixes for go command

* Fix tests

* Fix @NonNull annotation

* Fix home syntax listing reference for IslandDeleteHome

* Fixed deletehome for multiple islands.

* Fix /island command teleport to current island default home.

* Remove deprecated code.

* Fix tag for concurrent island setting in config.yml

* Improve error when trying to make additional islands over limit

* Update config.yml

* Correctly assign invites for islands.

* Switch to canExecute API in prep for multi-island handling

* Prevent players from obtaining more concurrent islands by owner transfer

* Handle leaving and disbanding of teams

* Fix tests

* Fix minor bugs or code smells.

* Restore the quarantine code from deprecation.

This code can stay. It checks if islands can load, and if not puts them
in a trash. It does no harm.

* Remove unneeded eq()'s

* Fix tests
2023-09-16 15:55:52 -07:00
tastybento 503107a90c Fix test 2023-09-04 14:58:23 -07:00
tastybento b276c0715c Fix reference for home list syntax in IslandDeletehomeCommand 2023-09-04 12:24:56 -07:00
tastybento 8d3be9c0ff Added island member placeholders
Relates to #2180
2023-09-03 10:50:47 -07:00
tastybento ba31dd78b4 Ignoring sculk sensor test for now.
It used to work, but now doesn't. I've tried updating various plugins
and trying different things, but it seems that the mocking is not
working correctly. I don't know why.
2023-09-03 08:58:00 -07:00
tastybento a7b791f4be Fixes #2173. Copies the correct range of blocks. Added test class.
Issue was with negative numbers that (int) rounds towards zero and not
towards negative infinity.
2023-09-02 08:04:34 -07:00
gitlocalize-app[bot] bcf569c019
Indonesian translation (#2184)
* Translate id.yml via GitLocalize

* Translate id.yml via GitLocalize

* Translate id.yml via GitLocalize

---------

Co-authored-by: Dusty <siapa-yg-mau-diblokir.kfrxp@simplelogin.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2023-09-01 15:28:49 -07:00
tastybento b2bdce4d25
Merge pull request #2183 from BentoBoxWorld/gitlocalize-26028
Japanese update
2023-09-01 14:59:14 -07:00
tastybento a6f47e5c54 Translate ja.yml via GitLocalize 2023-09-01 21:58:31 +00:00
mt-gitlocalize 33a694cfd1 Translate ja.yml via GitLocalize 2023-09-01 21:58:30 +00:00
tastybento 6a5ea171be
Merge pull request #2182 from BentoBoxWorld/gitlocalize-26016
Few RU translations
2023-09-01 14:35:38 -07:00
tastybento d6978ce0f6 Translate ru.yml via GitLocalize 2023-09-01 21:33:37 +00:00
tastybento e23207df2f
Merge pull request #2181 from BentoBoxWorld/ru-translation
Update ru.yml
2023-09-01 14:28:46 -07:00
tastybento 0f6d5af1f8
Update ru.yml
New translation provided by @Moltanicaa https://github.com/Moltanicaa
2023-09-01 14:25:07 -07:00
BONNe da2daae990
Fixes a bug with incorrectly indexed pages +1 (#2178)
* Fixes incorrect element displays in pages 1+.

The issue was introduced by #585f720f6f4a7e9480af5065469c92e23e5a987e which reduced complexity too much.

Fixes #2177

* Reformat and reduce complexity properly.

* Rework logic for getting number of same button types in the panel

* Add more comments.

* Add proper deprecated annotation.
2023-08-29 21:19:33 +03:00
tastybento aed78038ef Fix issue with NPE when Op does Command Ranks and does not own island
Related to #2170

Added a test class for CommandRankClickListener and reworked the logic.
2023-08-17 20:42:17 -07:00
tastybento 1228da131f
Merge pull request #2167 from BentoBoxWorld/ready_commands
Adds a config section in BentoBox to run commands when it is loaded
2023-08-15 17:26:34 -07:00
tastybento 08d73f232b Adds a config section in BentoBox to run commands when it is loaded
Commands are run as console.
2023-08-15 17:25:26 -07:00
tastybento 06ccb8a5e0 Fixed issue with demoting or promoting above yourself.
Addresses #2164

Added test class to check command. Added new error text.
2023-08-10 21:57:41 -07:00
tastybento 768988e803 Revert "Update ADDON.md - added Parkour"
This reverts commit fd91ab596b.
2023-08-06 09:18:45 -07:00
tastybento fd91ab596b
Update ADDON.md - added Parkour 2023-08-06 09:16:15 -07:00
tastybento 397f9e4700 Added more JavaDoc for API usage 2023-08-01 09:43:47 -07:00
tastybento 1b2748f920 Version 1.24.2 2023-08-01 09:43:31 -07:00
tastybento 78092aa3e9
Merge pull request #2158 from BentoBoxWorld/develop
Version 1.24.1
2023-07-29 11:40:08 -07:00
tastybento 475e67c447 Revert "Refactor YamlDatabaseHandler to reduce continue statements"
This reverts commit fec43adc35.
2023-07-24 18:27:06 -07:00
tastybento 2ad7796582 Refactor to reduce complexity 2023-07-22 16:20:21 -07:00
tastybento 3e1dc81592 Minor code smell reduction 2023-07-22 16:03:55 -07:00
tastybento fec43adc35 Refactor YamlDatabaseHandler to reduce continue statements
in a loop to just one.
2023-07-22 15:52:15 -07:00
tastybento 7a21aba3a8 Added more JavaDoc to ConfirmableCommand 2023-07-22 12:00:43 -07:00
tastybento 62a3a8d6fa Fixed NPE when getting UUID for unknown name.
Added test methods for PlayersManager
2023-07-22 12:00:10 -07:00
tastybento 1a0a314ece Added ClosestSafeSpotTeleport class
Removed code from the main class that was not being used. e.g.,
failureRunnable.
2023-07-20 15:09:14 -07:00
tastybento 2512a892fd Added clearInline Mocks just in case. 2023-07-20 00:38:58 -07:00
tastybento 850939f3a8 Fix issue were glow was not removed.
If glow was applied to a panel item, but then the glow setting was set
to false, the glow was not removed from the panel item.

Relates to https://github.com/BentoBoxWorld/AOneBlock/issues/326
2023-07-20 00:27:27 -07:00
tastybento 8ce78c8fe0 Added DefaultPasteUtil test class 2023-07-15 17:55:48 -07:00
tastybento 2dfd136591 Added test class 2023-07-15 16:55:59 -07:00
tastybento 357a8fdc5f Added test class for BlueprintEntity 2023-07-15 16:45:51 -07:00
tastybento a0b349ec27 Complexity reduction 2023-07-15 16:27:31 -07:00
tastybento 585f720f6f Reduce complexity 2023-07-10 22:48:33 -07:00
tastybento a6bb78c80f Refactor to reduce complexity 2023-07-10 22:25:30 -07:00
tastybento c5baa1d0eb Refactor to reduce complexity 2023-07-10 22:08:02 -07:00
tastybento 988ace8329 Update to latest Jacoco 2023-07-10 21:18:58 -07:00
tastybento 170c31fcec Judge location of damage by location of entity not user 2023-07-09 15:07:37 -07:00
tastybento 211d636bfb Remove dependency on apache util 2023-07-08 15:02:50 -07:00
tastybento 4ef974fbde Better error for unknown icon material, and test class 2023-07-08 08:52:20 -07:00
tastybento 9a464ab795 Minor refactor - use containsKey method. 2023-07-08 08:39:56 -07:00
tastybento 39a6fc5006 Refactor, use if statements instead of switches 2023-07-08 08:39:00 -07:00
tastybento 325c9c2351 JavaDoc fix 2023-07-08 08:38:30 -07:00
tastybento 30d29c6ff9 Refactor: Merge switch values 2023-07-08 08:38:20 -07:00
tastybento 2c8db2a797 Use any() which now is a varArg tester. Remove deprecation 2023-07-08 08:37:10 -07:00
tastybento df45fca562 Refactor - merge List creation and addAll into one 2023-07-08 08:36:29 -07:00
tastybento df6fb7d716 Fix JavaDoc 2023-07-08 08:35:31 -07:00
tastybento 54139b60f0 Remove unneeded return 2023-07-08 08:35:22 -07:00
tastybento 74a2e9d3c8 Change OP to op 2023-07-08 08:33:51 -07:00
tastybento 5953df9dbb Version 1.24.1 2023-07-08 08:33:24 -07:00
tastybento 363b23a049
Merge pull request #2152 from BentoBoxWorld/develop
Version 1.24.0
2023-07-03 16:27:37 -07:00
tastybento 9334858675 Added IslandDeletehomeCommand test class 2023-07-03 16:11:37 -07:00
tastybento 7d25aff28f Added test class for DefaultPlayerCommand 2023-07-02 15:52:20 -07:00
tastybento f47e87af82 Added test class for PanelItem 2023-07-02 15:10:23 -07:00
tastybento 6d3349a0f9
Merge pull request #2153 from BentoBoxWorld/delete_manager_refactor
Put island deletion under one class manager
2023-07-01 13:18:28 -07:00
tastybento 9bd0104a3d Put island deletion under one class manager
The goal is to make BentoBox less of a monster class and have fewer
dependent classes.
2023-07-01 12:49:18 -07:00
tastybento 7b8e18afff Adds Chiseled Bookshelf protection #2136 2023-07-01 10:04:05 -07:00
tastybento f23ebec34e Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2023-06-30 18:36:06 -07:00
tastybento 9c6cc78c32 Adjust priority of PlayerInteraction event listener to NORMAL
This may help avoid mob shop clashes.
2023-06-30 18:35:55 -07:00
tastybento c28ecb1c74 Add null check. Not really needed but make Sonar happy. 2023-06-29 17:29:04 -07:00
tastybento cbed9ac831
Merge pull request #2150 from BentoBoxWorld/mv_remove_on_disable
Unregisters worlds with MV on shutdown.
2023-06-28 21:03:09 -07:00
tastybento e846d26fab Fix test
Test doesn't do anything though...
2023-06-28 21:02:39 -07:00
tastybento 4c0cb9f17c Unregisters worlds with MV on shutdown.
Fixes #2149
2023-06-26 22:08:49 -07:00
tastybento 0038c3f2b7 Remove boolean return for listener. Check event cancelation instead. 2023-06-24 15:22:23 -07:00
tastybento 66d27e7070
Merge pull request #2148 from BentoBoxWorld/sculk_cal
Adds calibrated sculk sensor to protection
2023-06-24 12:55:36 -07:00
tastybento 4b0c5ec894
Merge pull request #2147 from BentoBoxWorld/suspicious_brushing
Protect against brushing
2023-06-24 12:12:00 -07:00
tastybento 1a416d9a23 Fixes #2137 adds calibrated sculk sensor and test class 2023-06-24 12:11:08 -07:00
tastybento 74e053ef8a Protect against brushing
Fixes #2138
2023-06-24 11:31:14 -07:00
tastybento 2ed2492ce3
Merge pull request #2146 from BentoBoxWorld/Ignore_waxed_signs
If sign is waxed (not editable) then no check is required
2023-06-24 11:13:33 -07:00
tastybento 2b4e1dd5eb Update to 1.21.1 to get isWaxed API
Remove 1.20 from server compatibility as a result.
2023-06-24 11:11:51 -07:00
tastybento 89a9b13d81 Hidden commands can be seen by console, but are hidden from players 2023-06-24 10:40:44 -07:00
tastybento b50063685f If sign is waxed (not editable) then no check is required 2023-06-24 10:31:51 -07:00
tastybento 01313def01
Merge pull request #2139 from ThexXTURBOXx/patch-1
Update GitHub actions to NodeJS 16
2023-06-24 10:17:12 -07:00
tastybento 2805e5889b
Merge pull request #2141 from BentoBoxWorld/bbox_perms_command
Bbox admin perms command
2023-06-24 10:16:07 -07:00
tastybento a7ffb79f1d
Merge branch 'develop' into bbox_perms_command 2023-06-24 10:15:34 -07:00
tastybento 0856d48470 Proactively clear Users before tests just in case. 2023-06-24 10:13:11 -07:00
tastybento 4d295068cf Fix onlyConsole usage and added test class 2023-06-24 09:45:40 -07:00
tastybento bcb4ed28b8
Fixes #2142 (#2144)
Fixes the logic and adds a bunch of tests to prove the logic works.
2023-06-21 08:15:53 +03:00
tastybento d6d86d39d2
Merge pull request #2143 from BentoBoxWorld/register_other_worlds
Add API to enable gamemodes to register ownership over additional worlds
2023-06-20 21:06:14 -07:00
tastybento c6a8f7c095 Add API to enable gamemodes to register ownership over additional worlds 2023-06-20 21:03:47 -07:00
tastybento 372f3f14c6 Rename getter to be more generic 2023-06-19 09:45:09 -07:00
tastybento 393a0131f9 Improved IslandInfoEvent to include the calling addon.
This makes it easier for listeners to determine if they should react or
not.
2023-06-19 09:38:49 -07:00
tastybento c0beba7467 Formatting 2023-06-19 09:38:00 -07:00
tastybento 5c2166fc93 Add some NPE protection for variable substitutions 2023-06-19 09:37:14 -07:00
tastybento a90a00b09b Fix new permission tests 2023-06-19 09:36:45 -07:00
tastybento 09c60f24cf Add IslandInfoEvent to allow addons to add to the island info command 2023-06-18 20:58:52 -07:00
tastybento b512975b13 Updated BentoBox perms in plugin.yml to add perm command 2023-06-18 13:55:50 -07:00
tastybento e9067cfb28 Add bbox admin perms command and adjust perms for commands
Made some admin commands have their own perm so that they can be
controlled  individually by permissions.
2023-06-18 13:55:21 -07:00
tastybento 5a52978803 Add perms test for admin teleport command 2023-06-18 13:35:16 -07:00
tastybento b64015e3b6
Update ServerCompatibility.java
Added 1.21.1
2023-06-15 18:43:17 -07:00
Nico Mexis c000da6e6f
Update GitHub actions to NodeJS 16 2023-06-11 18:50:48 +02:00
tastybento 2672ee5d1b Handle Boats and other Materials for 1.20. Uses Tags a lot.
May break compatibility with older server versions.
2023-06-10 21:31:39 -07:00
tastybento d25d1713f8 Adds protection for sign editing for 1.20
Fixes #2135
2023-06-10 15:49:10 -07:00
tastybento 6932ce3759 Added support for 1.20 2023-06-10 10:03:17 -07:00
tastybento 38ddeff369
Merge pull request #2133 from BentoBoxWorld/2132_IslandNameEvent
Add new IslandNameEvent and test class for command
2023-06-06 21:00:36 -07:00
tastybento e4a3891590 Add new IslandNameEvent and test class for command 2023-06-06 20:38:59 -07:00
tastybento 7bb1907897 Version 1.24.0 2023-06-06 18:41:06 -07:00
tastybento 5f6b91bc64 Version 1.23.3 2023-05-28 12:11:27 -07:00
tastybento fdc03df298
Merge pull request #2129 from BentoBoxWorld/develop
Version 1.23.2
2023-05-28 11:52:07 -07:00
tastybento eb76d00f80
Merge branch 'master' into develop 2023-05-28 11:39:17 -07:00
tastybento a7b1028496
Merge pull request #2128 from DustyGanteng/DustyGanteng-patch-1
Update en-US.yml
2023-05-28 11:37:01 -07:00
tastybento 9ffc838f2d
Merge pull request #2125 from zrdzn/patch-1
Fix typo in config
2023-05-28 11:35:40 -07:00
Dusty d0d01dbc2b
Update en-US.yml
Fixed weird remove reset count  message
2023-05-21 23:42:47 +07:00
tastybento 8f954090e7 Fixes VisitorKeepInventory to disregard coops and trusted
#2126
Also fixes some tests.
2023-05-13 09:20:47 -07:00
zrdzn 84adfaa622
Fix typo in config 2023-05-04 13:48:53 +02:00
tastybento f05d4dc57f Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2023-04-22 17:43:41 -07:00
steve4744 e991f09921
replace hardcoded file separator (#2121) 2023-04-21 13:05:00 +03:00
tastybento f52150ec1d Version 1.23.2 2023-04-18 16:22:15 -07:00
tastybento ce5830e2ad
Merge pull request #2120 from BentoBoxWorld/develop
1.23.1 release - hot fix
2023-04-18 16:20:00 -07:00
tastybento 577e476796 Remove console spamming debug. 2023-04-18 16:18:09 -07:00
tastybento 8d0fcf0169 Version 1.23.1 2023-04-18 14:33:20 -07:00
tastybento 0ee9ff4f10
Merge pull request #2118 from BentoBoxWorld/develop
Version 1.23.0
2023-04-15 11:03:45 -07:00
tastybento 84e6523b9d Only disable Addons, not Pladdons
Plugin disabling is done by the server.

Fixes #2117
2023-04-08 16:20:35 +01:00
tastybento 5c5077a6b5 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2023-04-08 15:49:36 +01:00
tastybento 9ee5bcbcf2 2023 2023-04-08 15:49:27 +01:00
BONNe c235abbfa6
Disable chunk generation on rengeneration (#2114)
The SimpleWorldRegenerator generated world new chunks that were not generated before. 
It is not necessary, as regeneration should happen only on used chunks.
2023-04-07 14:39:27 -07:00
tastybento b4737f104c Implements HARVEST and CROP_PLANTING protection flags
Requested by #2113
2023-04-07 20:51:59 +01:00
tastybento ba19b08f4c Version 1.23.0 2023-04-07 17:28:02 +01:00
tastybento 073f3a49d5
Adds API to enable translations to be set and specific API for flags (#2109)
This was a request on Discord so that plugins can set flags and set the
name and lore for them too.
2023-03-26 10:06:38 -07:00
tastybento 1a293a6936
Implements NMS for pasting and supports older versions of server (#2110)
* Implements NMS for pasting and supports older versions of server

* Remove trial code that shouldn't have been committed.

* Remove trial code that shouldn't have been committed.

* Remove 1.19.3 support.

* Use 1.19.4 in POM
2023-03-26 10:05:54 -07:00
tastybento 85a7375004 Remove warnings on loading addons (not Pladdons) 2023-03-25 15:50:39 -07:00
tastybento 25e601b26c Now with proper commit... 2023-03-25 09:08:34 -07:00
tastybento 907268c2f4 Use Bukkit loadPlugin for loading Pladdons
Provides compatibility with Paper. Ensured bytecode rewriting is done
when code is loaded.
2023-03-25 09:08:18 -07:00
tastybento 6894695024 Clean up test classes; use latest API 2023-03-23 11:02:08 -07:00
tastybento 233e5ceb65 Add SHULKER_TELEPORT flag English text. 2023-03-19 18:49:16 -04:00
tastybento 5d67dbe5e3 Javadoc clarification 2023-03-18 17:46:56 -04:00
tastybento 2cb561b943
Update to 1.19.3 for NMS (#2107)
* Update to 1.19.3 for NMS

Clean up some imports and deprecation notifications.

* Added 1.19.4 to compatible servers
2023-03-18 10:28:19 -07:00
tastybento ca604f893c
Relates to #2104. Adds user.displayName() as a placeholder option (#2108)
* Relates to #2104. Adds user.displayName() as a placeholder option

* Fix test
2023-03-18 10:27:50 -07:00
tastybento 3285ef1ad2 Fix and enhancement for #2105 2023-03-17 20:45:15 -07:00
tastybento 70f044265a Fix test - 54 flags 2023-03-13 20:42:26 -07:00
tastybento 2940d2e492 Add cheat flag to prevent Endermen from teleporting
Fixes #2105
2023-03-13 20:17:03 -07:00
tastybento c31458f8e6 Remove offending blueprint original particle shower which doesn't work
Never did anything useful anyway. Fixes #2100
2023-03-04 23:36:32 -08:00
tastybento 2b40ae47d7 Update Spigot version 2023-03-04 23:35:39 -08:00
tastybento 2c283ae3a9
Javadoc and other cleanup from code analysis (#2099) 2023-02-26 17:48:37 -08:00
tastybento f02b4d5011
Merge pull request #2096 from BentoBoxWorld/void_world_deletion 2023-02-26 07:14:39 -08:00
tastybento 6925a788f3 Put back the deprecated way to delete and adds a GameModeAddon API
to declare which type to use. AcidIsland will need updating to use this
API.
2023-02-24 17:33:53 -08:00
tastybento 1b81e74f4c Evaluates banned commands with spaces
Addresses #2092
2023-02-20 09:59:26 -08:00
tastybento 999ca242d7 Use Eclipse NonNull annotation instead of JetBrains NotNull 2023-02-19 08:41:21 -08:00
tastybento 7d0eb3586d Make events public so they can be instantiated in 3rd party JUnit tests
I need to create "fake" events to test addons but unless they are public
I cannot. All Bukkit events are public, so this should be fine.
2023-02-15 16:01:52 -08:00
tastybento a713b7be07 Version 1.21.1 2023-02-12 15:21:32 -08:00
tastybento fe888a8d0e
Merge pull request #2069 from BentoBoxWorld/develop
New version
2023-02-12 15:00:01 -08:00
tastybento 45f2dca414 Fixes max team size checks.
Fixes #2034
2023-02-12 12:08:38 -08:00
tastybento 8276406f9f Fix runCommands method
Note to future self, don't remove Util methods because someone might be
using them!
2023-02-12 08:22:22 -08:00
tastybento 5eaf54e905 Added AdminBlueprintLoadCommand test class 2023-02-11 15:47:52 -08:00
tastybento 04545e676f AdminBlueprintDeleteCommand test class. 2023-02-11 14:57:42 -08:00
tastybento 08a1ad8126 Added AdminBlueprintCopyCommand test class 2023-02-11 14:46:34 -08:00
tastybento 79d56a5cef Added test class for AdminBlueprintSaveCommand 2023-02-11 12:03:16 -08:00
tastybento f93c49184f Added AddonDescription test class 2023-02-11 10:11:51 -08:00
tastybento 0f0b5aacc2 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2023-02-11 09:09:36 -08:00
tastybento e978b29d85 Bug fix: Use same seed for seed world as main world. 2023-02-11 09:09:25 -08:00
tastybento 6c1bcdec05 Added test class for IslandChunkDeletionManager 2023-02-08 20:23:19 -08:00
tastybento a793a33758 Minimal test class for BlueprintPaster 2023-02-08 19:53:16 -08:00
tastybento 1783bb7058 Fixes bug where pasting was happening twice 2023-02-08 19:21:50 -08:00
tastybento a703499817 Added test class. 2023-02-08 19:16:32 -08:00
tastybento 224be307a0 Fix JavaDoc errors 2023-02-08 18:40:03 -08:00
tastybento b62a1862a2 Add ${argLine} to surefire plugin
This should fix the coverage reports.
2023-02-08 17:17:12 -08:00
JamesMCL44 3623537d84
Update zh-HK.yml (#2085)
Update to latest locale for latest plugin version
2023-02-06 12:56:02 -08:00
tastybento 6c8ccd2a57 Attempt to get coverage report from SonarCloud 2023-02-05 17:23:52 -08:00
tastybento f1eaa0fcde Refactor to reduce complexity 2023-02-05 16:23:23 -08:00
tastybento 9ccdcceaba Refactor to reduce complexity 2023-02-05 16:19:05 -08:00
tastybento 35ece03e5b Refactor to reduce complexity. 2023-02-05 16:08:23 -08:00
tastybento 760bc2e086 Define a constant instead of duplicating literals 2023-02-05 16:03:43 -08:00
tastybento caf994738e Defined a constant instead of duplicating "CHEST" 7 times 2023-02-05 16:00:06 -08:00
tastybento f00320095d Added since and forRemoval to Deprecated annotations. 2023-02-05 15:55:51 -08:00
tastybento 46d20f34ff Remove statements that are always true 2023-02-05 15:50:04 -08:00
tastybento df910e96d3 Refactor to reduce complexity. 2023-02-05 15:46:32 -08:00
tastybento f29e78557a Change location of seed world data.
Fixes #2087
2023-02-05 14:11:59 -08:00
tastybento 2d8b3074a9 Minor refactor to remove continue from for loop 2023-02-05 10:39:10 -08:00
tastybento ad2541963e Minor instanceof casting change 2023-02-05 10:38:09 -08:00
tastybento 2c1a018071 Clarify nonnull on Location 2023-02-05 10:37:43 -08:00
tastybento c6fc42846a Remove unused imports 2023-02-05 10:15:34 -08:00
tastybento 0e253f3ba5 Clarify that parameter is non-null 2023-02-05 10:10:25 -08:00
tastybento a68436fde1 Remove Optional from parameter. 2023-02-05 10:05:57 -08:00
tastybento 1d28979d5c Refactor to reduce complexity 2023-02-04 19:08:09 -08:00
tastybento 187132bc1d Refactor to reduce complixty. Added comments. 2023-02-04 19:00:18 -08:00
tastybento 3331ffac4e Refactor to reduce complexity. 2023-02-04 18:49:13 -08:00
tastybento f9e57898ab Refactor to reduce complexity. 2023-02-04 18:45:05 -08:00
tastybento 0c29d0b5dd
Uses seed worlds for deletion purposes (#2084)
* Uses seed worlds for deletion purposes

* Use Flat type

* Implement the superflat saver

* Fix nullable issue

* Fix tests.

* Fix code smells
2023-02-04 18:37:06 -08:00
tastybento 7040047fcb Fix broken test for IslandRespawnListener 2023-02-04 10:37:02 -08:00
BONNe ea6ca3d26b
Add variable parsing on reference text (#2073)
This solves the issue in customizable GUI's where variables are defined directly into the panel button name and description instead of providing the link to the locale. 

Not all users want to specify all text into a locale, and they would not get parsed variables just because of that.
This change fixes it and it does not give any bad situations even if the reference is just missing text in the locale, as there will be nothing for parsing.
2023-01-31 18:03:37 +02:00
tastybento a604d5cf82
Add [owner] in command placeholders #2080 (#2081) 2023-01-31 07:13:01 -08:00
tastybento bbb8207cf0 Teleportation was processed for entities even if it was switched off
Addresses #2078

Added test class to cover some of the logic in the teleportation class.
More tests can be written to cover the various outcomes.
2023-01-28 16:04:52 -08:00
BONNe e89970b49b
Remove 1.18.2 from supported version list. (#2074)
* Remove 1.18.2 from supported version list.

1.18.2 support was removed with changes in commit 056cff4b6f

Also, mark 1.19, 1.19.1 and 1.19.2 as incompatible. It happens because of GSon library changes that prevents bentobox compiled with Spigot 1.19.3 to run on older versions.

* Fixes GSON crashes introduced by migrating to 1.19.3 api.
2023-01-06 18:29:37 +02:00
tastybento 0de2c3a7b6 Update JavaDoc to indicate return value is immutable 2023-01-05 21:30:19 -08:00
tastybento 0f89de6c2a Make list mutable. 2023-01-05 21:16:50 -08:00
tastybento 8e40bf9dcf Added more validation for addon.yml and test class 2023-01-02 09:31:16 -08:00
tastybento 10a73e66b4 Added explicit @since annotation 2023-01-01 09:25:09 -08:00
tastybento 3746349e22 The protected modifier for constructor should be enough.
Constructors of abstract classes can only be called in constructors of
their subclasses. So there is no point in making them public.
2023-01-01 09:22:41 -08:00
tastybento 0e5db0f41f Quiet excessive warnings from JavaDoc plugin 2023-01-01 09:19:20 -08:00
tastybento 1262a08a22 Removed deprecated method isPlayer(User) 2023-01-01 08:43:31 -08:00
tastybento f545a4576e Remove unused comment 2023-01-01 00:19:10 -08:00
tastybento ab434b63fa Completed tests for User class 2023-01-01 00:17:30 -08:00
tastybento 6b88d31762 Bug fix for particle validation 2023-01-01 00:02:59 -08:00
tastybento 93ca9fb0e6 Correct nullable tags 2022-12-31 18:33:49 -08:00
tastybento 0bffe3806a Fix tests 2022-12-31 18:19:32 -08:00
tastybento 872940011a Loads of minor code smell fixes
Mostly replacing instanceof check and cast with e.g. 'instanceof Player
player'
Replaced some "switch" statements by "if" statements to increase
readability.
Used the primitive boolean check to avoid potential NPEs.
Reordered field modifiers to comply with the Java Language
Specification. Mainly @NonNull position.
2022-12-31 18:19:25 -08:00
tastybento 056cff4b6f
Code clean up from Sonar Cloud analysis (#2068)
* Code clean up from Sonar Cloud analysis

* Fix tests

* Remove code smell

* Rename "island" which hides the field declared at line 25.

* Removed code smells.

* Rename variable record to rec

Renamed "record" variable to not match a restricted identifier.
Restricted Identifiers should not be used as identifiers. "record" is
using in Java 16.

* Added private constructor to prevent instantiation of static class

Changed variable name to rec instead of restricted "record".

* Remove Blueprint code smells.

* Use a record for database settings constructor

Code smell: Methods should not have too many parameters. I'm not sure
what methods are using this class though.

* Update MyWorlds version

The POM for MyWorlds is invalid and causes a warning, but this still
persists with this version.

* Extracted nested try block into a separate method.

Makes it clear when reading the code what might be caught

* Extracted nested try block into a separate method.

* Fixed JavaDoc /** instead of just /*

* Extracted nested try block into a separate method.

* Refactored to not assign loop counter from within the loop body.

* Better delete option. With results.

That said, this is legacy code to handle an issue that occurred a long
time ago and this whole set of code can probably be removed.

* Catch Exceptions not Throwable

* Log error with BentoBox logError

* Use computeIfAbsent

Using these instead leads to cleaner and more readable code.

* User can no longer be null

* Added the missing @Deprecated annotation and @since ref

* Added @since reference

* Merge if statements

* Use BentoBox error logging.

* Added JavaDoc @since

* Remove deprecated class and move used class

* Remove deprecated WoodType and use Type.

* Remove unused import

* Extracted nested try block into a separate method.

* Comment empty default statement

* Clean up logic; avoid switch

* Use Java instead of Guava

* private constructor to hide the implicit public one.

* Private constructor to hide the implicit public one.

Merged if statement.

* Add comment

* if merge

* Make variable constant

* Remove unused imports

* Remove deprecated and unused method

* Remove unused import

* Typo

* Remove instanceof and cast

* Remove superfluous null check

* Put constant at bottom of file because @BONNe likes it there.

* Simplify particle validation code
2022-12-31 16:41:17 -08:00
tastybento 0183380b82 Edited US English text entries
Supercedes #1033
2022-12-30 11:22:01 -08:00
tastybento d541e7054c Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2022-12-30 11:20:31 -08:00
tastybento 36ae34da5d
2049 trees growing outside range (#2050)
* Fixes #2046. Handles null players for placeholders.

* Remove unused import

* Fixes #2049 for adjacent island spaces

This also deletes blocks in the event rather than setting them to AIR as
was done previously.
2022-12-30 09:37:18 -08:00
tastybento a7869832f1 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2022-12-29 11:31:18 -08:00
tastybento 18983e5570
Code cleanup (#2065)
* Cast operands to (double)

* Code clean up from static analysis

* Revert the GitHubAPI version change because it didn't work.
2022-12-29 10:01:25 -08:00
tastybento ebd9215c56 Cast operands to (double) 2022-12-28 12:56:05 -08:00
tastybento 70262be896 Update to latest AuthLib 2022-12-28 12:42:58 -08:00
tastybento bde294e0e6 Updated some dependency versions
https://github.com/BentoBoxWorld/BentoBox/issues/2064
2022-12-28 12:37:33 -08:00
tastybento e7f1269443
Support for 1.19.3 (#2060) 2022-12-11 17:51:52 -08:00
tastybento e2b5545f3f Expand remove mobs world setting to include respawning protection
Addresses #2058

We already have a option to protect against hostile mobs when logging in
or teleporting, but not respawn. This adds protection to that. Admin can
turn off the option if they want it more hard core.
2022-12-04 21:06:14 -08:00
tastybento 69fa951907 Handles null player world better on joining server
In theory, this should not be required, but it seems that a player's
world might be null in some edge cases. Alternatively, the Util.getWorld
function can return a null if the player was in a nether or end but that
does not have a corresponding overworld.
Addresses #2057
2022-12-04 20:36:41 -08:00
tastybento 7aa1e2da89 Fix to handle kelp and bamboo spread.
Addresses #2053. This is actually due to a server bug
https://hub.spigotmc.org/jira/browse/SPIGOT-5312. This change uses the
BlockSpreadEvent to trap kelp and bamboo and bamboo sapling.
2022-11-26 17:36:19 -08:00
tastybento 2629e940c7
2046 null player placeholder support (#2048)
* Fixes #2046. Handles null players for placeholders.

* Remove unused import
2022-11-13 08:49:22 +02:00
tastybento 1c4fb76828
Fixes #2045. Hides invisible team members from the count. (#2047) 2022-11-12 02:28:09 +02:00
BONNe acb984ee90
Fixes super-flat clearing
Superflat was not working properly. It was always clearing just 1 (first) chunk because it referenced the chunk from the first event that created the clearing task.
The issue should be fixed.
2022-11-01 23:04:59 +02:00
BONNe d9ad9f158b
Fixes a mistake in clean super flat listener
There was a logical mistake in checking if a clean super flat should work.
2022-11-01 20:10:45 +02:00
BONNe 87a6799cd1
Fixes clean super-flat issue from 1.19 worlds
In 1.19 worlds it was fixed that super-flat worlds are generated at min-height, instead at 0.
This change keeps the previous super-flat chunk generation until 1.18 is dropped.
2022-11-01 18:25:39 +02:00
tastybento d50208c0e1
Add and fix tests for end gateway and portal. (#2044) 2022-10-31 17:59:04 -07:00
BONNe a22ce33175
Mark end portals and gateways as unsafe location. (#2042)
The end portals and gateways teleports players to a different dimension upon player touches it. So teleportation should consider these positions as unsafe, otherwise player will be teleported to the different dimension instantly.

Fixes #2040
2022-10-19 11:41:00 +03:00
Irmo van den Berge ac4922534e
Register BentoBox World Generator option with MyWorlds (#2039)
Signed-off-by: Irmo van den Berge <irmo.vandenberge@ziggo.nl>
2022-10-19 11:27:12 +03:00
BONNe b48a5a73b8 Merge remote-tracking branch 'origin/master' into develop-fix
# Conflicts:
#	pom.xml
2022-10-11 09:55:16 +03:00
BONNe 065f4ba4bd Spigot does not support parallel stream processing for placing blocks and entities in the world.
This is a hot-fix release.
2022-10-11 09:37:59 +03:00
BONNe e47890c16e Merge remote-tracking branch 'origin/develop' into develop 2022-10-04 15:54:34 +03:00
BONNe 866dff6244 Fixes missing FLAG check on clicking minecart entities
Players could open minecart entities, even without flag requirement.

Fixes #2031
2022-10-04 15:54:18 +03:00
BONNe 369001d368
Rework SQL database implementation. (#1993)
This change switches from plain JDBC driver implementation to a HikariCP Pool implementation.

Pool Implementation is complete for PostgreSQL and SQLite, while MariaDB and MySQL implementation still uses JDBC drivers, however with HikariCP pools.

Also, I added extra properties for SQL databases, where users could specify their own datasource properties.
2022-10-04 12:31:51 +03:00
BONNe 4792ff3f62 Init version 1.22 - Customizable GUI's version. 2022-10-04 12:11:17 +03:00
BONNe 1a59ca7785 Merge remote-tracking branch 'origin/develop' into master
# Conflicts:
#	pom.xml
#	src/main/java/world/bentobox/bentobox/blueprints/BlueprintPaster.java
#	src/main/java/world/bentobox/bentobox/database/objects/Island.java
#	src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java
2022-10-04 11:56:32 +03:00
BONNe b263c92c13
Use Spigot only events. (#2029)
I missed that EntityMoveEvent is only Paper. Interesting why Spigot does not have such...

Fixes #2028
2022-10-01 01:50:32 +03:00
BONNe 52bca66149 Add better config description for safe-spot-search-range. 2022-09-30 21:46:01 +03:00
BONNe f01e5540c7 Deprecate old player teleportation code. 2022-09-30 21:41:02 +03:00
BONNe bf87cca754 Implement proper entity teleportation between dimension.
Teleportation via portals for entities was in a mixed state. It was not fully implemented and not fully prevented. Especially when portal linking was enabled.

Now I implemented world settings flag: ENTITY_PORTAL_TELEPORT.
Enabling this flag will allow entities to use portals to switch dimensions.

Fixes #2023 and #966
2022-09-30 21:33:17 +03:00
BONNe 682d835961 Add all dimension checks for methods. 2022-09-30 14:34:28 +03:00
BONNe 4458d16274 Fixes incorrect condition for missing island check. 2022-09-30 14:34:12 +03:00
BONNe d197ce8bea Add class description. 2022-09-30 02:34:46 +03:00
BONNe aa7abe02bf Teleport island-less player to the spawn if he exits nether 2022-09-30 02:33:07 +03:00
BONNe 97c4cf883f Implement proper end exit portal handling.
When players will exit the end-gate, they will be teleported to proper island, instead of world spawn point.

The only way how players can be teleported to the spawn is if they do not have islands.
2022-09-30 02:25:44 +03:00
BONNe eb5147e710 Change to direct PlayerTeleportListener#portalProcess method call. 2022-09-30 01:31:05 +03:00
BONNe 755aeb866e Rework player teleportation.
I reworked the classes and some teleportation operations. Now teleportation should find the closest available spot, instead of always being the highest block at original Y location. Part of #1994.
Also, I fixed an issue that portals stopped working if some conflicting options were enabled. Now portals will not work only if nether is disabled in config.
2022-09-30 01:26:31 +03:00
BONNe 173808b787
Adds glow berries protection. (#2027)
Uuups... someone forgot to add them.

Fixes #2020
2022-09-29 18:58:53 +03:00
BONNe 35ce1a7d81
Rework blueprint and blueprint bundle names. (#2026)
* Fixes issue when blueprint clipboard was stuck after saving.

The issue was that Map#putIfAbsent still creates a new task, even if object is already in map. The usage requires to use Map#computeIfAbsent.

* Rework blueprint and blueprint bundle names.

There was an issue with using non-english characters in blueprint names. It was not possible, as all chars for names were converted to lower cased english letters. It included display names.

I reworked it a bit and now it should be possible to set non-english names for bundles and blueprints.

Fixes #1954
2022-09-29 18:44:07 +03:00
Huynh Tien 12926f9ee7
setBlock & setEntity as CompletableFuture (#2019)
* setBlock & setEntity as CompletableFuture

* use collectingAndThen toArray
2022-09-05 09:28:16 +03:00
BONNe 9088ea4b80 Fix failing unit-test 2022-08-26 12:43:05 +03:00
BONNe 50bc236bbc Fix failing unit-test 2022-08-26 12:27:11 +03:00
BONNe c7b48b3d2a
Update Spigot 1.19.2. (#2017)
Add 1.19.2 compatibility.
2022-08-26 12:17:19 +03:00
BONNe 6f791420e7
Proper dimension processing (#2015)
* Adds ProtectionBoundingBox for environment

Since minecraft introduced different island heights for different dimensions, protection bounding box were returned wrong area for nether and end dimensions.
This change adds a new method that allows to get proper protection bounding box for requested dimension.
Due to the fact, that nether and end islands can be disabled, then this method may return null.

Fixes #2014

* Adds BoundingBox for environment

Since minecraft introduced different island heights for different dimensions, bounding box were returned wrong area for nether and end dimensions.
This change adds a new method that allows to get proper bounding box for requested dimension.
Due to the fact, that nether and end islands can be disabled, then this method may return null.

Part of #2014

* Fixes Island#onIsland check for non-island worlds

Island#onIsland method was missing checks if island mode is enabled for requested dimension. It returned false positive situations in cases when island generation were disabled in nether or the end worlds.

* Fixes Island#inIslandSpace check for non-island worlds

Island#inIslandSpace method was missing checks if island mode is enabled for requested dimension. It returned false positive situations in cases when island generation were disabled in nether or the end worlds.

* Adds some helper methods in Island object.

- Island#getNetherWorld - returns the nether world or null
- Island#getEndWorld - returns the end world or null
- Island#getWorld(Environment) - returns world of requested environment or null
- Island#isNetherIslandEnabled - returns if nether is generated and nether islands are enabled.
- Island#isEndIslandEnabled - returns if end is generated and end islands are enabled.
2022-08-26 11:32:03 +03:00
BONNe 6c1399c2b4
Fixes wrong admin permission.
Default Amin command should have `admin` instead of admin.*
2022-08-14 20:12:15 +03:00
BONNe 44201afa1f Fixes an issue with missing parent permission check before command execution.
Implement a new method that recursively checks if player has access permission to all commands in whole hierarchy.

Fixes #2010
2022-08-03 19:00:42 +03:00
BONNe 64b4c43742 Revert 6e734fc343
The commit was wrong. Protection range should not be decreased, as that would mean that max X and Z blocks are not included in island protection bounding box.
This change is the same as 3c65194dfb but was missed when it was fixed.

Relate to #473
2022-07-25 00:16:16 +03:00
BONNe a59697c14e Fixes particle spawning for older API usage.
Border addon used still older BentoBox API. This change fixes it, and fixes API compatibility with older addons/plugins.
2022-07-14 11:29:46 +03:00
BONNe 5a527d4119 Add parent for admin team's commands.
This is a parity change with player command.
2022-07-13 12:35:20 +03:00
BONNe aed8caeb76 Tweak translation for skulk sensor and shrieker. 2022-07-10 01:44:15 +03:00
BONNe 927fcba15a Disables biome copying by default in blueprints.
Biome will be copied only if it is required by copy command parameter.

Fixes #1862
2022-07-07 00:08:15 +03:00
BONNe 9ec8730359 Prevents liquids from being placed from dispenser outside island area.
Fixes #1869
2022-07-06 23:55:17 +03:00
BONNe 9643f617b6 Implement a command that allows to reset island name.
Fixes #1879
2022-07-06 23:30:51 +03:00
BONNe 3784aa48d7 Disables podzol and coarse dirt from becoming path.
This protection reuses Flags.COARSE_DIRT_TILLING.

Fixes #1878
2022-07-06 23:00:20 +03:00
BONNe 2f244c0606 Fixes an issue when portals did not operate under 0.
Fixes #1977
2022-07-06 22:12:47 +03:00
BONNe f969f2721f Disable DynmapHook as it is not implemented.
Fixes #1995
2022-07-06 20:39:15 +03:00
Galić Mijo cbd063c9e8
Check if unique name contains chars not supported in regex expression (#1998)
* Check if unique name contains chars not supported in regex expression
Cannot start, contain, or end with special char, cannot contain any numbers.
Can only contain - for word separation

* Negation fix
2022-07-06 20:21:36 +03:00
BONNe db323390cf
Implements adding unknown default island flag values in settings. (#2001)
* Implements option to define non-existing flags in WorldSettings.

This change adds 2 new methods in WorldSettings:
* WorldSettings#getDefaultIslandFlagNames
* WorldSettings#getDefaultIslandSettingNames

These methods replace getDefaultIslandFlags and getDefaultIslandSettings methods.
Default implementation just reads values from replaced methods.

Fixes #1830

* Implement conversion from flag id to actual flag object.

Replaces flag assignment to new island based on flag id's.

Fixes #1830

* Switch from Flag object to String object in Island class.

This switch allows to keep flags that are not present in current BentoBox installation. Otherwise, unknown flags may cause an issues.

Fixes #1830

* Implement FlagBooleanSerializer.

This serializer converts input map of String, Boolean to map of String, Integer.

This map is used to read island setting flags, and integer value is not classic boolean values. (0 for true and -1 for false).

Fixes #1830
2022-07-06 20:20:08 +03:00
BONNe d8fa029ac9
Fixes an issue where Blueprint Pasting did not send or include nether and the end blueprints times. (#1979) 2022-06-16 22:44:38 +03:00
Sliman4 3cd5b05130
Fix Actions (#1992)
* Move to new EngineHub maven repository

* Remove unused WorldEdit dependency

* Use Java 17 in GitHub Actions

* Also change maven-compiler-plugin and maven-javadoc-plugin versions to 17
2022-06-16 14:57:42 +03:00
BONNe 1991dc7236
Fixes failing test
Increase flag listener counter.
2022-06-14 09:09:06 +03:00
BONNe c54358441d
Raid abuse fix (#1991)
* Implements new VISITOR_TRIGGER_RAID flag.

This world settings flag allows toggling if visitors can or cannot start a raid on an island they are visiting.

Relates to #1976

* Fixes abuse of Raid Mechanism and Mob Natural Spawn Rules.

Fixes to #1976

* Simplify raid abuse detection.
2022-06-14 02:42:48 +03:00
BONNe e41f5ac24f
Fixes a bug that prevented mobs to attack
Commit 85b52f4b introduced a bug that prevented entities to target any island member. This should fix it.
2022-06-14 02:33:35 +03:00
BONNe d984955af3 Fixes Range Display command crashes.
Spigot in 1.18 introduce new way how block particles should be displayed. However, BentoBox never fully implemented it.
To avoid such issues in-future, I added checks for each particle type that requires extra data object.

Fixes #1989
2022-06-14 01:18:52 +03:00
BONNe 85b52f4bfb
Implement new 1.19 materials and entities (#1990)
* Update to Spigot and Paper API 1.19 and Java 17

* Set minimal version to 1.18.

* Add breeding protection for Axolotl and Goats (1.18)
Add breeding protection for Frog and Allay (1.19)

* Clean up switch statement in BlockInteractionListener

* Add chest boat support to BreakBlocksListener.

Part of #1987

* Add powdered snow pickup with bucket protection.

* Add glow ink sac protection on signs.
Switch to SheepDyeWoolEvent as it now contains player variable.

* Clean up Hurting Listener

* Mark 1.16.5, 1.17 and 1.17.1 as incompatible.
Mark 1.18, 1.18.1 and 1.18.2 as supported.

Add 1.19 as compatible.
Part of 1987

* Add ChestBoat inventory protection.
Part of #1987

* Clean up LockAndBanListener

* Add MANGROVE_PRESSURE_PLATE protection.
Part of #1987

* Add Glow Item Frame protection to player interact event.

* Clean up MobSpawnListener

* Clean up ChestDamageListener

* Change from custom method to Tag.PRESSURE_PLATES to detect pressure plates.
Part of #1987

* Implement proper chest boat protection.
Part of #1987

* Move to 1.19 R1 world regenerator.
Part of #1987

* Add allay to the animal entity list.
Part of #1987

* Add axolotl and other fish scooping protection.
Part of #1987

* Fixes Bucket and Glass Bottle filling.

Buckets and bottles were not working since cauldron splitting by type. This change fixes that.

It also protects from filling bottles with water from water sources or waterlogged blocks.

* Remove 1.17.1 compatibility check for biome adapter.
Add cheezee 1.19.1 compatibility version.

* Fixes chest boat interactions.

Part of #1987

* Implement Allay protection.
New flag ALLAY is required to interact with allays.

Part of #1987

* Prevent visitors for being targeted by entities if ENTITY_ATTACK is enabled.

* Implement Sculk Sensor and Shrieker activation protection setting.

Part of #1987

* Add music discs to the LangUtilsHook
Part of #1987

* Fixes failing unit-tests.
2022-06-14 00:38:53 +03:00
BONNe 6f2a9929c0
Fixes Glowing Item frames protection (#1985)
* Fixes glowing item frame protection.
Glowing item frames were not protected.

Fixes #475

* Fixes check-style.
2022-06-05 21:52:37 +03:00
BONNe aad10ad74f
Move database dependencies to the Spigot Libraries. (#1982) 2022-05-29 22:56:50 +03:00
BONNe 345b9e2564
Fixes an incorrect <Material>:CMD-<number> parsing
The issue was that it used an incorrect value for parsing Material. Instead of stripped part[0] it used whole text with CMD in it.
2022-05-23 23:23:22 +03:00
BONNe 3ab7ac8484
Implement ability to create ItemStacks with Custom Model Data. (#1981) 2022-05-23 20:46:02 +03:00
Huynh Tien 0f815d8175
More abstract on Blueprint Paster (#1970)
* more abstract on Paster

* comment and stuff

* How about Impl

* ok, PasteHandler

* PasteUtil

* don't check other BlockState if there is one matched

* world as an argument

* forgot the impl

* createBlockData from BlueprintBlock

* fix remaining conflicts

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2022-05-03 18:39:27 +01:00
evlad 4ab579f2cd
Feat: Filtering spectators from visitors (#1974)
* support 1.18 negative y

* feat: visitor gamemode spectator check
2022-05-03 08:03:49 +03:00
Huynh Tien 6fba4bfbea
More abstract on World Regenerator (#1969)
* more abstract on delete chunks

* update NMS to 1.18.2

* at most abstract, requires only the island and the world

* it's weird that we can't use whenComplete

* rename to WorldRegenerator
2022-05-02 15:23:31 -07:00
evlad d07c1b5a8c
support 1.18 negative y (#1973)
Fixes an issue where blueprint starting block could not be placed in negative area.
2022-05-01 08:44:42 +03:00
Huynh Tien 3e0368fbab
fix null on invitedPlayer (#1972) 2022-04-28 15:39:39 +03:00
BONNe 385aed02cd
Update to next BentoBox version 2022-04-26 12:07:44 +03:00
BONNe c4c51d00e2
Fixes wrong protection bounding box (#1971)
#1966 fixed a bounding box size for the whole island, while the protection bounding box was still wrong.

Fixes https://github.com/BentoBoxWorld/BSkyBlock/issues/473
2022-04-26 12:06:50 +03:00
tastybento 928f1eb1eb
Version 1.20.1 (#1966)
* Version 1.20.1

* Added name of the addon causing the issue.

https://github.com/BentoBoxWorld/BentoBox/issues/1944

* Use world min-height for island bounding box.

* Fixes a bug when fallback could not use reusable

There was an issue in PanelItemTemplate that prevented fallback buttons to be "reusable" things.
The issue was that reusable items were not passed to the panel item reader.

* Adjusted test to try to avoid errors

* Fix for random test failures.

* Added 1.18.2 support

* Address unnecessary PVP reports on each teleport (#1948)

If a player is teleporting on the same island in the same dimension, it keeps spamming that PVP is enabled in dimension. 
It should be enough with sending messages when the player teleports to the island.

Fixes #1885

* Fixes bug with Safe Spot Teleport (#1951)

There was a bug that prevented finding a safe spot if all valid blocks were in height with the `startY` location.

Reported via discord.

* Fix Exception error reported by IDE

I am not sure why Eclipse is saying this is an error.

* Fix for kicking offline players

https://github.com/BentoBoxWorld/BentoBox/issues/1950

* Add an option in SafeSpotTeleport to cancel if fail (#1952)

There was no option to cancel teleportation if SafeSpotTeleport could not find a valid spot. This option could be used to avoid creating "backup" blocks in situations when teleportation is avoidable, f.e. visiting an island.

* Replace peplaceAll with replace

It does the same thing if the first argument is not a regex.

* Use constants for common strings

* Use constants for common strings

* Go back to replaceAll

This is required.

* Clearer paster (#1953)

* WIP - make easier to understand.

* Small refactor of paster to make it easier to understand

* Fix tabs to spaces. Sorry - new editor!

* Fix tabs to spaces

* Fix tab to spaces

* Improve team kick command (#1957)

The kick command has an unnecessary owner check. As command should be configurable by island owners, then limiting it to an owner is wrong.
Add a code that allows kicking only lower-ranked players.

Add message that shows who kicked from the island.
Add message that shows that rank does not allow to kick.

* Solve crashes with Addon#allLoaded call (#1959)

If some addon has code in Addon#allLoaded that crashes the call, then it did not disable addon as well as did not call allLoaded for every other addon that was left in the list.

This should be solved by adding an extra try-catch.

* using java 16 syntax (#1958)

* Fixes kick command (#1960)

PR #1957 broke kick command and noone could kick players from teams.
This should fix it.

* Fixes a bug with blueprint height (#1961)

Blueprint clipboard was preventing setting Y below 0 or above 255. 

The code was not adjusted to 1.18 changes.
Reported via discord.

* Fixes Lava Duplication Glitch (#1964)

Due to the fact, that Obsidian Scooping uses one tick delay to remove obsidian, a player with a bucket in hand and offhand duplicated lava. 
To avoid that, added an extra check that ignores the interact event if a player holds a bucket in both hands, and interacted hand is offhand.

Fixes #1963

* Fixes failures in obsidian cooping listener. (#1965)

Failures happened after implementing #1964

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Invvk <70810073+Invvk@users.noreply.github.com>
2022-04-17 09:04:37 -07:00
BONNe b3e55a7b55
Fixes failures in obsidian cooping listener. (#1965)
Failures happened after implementing #1964
2022-04-11 08:23:13 +03:00
BONNe 9f163f0572
Fixes Lava Duplication Glitch (#1964)
Due to the fact, that Obsidian Scooping uses one tick delay to remove obsidian, a player with a bucket in hand and offhand duplicated lava. 
To avoid that, added an extra check that ignores the interact event if a player holds a bucket in both hands, and interacted hand is offhand.

Fixes #1963
2022-04-11 08:13:12 +03:00
BONNe 4341c28aca
Fixes a bug with blueprint height (#1961)
Blueprint clipboard was preventing setting Y below 0 or above 255. 

The code was not adjusted to 1.18 changes.
Reported via discord.
2022-04-03 21:36:51 +03:00
BONNe ad0931ffcb
Fixes kick command (#1960)
PR #1957 broke kick command and noone could kick players from teams.
This should fix it.
2022-04-03 08:12:41 +03:00
Invvk c02e566266
using java 16 syntax (#1958) 2022-04-01 00:12:47 +03:00
BONNe 36751d5573
Solve crashes with Addon#allLoaded call (#1959)
If some addon has code in Addon#allLoaded that crashes the call, then it did not disable addon as well as did not call allLoaded for every other addon that was left in the list.

This should be solved by adding an extra try-catch.
2022-04-01 00:12:27 +03:00
BONNe 9f21314818
Improve team kick command (#1957)
The kick command has an unnecessary owner check. As command should be configurable by island owners, then limiting it to an owner is wrong.
Add a code that allows kicking only lower-ranked players.

Add message that shows who kicked from the island.
Add message that shows that rank does not allow to kick.
2022-03-25 23:29:14 +02:00
tastybento 6796fceee8
Clearer paster (#1953)
* WIP - make easier to understand.

* Small refactor of paster to make it easier to understand

* Fix tabs to spaces. Sorry - new editor!

* Fix tabs to spaces

* Fix tab to spaces
2022-03-19 16:19:31 +00:00
tastybento 51dbca0f99 Go back to replaceAll
This is required.
2022-03-19 15:12:04 +00:00
tastybento b0f1417166 Use constants for common strings 2022-03-19 14:45:44 +00:00
tastybento 0b3ef8df6d Use constants for common strings 2022-03-19 14:43:52 +00:00
tastybento bda56763a8 Replace peplaceAll with replace
It does the same thing if the first argument is not a regex.
2022-03-19 14:29:54 +00:00
BONNe 3ba6620e73
Add an option in SafeSpotTeleport to cancel if fail (#1952)
There was no option to cancel teleportation if SafeSpotTeleport could not find a valid spot. This option could be used to avoid creating "backup" blocks in situations when teleportation is avoidable, f.e. visiting an island.
2022-03-19 14:22:03 +00:00
tastybento 4e8ca6d22c Fix for kicking offline players
https://github.com/BentoBoxWorld/BentoBox/issues/1950
2022-03-18 13:59:41 +00:00
tastybento fd44e03b7b Fix Exception error reported by IDE
I am not sure why Eclipse is saying this is an error.
2022-03-18 13:59:29 +00:00
BONNe 6d59e79e78
Fixes bug with Safe Spot Teleport (#1951)
There was a bug that prevented finding a safe spot if all valid blocks were in height with the `startY` location.

Reported via discord.
2022-03-18 15:06:31 +02:00
BONNe 0cf1d43a29
Address unnecessary PVP reports on each teleport (#1948)
If a player is teleporting on the same island in the same dimension, it keeps spamming that PVP is enabled in dimension. 
It should be enough with sending messages when the player teleports to the island.

Fixes #1885
2022-03-16 09:51:22 +02:00
tastybento e7599ec805 Added 1.18.2 support 2022-03-12 19:20:53 -08:00
tastybento 546cf2c9b9 Fix for random test failures. 2022-03-12 19:12:13 -08:00
tastybento 945bfa66ee Adjusted test to try to avoid errors 2022-03-12 12:20:27 -08:00
BONNe 50b677f4c5
Fixes a bug when fallback could not use reusable
There was an issue in PanelItemTemplate that prevented fallback buttons to be "reusable" things.
The issue was that reusable items were not passed to the panel item reader.
2022-03-12 12:15:10 +02:00
tastybento 6a009946f5 Use world min-height for island bounding box. 2022-02-26 10:22:41 -08:00
tastybento 6c7d77f093 Added name of the addon causing the issue.
https://github.com/BentoBoxWorld/BentoBox/issues/1944
2022-02-24 08:02:20 -08:00
tastybento 5910aa2685 Version 1.20.1 2022-02-19 11:19:33 -08:00
tastybento 73a432d75c Put NMS dependency back in to POM. 2022-02-19 10:05:57 -08:00
tastybento 4e3079b623
Merge branch 'master' into develop 2022-02-19 09:57:05 -08:00
BONNe d492539c51
Implement ACTION defining by ACTION TYPE (#1938)
This change implements a new way how to define actions via a custom panel template.

Previously there were 2 options how to define an action:
- by click-type
- as an array list

This adds the third type: by action type. It means that now developers could code that action is the main key for defining what happens when button is clicked:
```
    actions:
       visit:
          click-type: LEFT
          tooltip: Tooltip text for left click action
```
2022-02-11 18:07:14 -08:00
BONNe fa68f8f01c
Add missing templated panel builder method (#1935)
#1932 implemented a feature that allows reading multiple panels from a single file, however, there was a missing builder in TemplatedPanelBuilder class that would allow to use it.

This fixes it and adds the missing builder method.
2022-02-11 18:06:56 -08:00
tastybento cef31306ac Fix JoinLeaveListenerTest 2022-01-28 21:54:30 -08:00
tastybento ee94a49e85 Fix IslandInfoCommandTest 2022-01-28 21:40:45 -08:00
tastybento 78a3c49361 Fix AdminInfoCommandTest 2022-01-28 21:39:06 -08:00
tastybento 937e8bf614 Fix island test and max ever range bug 2022-01-28 21:33:39 -08:00
BONNe 09ab327551
Implement a flag that rules all flags. (#1927)
Add new Protection Flag: CHANGE_SETTINGS. This flag allows set which rank can change island settings. By default, it is set to OWNER rank and minimal value is MEMBER rank.

Fixes #1493
2022-01-28 18:45:50 -08:00
BONNe 90ebe103c4
Templated Panel Title Parameters (#1934)
* Implement parameter adding to the templated panel title.

There was an issue when templated panels were always static, and api did not have a way how to change it. This will allow passing parameters to the tile, and allow adding dynamic texts.

* Implement parameter adding to the templated panel title.

There was an issue when templated panels were always static, and api did not have a way how to change it. This will allow passing parameters to the tile, and allow adding dynamic texts.
2022-01-28 18:43:14 -08:00
Huynh Tien 178529adb1
Fix null in reject invite (#1933)
* remove debug

* fix null in reject invite
2022-01-28 18:42:56 -08:00
BONNe 52cc2a8593
Implement multiple panels in single file (#1932)
The existing template file reader could read a single configuration section from a file, and the section was required to start with the same name as the file.
This changes it and allows to read any configuration section from the panel.

This change is backward compatible and does not influence existing panels.
2022-01-28 18:42:23 -08:00
BONNe 3ecdda65b5
Fixed bug with running onLeave commands (#1929)
In discord, it was reported that running the admin delete command produces a null-pointer if `[player]` placeholder is used.

With fast checking I found out the issue: incorrect user was passed to the command.
Also, I moved runCommands below all resets, so commands could be used to give items, or xp or other stuff that is removed.
2022-01-28 18:42:02 -08:00
BONNe da98bbcf38
Extend ranked commands (#1928)
* Add CompositeCommand#defaultCommandRank variable.

This variable stores default rank for a command. This adds more flexibility to the Island#commandRanks object. This change allows specifying default rank for each command.

* Add more commands for users to configure on their islands.

Owners now can specify who can use:
- sethome command
- renamehome command
- deletehome command
- setname command
- resetname command
2022-01-28 18:41:20 -08:00
tastybento 7b94aa0ea2
Adds bonus range elements to Island object. (#1921)
* Adds bonus range elements to Island object.

Admin commands still need to be adjusted or added to support management
of the bonus ranges.

* Changed to mutable class

Fixed bugs.
2022-01-28 18:35:55 -08:00
BONNe 54869a400a
Implement duplicated actions click type (#1919)
Some addons may want to introduce two different actions with the same click type.
This change will allow to do it, as now actions may be defined as lists instead of configuration sections.

To use it, action must be defined a bit differently:
```
      actions:
        - click-type: <value of enum ClickType>  # required
          type: <value of String> # not required
          content: <value of String>  # not required
          tooltip: <value of String>  # not required
        - click-type: <value of enum ClickType>  # required
          type: <value of String> # not required
          content: <value of String>  # not required
          tooltip: <value of String>  # not required
```

Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2022-01-28 18:35:27 -08:00
BONNe 56c9f5c28e
Fixes a bug with on-join commands not working when players join the team. #1925 (#1926) 2022-01-23 15:25:27 -08:00
tastybento 2f0869fd6e More voiding of event listeners
https://github.com/BentoBoxWorld/BentoBox/issues/1918
2022-01-22 18:04:00 -08:00
tastybento d14ad977ab Clean up imports. 2022-01-22 13:14:57 -08:00
tastybento 9062aab34b Change behavior of teleporting to default nether
If create-and-link-portals is true, then teleporting to the nether will
not go to a central portal point and instead create a portal in the
nether at the same coordinates as the portal in the overworld.
Teleporting back will not be affected.

https://github.com/BentoBoxWorld/BSkyBlock/issues/464
2022-01-22 13:14:34 -08:00
tastybento 94b982f644 Fix tests
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1918
2022-01-22 12:16:27 -08:00
tastybento 5573e9f329 Fix TNTListener tests 2022-01-22 11:57:11 -08:00
tastybento 823bc5ceed Fix Fire tests 2022-01-22 11:54:00 -08:00
tastybento f25e54f3c8 Return void for event listeners
https://github.com/BentoBoxWorld/BentoBox/issues/1918
2022-01-21 18:47:31 -08:00
tastybento 5ab89ac63d Fix tests 2022-01-15 16:20:12 -08:00
tastybento 046c4fff35 Fix for double chest protection.
Double chests could be viewed but not accessed even if chest access or
trapped chest access was allowed.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1912
2022-01-09 20:53:47 -08:00
tastybento bea9f0e394 List must be mutable. Remove debug. 2022-01-09 20:41:20 -08:00
tastybento 721f0271aa Dynamically grab flags
https://github.com/BentoBoxWorld/BentoBox/issues/1915
2022-01-09 19:44:44 -08:00
tastybento b63df41082 Remove |+ block chunking marker
https://github.com/BentoBoxWorld/BentoBox/issues/1914
2022-01-09 14:14:04 -08:00
tastybento d06688ad45 Remove the |+ block code because it adds linefeeds
At least with the latest server it does.

https://github.com/BentoBoxWorld/BentoBox/issues/1914
2022-01-08 15:16:01 -08:00
tastybento 00e174258e Hook into Vault after other plugins have loaded.
https://github.com/BentoBoxWorld/BentoBox/issues/1913
2022-01-07 19:08:15 -08:00
BONNe eaf2236f7f
Fixes a bug that increased all button count by 1 (#1911)
This will fix an issue when the next button was not displayed when it was necessary.
2022-01-07 18:48:25 -08:00
gitlocalize-app[bot] ff4a7c9d61
Translate vi.yml via GitLocalize (#1910)
Co-authored-by: HSGamer <huynhqtienvtag@gmail.com>
2022-01-07 18:47:58 -08:00
gitlocalize-app[bot] 318d399a5b
Translate zh-CN.yml via GitLocalize (#1908)
Co-authored-by: DuckSoft <realducksoft@gmail.com>
2022-01-07 18:47:46 -08:00
tastybento 5c94f97e71 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2022-01-02 12:27:09 -08:00
tastybento bfbeb7964b Fix tests 2022-01-02 12:25:35 -08:00
tastybento 5c8af0c1df Fix tests 2022-01-02 12:21:28 -08:00
tastybento 728c60d472 Fix failing tests. 2022-01-02 12:19:43 -08:00
gitlocalize-app[bot] 2d08e28a01
Translate zh-CN.yml via GitLocalize (#1907)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2022-01-01 18:20:24 -08:00
gitlocalize-app[bot] c1f33ad1d4
Dutch translation (#1906)
* Translate nl.yml via GitLocalize

* Translate nl.yml via GitLocalize

* Translate nl.yml via GitLocalize

Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: Dark_Ville <darkville13@gmail.com>
2022-01-01 18:16:50 -08:00
gitlocalize-app[bot] a5f093758c
Translate tr.yml via GitLocalize (#1905)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2022-01-01 18:13:29 -08:00
tastybento 20a6846523 Remove deprecated methods 2022-01-01 18:05:40 -08:00
tastybento 796114eeef Fix tests 2022-01-01 18:01:50 -08:00
BONNe d1eb175e88
Change AbsolutePath to CanonicalPath (#1901)
I am not 100% sure if this would fix https://github.com/BentoBoxWorld/BSkyBlock/issues/451

However, searching google I found that others had a similar issue with the absolute path and they changed it to canonical. 

As I was not able to reproduce the main reported issue, I do not know if this fixes it.
2022-01-01 17:45:05 -08:00
Huynh Tien 7088a2736f
Flags for Block Explode (#1899)
* add Block Explosion support for TNT_DAMAGE flag

* prevent block explosion from damaging entities

* seperated flags for block explode

* invert and cancel

* pattern instanceof
2022-01-01 17:42:46 -08:00
Huynh Tien dffe8a811e
fix an exception when cleaning an offline player (#1898) 2022-01-01 17:39:25 -08:00
Huynh Tien ce1d8e5117
Rework the chunk deletion (#1897)
* delete island one by one

* register before IslandDeletionManager

* optimize imports

* setting

* just some indents

* config

* run synchronously

* a bit reformat before recoding

* proper delete chunks

* comment

* combine the task call

* expose the NMS Handler

* don't have to try-catch this

* we know that this is final

* expose copy chunk data so that it can be overridden

* Don't have to use Vector

* set block from minimum height

* remove NMS and use fallback if not set

* only get the height once

* fix test
2022-01-01 17:38:27 -08:00
Huynh Tien c9c9ea0389
Fix Biome compatibility on 1.17 (#1895)
* Fix Biome compatibility on 1.17

* check if it's not 1.17.1
2022-01-01 17:34:51 -08:00
gitlocalize-app[bot] b6a739db00
German Translation (#1904)
* Translate de.yml via GitLocalize

* Translate de.yml via GitLocalize

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: Rikamo045 <rik.amos.krajinovic@gmail.com>
2022-01-01 17:33:11 -08:00
tastybento 17ac4f688a Add an admin delete homes command
https://github.com/BentoBoxWorld/BentoBox/issues/1900

This admin command removes all homes from a specified island.
2021-12-31 10:51:16 -08:00
tastybento bf62f18de5 Version 1.20.0 2021-12-31 10:49:46 -08:00
tastybento acba363523 Added why to VisitorKeepInventory and test class
why reports can now be made by subclasses of FlagListener
2021-12-21 10:12:20 -08:00
tastybento 7fbae58bb2 Make check an NPE check and failure 2021-12-20 15:30:29 -08:00
tastybento 3ece0d049a Added test for SafeSpotTeleport and removed old test 2021-12-20 09:50:45 -08:00
tastybento f049fc6941 Added SafeSpotTepeortTest 2021-12-20 09:46:04 -08:00
Huynh Tien e3b99195ca
Optimize safe location searching with configurable range limit (#1892)
* better safe spot finder

* this should be better

* limit how far the y-coordinate will be expanded

* load chunks passively
2021-12-20 09:44:34 -08:00
Huynh Tien 333c9a8272 load chunks passively 2021-12-20 19:37:50 +07:00
BONNe ed50765b98
Addres Biome's changes in Minecraft 1.18. (#1893)
Since Minecraft 1.18 renamed and removed a lot of biomes, some addons that stored them in the database may require migration.

This Adapter will fix that as now BentoBox itself will do migration for all Biome.class objects.
2021-12-19 08:49:29 -08:00
Huynh Tien 168de622ea limit how far the y-coordinate will be expanded 2021-12-19 20:40:01 +07:00
Huynh Tien f91ed4705a this should be better 2021-12-16 17:18:09 +07:00
Huynh Tien 6f01310f92 better safe spot finder 2021-12-16 08:58:41 +07:00
tastybento 2e6ef59040 Update dates in BentoBox console banner 2021-12-13 21:59:30 -08:00
tastybento cec58c7a45 Version 1.19.1 2021-12-13 21:59:11 -08:00
tastybento 60cde5334d
Release 1.19.0 (#1890)
* Update to Minecraft 1.18 (#1887)

* Make BlockEndDragon support custom max world height (#1888)

Use max world height instead of magic 255 value.

* Send PVP toggle messages only to on-island players.

https://github.com/BentoBoxWorld/BentoBox/issues/1885

* Add 1.18.1

Co-authored-by: BONNe <bonne@bonne.id.lv>
2021-12-12 20:26:48 -08:00
tastybento 6567104f94 Add 1.18.1 2021-12-11 10:50:09 -08:00
tastybento 524cb46e27 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-12-07 19:35:08 -08:00
tastybento 8a6cc39214 Send PVP toggle messages only to on-island players.
https://github.com/BentoBoxWorld/BentoBox/issues/1885
2021-12-07 19:34:58 -08:00
BONNe 3b64973c2b
Make BlockEndDragon support custom max world height (#1888)
Use max world height instead of magic 255 value.
2021-12-06 11:49:22 +01:00
tastybento 59de6a8efb
Update to Minecraft 1.18 (#1887) 2021-12-05 21:03:21 -08:00
tastybento 11a3bf9227
Release 1.18.1 (#1886)
* Version 1.18.1

* Added snapshot repo to Maven info.

* Fixes console teleporting.

https://github.com/BentoBoxWorld/BentoBox/issues/1877

* Address potential NPE's

* Preveent NPEs and other items.

* Remove dead paper forks (#1884)

Tuinity has since merged with Paper, and is now not a valid fork

Airplane is shutting down / not updating to 1.18.x

* Added toString to resolve issue with arrays

* Fix minor JavaDoc mistake

Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
2021-12-05 13:46:54 -08:00
tastybento 79abf965f3 Fix minor JavaDoc mistake 2021-12-05 12:01:27 -08:00
tastybento de951b6c4b Added toString to resolve issue with arrays 2021-12-05 11:58:20 -08:00
Fredthedoggy 2d032523c7
Remove dead paper forks (#1884)
Tuinity has since merged with Paper, and is now not a valid fork

Airplane is shutting down / not updating to 1.18.x
2021-12-05 11:33:42 -08:00
tastybento 7277432c41 Preveent NPEs and other items. 2021-11-12 15:39:52 -08:00
tastybento 7616f6aa2a Address potential NPE's 2021-11-12 13:19:19 -08:00
tastybento 6b6ab711e5 Fixes console teleporting.
https://github.com/BentoBoxWorld/BentoBox/issues/1877
2021-11-11 15:36:49 -08:00
tastybento f3a7ed1303 Added snapshot repo to Maven info. 2021-11-11 15:12:03 -08:00
tastybento 7b1c19c084 Version 1.18.1 2021-11-11 10:41:16 -08:00
tastybento 22c62c91b4 Merge branch 'master' into develop
Conflicts:
	src/main/java/world/bentobox/bentobox/api/events/team/TeamEvent.java
	src/main/java/world/bentobox/bentobox/api/panels/reader/TemplateReader.java
	src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardFormat.java
	src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardReader.java
	src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintClipboardWriter.java
	src/main/java/world/bentobox/bentobox/blueprints/worldedit/BlueprintSchematicConverter.java
	src/main/java/world/bentobox/bentobox/listeners/flags/settings/PVPListener.java
2021-11-11 10:40:17 -08:00
tastybento df2b445002
Release 1.18.0 (#1876)
## Change Log 

* Island range perms could be 2x island distance

This could allow protection ranges to be much greater than the island
range and therefore overflow into adjacent islands.

https://github.com/BentoBoxWorld/BentoBox/issues/1851

* Ensure maxEverProtectionRange is less than range.

https://github.com/BentoBoxWorld/BentoBox/issues/1851

* Added test class for Island.

* Added access to private fields for JUnit tests

* Added Sonar Properties to POM

* Changes to analyze using sonar.

* Remove zip reference

* Avoid zipSlip vulnerability.

* Removed the unused WorldEdit hook. (#1853)

If someone wants to add it back later they can, but this code does
nothing right now.

* Requires nonNull parameters for User.instanceOf (#1852)

* Requires nonNull parameters for User.instanceOf

The only reason why User.instanceOf was returning a potential null was
if the parameter was null. Further absolutely no null checking was being
done, so the assumption was that User.instanceOf should never return a
null. This corrects the annotations and requires non-Null parameters.

* Remove null player test

* Fix JavaDoc for GameModeAddon inWorld method

* Fix max-range bug

* Remove illegal tag as it's not needed

* Code smell reduction

* Add missing packages so tests can pass.

* Non null user methods (#1856)

* This makes some User methods non-null.

Instead of returning null, some methods will throw an error if they are
called on non-Players. This means code does not have to do null checks.

* Perform null check in method.

* Null check

* Fix test

* Fix test.

* Npe squashing (#1857)

* Fix hanging [uuid] in info.

* NPE checking

* Make getProtectionCenter nonNull

* More NPE fixes.

* Fix test

* Make getPlayer() and getOfflinePlayer() nonNull returns

This requires addons to not use null checks and instead us the isPlayer
or isOfflinePlayer methods.

* NPE blockers

* Deprecate CompositeCommand isPlayer method.

* Fix test

* Use isPlayer instead of null check.

Refactor code to be easier to understand.

* Fix

* Fix some code smells.

* Prevent NPE possibility.

* Fix "ugly" enchant name. (#1858)

Enchant names did not have a touch of Util#prettifyText code.

* Remove deprecated events.

* Fix IslandSethomeCommand test

* Fix tests - just counts of events.

* Added API to get a translation without color conversion

It may be necessary to read the translation without converting colors to
Bukkit colors.

* Fix tests.

* Fix test.

* Version NPE protections.

* 1.18.0 (#1860)

* Mark all home-related methods in PlayersManager as deprecated.

* Fix bug where maxHomes was shown as null

* Version 1.18.0

API changes may break compatibility with Addons.

* Remove usage of deprecated methods except for migration code.

* Prevent NPE. Check isPlayer instead of getWorld() == null

* Fix nullability issue and added to @Deprecated annotations

* Added deprecation notices to home methods in Players

* Fix missing color conversion for translations.

* Fix mis-statement of island protection coordinates in info command.

* Fix tests

* Fix missing edge deletion of island.

The bounding box was erroneously being made smaller when it was not
required because the inBounds check does that already.

https://github.com/BentoBoxWorld/BentoBox/issues/1863

* Prevent rare NPE

* Ensure oldIsland is never null.

* Refactored setowner command and added test class

* API: Require getWorldSettings to be a game mode world

* Fix tests and refactor code

* Fix test

IWM inWworld needs to return true

* Fix test

IWM inWorld must return true

* Put default getHandlers back in for backwards compatibility

Without them, too many older addons break. They need updating to have
their own handlers. Once that is done, the default ones can be removed
maybe.

* Refactor code for clarity.

* NPE protections

* Prevent NPE

@Poslovich - you were right!

* Throw an error if player is null

* Prevent NPE

* Prevent NPE

* Do a null check

* World should never be null.

* Require non-null world

* Require owner to be non-null. It should never be null

* Prevent NPEs

* Clear the going home flag for edge cases

There were a few potential times when the flag may not have been
cleared. These were mostly teleport failure scenarios. Hopefully, this
is all of them.

https://github.com/BentoBoxWorld/BentoBox/issues/1864

* Shift priority of EntityPortalEnterEvent to HIGH

https://github.com/BentoBoxWorld/BentoBox/issues/1866

This will allow other plugins running at NORMAL to cancel the event
before BentoBox does something.

* Increase priority of EntityPortalEvent listener

https://github.com/BentoBoxWorld/BentoBox/issues/1866

* Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1868

* Code refactoring around User.getInstance(player)

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
Co-authored-by: Justin <jstnf@users.noreply.github.com>
Co-authored-by: gecko10000 <60494179+levtey@users.noreply.github.com>
2021-11-08 21:28:12 -08:00
tastybento 5e3cffbe11 Code refactoring around User.getInstance(player) 2021-10-30 14:02:30 -07:00
tastybento 1953caa7c5 Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1868 2021-10-30 13:44:34 -07:00
tastybento 8752e19e5d Increase priority of EntityPortalEvent listener
https://github.com/BentoBoxWorld/BentoBox/issues/1866
2021-10-26 20:49:38 -07:00
tastybento 6aa7869b17 Shift priority of EntityPortalEnterEvent to HIGH
https://github.com/BentoBoxWorld/BentoBox/issues/1866

This will allow other plugins running at NORMAL to cancel the event
before BentoBox does something.
2021-10-23 16:39:36 -07:00
tastybento 930433b64b Clear the going home flag for edge cases
There were a few potential times when the flag may not have been
cleared. These were mostly teleport failure scenarios. Hopefully, this
is all of them.

https://github.com/BentoBoxWorld/BentoBox/issues/1864
2021-10-16 20:44:18 -07:00
tastybento 731d902d98 Prevent NPEs 2021-10-11 18:02:23 -07:00
tastybento 1a802a7510 Require owner to be non-null. It should never be null 2021-10-11 17:56:06 -07:00
tastybento 37e7846908 Require non-null world 2021-10-11 17:55:04 -07:00
tastybento 28a2ecfcfa World should never be null. 2021-10-11 17:52:38 -07:00
tastybento 081f74544b Do a null check 2021-10-11 17:48:41 -07:00
tastybento 2ffa4c9874 Prevent NPE 2021-10-11 17:43:04 -07:00
tastybento 907c6b3534 Prevent NPE 2021-10-11 17:27:24 -07:00
tastybento 8852b9d660 Throw an error if player is null 2021-10-11 17:21:07 -07:00
tastybento 765f774652 Prevent NPE
@Poslovich - you were right!
2021-10-11 17:15:51 -07:00
tastybento db464d5c04 NPE protections 2021-10-09 11:40:15 -07:00
tastybento f37226b115 Refactor code for clarity. 2021-10-09 11:20:48 -07:00
tastybento 536d7ef1c3 Put default getHandlers back in for backwards compatibility
Without them, too many older addons break. They need updating to have
their own handlers. Once that is done, the default ones can be removed
maybe.
2021-10-09 09:29:52 -07:00
tastybento d3e7b55be5 Fix test
IWM inWorld must return true
2021-10-08 17:27:52 -07:00
tastybento 665724e825 Fix test
IWM inWworld needs to return true
2021-10-08 17:25:20 -07:00
tastybento fe2e9e1a9e Fix tests and refactor code 2021-10-08 17:15:41 -07:00
tastybento b5d9d2e52e API: Require getWorldSettings to be a game mode world 2021-10-03 20:33:50 -07:00
tastybento cfad9879a5 Refactored setowner command and added test class 2021-10-03 19:20:45 -07:00
tastybento 3a43c40634 Ensure oldIsland is never null. 2021-10-03 16:16:58 -07:00
tastybento 60c62a5967 Prevent rare NPE 2021-10-03 16:08:49 -07:00
tastybento 3c65194dfb Fix missing edge deletion of island.
The bounding box was erroneously being made smaller when it was not
required because the inBounds check does that already.

https://github.com/BentoBoxWorld/BentoBox/issues/1863
2021-10-03 15:50:18 -07:00
tastybento a1c3f78293 Fix tests 2021-10-03 15:46:11 -07:00
tastybento b5fd992fa7 Fix mis-statement of island protection coordinates in info command. 2021-10-03 15:29:57 -07:00
tastybento bc6ec36f87 Fix missing color conversion for translations. 2021-10-03 15:04:14 -07:00
tastybento d780bbd4c0
1.18.0 (#1860)
* Mark all home-related methods in PlayersManager as deprecated.

* Fix bug where maxHomes was shown as null

* Version 1.18.0

API changes may break compatibility with Addons.

* Remove usage of deprecated methods except for migration code.

* Prevent NPE. Check isPlayer instead of getWorld() == null

* Fix nullability issue and added to @Deprecated annotations

* Added deprecation notices to home methods in Players
2021-09-26 13:35:18 -07:00
tastybento f66eeed4a1 Version NPE protections. 2021-09-25 16:42:54 -07:00
tastybento 3236c68e5c Fix test. 2021-09-25 09:42:51 -07:00
tastybento 500f7292c6 Fix tests. 2021-09-25 09:34:02 -07:00
tastybento af3c0a874c Added API to get a translation without color conversion
It may be necessary to read the translation without converting colors to
Bukkit colors.
2021-09-25 08:47:14 -07:00
tastybento 0a4fc83218 Fix tests - just counts of events. 2021-09-25 08:33:07 -07:00
tastybento 8b474833e1 Fix IslandSethomeCommand test 2021-09-25 08:07:15 -07:00
tastybento bc737d4d66 Remove deprecated events. 2021-09-25 08:00:04 -07:00
tastybento 3d2a99c036 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-09-25 07:51:13 -07:00
BONNe 16d5775c84
Fix "ugly" enchant name. (#1858)
Enchant names did not have a touch of Util#prettifyText code.
2021-09-22 19:35:22 -07:00
tastybento 690ea2f99e Prevent NPE possibility. 2021-09-19 18:36:37 -07:00
tastybento 29fa03976c Fix some code smells. 2021-09-19 18:29:19 -07:00
tastybento f6f5b63110 Fix 2021-09-19 08:45:19 -07:00
tastybento 110a282deb Use isPlayer instead of null check.
Refactor code to be easier to understand.
2021-09-19 08:42:37 -07:00
tastybento 53f02ae686
Npe squashing (#1857)
* Fix hanging [uuid] in info.

* NPE checking

* Make getProtectionCenter nonNull

* More NPE fixes.

* Fix test

* Make getPlayer() and getOfflinePlayer() nonNull returns

This requires addons to not use null checks and instead us the isPlayer
or isOfflinePlayer methods.

* NPE blockers

* Deprecate CompositeCommand isPlayer method.

* Fix test
2021-09-18 17:59:32 -07:00
tastybento b247b360bf Fix test. 2021-09-18 12:25:25 -07:00
tastybento 68890eba00 Fix test 2021-09-18 12:25:14 -07:00
tastybento 8eac5070f0 Null check 2021-09-18 11:37:33 -07:00
tastybento 25fe86d812
Non null user methods (#1856)
* This makes some User methods non-null.

Instead of returning null, some methods will throw an error if they are
called on non-Players. This means code does not have to do null checks.

* Perform null check in method.
2021-09-18 11:22:28 -07:00
tastybento cad7dc1666 Add missing packages so tests can pass. 2021-09-18 10:50:21 -07:00
tastybento 8eb45e817c Code smell reduction 2021-09-18 08:15:15 -07:00
tastybento 4f22df069b Remove illegal tag as it's not needed 2021-09-18 08:15:07 -07:00
tastybento 3282b1bd12 Fix max-range bug 2021-09-18 08:14:43 -07:00
tastybento d92632341d Fix JavaDoc for GameModeAddon inWorld method 2021-09-16 16:47:42 -07:00
tastybento 02a14cab6e
Requires nonNull parameters for User.instanceOf (#1852)
* Requires nonNull parameters for User.instanceOf

The only reason why User.instanceOf was returning a potential null was
if the parameter was null. Further absolutely no null checking was being
done, so the assumption was that User.instanceOf should never return a
null. This corrects the annotations and requires non-Null parameters.

* Remove null player test
2021-09-16 16:41:27 -07:00
tastybento 063fa97cf3
Removed the unused WorldEdit hook. (#1853)
If someone wants to add it back later they can, but this code does
nothing right now.
2021-09-13 19:46:10 -07:00
tastybento bb7f124066 Avoid zipSlip vulnerability. 2021-09-12 21:53:26 -07:00
tastybento 5b838d0668 Remove zip reference 2021-09-12 21:03:49 -07:00
tastybento 005007b0db Changes to analyze using sonar. 2021-09-12 20:54:30 -07:00
tastybento 5c063a692d Added Sonar Properties to POM 2021-09-12 20:22:43 -07:00
tastybento c7a1220028 Added access to private fields for JUnit tests 2021-09-12 17:54:29 -07:00
tastybento 0e744cae81 Added test class for Island. 2021-09-12 17:35:52 -07:00
tastybento 7271096ace Ensure maxEverProtectionRange is less than range.
https://github.com/BentoBoxWorld/BentoBox/issues/1851
2021-09-11 16:53:27 -07:00
tastybento 3dd9e973fb Island range perms could be 2x island distance
This could allow protection ranges to be much greater than the island
range and therefore overflow into adjacent islands.

https://github.com/BentoBoxWorld/BentoBox/issues/1851
2021-09-11 16:50:43 -07:00
tastybento 2b452cddbd Version 1.17.4 2021-09-11 16:45:01 -07:00
tastybento a6d70d6e90
Release 1.17.3 (#1827)
* Version 1.17.1

* Add homes command to default player options.

* Implements better online player counter. (#1791)

Current one is based on online player count when bStats sends data to the server.
This one will send data about amount of players who logged in the server.

* Add Boxed Gamemode to the list. (#1793)

* Add Bank addon (#1792)

* Add Holographic Displays as SoftDepend for AOneBlock (#1794)

* Custom date time format support for /<admin> info <player> (#1783)

Fixes #1720

* Parent/sub-flag support, split up and designate CONTAINER flag as parent flag (#1784)

* Split CONTAINER flag into multiple flags

CONTAINER split into
- CONTAINER (Chest/Minecart Chest)
- BARREL (Barrel)
- COMPOSTER (Composter)
- FLOWER_POT (Flower Pot)
- SHULKER_BOX (Shulker Box)
- TRAPPED_CHEST (Trapped Chest)

Fixes #1777

* Add subflag support

* Create container parent flag, chest subflag

* Remove extra string from when CHEST was CONTAINER

* Fix incorrect flag specified on fired event in IslandToggleClick

* Add missing world subflag event firing

* Remove extra import

* Add world setting flag for island visitors keep inventory (#1785)

* Implement 3 bar charts: addons, gamemodes, hooks (#1790)

BStats supports sending Bar chart data, however, it does not display it via their site directly.
It can be called manually, to view.

PieChart does not work very well for addons and hooks. BarChart however allows viewing each addon separately. 

This change allows sending data to the server about bar charts.

* Update action versions

* Declare distribution - adopt

* Cache Java Maven files differently.

* Try dependency for shade snapshot

* Add support for Minecraft 1.17.1

* Fix test by incrementing listener value check

There is a new listener now.

* Spigot 1.17.1

* Fix test for InventoryListener

* Re-order repos.

* Downgrading to 1.17 Spigot for now to enable building.

* Try pluginRespositories tag in POM to enable Github actions

* Version 1.17.2

* Add alternative Addon#getIslandManager method (#1797)

We have already done this to the main BentoBox class. But add-ons missed it, and it still has this weird structure: Addon#getIslands().getIslands().

This will just add the same method as it is already in BentoBox class.

* Fix bucket dupe (#1806)

Fix https://discord.com/channels/272499714048524288/310623455462686720/867790395442462760

* Removes unneeded exploit protection code for skulls (#1810)

This no longer seems to be required with 1.17.1

https://github.com/BentoBoxWorld/BSkyBlock/issues/430

* Quote filename of addon that cannot be loaded.

Provides a better understanding of which addon failed.

* Remove update when pasting chest.

* Version 1.17.3

* Java upgrade (#1814)

* Version 1.17.3

* Use Map.of and List.of instead of Immutable collections

* Replace explicit type argument.

* Replace lamba with method reference

* Replace condition with Objects.requireNonNullElseGet

* Use String.repeat

* Use new switch expressions

* Use instanceof patten variables which are more compact

* Fuse toUnmodifiableList into stream and return it.

* Remove unnecessary toString() calls.

* Remove unnecessary public

* Extracted common part from if

* Simplify conditional expressions

* Remove unused IOExceptions

* Cast to long

* Use Map.putAll

* Use primitives

* Clarify what is null or not

* Addedd @Serial annotation introduced with Java 14.

* Use Optional.isEmpty instead of !isPresent

* Use flatMap then ifPresent

* Just use Arrays.stream

* Swap map and filter for null with Objects::nonNull

* Use expression lambda

* Update JavaDoc version to 16

* Make spawn protection area square instead of circle.

https://github.com/BentoBoxWorld/BentoBox/issues/1819

* Improve ItemParser code. (#1821)

* Improve ItemParser code.

Add ability to parse text if Material is just a single string.
Add ability to parse player heads.

Add comments to the code.

* Fixes failing test.

* Added deprecation suppression.

* Added Pladdon to loadbefore.

* Prevent bucket duping when scooping obsidian Part 35

https://github.com/BentoBoxWorld/BentoBox/issues/1825

* Warns visitors that PVP is active if they teleport to an island

https://github.com/BentoBoxWorld/BentoBox/issues/1780

* Prevents repeated portaling when nether is disabled. (#1826)

* Prevents repeated portaling when nether is disabled.

https://github.com/BentoBoxWorld/BentoBox/issues/1782

* Fix test

* Hex pr 1820 (#1822)

* Fix @since for subflags. (#1831)

* Add Citizens to Softdepend list for NPC support (#1834)

* Fix color test.

* Set the default game mode when player makes island.

* Corrects JavaDocs link

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1838

* Update to 3.3.1-SNAPSHOT for shade plugin.

* Package info files for better JavaDocs (#1839)

* Added package info files for better JavaDocs

* Implement Flag icon changing via Locales file. (#1829)

This commit contains 2 changes:
- An option for Flag to use icon that is defined in locales after "icon" string.
- An option for ItemParser to parse icon or return given value, if parsing was not successful.

The flag option is not ideal, but it is simpler and easier to maintain then adding new config section where icons can be changed, as the locales file already contains a lot of info about each flag.

* Support glowing ink in blueprints (#1842)

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1837

* Panel template (#1841)

* Implement basic functionality to read data from template panels.

Create TemplateReader class that has static method which generates a PanelTemplateRecord. This record contains every necessary information from user created template file so everyone could use it to generate a functional panel.
These classes are just for reading templates and do not create actual panel.

* Add template clearing via bentobox reload command.

* Fix version command to show addon state.

* Fix German flag banner description.

Added more debug around error reporting.

* Make variables final if they can be. (#1843)

* Make variables final if they can be.

* Do not use final so that test can pass.

For testing, we use a trick to set this variable, but it won't work if
it is final. Right now, I'd like to keep the test.

* Static code analysis (#1844)

* StringBuffer (Java 5) may be declared as StringBuilder

* Replace map with flatMap

* Use instanceof naming

* No need to specify paramter types.

* Remove verbose code

* Fix JavaDoc issues

* Make internal class a record.

* Remove unused import.

* Make internal class a record.

* Made internal class a record.

* Removed unused import

* Fix typos

* Fix typo in test.

* Prevent home teleport when already home teleporting

Home teleporting is async so it's possible to issue the command multiple
times. This puts a flag in so that if a playeer is mid-teleport, then
issuing the go command will just repeat the text that the player is
teleporting home.

https://github.com/BentoBoxWorld/BentoBox/issues/1637

* Extracted island info from the Island object.

This is in preparation to have a different info for players than admins.

https://github.com/BentoBoxWorld/BentoBox/issues/1501

* Slimmer trimmer island info for players.

https://github.com/BentoBoxWorld/BentoBox/issues/1501

* Fixes coop and trust invites when team invites are not allowed

If a member or sub-owner has coop or trust invite capability and not
team invite rank, and confirm invites is on, then the invites were not
working.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1452

* Remove invulnerable visitor protection if island is in PVP mode

We now have alerts if a player teleports to a PVP island.

https://github.com/BentoBoxWorld/BentoBox/issues/668

* Fixes NPE in admin tp if nether or end worlds don't exist.

* Adds arrow sound when teleporting into a PVP island.

* Fix PVPListenerTest

Visitors are no longer invincible when on a PVP island.

* Fix test failure in InvincibleVisitorsListenerTest

* Minor JavaDoc correction.

* Go command was not working second time.

* Prevents console errors for missing icons in locale files.

* Fixes PlAddon disabling issue. (#1847)

There was a bug that did not unload PlAddon classes from JVM.
This change should fix it, as it will unload PlAddons via pluginLoader.

* Clear code - set worlds once on construction.

* Fixes a bug when Potion metadata was not applied (#1849)

There was missing potion meta data applying after creating an item. 
This affects both: Potions and Tipped arrows.

* Adds force-shown lines to the template. (#1850)

* Adds force-shown lines to the template.
Fixes a bug when template type was not selected correctly.
Changes "name" to "title" for border and background icons.

* Create TemplatedPanel and TemplatedPanelBuilder.

TemplatedPanel uses PanelTemplateRecord to create a functional panel.
Builder contains button builder callbacks that are used to create buttons with custom data.

* Use final vars for lambdas to avoid them changing async

The processes could run async, especially when chunk loading took a
while, so using fields that could change would result in blocks being
missed, especially in the nether or end. Switching to final vars avoids
this.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1840

* Remove travis.

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
Co-authored-by: Justin <jstnf@users.noreply.github.com>
Co-authored-by: gecko10000 <60494179+levtey@users.noreply.github.com>
2021-09-10 18:39:16 -07:00
tastybento e71d67d3c9 Remove travis. 2021-09-08 21:12:09 -07:00
tastybento b24c1fdc86 Use final vars for lambdas to avoid them changing async
The processes could run async, especially when chunk loading took a
while, so using fields that could change would result in blocks being
missed, especially in the nether or end. Switching to final vars avoids
this.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1840
2021-09-08 21:08:15 -07:00
tastybento 6bff4c8132 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-09-08 20:26:56 -07:00
BONNe 9da329356a
Adds force-shown lines to the template. (#1850)
* Adds force-shown lines to the template.
Fixes a bug when template type was not selected correctly.
Changes "name" to "title" for border and background icons.

* Create TemplatedPanel and TemplatedPanelBuilder.

TemplatedPanel uses PanelTemplateRecord to create a functional panel.
Builder contains button builder callbacks that are used to create buttons with custom data.
2021-09-08 19:58:26 -07:00
BONNe 6bf3264ab5
Fixes a bug when Potion metadata was not applied (#1849)
There was missing potion meta data applying after creating an item. 
This affects both: Potions and Tipped arrows.
2021-09-08 19:57:27 -07:00
tastybento 3650863fa1 Clear code - set worlds once on construction. 2021-09-08 19:50:23 -07:00
BONNe 23e3554c4d
Fixes PlAddon disabling issue. (#1847)
There was a bug that did not unload PlAddon classes from JVM.
This change should fix it, as it will unload PlAddons via pluginLoader.
2021-09-05 17:28:49 -07:00
tastybento 938940199d Prevents console errors for missing icons in locale files. 2021-09-04 17:21:45 -07:00
tastybento 184c86bafe Go command was not working second time. 2021-09-04 17:21:25 -07:00
tastybento 0844e97530 Minor JavaDoc correction. 2021-09-04 15:43:51 -07:00
tastybento 28cecc2658 Fix test failure in InvincibleVisitorsListenerTest 2021-09-04 15:43:41 -07:00
tastybento e77a40f0cb Fix PVPListenerTest
Visitors are no longer invincible when on a PVP island.
2021-09-04 15:12:26 -07:00
tastybento 7d7882c309 Adds arrow sound when teleporting into a PVP island. 2021-09-04 14:46:41 -07:00
tastybento 74ded5a187 Fixes NPE in admin tp if nether or end worlds don't exist. 2021-09-04 14:46:26 -07:00
tastybento 7621677c93 Remove invulnerable visitor protection if island is in PVP mode
We now have alerts if a player teleports to a PVP island.

https://github.com/BentoBoxWorld/BentoBox/issues/668
2021-09-04 14:36:47 -07:00
tastybento 23522a297f Fixes coop and trust invites when team invites are not allowed
If a member or sub-owner has coop or trust invite capability and not
team invite rank, and confirm invites is on, then the invites were not
working.

Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1452
2021-09-04 13:47:44 -07:00
tastybento 0f7ca6ac60 Slimmer trimmer island info for players.
https://github.com/BentoBoxWorld/BentoBox/issues/1501
2021-09-04 10:02:37 -07:00
tastybento 547c266975 Extracted island info from the Island object.
This is in preparation to have a different info for players than admins.

https://github.com/BentoBoxWorld/BentoBox/issues/1501
2021-09-04 09:46:28 -07:00
tastybento 5bb12d53bd Prevent home teleport when already home teleporting
Home teleporting is async so it's possible to issue the command multiple
times. This puts a flag in so that if a playeer is mid-teleport, then
issuing the go command will just repeat the text that the player is
teleporting home.

https://github.com/BentoBoxWorld/BentoBox/issues/1637
2021-09-03 15:39:08 -07:00
tastybento 23857501f5
Static code analysis (#1844)
* StringBuffer (Java 5) may be declared as StringBuilder

* Replace map with flatMap

* Use instanceof naming

* No need to specify paramter types.

* Remove verbose code

* Fix JavaDoc issues

* Make internal class a record.

* Remove unused import.

* Make internal class a record.

* Made internal class a record.

* Removed unused import

* Fix typos

* Fix typo in test.
2021-08-29 18:17:38 -07:00
tastybento 9dc4ebc2d1
Make variables final if they can be. (#1843)
* Make variables final if they can be.

* Do not use final so that test can pass.

For testing, we use a trick to set this variable, but it won't work if
it is final. Right now, I'd like to keep the test.
2021-08-29 18:17:21 -07:00
tastybento a9a7673ce8 Fix German flag banner description.
Added more debug around error reporting.
2021-08-29 15:04:20 -07:00
tastybento 44f06f2911 Fix version command to show addon state. 2021-08-29 14:56:54 -07:00
BONNe 2607256c06
Panel template (#1841)
* Implement basic functionality to read data from template panels.

Create TemplateReader class that has static method which generates a PanelTemplateRecord. This record contains every necessary information from user created template file so everyone could use it to generate a functional panel.
These classes are just for reading templates and do not create actual panel.

* Add template clearing via bentobox reload command.
2021-08-29 14:34:05 -07:00
tastybento faf351fd59
Support glowing ink in blueprints (#1842)
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1837
2021-08-29 14:17:42 -07:00
BONNe fa41abc062
Implement Flag icon changing via Locales file. (#1829)
This commit contains 2 changes:
- An option for Flag to use icon that is defined in locales after "icon" string.
- An option for ItemParser to parse icon or return given value, if parsing was not successful.

The flag option is not ideal, but it is simpler and easier to maintain then adding new config section where icons can be changed, as the locales file already contains a lot of info about each flag.
2021-08-29 13:30:45 -07:00
tastybento 0341515fc8
Package info files for better JavaDocs (#1839)
* Added package info files for better JavaDocs
2021-08-29 13:28:16 -07:00
tastybento ed4cc0eb8b Update to 3.3.1-SNAPSHOT for shade plugin. 2021-08-29 13:00:48 -07:00
tastybento 773f0391cf
Corrects JavaDocs link
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1838
2021-08-25 17:41:31 -07:00
tastybento 876ba91837 Set the default game mode when player makes island. 2021-08-23 19:40:46 -07:00
tastybento 6c75733417 Fix color test. 2021-08-21 09:23:18 -07:00
Fredthedoggy 705693f904
Add Citizens to Softdepend list for NPC support (#1834) 2021-08-21 11:43:42 +02:00
BONNe c635647f85
Fix @since for subflags. (#1831) 2021-08-19 12:10:44 -07:00
tastybento b906f5561a
Hex pr 1820 (#1822) 2021-08-19 12:09:23 -07:00
tastybento e0d6e4df30
Merge branch 'master' into develop 2021-08-15 17:08:52 -07:00
tastybento 32638e7a87
Prevents repeated portaling when nether is disabled. (#1826)
* Prevents repeated portaling when nether is disabled.

https://github.com/BentoBoxWorld/BentoBox/issues/1782

* Fix test
2021-08-15 17:05:20 -07:00
tastybento 233c058bfe Warns visitors that PVP is active if they teleport to an island
https://github.com/BentoBoxWorld/BentoBox/issues/1780
2021-08-14 21:02:53 -07:00
tastybento 6b839d9c69 Prevent bucket duping when scooping obsidian Part 35
https://github.com/BentoBoxWorld/BentoBox/issues/1825
2021-08-14 16:23:13 -07:00
tastybento 7cadebc792 Added Pladdon to loadbefore. 2021-08-12 18:23:00 -07:00
tastybento cc974c795f Added deprecation suppression. 2021-08-08 18:41:43 -07:00
BONNe 56a1fdb55a
Improve ItemParser code. (#1821)
* Improve ItemParser code.

Add ability to parse text if Material is just a single string.
Add ability to parse player heads.

Add comments to the code.

* Fixes failing test.
2021-08-08 10:05:14 -07:00
tastybento cf8df9c2a8 Make spawn protection area square instead of circle.
https://github.com/BentoBoxWorld/BentoBox/issues/1819
2021-08-07 20:14:40 -07:00
tastybento 27e9c9c54f Update JavaDoc version to 16 2021-07-31 10:16:23 -07:00
tastybento 1bd11bbff6 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-07-31 08:48:58 -07:00
tastybento acfc001cd6
Java upgrade (#1814)
* Version 1.17.3

* Use Map.of and List.of instead of Immutable collections

* Replace explicit type argument.

* Replace lamba with method reference

* Replace condition with Objects.requireNonNullElseGet

* Use String.repeat

* Use new switch expressions

* Use instanceof patten variables which are more compact

* Fuse toUnmodifiableList into stream and return it.

* Remove unnecessary toString() calls.

* Remove unnecessary public

* Extracted common part from if

* Simplify conditional expressions

* Remove unused IOExceptions

* Cast to long

* Use Map.putAll

* Use primitives

* Clarify what is null or not

* Addedd @Serial annotation introduced with Java 14.

* Use Optional.isEmpty instead of !isPresent

* Use flatMap then ifPresent

* Just use Arrays.stream

* Swap map and filter for null with Objects::nonNull

* Use expression lambda
2021-07-31 08:48:26 -07:00
tastybento f89050fe2f Version 1.17.3 2021-07-25 10:54:19 -07:00
tastybento 1a7b7cdeb7
1.17.2 Release (#1812)
* Version 1.17.2

* Add alternative Addon#getIslandManager method (#1797)

We have already done this to the main BentoBox class. But add-ons missed it, and it still has this weird structure: Addon#getIslands().getIslands().

This will just add the same method as it is already in BentoBox class.

* Fix bucket dupe (#1806)

Fix https://discord.com/channels/272499714048524288/310623455462686720/867790395442462760

* Removes unneeded exploit protection code for skulls (#1810)

This no longer seems to be required with 1.17.1

https://github.com/BentoBoxWorld/BSkyBlock/issues/430

* Quote filename of addon that cannot be loaded.

Provides a better understanding of which addon failed.

* Remove update when pasting chest.

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
Co-authored-by: Justin <jstnf@users.noreply.github.com>
Co-authored-by: gecko10000 <60494179+levtey@users.noreply.github.com>
2021-07-25 10:52:03 -07:00
tastybento 5d7c199d12 Merge branch 'master' into develop
Conflicts:
	pom.xml
2021-07-25 10:43:24 -07:00
tastybento b5725e7107 Remove update when pasting chest. 2021-07-25 10:23:03 -07:00
tastybento 6bac200ac0 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-07-25 09:32:05 -07:00
tastybento 3b136f0d68 Quote filename of addon that cannot be loaded.
Provides a better understanding of which addon failed.
2021-07-25 09:31:53 -07:00
tastybento 9b1d61659b
Removes unneeded exploit protection code for skulls (#1810)
This no longer seems to be required with 1.17.1

https://github.com/BentoBoxWorld/BSkyBlock/issues/430
2021-07-24 12:27:35 -07:00
gecko10000 21fed6ebfe
Fix bucket dupe (#1806)
Fix https://discord.com/channels/272499714048524288/310623455462686720/867790395442462760
2021-07-23 21:01:40 -07:00
BONNe 2044c73f05
Add alternative Addon#getIslandManager method (#1797)
We have already done this to the main BentoBox class. But add-ons missed it, and it still has this weird structure: Addon#getIslands().getIslands().

This will just add the same method as it is already in BentoBox class.
2021-07-23 19:32:00 -07:00
tastybento 09d6a9fc6f Version 1.17.2 2021-07-14 08:03:02 -07:00
tastybento 376ac167e8
Version 1.17.1 (#1796)
* Version 1.17.1

* Add homes command to default player options.

* Implements better online player counter. (#1791)

Current one is based on online player count when bStats sends data to the server.
This one will send data about amount of players who logged in the server.

* Add Boxed Gamemode to the list. (#1793)

* Add Bank addon (#1792)

* Add Holographic Displays as SoftDepend for AOneBlock (#1794)

* Custom date time format support for /<admin> info <player> (#1783)

Fixes #1720

* Parent/sub-flag support, split up and designate CONTAINER flag as parent flag (#1784)

* Split CONTAINER flag into multiple flags

CONTAINER split into
- CONTAINER (Chest/Minecart Chest)
- BARREL (Barrel)
- COMPOSTER (Composter)
- FLOWER_POT (Flower Pot)
- SHULKER_BOX (Shulker Box)
- TRAPPED_CHEST (Trapped Chest)

Fixes #1777

* Add subflag support

* Create container parent flag, chest subflag

* Remove extra string from when CHEST was CONTAINER

* Fix incorrect flag specified on fired event in IslandToggleClick

* Add missing world subflag event firing

* Remove extra import

* Add world setting flag for island visitors keep inventory (#1785)

* Implement 3 bar charts: addons, gamemodes, hooks (#1790)

BStats supports sending Bar chart data, however, it does not display it via their site directly.
It can be called manually, to view.

PieChart does not work very well for addons and hooks. BarChart however allows viewing each addon separately. 

This change allows sending data to the server about bar charts.

* Update action versions

* Declare distribution - adopt

* Cache Java Maven files differently.

* Try dependency for shade snapshot

* Add support for Minecraft 1.17.1

* Fix test by incrementing listener value check

There is a new listener now.

* Spigot 1.17.1

* Fix test for InventoryListener

* Re-order repos.

* Downgrading to 1.17 Spigot for now to enable building.

* Try pluginRespositories tag in POM to enable Github actions

Co-authored-by: BONNe <bonne@bonne.id.lv>
Co-authored-by: Fredthedoggy <45927799+Fredthedoggy@users.noreply.github.com>
Co-authored-by: Justin <jstnf@users.noreply.github.com>
2021-07-10 18:27:57 -07:00
tastybento 9cdebe8f9f Try pluginRespositories tag in POM to enable Github actions 2021-07-08 17:18:59 -07:00
tastybento 03f6b95a68 Downgrading to 1.17 Spigot for now to enable building. 2021-07-06 18:40:38 -07:00
tastybento e1c08107c2 Re-order repos. 2021-07-06 18:16:21 -07:00
tastybento 3d1481fec5 Fix test for InventoryListener 2021-07-06 15:15:35 -07:00
tastybento ce4b124279 Spigot 1.17.1 2021-07-06 14:55:48 -07:00
tastybento 9e3a57248a Fix test by incrementing listener value check
There is a new listener now.
2021-07-06 14:55:19 -07:00
tastybento 0845722150 Add support for Minecraft 1.17.1 2021-07-06 14:54:43 -07:00
tastybento 3450bae8f0 Try dependency for shade snapshot 2021-07-06 14:07:18 -07:00
tastybento 4da61f6849 Cache Java Maven files differently. 2021-07-06 14:04:28 -07:00
tastybento 70a2c20e51 Declare distribution - adopt 2021-07-06 13:58:17 -07:00
tastybento ed0084f2c4 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-07-06 13:54:54 -07:00
tastybento dfca46abf1 Update action versions 2021-07-06 13:54:44 -07:00
BONNe 69f7b49469
Implement 3 bar charts: addons, gamemodes, hooks (#1790)
BStats supports sending Bar chart data, however, it does not display it via their site directly.
It can be called manually, to view.

PieChart does not work very well for addons and hooks. BarChart however allows viewing each addon separately. 

This change allows sending data to the server about bar charts.
2021-07-06 13:50:58 -07:00
Justin 759ba522f4
Add world setting flag for island visitors keep inventory (#1785) 2021-07-06 13:47:35 -07:00
Justin 0f7866a00b
Parent/sub-flag support, split up and designate CONTAINER flag as parent flag (#1784)
* Split CONTAINER flag into multiple flags

CONTAINER split into
- CONTAINER (Chest/Minecart Chest)
- BARREL (Barrel)
- COMPOSTER (Composter)
- FLOWER_POT (Flower Pot)
- SHULKER_BOX (Shulker Box)
- TRAPPED_CHEST (Trapped Chest)

Fixes #1777

* Add subflag support

* Create container parent flag, chest subflag

* Remove extra string from when CHEST was CONTAINER

* Fix incorrect flag specified on fired event in IslandToggleClick

* Add missing world subflag event firing

* Remove extra import
2021-07-06 13:41:23 -07:00
Justin f88b8d4d6d
Custom date time format support for /<admin> info <player> (#1783)
Fixes #1720
2021-07-06 13:33:10 -07:00
Fredthedoggy 7de6563ece
Add Holographic Displays as SoftDepend for AOneBlock (#1794) 2021-07-06 13:30:54 -07:00
BONNe 89625a78b8
Add Bank addon (#1792) 2021-07-06 15:23:28 +02:00
BONNe 4139c5f252
Add Boxed Gamemode to the list. (#1793) 2021-07-06 15:23:09 +02:00
BONNe 3a1ec0a570
Implements better online player counter. (#1791)
Current one is based on online player count when bStats sends data to the server.
This one will send data about amount of players who logged in the server.
2021-07-06 15:22:44 +02:00
tastybento 19ddd73204 Add homes command to default player options. 2021-07-05 18:31:06 -07:00
tastybento 85a9e44c4d Revert "Revert "Revert "WIP prevent repeated teleports in portals"""
This reverts commit 17d9271369.
2021-06-27 16:55:14 -07:00
tastybento 17d9271369 Revert "Revert "WIP prevent repeated teleports in portals""
This reverts commit e0e1f2878c.
2021-06-27 16:53:08 -07:00
tastybento 1ee83cd782 Version 1.17.1 2021-06-27 16:50:23 -07:00
tastybento e0e1f2878c Revert "WIP prevent repeated teleports in portals"
This reverts commit 2a5d8c14e3.
2021-06-20 16:29:24 -07:00
tastybento c89fd03833 Fixed tests 2021-06-20 16:24:36 -07:00
tastybento a0f2f11499 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-06-20 12:19:09 -07:00
tastybento c2e832095a
Minecraft 1.17 support (#1781)
* Java 16 preliminary branch.

This compiles, but junit tests fail all over because there is a lack of
Java 16 support right now.

* Use Java 16

* 1.17 WIP

* Require 1.17

* Fixes tests.

* WIP fixing tests

* Exclude maven from shading

* Try update snapshots to build action

* Try repo on command line

* Remove reference to shade plugin in dependencies
2021-06-20 12:18:10 -07:00
tastybento 2a5d8c14e3 WIP prevent repeated teleports in portals 2021-06-19 09:59:20 -07:00
tastybento cc43dbfabe Fix slime split test as it is allowed. 2021-06-12 09:25:29 -07:00
gecko10000 7768cc69cc
Remove SLIME_SPLIT spawn reason from natural mob spawning flag (#1774) 2021-06-08 10:40:20 -07:00
Korinocho a91203d439
Update es.yml (#1775)
* Update es.yml

Many syntax and semantic fixes

* Update es.yml
2021-06-08 10:39:34 -07:00
tastybento 044a0c5c0d Fix JavaDoc 2021-05-31 16:01:21 -07:00
tastybento c3b25c92b5 Added JavaDoc 2021-05-31 16:01:12 -07:00
tastybento d647f58e36 Version 1.17.0 2021-05-31 12:44:01 -07:00
tastybento 6e9513f2ea
Enable plugins to register Addons (#1768)
* Enable plugins to register Addons

* Pladdon approach. Loads addons as plugins.

* Added auto-move for pladdons.
2021-05-31 12:42:54 -07:00
tastybento 9fc22aa8e7 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-05-28 17:25:03 -07:00
tastybento bedeb7c168 Addd GSON enum type adapter for compatibility with Mohist
https://github.com/BentoBoxWorld/BentoBox/issues/1766
2021-05-28 17:24:51 -07:00
gitlocalize-app[bot] 7f8297475d
Total translation rate that appears to be incorrect in the system (#1765)
* Translate tr.yml via GitLocalize
Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com>
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: ZyXpe <thechefsmaster@gmail.com>
Co-authored-by: Over_Brave <soncesurlar@gmail.com>
Co-authored-by: trafoziya <omerziyaakay@gmail.com>
2021-05-28 16:32:30 -07:00
tastybento e472e07c34 Fixes Invincible Visitor settings panel
https://github.com/BentoBoxWorld/BentoBox/issues/1762
2021-05-22 17:41:47 -07:00
tastybento a1a78de96f Version 1.16.3 2021-05-22 16:37:51 -07:00
gitlocalize-app[bot] 03417a78f7
Translate tr.yml via GitLocalize (#1761)
Co-authored-by: trafoziya <omerziyaakay@gmail.com>
2021-05-22 16:29:33 -07:00
Fredthedoggy ca39e05fc7
Allow Non-Bentobox commands as default player (commands) (#1760)
* Update ServerCompatibility.java

* Update Default Player Command to support non-bentobox commands
2021-05-15 13:20:09 -07:00
tastybento 64c83f809c Adjusted default command for backwards compatibility 2021-05-13 08:01:22 -07:00
tastybento 36d0a07e35 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-05-13 07:37:52 -07:00
tastybento 2bc35c8936 Runs player go and create commands as direct calls.
Avoids performCommand because it can be trapped as a cheat because it
executes too fast. Allows use of non-subcommands.
2021-05-13 07:37:41 -07:00
Fredthedoggy 022d343295
Update Plugin.yml for new Permissions Addon (#1758) 2021-05-11 21:36:59 -07:00
tastybento 10a51a5874 Use https for placeholder api repo 2021-05-11 21:36:03 -07:00
tastybento 982dc2fd52 Require non null getCenter
This will throw a big runtime error if range is adjusted without a
center location being defined. Should only affect addons like the
Converter.
2021-05-11 21:29:44 -07:00
tastybento 17af44e67a Fix GameModePlaceholderTest 2021-05-09 13:10:37 -07:00
tastybento 839f05de71 Fix maxteamsize placeholders
The placeholders were not using the correct API call and so were
reporting based on the player's permission and not the actual island's
maxteam size.

https://github.com/BentoBoxWorld/BentoBox/issues/1753
2021-05-07 12:36:12 -07:00
Fredthedoggy 9ed0620bb0
Update ServerCompatibility.java (#1751) 2021-05-01 09:37:11 -07:00
tastybento 09bac48b5e Adjusted portal search radius to 64. 2021-04-25 09:35:45 -07:00
tastybento 352c6dec84 Use home names instead of numbers in locale 2021-04-24 10:50:23 -07:00
gitlocalize-app[bot] 3242a497db
NL Translation (#1748)
* Translate nl.yml via GitLocalize

* Translate nl.yml via GitLocalize

* Update nl.yml

* Update nl.yml

Co-authored-by: tastybento <tastybento@wasteofplastic.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: tastybento <tastybento@users.noreply.github.com>
2021-04-24 10:45:25 -07:00
tastybento b1ab2ecece Corrected since reference, removed debug. 2021-04-24 09:02:47 -07:00
tastybento 33b49a2349 Set minimum portal search radius to 8. Added config option.
https://github.com/BentoBoxWorld/BentoBox/issues/1747
2021-04-24 08:45:05 -07:00
fredthedoggy 2a1d9fcc61
Add WildStacker as SoftDepend to plugin.yml (#1742) 2021-04-18 13:47:07 -07:00
tastybento ac8458fd3c Add IslandHomesCommand
https://github.com/BentoBoxWorld/BentoBox/issues/1737
2021-04-07 21:36:49 -07:00
tastybento 2098fd636a Fix home listing on error.
https://github.com/BentoBoxWorld/BentoBox/issues/1737
2021-04-07 21:20:48 -07:00
tastybento 3e068e4283 Fix missing fields in copy constructor for Island class. 2021-04-03 16:33:55 -07:00
tastybento fb2c9c0553 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-03-27 18:21:45 -07:00
tastybento c85f3b9af2 Fix test 2021-03-27 18:21:32 -07:00
BONNe dc829786d3
Fixes a bug with Commands not being able to find subcommand if defined label contains capital letter. (#1724) 2021-03-27 18:19:44 -07:00
tastybento 9220f9652b Fix broken tests. 2021-03-27 17:20:43 -07:00
tastybento 609322d582 Remove debug. 2021-03-27 17:05:13 -07:00
tastybento 4b9b6774ea Teleport based on protection center and world height. 2021-03-27 16:57:15 -07:00
tastybento c6e2a9bd66 Admin teleport now goes to island spawn locations if they exist 2021-03-27 09:23:24 -07:00
Qumoo 9db7721016
Updated german language file (#1727)
* Updated german language file

Fixed typo, fixed consistency (beacon = english, Leuchtfeuer = german)

* Updated german language filede.yml

fixed typos, translated messages that were still in English
2021-03-24 22:18:16 -07:00
tastybento ed46695546 Added teleport method test to DelayedTeleportCommandTest. 2021-03-21 15:58:10 -07:00
tastybento 3c4b7deb75 Treat teleporting as moving for delayed commands.
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1722
2021-03-21 15:50:47 -07:00
Florian CUNY 8ecfee4359
Added since javadoc to getIslandsManager and getPlayersManager()
Completes 2bef0fe57c
2021-03-21 22:08:21 +01:00
tastybento 9e0fdbd210 Add Util method to enable multi-lingual broadcasts. 2021-03-21 09:04:30 -07:00
tastybento 1bc58facee Version 1.16.2 2021-03-20 08:07:25 -07:00
tastybento 99e6e11a4a Disable LangUtilsHook for now until @Poslovitch can review. 2021-03-19 15:18:44 -07:00
tastybento 8f767d5e44 Fix tests - event is canceled now.
Change done here: 592b4e3d1f
2021-03-18 18:45:05 -07:00
tastybento 87bdee0946 Return island protection center instead of island logical center
IslandsManager.getIslandLocation(world, uuid) is used to find the
starting point for the island home if a safe home for players cannot be
found. It returns the island center location but should return the
center of the island protection because that can now be in a different
location.
2021-03-17 15:18:55 -07:00
apachezy b8d9b73103
Added DamageCause into zh-CN.yml and zh-HK.yml. (#1721)
* Added getMusicDiskDesc into LangUtilsHook.

* Added DamageCause into zh-CN.yml and zh-HK.yml.

Co-authored-by: zhangYi <apachezy@hotmail.com>
2021-03-16 19:08:16 -07:00
BONNe 592b4e3d1f
Fixes player teleportation in void. (#1716)
The issue happens mostly with the end portals. Apparently, teleportation to the end is processed before the Bukkit task that runs SafeSpotTeleport. The first check if a player is in the starting world fails, and SafeSpotTeleport is not called. 

I set the event to be cancelled in all situations when we do not create portals, so it always triggers SafeSpotTeleport. This should fix the bug with players appearing in the void.
2021-03-16 19:07:36 -07:00
BONNe 8ce30a7cb5
Fixes spawn platform position in the end. (#1713)
In the vanilla end, the highest block in the End can be above 63. Check for just 0, 63, 0 may be incorrect for most of the end vanilla worlds.
2021-03-16 19:07:05 -07:00
apachezy 4582b23522
Added getMusicDiskDesc into LangUtilsHook. (#1715)
Co-authored-by: zhangYi <apachezy@hotmail.com>
2021-03-15 14:27:28 +01:00
BONNe cf5483e49d
Fixed end platform generation (#1717)
If a player has end portal at Y=0 or Y=1, then they were spawned in the void, if makePortals option was enabled.
It happens because end portals are generated with 2 air layers above the obsidian platform. So minimal location is necessary to be at least 2 (2 for air and obsidian at 0).
2021-03-15 14:23:25 +01:00
apachezy 17d5b7392b
Additional improvements to zh-CN (#1714)
Co-authored-by: zhangYi <apachezy@hotmail.com>
2021-03-15 14:20:54 +01:00
tastybento 69b059f423 Merge branch 'develop' of https://github.com/BentoBoxWorld/BentoBox.git into develop 2021-03-14 18:20:52 -07:00
tastybento d70b07c273 Date formatting was choking sometimes. This is simpler. 2021-03-14 18:20:36 -07:00
tastybento 6c775329d0 Fix InvincibleVisitorsListenerTest. 2021-03-14 13:48:31 -07:00
tastybento 74023914a7 Fixed enum locale for English and Japanese.
Sort based on translated name in InvincibleVisitors.
2021-03-14 11:57:55 -07:00
gitlocalize-app[bot] 487fa6af8e
Translate ja.yml via GitLocalize (#1711)
Co-authored-by: tastybento <tastybento@wasteofplastic.com>
2021-03-14 11:24:49 -07:00
tastybento 0769af0c65 Remove debug lines. 2021-03-14 10:29:02 -07:00
tastybento d97d15d47a Added locale strings for the DamageCause enum. Used in InvincibleVistors
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1705
2021-03-14 10:18:33 -07:00
tastybento 1feaddf704 Avoid loading or enabling disabled addons. 2021-03-13 15:00:49 -08:00
tastybento 45bcf64da5 Added IslandNewIslandEvent
Gets fired when a player is teleported to their new island for the first
time.
2021-03-13 10:47:02 -08:00
apachezy 4544cada3a
Improved zh-CN (#1710)
* Improved zh-CN.yml

* Delete an error.

* days-one-or-more #1707

* Patch NO.1

Co-authored-by: zhangYi <apachezy@hotmail.com>
2021-03-13 18:49:57 +01:00
apachezy e36dc68ccd
Improved zh-CN.yml (#1708)
* Improved zh-CN.yml

* Delete an error.

* days-one-or-more #1707

Co-authored-by: zhangYi <apachezy@hotmail.com>
2021-03-13 08:44:19 -08:00
apachezy 0f0d8b9ebc
Added LangUtils support. (#1693) 2021-03-12 20:04:34 -08:00
poslovitch 40d1c318d5 Fixed purge command "one day or more" message
Fixes https://github.com/BentoBoxWorld/BentoBox/issues/1707
2021-03-12 22:02:16 +01:00
tastybento 031d789896 Enable to use command line to set spawn island settings.
https://github.com/BentoBoxWorld/BentoBox/issues/1703
2021-03-09 22:18:48 -08:00
tastybento 9f9dc40773 Version 1.16.1 2021-03-07 11:53:12 -08:00
tastybento 6849c2a8bd Delete backup database during tests. 2021-03-07 11:34:30 -08:00
tastybento 7d6cce6563 Minor Java doc addition to clarify getAddon() 2021-03-07 10:09:18 -08:00
Huynh Tien 5142894127
rework on Vietnamese translation (#1671)
* rework on Vietnamese translation
2021-03-07 09:29:58 -08:00
tastybento 9c1a239501 Fixed bug where AllLoaded method was not being called on BentoBox reload 2021-03-06 15:08:54 -08:00
622 changed files with 59647 additions and 20638 deletions

View File

@ -10,30 +10,35 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 8
uses: actions/setup-java@v1
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: 8
distribution: 'adopt'
java-version: '21'
- name: Cache SonarCloud packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build
run: mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=BentoBoxWorld_BentoBox
- run: mvn --batch-mode clean org.jacoco:jacoco-maven-plugin:prepare-agent install
- run: mkdir staging && cp target/*.jar staging
- name: Save artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: Package
path: staging

3
.gitignore vendored
View File

@ -84,4 +84,5 @@ dist/
nbdist/
nbactions.xml
nb-configuration.xml
.nb-gradle/
.nb-gradle/
/BentoBox/

View File

@ -1,23 +0,0 @@
language: java
sudo: false
addons:
sonarcloud:
organization: "bentobox-world"
jdk:
- openjdk8
- openjdk11
matrix:
allow_failures:
- jdk: openjdk11
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh && . ./install-jdk.sh -F 11 -L GPL
- mvn sonar:sonar -Dsonar.projectKey=BentoBoxWorld_BentoBox
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'

View File

@ -1,4 +1,5 @@
The following is a list of all addons currently made for BentoBox:
* [**Bank**](https://github.com/BentoBoxWorld/Bank/): Provides an island bank to enable island members to share money.
* [**Biomes**](https://github.com/BentoBoxWorld/Biomes/): Enables players to change biomes on islands.
* [**Border**](https://github.com/BentoBoxWorld/Border/): Adds a world border around islands.
* [**Cauldron Witchery**](https://github.com/BentoBoxWorld/CauldronWitchery/): Allows summoning mobs using some magic!

View File

@ -2,7 +2,6 @@
[![Discord](https://img.shields.io/discord/272499714048524288.svg?logo=discord)](https://discord.bentobox.world)
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/BentoBox)](https://ci.codemc.org/job/BentoBoxWorld/job/BentoBox/)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=ncloc)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=BentoBoxWorld_BentoBox&metric=security_rating)](https://sonarcloud.io/dashboard?id=BentoBoxWorld_BentoBox)
@ -25,6 +24,7 @@ Start now to create the server you've dreamed of!
These are some popular Gamemodes:
* [**AcidIsland**](https://github.com/BentoBoxWorld/AcidIsland): You are marooned in a sea of acid!
* [**AOneBlock**](https://github.com/BentoBoxWorld/AOneBlock): Start to play with only 1 magical block.
* [**Boxed**](https://github.com/BentoBoxWorld/Boxed): A game mode where you are boxed into a tiny space that only expands by completing advancements.
* [**BSkyBlock**](https://github.com/BentoBoxWorld/BSkyBlock): The successor to the popular ASkyBlock.
* [**CaveBlock**](https://github.com/BentoBoxWorld/CaveBlock): Try to live underground!
* [**SkyGrid**](https://github.com/BentoBoxWorld/SkyGrid): Survive in world made up of scattered blocks - what an adventure!
@ -37,7 +37,7 @@ There are also plenty of other official or community-made Addons you can try and
## Documentation
* Start reading: [https://docs.bentobox.world](https://docs.bentobox.world)
* For developers: [Javadocs](https://bentoboxworld.github.io/BentoBox/)
* For developers: [Javadocs](https://ci.codemc.io/job/BentoBoxWorld/job/BentoBox/ws/target/apidocs/index.html)
## Downloads
@ -84,6 +84,10 @@ BentoBox uses Maven, and its Maven repository is kindly provided by [CodeMC](htt
### Maven
```xml
<repositories>
<repository>
<id>codemc-snapshots</id>
<url>https://repo.codemc.org/repository/maven-snapshots</url>
</repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
@ -107,9 +111,10 @@ repositories {
}
dependencies {
compileOnly 'world.bentobox:bentobox:PUT-VERSION-HERE'
compileOnly 'world.bentobox:bentobox:PUT-VERSION-HERE-SNAPSHOT'
}
```
**Note:** Due to a Gradle issue with versions for Maven, you need to use -SNAPSHOT at the end.
### History

328
pom.xml
View File

@ -63,27 +63,35 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>17</java.version>
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.4</powermock.version>
<mongodb.version>3.8.0</mongodb.version>
<powermock.version>2.0.9</powermock.version>
<!-- Database related dependencies -->
<mongodb.version>3.12.12</mongodb.version>
<mariadb.version>3.0.5</mariadb.version>
<mysql.version>8.0.27</mysql.version>
<postgresql.version>42.2.18</postgresql.version>
<hikaricp.version>5.0.1</hikaricp.version>
<!-- More visible way to change dependency versions -->
<spigot.version>1.16.4-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.20.5-R0.1-SNAPSHOT</spigot.version>
<!-- Might differ from the last Spigot release for short periods
of time -->
<paper.version>1.16.4-R0.1-SNAPSHOT</paper.version>
<bstats.version>2.1.0</bstats.version>
<vault.version>1.7</vault.version>
<paper.version>1.20.6-R0.1-SNAPSHOT</paper.version>
<bstats.version>3.0.0</bstats.version>
<vault.version>1.7.1</vault.version>
<placeholderapi.version>2.10.9</placeholderapi.version>
<githubapi.version>d5f5e0bbd8</githubapi.version>
<dynmap.version>3.0-SNAPSHOT</dynmap.version>
<worldedit.version>7.0.0</worldedit.version>
<myworlds.version>1.19.3-v1</myworlds.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.16.0</build.version>
<build.version>2.4.0</build.version>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<server.jars>${project.basedir}/lib</server.jars>
</properties>
<!-- Profiles will allow to automatically change build version. -->
@ -119,6 +127,7 @@
<value>origin/master</value>
</property>
</activation>
<!--suppress CheckTagEmptyBody -->
<properties>
<!-- Override only if necessary -->
<revision>${build.version}</revision>
@ -126,34 +135,20 @@
<build.number></build.number>
</properties>
</profile>
<profile>
<id>sonar</id>
<properties>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>bentobox-world</sonar.organization>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<!-- was <version>3.4.1.1168</version> -->
<version>3.6.0.1398</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sonar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<pluginRepositories>
<pluginRepository>
<id>apache.snapshots</id>
<url>https://repository.apache.org/snapshots/</url>
</pluginRepository>
</pluginRepositories>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
@ -162,64 +157,60 @@
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>placeholderapi-repo</id>
<url>http://repo.extendedclip.com/content/repositories/placeholderapi/</url>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>dynmap-repo</id>
<url>https://repo.mikeprimm.com/</url>
</repository>
<repository>
<id>worldedit-repo</id>
<url>https://maven.sk89q.com/repo/</url>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
<!-- This is a temporary reference as the Maven Shade plugin
that supports Java 16 is not released yet -->
<id>maven-snapshots</id>
<url>https://repository.apache.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net/</url>
</repository>
<!-- Spigot NMS required for world regeneration :( -->
<repository>
<id>nms-repo</id>
<url>https://repo.codemc.io/repository/nms/</url>
</repository>
<!-- Used for MyWorlds hook -->
<repository>
<id>MG-Dev Jenkins CI Maven Repository</id>
<url>https://ci.mg-dev.eu/plugin/repository/everything</url>
</repository>
<!-- For MythicMobs -->
<repository>
<id>nexus</id>
<name>Lumine Releases</name>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<!-- For Multipaper -->
<repository>
<id>clojars</id>
<url>https://repo.clojars.org/</url>
</repository>
</repositories>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- Paper API -->
<dependency>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<!-- Spigot NMS. Used for Head Getter and chunk deletion. -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>${bstats.version}</version>
</dependency>
<!-- Mockito (Unit testing) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<!-- Mockito (Unit testing) This goes at the top to ensure the dependencies are accurate. -->
<!-- This is required for PowerMockito to work and must be placed before it -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.30.2-GA</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
@ -231,16 +222,69 @@
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc....</groupId>
<artifactId>spigot</artifactId>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc.</groupId>
<artifactId>spigot</artifactId>
<version>1.20.3-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc..</groupId>
<artifactId>spigot</artifactId>
<version>1.20.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc...</groupId>
<artifactId>spigot</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Paper API -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>${bstats.version}</version>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>${mongodb.version}</version>
<scope>provided</scope>
</dependency>
<!-- HikariCP database handler -->
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901-1.jdbc4</version>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikaricp.version}</version>
<scope>provided</scope>
</dependency>
<!-- Vault: as their maven repo is down, we need to get it from jitpack -->
<!-- See https://github.com/MilkBowl/VaultAPI/issues/69 -->
@ -265,9 +309,15 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-core</artifactId>
<version>${worldedit.version}</version>
<groupId>com.bergerkiller.bukkit</groupId>
<artifactId>MyWorlds</artifactId>
<version>${myworlds.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>Mythic-Dist</artifactId>
<version>5.3.5</version>
<scope>provided</scope>
</dependency>
<!-- Shaded APIs -->
@ -276,6 +326,11 @@
<artifactId>GitHubWebAPI4Java</artifactId>
<version>${githubapi.version}</version>
</dependency>
<dependency>
<groupId>com.github.Marcono1234</groupId>
<artifactId>gson-record-type-adapter-factory</artifactId>
<version>0.3.0</version>
</dependency>
<!-- Static analysis -->
<!-- We are using Eclipse's annotations. If you're using IDEA, update
your project settings to take these into account for in real time static
@ -283,7 +338,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.annotation</artifactId>
<version>2.2.200</version>
<version>2.2.600</version>
</dependency>
<!-- PaperLib -->
<dependency>
@ -292,6 +347,41 @@
<version>1.0.6</version>
<scope>compile</scope>
</dependency>
<!-- LangUtils -->
<dependency>
<groupId>com.github.apachezy</groupId>
<artifactId>LangUtils</artifactId>
<version>3.2.2</version>
<scope>provided</scope>
</dependency>
<!-- Spigot NMS. Used for chunk deletion and pasting.-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<!-- Slimefun -->
<dependency>
<groupId>com.github.Slimefun</groupId>
<artifactId>Slimefun4</artifactId>
<version>RC-37</version>
<scope>provided</scope>
</dependency>
<!-- ItemsAdder -->
<dependency>
<groupId>com.github.LoneDev6</groupId>
<artifactId>api-itemsadder</artifactId>
<version>3.6.3-beta-14</version>
<scope>provided</scope>
</dependency>
<!-- Multipaper -->
<dependency>
<groupId>com.github.puregero</groupId>
<artifactId>multilib</artifactId>
<version>1.1.13</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@ -326,21 +416,48 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<release>${java.version}</release>
<!-- <source>${java.version}</source> <target>${java.version}</target> -->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<version>3.0.0-M5</version>
<!--suppress MavenModelInspection -->
<configuration>
<argLine>
${argLine}
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/java.util.regex=ALL-UNNAMED
--add-opens java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
--add-opens java.base/java.nio.file=ALL-UNNAMED
--add-opens java.base/java.nio.charset=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/java.util.zip=ALL-UNNAMED
--add-opens=java.base/java.security=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -350,19 +467,20 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<version>3.4.1</version>
<configuration>
<source>8</source>
<source>${java.version}</source>
<show>private</show>
<quiet>true</quiet>
<failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption>
<!-- To compile with Java 11, this tag may be required -->
<!-- <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable> -->
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>install</phase>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
@ -386,9 +504,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<version>3.4.0</version>
<configuration>
<minimizeJar>true</minimizeJar>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
@ -400,11 +519,17 @@
</relocation>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>world.bentobox.bentobox.paperlib</shadedPattern> <!-- Replace this -->
<shadedPattern>world.bentobox.bentobox.paperlib</shadedPattern>
</relocation>
</relocations>
<relocation>
<pattern>com.github.puregero.multilib</pattern>
<shadedPattern>world.bentobox.bentobox.multilib</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>org.apache.maven.shared:*</exclude>
<exclude>org.apache.maven:*</exclude>
<exclude>com.google.code.gson:*</exclude>
<exclude>org.mongodb:*</exclude>
<exclude>org.eclipse.jdt:*</exclude>
@ -442,27 +567,34 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.4</version>
<version>0.8.10</version>
<configuration>
<append>true</append>
<excludes>
<!-- This is required to prevent Jacoco from adding
synthetic fields to a JavaBean class (causes errors in testing) -->
<exclude>**/*Names*</exclude>
<!-- Prevents the Material is too large to mock error -->
<exclude>org/bukkit/Material*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>pre-unit-test</id>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution>
</executions>
</plugin>

View File

@ -1,16 +1,18 @@
package world.bentobox.bentobox;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.SimpleBarChart;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.flags.Flag;
/**
* @author Poslovitch
@ -29,6 +31,13 @@ public class BStats {
*/
private int islandsCreatedCount = 0;
/**
* Contains the amount of connected players since last data send.
* @since 1.17.1
*/
private final Set<UUID> connectedPlayerSet = new HashSet<>();
BStats(BentoBox plugin) {
this.plugin = plugin;
}
@ -48,11 +57,15 @@ public class BStats {
registerGameModeAddonsChart();
registerHooksChart();
registerPlayersPerServerChart();
registerFlagsDisplayModeChart();
// Single Line charts
registerIslandsCountChart();
registerIslandsCreatedChart();
// Bar Charts
registerAddonsBarChart();
registerGameModeAddonsBarChart();
registerHooksBarChart();
}
private void registerDefaultLanguageChart() {
@ -86,6 +99,15 @@ public class BStats {
islandsCreatedCount++;
}
/**
* Adds given UUID to the connected player set.
* @param uuid UUID of a player who logins.
* @since 1.17.1
*/
public void addPlayer(UUID uuid) {
this.connectedPlayerSet.add(uuid);
}
/**
* Sends the enabled addons (except GameModeAddons) of this server.
* @since 1.1
@ -132,7 +154,9 @@ public class BStats {
*/
private void registerPlayersPerServerChart() {
metrics.addCustomChart(new SimplePie("playersPerServer", () -> {
int players = Bukkit.getOnlinePlayers().size();
int players = this.connectedPlayerSet.size();
this.connectedPlayerSet.clear();
if (players <= 0) return "0";
else if (players <= 10) return "1-10";
else if (players <= 30) return "11-30";
@ -145,22 +169,41 @@ public class BStats {
}
/**
* Sends the "flags display mode" of all the online players.
* @since 1.6.0
* Sends the enabled addons (except GameModeAddons) of this server as bar chart.
* @since 1.17.1
*/
private void registerFlagsDisplayModeChart() {
metrics.addCustomChart(new AdvancedPie("flagsDisplayMode", () -> {
private void registerAddonsBarChart() {
metrics.addCustomChart(new SimpleBarChart("addonsBar", () -> {
Map<String, Integer> values = new HashMap<>();
plugin.getAddonsManager().getEnabledAddons().stream()
.filter(addon -> !(addon instanceof GameModeAddon) && addon.getDescription().isMetrics())
.forEach(addon -> values.put(addon.getDescription().getName(), 1));
return values;
}));
}
Bukkit.getOnlinePlayers().forEach(player -> {
Flag.Mode mode = plugin.getPlayers().getFlagsDisplayMode(player.getUniqueId());
if (values.containsKey(mode.name())) {
values.put(mode.name(), values.get(mode.name()) + 1);
} else {
values.put(mode.name(), 1);
}
});
/**
* Sends the enabled GameModeAddons of this server as a bar chart.
* @since 1.17.1
*/
private void registerGameModeAddonsBarChart() {
metrics.addCustomChart(new SimpleBarChart("gameModeAddonsBar", () -> {
Map<String, Integer> values = new HashMap<>();
plugin.getAddonsManager().getGameModeAddons().stream()
.filter(gameModeAddon -> gameModeAddon.getDescription().isMetrics())
.forEach(gameModeAddon -> values.put(gameModeAddon.getDescription().getName(), 1));
return values;
}));
}
/**
* Sends the enabled Hooks of this server as a bar chart.
* @since 1.17.1
*/
private void registerHooksBarChart() {
metrics.addCustomChart(new SimpleBarChart("hooksBar", () -> {
Map<String, Integer> values = new HashMap<>();
plugin.getHooks().getHooks().forEach(hook -> values.put(hook.getPluginName(), 1));
return values;
}));
}

View File

@ -1,11 +1,15 @@
package world.bentobox.bentobox;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerCommandEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@ -20,18 +24,24 @@ import world.bentobox.bentobox.api.user.Notifier;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.BentoBoxCommand;
import world.bentobox.bentobox.database.DatabaseSetup;
import world.bentobox.bentobox.hooks.DynmapHook;
import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.MultipaperHook;
import world.bentobox.bentobox.hooks.MultiverseCoreHook;
import world.bentobox.bentobox.hooks.MyWorldsHook;
import world.bentobox.bentobox.hooks.MythicMobsHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
import world.bentobox.bentobox.hooks.VaultHook;
import world.bentobox.bentobox.hooks.WorldEditHook;
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
import world.bentobox.bentobox.listeners.BannedCommands;
import world.bentobox.bentobox.listeners.BlockEndDragon;
import world.bentobox.bentobox.listeners.DeathListener;
import world.bentobox.bentobox.listeners.JoinLeaveListener;
import world.bentobox.bentobox.listeners.PanelListenerManager;
import world.bentobox.bentobox.listeners.PortalTeleportationListener;
import world.bentobox.bentobox.listeners.PrimaryIslandListener;
import world.bentobox.bentobox.listeners.SeedWorldMakerListener;
import world.bentobox.bentobox.listeners.StandardSpawnProtectionListener;
import world.bentobox.bentobox.listeners.teleports.EntityTeleportListener;
import world.bentobox.bentobox.listeners.teleports.PlayerTeleportListener;
import world.bentobox.bentobox.managers.AddonsManager;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.CommandsManager;
@ -52,7 +62,7 @@ import world.bentobox.bentobox.versions.ServerCompatibility;
* Main BentoBox class
* @author tastybento, Poslovitch
*/
public class BentoBox extends JavaPlugin {
public class BentoBox extends JavaPlugin implements Listener {
private static BentoBox instance;
@ -66,7 +76,6 @@ public class BentoBox extends JavaPlugin {
private AddonsManager addonsManager;
private FlagsManager flagsManager;
private IslandWorldManager islandWorldManager;
private RanksManager ranksManager;
private BlueprintsManager blueprintsManager;
private HooksManager hooksManager;
private PlaceholdersManager placeholdersManager;
@ -95,6 +104,8 @@ public class BentoBox extends JavaPlugin {
@Override
public void onEnable(){
setInstance(this);
if (!ServerCompatibility.getInstance().checkCompatibility().isCanLaunch()) {
// The server's most likely incompatible.
// Show a warning
@ -103,7 +114,7 @@ public class BentoBox extends JavaPlugin {
logWarning("BentoBox is tested only on the following Spigot versions:");
List<String> versions = ServerCompatibility.ServerVersion.getVersions(ServerCompatibility.Compatibility.COMPATIBLE, ServerCompatibility.Compatibility.SUPPORTED)
.stream().map(ServerCompatibility.ServerVersion::toString).collect(Collectors.toList());
.stream().map(ServerCompatibility.ServerVersion::toString).toList();
logWarning(String.join(", ", versions));
logWarning("**************************************");
@ -116,7 +127,6 @@ public class BentoBox extends JavaPlugin {
// Save the default config from config.yml
saveDefaultConfig();
setInstance(this);
// Load Flags
flagsManager = new FlagsManager(this);
@ -134,7 +144,6 @@ public class BentoBox extends JavaPlugin {
return;
}
islandsManager = new IslandsManager(this);
ranksManager = new RanksManager();
// Start head getter
headGetter = new HeadGetter(this);
@ -159,7 +168,6 @@ public class BentoBox extends JavaPlugin {
// Load hooks
hooksManager = new HooksManager(this);
hooksManager.registerHook(new VaultHook());
// Load addons. Addons may load worlds, so they must go before islands are loaded.
addonsManager = new AddonsManager(this);
@ -172,12 +180,21 @@ public class BentoBox extends JavaPlugin {
completeSetup(loadTime);
} catch (Exception e) {
fireCriticalError(e.getMessage(), "");
e.printStackTrace();
}
});
}
private void completeSetup(long loadTime) {
final long enableStart = System.currentTimeMillis();
hooksManager.registerHook(new MultipaperHook());
hooksManager.registerHook(new VaultHook());
// MythicMobs
hooksManager.registerHook(new MythicMobsHook());
hooksManager.registerHook(new PlaceholderAPIHook());
// Setup the Placeholders manager
placeholdersManager = new PlaceholdersManager(this);
@ -199,20 +216,6 @@ public class BentoBox extends JavaPlugin {
return;
}
// Save islands & players data every X minutes
Bukkit.getScheduler().runTaskTimer(instance, () -> {
if (!playersManager.isSaveTaskRunning()) {
playersManager.saveAll(true);
} else {
getLogger().warning("Tried to start a player data save task while the previous auto save was still running!");
}
if (!islandsManager.isSaveTaskRunning()) {
islandsManager.saveAll(true);
} else {
getLogger().warning("Tried to start a island data save task while the previous auto save was still running!");
}
}, getSettings().getDatabaseBackupPeriod() * 20 * 60L, getSettings().getDatabaseBackupPeriod() * 20 * 60L);
// Make sure all flag listeners are registered.
flagsManager.registerListeners();
@ -223,11 +226,19 @@ public class BentoBox extends JavaPlugin {
// Register Multiverse hook - MV loads AFTER BentoBox
// Make sure all worlds are already registered to Multiverse.
hooksManager.registerHook(new MultiverseCoreHook());
islandWorldManager.registerWorldsToMultiverse();
hooksManager.registerHook(new MyWorldsHook());
islandWorldManager.registerWorldsToMultiverse(true);
// Register additional hooks
hooksManager.registerHook(new DynmapHook());
hooksManager.registerHook(new WorldEditHook());
// Register Slimefun
hooksManager.registerHook(new SlimefunHook());
// Register ItemsAdder
hooksManager.registerHook(new ItemsAdderHook(this));
// TODO: re-enable after implementation
//hooksManager.registerHook(new DynmapHook());
// TODO: re-enable after rework
//hooksManager.registerHook(new LangUtilsHook());
webManager = new WebManager(this);
@ -245,6 +256,8 @@ public class BentoBox extends JavaPlugin {
// Tell all addons that everything is loaded
isLoaded = true;
this.addonsManager.allLoaded();
// Run ready commands
settings.getReadyCommands().forEach(cmd -> Bukkit.getServer().dispatchCommand(getServer().getConsoleSender(), cmd));
// Fire plugin ready event - this should go last after everything else
Bukkit.getPluginManager().callEvent(new BentoBoxReadyEvent());
instance.log("All blueprints loaded.");
@ -261,7 +274,7 @@ public class BentoBox extends JavaPlugin {
}
private void fireCriticalError(String message, String error) {
logError("*****************CRITIAL ERROR!******************");
logError("*****************CRITICAL ERROR!******************");
logError(message);
logError(error + " Disabling BentoBox...");
logError("*************************************************");
@ -285,17 +298,25 @@ public class BentoBox extends JavaPlugin {
manager.registerEvents(new PanelListenerManager(), this);
// Standard Nether/End spawns protection
manager.registerEvents(new StandardSpawnProtectionListener(this), this);
// Nether portals
manager.registerEvents(new PortalTeleportationListener(this), this);
// Player portals
manager.registerEvents(new PlayerTeleportListener(this), this);
// Entity portals
manager.registerEvents(new EntityTeleportListener(this), this);
// End dragon blocking
manager.registerEvents(new BlockEndDragon(this), this);
// Banned visitor commands
manager.registerEvents(new BannedCommands(this), this);
// Death counter
manager.registerEvents(new DeathListener(this), this);
// MV unregister
manager.registerEvents(this, this);
// Island Delete Manager
islandDeletionManager = new IslandDeletionManager(this);
manager.registerEvents(islandDeletionManager, this);
// Primary Island Listener
manager.registerEvents(new PrimaryIslandListener(this), this);
// Seed world chunk generator
manager.registerEvents(new SeedWorldMakerListener(this), this);
}
@Override
@ -313,6 +334,19 @@ public class BentoBox extends JavaPlugin {
if (islandsManager != null) {
islandsManager.shutdown();
}
}
@EventHandler
public void onServerStop(ServerCommandEvent e) {
/* This is no longer needed as with https://github.com/Multiverse/Multiverse-Core/releases/tag/4.3.12 (or maybe earlier) the issue
* is fixed where the generator was not remembered across reboots.
*/
/*
if (islandWorldManager != null && (e.getCommand().equalsIgnoreCase("stop") || e.getCommand().equalsIgnoreCase("restart"))) {
// Unregister any MV worlds if () {
//islandWorldManager.registerWorldsToMultiverse(false);
}*/
}
/**
@ -328,9 +362,10 @@ public class BentoBox extends JavaPlugin {
* Returns the player manager
* @return the player manager
* @see #getPlayers()
* @since 1.16.0
*/
public PlayersManager getPlayersManager() {
return playersManager;
return getPlayers();
}
/**
@ -346,9 +381,10 @@ public class BentoBox extends JavaPlugin {
* Returns the island manager
* @return the island manager
* @see #getIslands()
* @since 1.16.0
*/
public IslandsManager getIslandsManager() {
return islandsManager;
return getIslands();
}
private static void setInstance(BentoBox plugin) {
@ -389,9 +425,11 @@ public class BentoBox extends JavaPlugin {
/**
* @return the ranksManager
* @deprecated Just use {@code RanksManager.getInstance()}
*/
@Deprecated(since = "2.0.0", forRemoval = true)
public RanksManager getRanksManager() {
return ranksManager;
return RanksManager.getInstance();
}
/**
@ -425,6 +463,17 @@ public class BentoBox extends JavaPlugin {
getPluginLoader().disablePlugin(this);
return false;
}
log("Saving default panels...");
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "island_creation_panel.yml"))) {
log("Saving default island_creation_panel...");
this.saveResource("panels/island_creation_panel.yml", false);
}
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "language_panel.yml"))) {
log("Saving default language_panel...");
this.saveResource("panels/language_panel.yml", false);
}
return true;
}

View File

@ -1,10 +1,18 @@
package world.bentobox.bentobox;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.bukkit.Material;
import com.google.common.collect.ImmutableList;
import world.bentobox.bentobox.api.configuration.ConfigComment;
import world.bentobox.bentobox.api.configuration.ConfigEntry;
import world.bentobox.bentobox.api.configuration.ConfigObject;
@ -13,34 +21,15 @@ import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
/**
* All the plugin settings are here
*
* @author tastybento
*/
@StoreAt(filename="config.yml") // Explicitly call out what name this should have.
@StoreAt(filename = "config.yml") // Explicitly call out what name this should have.
@ConfigComment("BentoBox v[version] configuration file.")
@ConfigComment("")
@ConfigComment("This configuration file contains settings that mainly apply to or manage the following elements:")
@ConfigComment(" * Data storage")
@ConfigComment(" * Gamemodes (commands, ...)")
@ConfigComment(" * Internet connectivity (web-based content-enriched features, ...)")
@ConfigComment("")
@ConfigComment("Note that this configuration file is dynamic:")
@ConfigComment(" * It gets updated with the newest settings and comments after BentoBox loaded its settings from it.")
@ConfigComment(" * Upon updating BentoBox, new settings will be automatically added into this configuration file.")
@ConfigComment(" * Said settings are distinguishable by a dedicated comment, which looks like this:")
@ConfigComment(" Added since X.Y.Z.")
@ConfigComment(" * They are provided with default values that should not cause issues on live production servers.")
@ConfigComment(" * You can however edit this file while the server is online.")
@ConfigComment(" You will therefore need to run the following command in order to take the changes into account: /bentobox reload.")
@ConfigComment("")
@ConfigComment("Here are a few pieces of advice before you get started:")
@ConfigComment(" * You should check out our Wiki, which may provide you useful tips or insights about BentoBox's features.")
@ConfigComment(" Link: https://github.com/BentoBoxWorld/BentoBox/wiki")
@ConfigComment(" * You should edit this configuration file while the server is offline.")
@ConfigComment(" * Moreover, whenever you update BentoBox, you should do so on a test server first.")
@ConfigComment(" This will allow you to configure the new settings beforehand instead of applying them inadvertently on a live production server.")
public class Settings implements ConfigObject {
/* GENERAL */
/* GENERAL */
@ConfigComment("Default language for new players.")
@ConfigComment("This is the filename in the locale folder without .yml.")
@ConfigComment("If this does not exist, the default en-US will be used.")
@ -52,10 +41,17 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "general.use-economy")
private boolean useEconomy = true;
/* COMMANDS */
@ConfigComment("Console commands to run when BentoBox has loaded all worlds and addons.")
@ConfigComment("Commands are run as the console.")
@ConfigComment("e.g. set aliases for worlds in Multiverse here, or anything you need to")
@ConfigComment("run after the plugin is fully loaded.")
@ConfigEntry(path = "general.ready-commands", since = "1.24.2")
private List<String> readyCommands = new ArrayList<>();
// Database
@ConfigComment("JSON, MYSQL, MARIADB, MONGODB, SQLITE, POSTGRESQL and YAML(deprecated).")
@ConfigComment("JSON, MYSQL, MARIADB, MONGODB, SQLITE, and POSTGRESQL.")
@ConfigComment("Transition database options are:")
@ConfigComment(" YAML2JSON, YAML2MARIADB, YAML2MYSQL, YAML2MONGODB, YAML2SQLITE")
@ConfigComment(" JSON2MARIADB, JSON2MYSQL, JSON2MONGODB, JSON2SQLITE, JSON2POSTGRESQL")
@ConfigComment(" MYSQL2JSON, MARIADB2JSON, MONGODB2JSON, SQLITE2JSON, POSTGRESQL2JSON")
@ConfigComment("If you need others, please make a feature request.")
@ -66,8 +62,9 @@ public class Settings implements ConfigObject {
@ConfigComment(" SQLite versions 3.28 or later")
@ConfigComment(" PostgreSQL versions 9.4 or later")
@ConfigComment("Transition options enable migration from one database type to another. Use /bbox migrate.")
@ConfigComment("YAML and JSON are file-based databases.")
@ConfigComment("JSON is a file-based database.")
@ConfigComment("MYSQL might not work with all implementations: if available, use a dedicated database type (e.g. MARIADB).")
@ConfigComment("BentoBox uses HikariCP for connecting with SQL databases.")
@ConfigComment("If you use MONGODB, you must also run the BSBMongo plugin (not addon).")
@ConfigComment("See https://github.com/tastybento/bsbMongo/releases/.")
@ConfigEntry(path = "general.database.type", video = "https://youtu.be/FFzCk5-y7-g")
@ -107,6 +104,11 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "general.database.max-saved-islands-per-tick")
private int maxSavedIslandsPerTick = 20;
@ConfigComment("Number of active connections to the SQL database at the same time.")
@ConfigComment("Default 10.")
@ConfigEntry(path = "general.database.max-pool-size", since = "1.21.0")
private int maximumPoolSize = 10;
@ConfigComment("Enable SSL connection to MongoDB, MariaDB, MySQL and PostgreSQL databases.")
@ConfigEntry(path = "general.database.use-ssl", since = "1.12.0")
private boolean useSSL = false;
@ -118,6 +120,16 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "general.database.prefix-character", since = "1.13.0")
private String databasePrefix = "";
@ConfigComment("Custom connection datasource properties that will be applied to connection pool.")
@ConfigComment("Check available values to your SQL driver implementation.")
@ConfigComment("Example: ")
@ConfigComment(" custom-properties: ")
@ConfigComment(" cachePrepStmts: 'true'")
@ConfigComment(" prepStmtCacheSize: '250'")
@ConfigComment(" prepStmtCacheSqlLimit: '2048'")
@ConfigEntry(path = "general.database.custom-properties", since = "1.21.0")
private Map<String, String> customPoolProperties = new HashMap<>();
@ConfigComment("MongoDB client connection URI to override default connection options.")
@ConfigComment("See: https://docs.mongodb.com/manual/reference/connection-string/")
@ConfigEntry(path = "general.database.mongodb-connection-uri", since = "1.14.0")
@ -177,6 +189,18 @@ public class Settings implements ConfigObject {
/*
* Island
*/
// Number of islands
@ConfigComment("The default number of concurrent islands a player may have.")
@ConfigComment("This may be overridden by individual game mode config settings.")
@ConfigEntry(path = "island.concurrent-islands")
private int islandNumber = 1;
@ConfigComment("Hide used blueprints.")
@ConfigComment("Blueprints can have a maximum use when players have concurrent islands.")
@ConfigComment("If this is true, then ones that are used up will not be shown in the island create menu.")
@ConfigEntry(path = "island.hide-used-blueprints", since = "2.3.0")
private boolean hideUsedBlueprints = false;
// Cooldowns
@ConfigComment("How long a player must wait until they can rejoin a team island after being kicked in minutes.")
@ConfigComment("This slows the effectiveness of players repeating challenges")
@ -248,6 +272,11 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.clear-radius", since = "1.6.0")
private int clearRadius = 5;
@ConfigComment("Minimum nether portal search radius. If this is too low, duplicate portals may appear.")
@ConfigComment("Vanilla default is 128.")
@ConfigEntry(path = "island.portal-search-radius", since = "1.16.2")
private int minPortalSearchRadius = 64;
@ConfigComment("Number of blocks to paste per tick when pasting blueprints.")
@ConfigComment("Smaller values will help reduce noticeable lag but will make pasting take slightly longer.")
@ConfigComment("On the contrary, greater values will make pasting take less time, but this benefit is quickly severely impacted by the")
@ -262,25 +291,6 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.delete-speed", since = "1.7.0")
private int deleteSpeed = 1;
// Automated ownership transfer
@ConfigComment("Toggles the automated ownership transfer.")
@ConfigComment("It automatically transfers the ownership of an island to one of its members in case the current owner is inactive.")
@ConfigComment("More precisely, it transfers the ownership of the island to the player who's active, whose rank is the highest")
@ConfigComment("and who's been part of the island the longest time.")
@ConfigComment("Setting this to 'false' will disable the feature.")
@ConfigEntry(path = "island.automated-ownership-transfer.enable", hidden = true)
private boolean enableAutoOwnershipTransfer = false;
@ConfigComment("Time in days since the island owner's last disconnection before they are considered inactive.")
@ConfigEntry(path = "island.automated-ownership-transfer.inactivity-threshold", hidden = true)
private int autoOwnershipTransferInactivityThreshold = 30;
@ConfigComment("Ranks are being considered when transferring the island ownership to one of its member.")
@ConfigComment("Ignoring ranks will result in the island ownership being transferred to the player who's active and")
@ConfigComment("who's been member of the island the longest time.")
@ConfigEntry(path = "island.automated-ownership-transfer.ignore-ranks", hidden = true)
private boolean autoOwnershipTransferIgnoreRanks = false;
// Island deletion related settings
@ConfigComment("Toggles whether islands, when players are resetting them, should be kept in the world or deleted.")
@ConfigComment("* If set to 'true', whenever a player resets his island, his previous island will become unowned and won't be deleted from the world.")
@ -293,6 +303,28 @@ public class Settings implements ConfigObject {
@ConfigEntry(path = "island.deletion.keep-previous-island-on-reset", since = "1.13.0")
private boolean keepPreviousIslandOnReset = false;
@ConfigComment("Toggles how the islands are deleted.")
@ConfigComment("* If set to 'false', all islands will be deleted at once.")
@ConfigComment(" This is fast but may cause an impact on the performance")
@ConfigComment(" as it'll load all the chunks of the in-deletion islands.")
@ConfigComment("* If set to 'true', the islands will be deleted one by one.")
@ConfigComment(" This is slower but will not cause any impact on the performance.")
@ConfigEntry(path = "island.deletion.slow-deletion", since = "1.19.1")
private boolean slowDeletion = false;
@ConfigComment("By default, If the destination is not safe, the plugin will try to search for a safe spot around the destination,")
@ConfigComment("then it will try to expand the y-coordinate up and down from the destination.")
@ConfigComment("This setting limits how far the y-coordinate will be expanded.")
@ConfigComment("If set to 0 or lower, the plugin will not expand the y-coordinate.")
@ConfigEntry(path = "island.safe-spot-search-vertical-range", since = "1.19.1")
private int safeSpotSearchVerticalRange = 400;
@ConfigComment("By default, if the destination is not safe, the plugin will try to search for a safe spot around the destination.")
@ConfigComment("This allows to change the distance for searching this spot. Larger value will mean longer position search.")
@ConfigComment("This value is also used for valid nether portal linking between dimension.")
@ConfigEntry(path = "island.safe-spot-search-range", since = "1.21.0")
private int safeSpotSearchRange = 16;
/* WEB */
@ConfigComment("Toggle whether BentoBox can connect to GitHub to get data about updates and addons.")
@ConfigComment("Disabling this will result in the deactivation of the update checker and of some other")
@ -355,6 +387,7 @@ public class Settings implements ConfigObject {
/**
* This method returns the useSSL value.
*
* @return the value of useSSL.
* @since 1.12.0
*/
@ -364,6 +397,7 @@ public class Settings implements ConfigObject {
/**
* This method sets the useSSL value.
*
* @param useSSL the useSSL new value.
* @since 1.12.0
*/
@ -583,30 +617,6 @@ public class Settings implements ConfigObject {
this.deleteSpeed = deleteSpeed;
}
public boolean isEnableAutoOwnershipTransfer() {
return enableAutoOwnershipTransfer;
}
public void setEnableAutoOwnershipTransfer(boolean enableAutoOwnershipTransfer) {
this.enableAutoOwnershipTransfer = enableAutoOwnershipTransfer;
}
public int getAutoOwnershipTransferInactivityThreshold() {
return autoOwnershipTransferInactivityThreshold;
}
public void setAutoOwnershipTransferInactivityThreshold(int autoOwnershipTransferInactivityThreshold) {
this.autoOwnershipTransferInactivityThreshold = autoOwnershipTransferInactivityThreshold;
}
public boolean isAutoOwnershipTransferIgnoreRanks() {
return autoOwnershipTransferIgnoreRanks;
}
public void setAutoOwnershipTransferIgnoreRanks(boolean autoOwnershipTransferIgnoreRanks) {
this.autoOwnershipTransferIgnoreRanks = autoOwnershipTransferIgnoreRanks;
}
public boolean isLogCleanSuperFlatChunks() {
return logCleanSuperFlatChunks;
}
@ -678,7 +688,8 @@ public class Settings implements ConfigObject {
* @return the clearRadius
*/
public int getClearRadius() {
if (clearRadius < 0) clearRadius = 0;
if (clearRadius < 0)
clearRadius = 0;
return clearRadius;
}
@ -686,7 +697,8 @@ public class Settings implements ConfigObject {
* @param clearRadius the clearRadius to set. Cannot be negative.
*/
public void setClearRadius(int clearRadius) {
if (clearRadius < 0) clearRadius = 0;
if (clearRadius < 0)
clearRadius = 0;
this.clearRadius = clearRadius;
}
@ -710,7 +722,8 @@ public class Settings implements ConfigObject {
* @return the databasePrefix
*/
public String getDatabasePrefix() {
if (databasePrefix == null) databasePrefix = "";
if (databasePrefix == null)
databasePrefix = "";
return databasePrefix.isEmpty() ? "" : databasePrefix.replaceAll("[^a-zA-Z0-9]", "_");
}
@ -723,7 +736,9 @@ public class Settings implements ConfigObject {
/**
* Returns whether islands, when reset, should be kept or deleted.
* @return {@code true} if islands, when reset, should be kept; {@code false} otherwise.
*
* @return {@code true} if islands, when reset, should be kept; {@code false}
* otherwise.
* @since 1.13.0
*/
public boolean isKeepPreviousIslandOnReset() {
@ -732,7 +747,9 @@ public class Settings implements ConfigObject {
/**
* Sets whether islands, when reset, should be kept or deleted.
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should be kept; {@code false} otherwise.
*
* @param keepPreviousIslandOnReset {@code true} if islands, when reset, should
* be kept; {@code false} otherwise.
* @since 1.13.0
*/
public void setKeepPreviousIslandOnReset(boolean keepPreviousIslandOnReset) {
@ -740,10 +757,13 @@ public class Settings implements ConfigObject {
}
/**
* Returns a MongoDB client connection URI to override default connection options.
* Returns a MongoDB client connection URI to override default connection
* options.
*
* @return mongodb client connection.
* @see <a href="https://docs.mongodb.com/manual/reference/connection-string/">MongoDB Documentation</a>
* @see <a href=
* "https://docs.mongodb.com/manual/reference/connection-string/">MongoDB
* Documentation</a>
* @since 1.14.0
*/
public String getMongodbConnectionUri() {
@ -752,6 +772,7 @@ public class Settings implements ConfigObject {
/**
* Set the MongoDB client connection URI.
*
* @param mongodbConnectionUri connection URI.
* @since 1.14.0
*/
@ -760,8 +781,11 @@ public class Settings implements ConfigObject {
}
/**
* Returns the Material of the item to preferably use when one needs to fill gaps in Panels.
* @return the Material of the item to preferably use when one needs to fill gaps in Panels.
* Returns the Material of the item to preferably use when one needs to fill
* gaps in Panels.
*
* @return the Material of the item to preferably use when one needs to fill
* gaps in Panels.
* @since 1.14.0
*/
public Material getPanelFillerMaterial() {
@ -769,37 +793,38 @@ public class Settings implements ConfigObject {
}
/**
* Sets the Material of the item to preferably use when one needs to fill gaps in Panels.
* @param panelFillerMaterial the Material of the item to preferably use when one needs to fill gaps in Panels.
* Sets the Material of the item to preferably use when one needs to fill gaps
* in Panels.
*
* @param panelFillerMaterial the Material of the item to preferably use when
* one needs to fill gaps in Panels.
* @since 1.14.0
*/
public void setPanelFillerMaterial(Material panelFillerMaterial) {
this.panelFillerMaterial = panelFillerMaterial;
}
/**
* Method Settings#getPlayerHeadCacheTime returns the playerHeadCacheTime of this object.
/**
* Method Settings#getPlayerHeadCacheTime returns the playerHeadCacheTime of
* this object.
*
* @return the playerHeadCacheTime (type long) of this object.
* @since 1.14.1
*/
public long getPlayerHeadCacheTime()
{
return playerHeadCacheTime;
}
public long getPlayerHeadCacheTime() {
return playerHeadCacheTime;
}
/**
* Method Settings#setPlayerHeadCacheTime sets new value for the playerHeadCacheTime of this object.
/**
* Method Settings#setPlayerHeadCacheTime sets new value for the
* playerHeadCacheTime of this object.
*
* @param playerHeadCacheTime new value for this object.
* @since 1.14.1
*/
public void setPlayerHeadCacheTime(long playerHeadCacheTime)
{
this.playerHeadCacheTime = playerHeadCacheTime;
}
public void setPlayerHeadCacheTime(long playerHeadCacheTime) {
this.playerHeadCacheTime = playerHeadCacheTime;
}
/**
* Is use cache server boolean.
@ -807,68 +832,206 @@ public class Settings implements ConfigObject {
* @return the boolean
* @since 1.16.0
*/
public boolean isUseCacheServer()
{
public boolean isUseCacheServer() {
return useCacheServer;
}
/**
* Sets use cache server.
*
* @param useCacheServer the use cache server
* @since 1.16.0
*/
public void setUseCacheServer(boolean useCacheServer)
{
public void setUseCacheServer(boolean useCacheServer) {
this.useCacheServer = useCacheServer;
}
/**
* Gets heads per call.
*
* @return the heads per call
* @since 1.16.0
*/
public int getHeadsPerCall()
{
public int getHeadsPerCall() {
return headsPerCall;
}
/**
* Sets heads per call.
*
* @param headsPerCall the heads per call
* @since 1.16.0
*/
public void setHeadsPerCall(int headsPerCall)
{
public void setHeadsPerCall(int headsPerCall) {
this.headsPerCall = headsPerCall;
}
/**
* Gets ticks between calls.
*
* @return the ticks between calls
* @since 1.16.0
*/
public long getTicksBetweenCalls()
{
public long getTicksBetweenCalls() {
return ticksBetweenCalls;
}
/**
* Sets ticks between calls.
*
* @param ticksBetweenCalls the ticks between calls
* @since 1.16.0
*/
public void setTicksBetweenCalls(long ticksBetweenCalls)
{
public void setTicksBetweenCalls(long ticksBetweenCalls) {
this.ticksBetweenCalls = ticksBetweenCalls;
}
/**
* @return the minPortalSearchRadius
*/
public int getMinPortalSearchRadius() {
return minPortalSearchRadius;
}
/**
* @param minPortalSearchRadius the minPortalSearchRadius to set
*/
public void setMinPortalSearchRadius(int minPortalSearchRadius) {
this.minPortalSearchRadius = minPortalSearchRadius;
}
/**
* Gets safe spot search vertical range.
*
* @return the safe spot search vertical range
*/
public int getSafeSpotSearchVerticalRange() {
return safeSpotSearchVerticalRange;
}
/**
* Sets safe spot search vertical range.
*
* @param safeSpotSearchVerticalRange the safe spot search vertical range
*/
public void setSafeSpotSearchVerticalRange(int safeSpotSearchVerticalRange) {
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
}
/**
* Is slow deletion boolean.
*
* @return the boolean
*/
public boolean isSlowDeletion() {
return slowDeletion;
}
/**
* Sets slow deletion.
*
* @param slowDeletion the slow deletion
*/
public void setSlowDeletion(boolean slowDeletion) {
this.slowDeletion = slowDeletion;
}
/**
* Gets maximum pool size.
*
* @return the maximum pool size
*/
public int getMaximumPoolSize() {
return maximumPoolSize;
}
/**
* Gets safe spot search range.
*
* @return the safe spot search range
*/
public int getSafeSpotSearchRange() {
return safeSpotSearchRange;
}
/**
* Sets maximum pool size.
*
* @param maximumPoolSize the maximum pool size
*/
public void setMaximumPoolSize(int maximumPoolSize) {
this.maximumPoolSize = maximumPoolSize;
}
/**
* Gets custom pool properties.
*
* @return the custom pool properties
*/
public Map<String, String> getCustomPoolProperties() {
return customPoolProperties;
}
/**
* Sets custom pool properties.
*
* @param customPoolProperties the custom pool properties
*/
public void setCustomPoolProperties(Map<String, String> customPoolProperties) {
this.customPoolProperties = customPoolProperties;
}
/**
* Sets safe spot search range.
*
* @param safeSpotSearchRange the safe spot search range
*/
public void setSafeSpotSearchRange(int safeSpotSearchRange) {
this.safeSpotSearchRange = safeSpotSearchRange;
}
/**
* @return an immutable list of readyCommands
*/
public List<String> getReadyCommands() {
return ImmutableList.copyOf(Objects.requireNonNullElse(readyCommands, Collections.emptyList()));
}
/**
* @param readyCommands the readyCommands to set
*/
public void setReadyCommands(List<String> readyCommands) {
this.readyCommands = readyCommands;
}
/**
* @return the islandNumber
* @since 2.0.0
*/
public int getIslandNumber() {
return islandNumber;
}
/**
* @param islandNumber the islandNumber to set
* @since 2.0.0
*/
public void setIslandNumber(int islandNumber) {
this.islandNumber = islandNumber;
}
/**
* @return the hideUsedBlueprints
*/
public boolean isHideUsedBlueprints() {
return hideUsedBlueprints;
}
/**
* @param hideUsedBlueprints the hideUsedBlueprints to set
*/
public void setHideUsedBlueprints(boolean hideUsedBlueprints) {
this.hideUsedBlueprints = hideUsedBlueprints;
}
}

View File

@ -12,6 +12,7 @@ import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import org.bukkit.Bukkit;
import org.bukkit.Server;
@ -20,6 +21,8 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.Listener;
import com.github.puregero.multilib.MultiLib;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.request.AddonRequestHandler;
import world.bentobox.bentobox.api.flags.Flag;
@ -40,10 +43,12 @@ public abstract class Addon {
private FileConfiguration config;
private File dataFolder;
private File file;
private Map<String, AddonRequestHandler> requestHandlers = new HashMap<>();
private final Map<String, AddonRequestHandler> requestHandlers = new HashMap<>();
protected Addon() {
state = State.DISABLED;
// If the config is updated, update the config.
MultiLib.onString(getPlugin(), "bentobox-config-update", v -> this.reloadConfig());
}
/**
@ -273,7 +278,9 @@ public abstract class Addon {
"The embedded resource '" + jarResource + "' cannot be found in " + jar.getName());
}
// There are two options, use the path of the resource or not
File outFile = new File(destinationFolder, jarResource);
File outFile = new File(destinationFolder,
jarResource.replaceAll("/", Matcher.quoteReplacement(File.separator)));
if (noPath) {
outFile = new File(destinationFolder, outFile.getName());
}
@ -393,6 +400,16 @@ public abstract class Addon {
public IslandsManager getIslands() {
return getPlugin().getIslands();
}
/**
* Get Islands Manager
* @return Islands manager
* @see #getIslands()
* @since 1.17.1
*/
public IslandsManager getIslandsManager() {
return getPlugin().getIslandsManager();
}
/**
* Get the Addon By Name

View File

@ -7,6 +7,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -30,17 +31,30 @@ import world.bentobox.bentobox.managers.AddonsManager;
public class AddonClassLoader extends URLClassLoader {
private final Map<String, Class<?>> classes = new HashMap<>();
private Addon addon;
private AddonsManager loader;
private final Addon addon;
private final AddonsManager loader;
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File path, ClassLoader parent)
/**
* For testing only
* @param addon addon
* @param loader Addons Manager
* @param jarFile Jar File
* @throws MalformedURLException exception
*/
protected AddonClassLoader(Addon addon, AddonsManager loader, File jarFile) throws MalformedURLException {
super(new URL[]{jarFile.toURI().toURL()});
this.addon = addon;
this.loader = loader;
}
public AddonClassLoader(AddonsManager addonsManager, YamlConfiguration data, File jarFile, ClassLoader parent)
throws InvalidAddonInheritException,
MalformedURLException,
InvalidDescriptionException,
InvalidAddonDescriptionException,
InstantiationException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
super(new URL[]{path.toURI().toURL()}, parent);
super(new URL[]{jarFile.toURI().toURL()}, parent);
loader = addonsManager;
@ -55,7 +69,7 @@ public class AddonClassLoader extends URLClassLoader {
throw new InvalidAddonFormatException("Package declaration cannot start with 'world.bentobox.bentobox'");
}
} catch (Exception e) {
throw new InvalidDescriptionException("Could not load '" + path.getName() + "' in folder '" + path.getParent() + "' - " + e.getMessage());
throw new InvalidDescriptionException("Could not load '" + jarFile.getName() + "' in folder '" + jarFile.getParent() + "' - " + e.getMessage());
}
Class<? extends Addon> addonClass;
@ -78,9 +92,28 @@ public class AddonClassLoader extends URLClassLoader {
* @throws InvalidAddonDescriptionException - if there's a bug in the addon.yml
*/
@NonNull
private AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
AddonDescription.Builder builder = new AddonDescription.Builder(data.getString("main"), data.getString("name"), data.getString("version"))
.authors(data.getString("authors"))
public static AddonDescription asDescription(YamlConfiguration data) throws InvalidAddonDescriptionException {
// Validate addon.yml
if (!data.contains("main")) {
throw new InvalidAddonDescriptionException("Missing 'main' tag. A main class must be listed in addon.yml");
}
if (!data.contains("name")) {
throw new InvalidAddonDescriptionException("Missing 'name' tag. An addon name must be listed in addon.yml");
}
if (!data.contains("version")) {
throw new InvalidAddonDescriptionException("Missing 'version' tag. A version must be listed in addon.yml");
}
if (!data.contains("authors")) {
throw new InvalidAddonDescriptionException("Missing 'authors' tag. At least one author must be listed in addon.yml");
}
AddonDescription.Builder builder = new AddonDescription.Builder(
// Mandatory elements
Objects.requireNonNull(data.getString("main")),
Objects.requireNonNull(data.getString("name")),
Objects.requireNonNull(data.getString("version")))
.authors(Objects.requireNonNull(data.getString("authors")))
// Optional elements
.metrics(data.getBoolean("metrics", true))
.repository(data.getString("repository", ""));
@ -92,7 +125,11 @@ public class AddonClassLoader extends URLClassLoader {
if (softDepend != null) {
builder.softDependencies(Arrays.asList(softDepend.split("\\s*,\\s*")));
}
builder.icon(Material.getMaterial(data.getString("icon", "PAPER")));
Material icon = Material.getMaterial(data.getString("icon", "PAPER").toUpperCase(Locale.ENGLISH));
if (icon == null) {
throw new InvalidAddonDescriptionException("'icon' tag refers to an unknown Material: " + data.getString("icon"));
}
builder.icon(Objects.requireNonNull(icon));
String apiVersion = data.getString("api-version");
if (apiVersion != null) {

View File

@ -158,23 +158,35 @@ public final class AddonDescription {
* @return the permissions
* @since 1.13.0
*/
@Nullable
public ConfigurationSection getPermissions() {
return permissions;
}
public static class Builder {
private @NonNull String main;
private @NonNull String name;
private @NonNull String version;
private @NonNull String description = "";
private @NonNull List<String> authors = new ArrayList<>();
private @NonNull List<String> dependencies = new ArrayList<>();
private @NonNull List<String> softDependencies = new ArrayList<>();
@NonNull
private final String main;
@NonNull
private final String name;
@NonNull
private final String version;
@NonNull
private String description = "";
@NonNull
private List<String> authors = new ArrayList<>();
@NonNull
private List<String> dependencies = new ArrayList<>();
@NonNull
private List<String> softDependencies = new ArrayList<>();
private boolean metrics = true;
private @NonNull String repository = "";
private @NonNull Material icon = Material.PAPER;
private @NonNull String apiVersion = "1";
private @Nullable ConfigurationSection permissions;
@NonNull
private String repository = "";
@NonNull
private Material icon = Material.PAPER;
@NonNull
private String apiVersion = "1";
@Nullable
private ConfigurationSection permissions;
/**
* @since 1.1
@ -275,7 +287,7 @@ public final class AddonDescription {
*/
@Override
public String toString() {
return "AddonDescription [" + (name != null ? "name=" + name + ", " : "")
+ (version != null ? "version=" + version : "") + "]";
return "AddonDescription [" + "name=" + name + ", "
+ "version=" + version + "]";
}
}

View File

@ -8,6 +8,8 @@ import org.bukkit.generator.ChunkGenerator;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import com.github.puregero.multilib.MultiLib;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.util.Util;
@ -52,9 +54,9 @@ public abstract class GameModeAddon extends Addon {
public abstract WorldSettings getWorldSettings();
/**
* Checks if a player is in any of the island worlds
* @param loc - player to check
* @return true if in a world or false if not
* Checks if location is governed by this game mode
* @param loc - location to check
* @return true if location in covered by this addon or false if not
*/
public boolean inWorld(Location loc) {
return Util.sameWorld(loc.getWorld(), islandWorld);
@ -63,7 +65,7 @@ public abstract class GameModeAddon extends Addon {
/**
* Checks if world is governed by this game mode
* @param world - world to check
* @return true if in a world or false if not
* @return true if world in covered by this addon or false if not
* @since 1.2.0
*/
public boolean inWorld(World world) {
@ -117,7 +119,7 @@ public abstract class GameModeAddon extends Addon {
/**
* Defines the world generator for this game mode
* @param worldName - name of world that this applies to
* @param id - id if any
* @param id - id if any. "delete" is used when this request is for island deletion purposes
* @return Chunk generator or null if one does not exist, e.g. the use own generator setting is true
* @since 1.2.0
*/
@ -129,6 +131,18 @@ public abstract class GameModeAddon extends Addon {
* in-game and need to be saved.
* @since 1.4.0
*/
public abstract void saveWorldSettings();
public void saveWorldSettings() {
// Inform other servers
MultiLib.notify("bentobox-config-update", "");
}
/**
* Defines if the game mode uses the latest {@link ChunkGenerator} API or
* deprecated {@link ChunkGenerator#generateChunkData(World, java.util.Random, int, int, org.bukkit.generator.ChunkGenerator.BiomeGrid)} approach.
* @return true if this game mode is a void world and should just be deleted as such
*/
public boolean isUsesNewChunkGeneration() {
return false;
}
}

View File

@ -0,0 +1,66 @@
package world.bentobox.bentobox.api.addons;
import java.io.File;
import java.io.IOException;
import org.bukkit.plugin.java.JavaPlugin;
import com.google.common.io.Files;
/**
* Provides a shell for addons to become Plugins so that other Plugins
* can tap into their API more easily. Plugin + addon = Pladdon
* @author tastybento
*
*/
public abstract class Pladdon extends JavaPlugin {
private static final String ADDONS_FOLDER = "BentoBox" + File.separator + "addons";
/**
* This must return a new instance of the addon. It is called when the Pladdon is loaded.
* @return new instance of the addon
*/
public abstract Addon getAddon();
@Override
public void onLoad() {
String parentFolder = getFile().getParent();
if (parentFolder == null || !parentFolder.endsWith(ADDONS_FOLDER)) {
// Jar is in the wrong place. Let's move it
//moveJar();
}
}
protected void moveJar() {
getLogger().severe(getFile().getName() + " must be in the " + ADDONS_FOLDER + " folder! Trying to move it there...");
File addons = new File(getFile().getParent(), ADDONS_FOLDER);
if (addons.exists() || addons.mkdirs()) {
File to = new File(addons, getFile().getName());
if (!to.exists()) {
try {
Files.move(getFile(), to);
getLogger().severe(getFile().getName() + " moved successfully.");
} catch (IOException ex) {
getLogger().severe("Failed to move it. " + ex.getMessage());
getLogger().severe("Move " + getFile().getName() + " manually into the " + ADDONS_FOLDER + " folder. Then restart server.");
}
} else {
getLogger().warning(getFile().getName() + " already is in the addons folder. Delete the one in the plugins folder.");
}
} else {
getLogger().severe("BentoBox addons folder could not be made! " + addons.getAbsolutePath());
}
}
/**
* This method enables marks pladdons as enabled.
* By default, enable status is not set because onEnable and onLoad is not triggered.
*/
public void setEnabled() {
this.setEnabled(true);
}
}

View File

@ -1,10 +1,13 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public abstract class AddonException extends Exception {
/**
*
*/
@Serial
private static final long serialVersionUID = 4203162022348693854L;
protected AddonException(String errorMessage){

View File

@ -1,10 +1,12 @@
package world.bentobox.bentobox.api.addons.exceptions;
public class AddonRequestException extends AddonException
{
private static final long serialVersionUID = -5698456013070166174L;
import java.io.Serial;
public AddonRequestException(String errorMessage) {
super(errorMessage);
}
public class AddonRequestException extends AddonException {
@Serial
private static final long serialVersionUID = -5698456013070166174L;
public AddonRequestException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -1,5 +1,7 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
/**
* @since 1.11.0
*/
@ -8,6 +10,7 @@ public class InvalidAddonDescriptionException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = 7741502900847049986L;
public InvalidAddonDescriptionException(String errorMessage) {

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
import java.util.logging.Level;
import org.bukkit.Bukkit;
@ -9,6 +10,7 @@ public class InvalidAddonFormatException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = 7741502900847049986L;
public InvalidAddonFormatException(String errorMessage) {

View File

@ -1,10 +1,13 @@
package world.bentobox.bentobox.api.addons.exceptions;
import java.io.Serial;
public class InvalidAddonInheritException extends AddonException {
/**
*
*/
@Serial
private static final long serialVersionUID = -5847358994397613244L;
public InvalidAddonInheritException(String errorMessage) {

View File

@ -0,0 +1,10 @@
/**
* This package covers Addon exceptions
* <p>
* These exceptions can be thrown when the addon is loaded.
* </p>
*
* @since 1.0
* @author tastybento
*/
package world.bentobox.bentobox.api.addons.exceptions;

View File

@ -0,0 +1,12 @@
/**
* This package covers all addon-specific API
* <p>
* The Addon class and the associated Pladdon are like Bukkit plugins
* but contain extra API specific for BentoBox games.
* </p>
*
* @since 1.0
* @author tastybento
*
*/
package world.bentobox.bentobox.api.addons;

View File

@ -20,7 +20,7 @@ public class AddonRequestBuilder
{
private String addonName;
private String requestLabel;
private Map<String, Object> metaData = new HashMap<>();
private final Map<String, Object> metaData = new HashMap<>();
/**
* Define the addon you wish to request.

View File

@ -0,0 +1,13 @@
/**
* API to enable plugins to request data from addons.
* <p>
* Addons can expose data that they want to expose. To access it, call this class with the appropriate addon name,
* the label for the data that is requested and if required, a map of key-value pairs that will be given to the addon.
* <p>
* <b>Note</b> Since BentoBox 1.17.0, Addons can be declared as Pladdons and be loaded by the Bukkit classloader. This
* enables Plugins to access Addon methods directly so this API is not required.
* </p>
*
* @author tastybento
*/
package world.bentobox.bentobox.api.addons.request;

View File

@ -11,7 +11,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.bukkit.World;
import org.bukkit.command.Command;
@ -27,13 +26,16 @@ import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.events.command.CommandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* BSB composite command
* BentoBox composite command. Provides an abstract implementation of a command.
*
* @author tastybento
* @author Poslovitch
*/
@ -48,23 +50,40 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
*/
private boolean onlyPlayer = false;
/**
* True if the command is only for the console
*
* @since 1.24.0
*/
private boolean onlyConsole = false;
/**
* True if command is a configurable rank
*/
private boolean configurableRankCommand = false;
/**
* Make default command rank as owner rank.
*
* @since 1.20.0
*/
private int defaultCommandRank = RanksManager.OWNER_RANK;
/**
* True if command is hidden from help and tab complete
*
* @since 1.13.0
*/
private boolean hidden = false;
/**
* The parameters string for this command. It is the commands followed by a locale reference.
* The parameters string for this command. It is the commands followed by a
* locale reference.
*/
private String parameters = "";
/**
* The parent command to this one. If this is a top-level command it will be empty.
* The parent command to this one. If this is a top-level command it will be
* empty.
*/
protected final CompositeCommand parent;
/**
@ -78,12 +97,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Map of sub commands
*/
private Map<String, CompositeCommand> subCommands;
private final Map<String, CompositeCommand> subCommands;
/**
* Map of aliases for subcommands
*/
private Map<String, CompositeCommand> subCommandAliases;
private final Map<String, CompositeCommand> subCommandAliases;
/**
* The command chain from the very top, e.g., island team promote
*/
@ -93,33 +112,35 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* The prefix to be used in this command
*/
@Nullable
private String permissionPrefix;
private final String permissionPrefix;
/**
* The world that this command operates in. This is an overworld and will cover any associated nether or end
* If the world value does not exist, then the command is general across worlds
* The world that this command operates in. This is an overworld and will cover
* any associated nether or end If the world value does not exist, then the
* command is general across worlds
*/
private World world;
/**
* The addon creating this command, if any
*/
private Addon addon;
private final Addon addon;
/**
* The top level label
*/
private String topLabel;
private final String topLabel;
/**
* Cool down tracker
*/
private Map<String, Map<String, Long>> cooldowns = new HashMap<>();
private final Map<String, Map<String, Long>> cooldowns = new HashMap<>();
/**
* Top level command
* @param addon - addon creating the command
* @param label - string for this command
*
* @param addon - addon creating the command
* @param label - string for this command
* @param aliases - aliases
*/
protected CompositeCommand(Addon addon, String label, String... aliases) {
@ -144,24 +165,26 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// Run setup
setup();
if (!getSubCommand("help").isPresent() && !label.equals("help")) {
if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this);
}
}
/**
* This is the top-level command constructor for commands that have no parent.
* @param label - string for this command
*
* @param label - string for this command
* @param aliases - aliases for this command
*/
protected CompositeCommand(String label, String... aliases) {
this((Addon)null, label, aliases);
this((Addon) null, label, aliases);
}
/**
* Sub-command constructor
* @param parent - the parent composite command
* @param label - string label for this subcommand
*
* @param parent - the parent composite command
* @param label - string label for this subcommand
* @param aliases - aliases for this subcommand
*/
protected CompositeCommand(CompositeCommand parent, String label, String... aliases) {
@ -169,12 +192,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Command to register a command from an addon under a parent command (that could be from another addon)
* @param addon - this command's addon
* @param parent - parent command
* Command to register a command from an addon under a parent command (that
* could be from another addon)
*
* @param addon - this command's addon
* @param parent - parent command
* @param aliases - aliases for this command
*/
protected CompositeCommand(Addon addon, CompositeCommand parent, String label, String... aliases ) {
protected CompositeCommand(Addon addon, CompositeCommand parent, String label, String... aliases) {
super(label, "", "", Arrays.asList(aliases));
this.topLabel = parent.getTopLabel();
this.plugin = BentoBox.getInstance();
@ -182,13 +207,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
this.parent = parent;
subCommandLevel = parent.getLevel() + 1;
// Add this sub-command to the parent
parent.getSubCommands().put(label, this);
parent.getSubCommands().put(label.toLowerCase(java.util.Locale.ENGLISH), this);
setAliases(new ArrayList<>(Arrays.asList(aliases)));
subCommands = new LinkedHashMap<>();
subCommandAliases = new LinkedHashMap<>();
// Add aliases to the parent for this command
for (String alias : aliases) {
parent.getSubCommandAliases().put(alias, this);
parent.getSubCommandAliases().put(alias.toLowerCase(java.util.Locale.ENGLISH), this);
}
setUsage("");
// Inherit permission prefix
@ -204,11 +229,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
p = p.getParent();
index++;
}
setDescription(COMMANDS + reference.toString() + ".description");
setParametersHelp(COMMANDS + reference.toString() + ".parameters");
setDescription(COMMANDS + reference + ".description");
setParametersHelp(COMMANDS + reference + ".parameters");
setup();
// If this command does not define its own help class, then use the default help command
if (!getSubCommand("help").isPresent() && !label.equals("help")) {
// If this command does not define its own help class, then use the default help
// command
if (getSubCommand("help").isEmpty() && !label.equals("help")) {
new DefaultHelpCommand(this);
}
}
@ -218,46 +244,46 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* subcommands until it finds the right object and then runs execute on it.
*/
@Override
public boolean execute(CommandSender sender, String label, String[] args) {
public boolean execute(@NonNull CommandSender sender, @NonNull String label, String[] args) {
// Get the User instance for this sender
User user = User.getInstance(sender);
// Fire an event to see if this command should be cancelled
CommandEvent event = CommandEvent.builder()
.setCommand(this)
.setLabel(label)
.setSender(sender)
.setArgs(args)
CommandEvent event = CommandEvent.builder().setCommand(this).setLabel(label).setSender(sender).setArgs(args)
.build();
if (event.isCancelled()) {
return false;
}
// Get command
CompositeCommand cmd = getCommandFromArgs(args);
String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel-1] : label;
String cmdLabel = (cmd.subCommandLevel > 0) ? args[cmd.subCommandLevel - 1] : label;
List<String> cmdArgs = Arrays.asList(args).subList(cmd.subCommandLevel, args.length);
// Call
return cmd.call(user, cmdLabel, cmdArgs);
}
/**
* Calls this composite command.
* Does not traverse the tree of subcommands in args.
* Event is not fired and it cannot be cancelled.
* @param user - user calling this command
* Calls this composite command. Does not traverse the tree of subcommands in
* args. Event is not fired and it cannot be cancelled.
*
* @param user - user calling this command
* @param cmdLabel - label used
* @param cmdArgs - list of args
* @param cmdArgs - list of args
* @return {@code true} if successful, {@code false} if not.
* @since 1.5.3
*/
public boolean call(User user, String cmdLabel, List<String> cmdArgs) {
// Check for console and permissions
if (onlyPlayer && !user.isPlayer()) {
if (isOnlyPlayer() && !user.isPlayer()) {
user.sendMessage("general.errors.use-in-game");
return false;
}
// Check perms, but only if this isn't the console
if (user.isPlayer() && !user.isOp() && getPermission() != null && !getPermission().isEmpty() && !user.hasPermission(getPermission())) {
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, getPermission());
if (isOnlyConsole() && user.isPlayer()) {
user.sendMessage("general.errors.use-in-console");
return false;
}
if (!this.runPermissionCheck(user)) {
// Error message is displayed by permission check.
return false;
}
// Set the user's addon context
@ -266,8 +292,29 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return canExecute(user, cmdLabel, cmdArgs) && execute(user, cmdLabel, cmdArgs);
}
/**
* This method checks and returns if user has access to the called command. It
* also recursively checks if user has access to the all parent commands.
*
* @param user User who permission must be checked.
* @return {@code true} is user can execute given command, {@code false}
* otherwise.
*/
private boolean runPermissionCheck(User user) {
// Check perms, but only if this isn't the console
if (user.isPlayer() && !user.isOp() && this.getPermission() != null && !this.getPermission().isEmpty()
&& !user.hasPermission(this.getPermission())) {
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, this.getPermission());
return false;
}
// Recursive permission check to find if user has access to the parent command.
return this.getParent() == null || this.getParent().runPermissionCheck(user);
}
/**
* Get the current composite command based on the arguments
*
* @param args - arguments
* @return the current composite command based on the arguments
*/
@ -278,7 +325,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// get the subcommand corresponding to the arg
if (subCommand.hasSubCommands()) {
Optional<CompositeCommand> sub = subCommand.getSubCommand(arg);
if (!sub.isPresent()) {
if (sub.isEmpty()) {
return subCommand;
}
// Step down one
@ -296,6 +343,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslands() {
@ -303,8 +351,17 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* @return this command's sub-level. Top level is 0.
* Every time a command registers with a parent, their level will be set.
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslandsManager() {
return plugin.getIslandsManager();
}
/**
* @return this command's sub-level. Top level is 0. Every time a command
* registers with a parent, their level will be set.
*/
protected int getLevel() {
return subCommandLevel;
@ -318,13 +375,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Convenience method to obtain team members
* Convenience method to obtain team members of the active island for user. Note
* that the user may have more than one island in this world.
*
* @param world - world to check
* @param user - the User
* @return set of UUIDs of all team members
* @param user - the User
* @return set of UUIDs of all team members, or empty set if there is no island
*/
protected Set<UUID> getMembers(World world, User user) {
return plugin.getIslands().getMembers(world, user.getUniqueId());
Island island = plugin.getIslands().getIsland(world, user);
if (island == null) {
return Set.of();
}
return island.getMemberSet();
}
public String getParameters() {
@ -345,6 +408,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the player manager
*
* @return PlayersManager
*/
protected PlayersManager getPlayers() {
@ -352,17 +416,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
@Override
public BentoBox getPlugin() {
public @NonNull BentoBox getPlugin() {
return plugin;
}
/**
* Get the island worlds manager
*
* @return island worlds manager
*/
public IslandWorldManager getIWM() {
return plugin.getIWM();
}
/**
* @return Settings object
*/
@ -372,6 +438,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Returns the CompositeCommand object referring to this command label
*
* @param label - command label or alias
* @return CompositeCommand or null if none found
*/
@ -395,9 +462,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Returns a map of sub commands for this command.
* As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #getSubCommands()} when no such distinction is needed.
* @param ignoreHelp Whether the Help subcommand should not be returned in the map or not.
* Returns a map of sub commands for this command. As it needs more calculations
* to handle the Help subcommand, it is preferable to use
* {@link #getSubCommands()} when no such distinction is needed.
*
* @param ignoreHelp Whether the Help subcommand should not be returned in the
* map or not.
* @return Map of sub commands for this command
* @see #hasSubCommands(boolean)
*/
@ -410,24 +480,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return getSubCommands();
}
/**
* Convenience method to obtain the user's island owner
* @param world world to check
* @param user the User
* @return UUID of player's island owner or null if user has no island
*/
@Nullable
protected UUID getOwner(@NonNull World world, @NonNull User user) {
return plugin.getIslands().getOwner(world, user.getUniqueId());
}
@Override
public String getUsage() {
public @NonNull String getUsage() {
return "/" + usage;
}
/**
* Check if this command has a specific sub command.
*
* @param subCommand - sub command
* @return true if this command has this sub command
*/
@ -437,6 +497,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command has any sub commands.
*
* @return true if this command has subcommands
*/
protected boolean hasSubCommands() {
@ -444,9 +505,12 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Check if this command has any sub commands.
* As it needs more calculations to handle the Help subcommand, it is preferable to use {@link #hasSubCommands()} when no such distinction is needed.
* @param ignoreHelp Whether the Help subcommand should not be taken into account or not.
* Check if this command has any sub commands. As it needs more calculations to
* handle the Help subcommand, it is preferable to use {@link #hasSubCommands()}
* when no such distinction is needed.
*
* @param ignoreHelp Whether the Help subcommand should not be taken into
* account or not.
* @return true if this command has subcommands
* @see #getSubCommands(boolean)
*/
@ -456,8 +520,10 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to check if a user has a team.
* Consider checking the island itself {@link Island#inTeam(UUID)}
*
* @param world - the world to check
* @param user - the User
* @param user - the User
* @return true if player is in a team
*/
protected boolean inTeam(World world, User user) {
@ -466,6 +532,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command is only for players.
*
* @return true or false
*/
public boolean isOnlyPlayer() {
@ -473,20 +540,23 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Convenience method to check if a user is a player
* @param user - the User
* @return true if sender is a player
* Check if this command is only for consoles.
*
* @return true or false
*/
protected boolean isPlayer(User user) {
return user.getPlayer() != null;
public boolean isOnlyConsole() {
return onlyConsole;
}
/**
* Sets whether this command should only be run by players.
* If this is set to {@code true}, this command will only be runnable by objects implementing {@link Player}.
* <br/><br/>
* The default value provided when instantiating this CompositeCommand is {@code false}.
* Therefore, this method should only be used in case you want to explicitly edit the value.
* Sets whether this command should only be run by players. If this is set to
* {@code true}, this command will only be runnable by objects implementing
* {@link Player}. <br/>
* <br/>
* The default value provided when instantiating this CompositeCommand is
* {@code false}. Therefore, this method should only be used in case you want to
* explicitly edit the value.
*
* @param onlyPlayer {@code true} if this command should only be run by players.
*/
public void setOnlyPlayer(boolean onlyPlayer) {
@ -494,61 +564,86 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Sets locale reference to this command's description.
* It is used to display the help of this command.
* Sets whether this command should only be run in the console. This is for
* commands that dump a lot of data or are for debugging. The default value
* provided when instantiating this CompositeCommand is {@code false}.
* Therefore, this method should only be used in case you want to explicitly
* edit the value.
*
* @param onlyConsole {@code true} if this command should only be run in the
* console.
* @since 1.24.0
*/
public void setOnlyConsole(boolean onlyConsole) {
this.onlyConsole = onlyConsole;
}
/**
* Sets locale reference to this command's description. It is used to display
* the help of this command.
*
* <br/><br/>
* <br/>
* <br/>
*
* A default value is provided when instantiating this CompositeCommand:
*
* <ul>
* <li>{@code "commands." + getLabel() + ".description"} if this is a top-level command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".description"} if this is a sub-command.
* <br/>
* Note that it can have up to 20 parent commands' labels being inserted before this sub-command's label.
* Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.description"};</li>
* <li>/bsbadmin range set : {@code "commands.bsbadmin.range.set.description"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 : {@code "commands.sub3.[...].sub20.sub21.sub22.description"}.</li>
* </ul>
* </li>
* <li>{@code "commands." + getLabel() + ".description"} if this is a top-level
* command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".description"}
* if this is a sub-command. <br/>
* Note that it can have up to 20 parent commands' labels being inserted before
* this sub-command's label. Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.description"};</li>
* <li>/bsbadmin range set :
* {@code "commands.bsbadmin.range.set.description"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 :
* {@code "commands.sub3.[...].sub20.sub21.sub22.description"}.</li>
* </ul>
* </li>
* </ul>
*
* This method should therefore only be used in case you want to provide a different value than the default one.
* This method should therefore only be used in case you want to provide a
* different value than the default one.
*
* @param description The locale command's description reference to set.
* @return The instance of this {@link Command}.
*/
@Override
public Command setDescription(String description) {
public @NonNull Command setDescription(@NonNull String description) {
super.setDescription(description);
return this;
}
/**
* Sets locale reference to this command's parameters.
* It is used to display the help of this command.
* Sets locale reference to this command's parameters. It is used to display the
* help of this command.
*
* <br/><br/>
* <br/>
* <br/>
*
* A default value is provided when instantiating this CompositeCommand:
*
* <ul>
* <li>{@code "commands." + getLabel() + ".parameters"} if this is a top-level command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".parameters"} if this is a sub-command.
* <br/>
* Note that it can have up to 20 parent commands' labels being inserted before this sub-command's label.
* Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.parameters"};</li>
* <li>/bsbadmin range set : {@code "commands.bsbadmin.range.set.parameters"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 : {@code "commands.sub3.[...].sub20.sub21.sub22.parameters"}.</li>
* </ul>
* </li>
* <li>{@code "commands." + getLabel() + ".parameters"} if this is a top-level
* command;</li>
* <li>{@code "commands." + getParent.getLabel() + getLabel() + ".parameters"}
* if this is a sub-command. <br/>
* Note that it can have up to 20 parent commands' labels being inserted before
* this sub-command's label. Here are a few examples :
* <ul>
* <li>/bentobox info : {@code "commands.bentobox.info.parameters"};</li>
* <li>/bsbadmin range set :
* {@code "commands.bsbadmin.range.set.parameters"};</li>
* <li>/mycommand sub1 sub2 sub3 [...] sub22 :
* {@code "commands.sub3.[...].sub20.sub21.sub22.parameters"}.</li>
* </ul>
* </li>
* </ul>
*
* This method should therefore only be used in case you want to provide a different value than the default one.
* This method should therefore only be used in case you want to provide a
* different value than the default one.
*
* @param parametersHelp The locale command's paramaters reference to set.
*/
@ -556,12 +651,15 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
this.parameters = parametersHelp;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see org.bukkit.command.Command#setPermission(java.lang.String)
*/
@Override
public void setPermission(String permission) {
this.permission = ((permissionPrefix != null && !permissionPrefix.isEmpty()) ? permissionPrefix : "") + permission;
this.permission = ((permissionPrefix != null && !permissionPrefix.isEmpty()) ? permissionPrefix : "")
+ permission;
}
/**
@ -575,7 +673,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* This creates the full linking chain of commands
*/
@Override
public Command setUsage(String usage) {
public @NonNull Command setUsage(@NonNull String usage) {
// Go up the chain
CompositeCommand parentCommand = getParent();
StringBuilder u = new StringBuilder().append(getLabel()).append(" ").append(usage);
@ -590,24 +688,31 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
@Override
@NonNull
public List<String> tabComplete(final CommandSender sender, final String alias, final String[] args) {
List<String> options = new ArrayList<>();
public List<String> tabComplete(final @NonNull CommandSender sender, final @NonNull String alias,
final String[] args) {
// Get command object based on args entered so far
CompositeCommand command = getCommandFromArgs(args);
// Check for console and permissions
if (command.isOnlyPlayer() && !(sender instanceof Player)) {
return options;
if ((command.isOnlyPlayer() && !(sender instanceof Player))
|| (command.isOnlyConsole() && sender instanceof Player)) {
return List.of();
}
if (command.getPermission() != null && !command.getPermission().isEmpty() && !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
return options;
if (command.getPermission() != null && !command.getPermission().isEmpty()
&& !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
return List.of();
}
// Add any tab completion from the subcommand
options.addAll(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(() -> new ArrayList<>()));
List<String> options = new ArrayList<>(
command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args)))
.orElseGet(ArrayList::new));
if (command.hasSubCommands()) {
options.addAll(getSubCommandLabels(sender, command));
}
/* /!\ The following check is likely a poor quality patch-up job. If any better solution can be applied, don't hesitate to do so. */
/*
* /!\ The following check is likely a poor quality patch-up job. If any better
* solution can be applied, don't hesitate to do so.
*/
// See https://github.com/BentoBoxWorld/BentoBox/issues/416
// "help" shouldn't appear twice, so remove it if it is already in the args.
@ -618,31 +723,44 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/* ------------ */
String lastArg = args.length != 0 ? args[args.length - 1] : "";
return Util.tabLimit(options, lastArg).stream().sorted().collect(Collectors.toList());
return Util.tabLimit(options, lastArg).stream().sorted().toList();
}
/**
* Returns a list containing all the labels of the subcommands for the provided CompositeCommand excluding any hidden commands
* @param sender the CommandSender
* Returns a list containing all the labels of the subcommands for the provided
* CompositeCommand excluding any hidden commands
*
* @param sender the CommandSender
* @param command the CompositeCommand to get the subcommands from
* @return a list of subcommands labels or an empty list.
*/
@NonNull
private List<String> getSubCommandLabels(@NonNull CommandSender sender, @NonNull CompositeCommand command) {
return command.getSubCommands().values().stream()
.filter(cmd -> !cmd.isHidden())
.filter(cmd -> !cmd.isOnlyPlayer() || sender.isOp() || (sender instanceof Player && cmd.getPermission() != null && (cmd.getPermission().isEmpty() || sender.hasPermission(cmd.getPermission()))) )
.map(CompositeCommand::getLabel).collect(Collectors.toList());
List<String> result = new ArrayList<>();
for (CompositeCommand cc : command.getSubCommands().values()) {
// Player or not
if (sender instanceof Player) {
if (!cc.isHidden() && !cc.isOnlyConsole()
&& (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) {
result.add(cc.getLabel());
}
} else if (!cc.isOnlyPlayer()) {
result.add(cc.getLabel());
}
}
return result;
}
/**
* Show help
*
* @param command - command that this help is for
* @param user - the User
* @param user - the User
* @return result of help command or false if no help defined
*/
protected boolean showHelp(CompositeCommand command, User user) {
return command.getSubCommand("help").map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false);
return command.getSubCommand("help")
.map(helpCommand -> helpCommand.execute(user, helpCommand.getLabel(), new ArrayList<>())).orElse(false);
}
/**
@ -653,7 +771,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* If the permission prefix has been set, will return the prefix plus a trailing dot.
* If the permission prefix has been set, will return the prefix plus a trailing
* dot.
*
* @return the permissionPrefix
*/
@Nullable
@ -663,6 +783,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* The the world that this command applies to.
*
* @return the world
*/
public World getWorld() {
@ -678,6 +799,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* Get the parental addon
*
* @return the addon
*/
@SuppressWarnings("unchecked")
@ -694,28 +817,33 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Set a cool down - can be set by other commands on this one
* @param uniqueId - the unique ID that is having the cooldown
* @param targetUUID - the target (if any)
*
* @param uniqueId - the unique ID that is having the cooldown
* @param targetUUID - the target (if any)
* @param timeInSeconds - time in seconds to cool down
* @since 1.5.0
*/
public void setCooldown(String uniqueId, String targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID, System.currentTimeMillis() + timeInSeconds * 1000);
cooldowns.computeIfAbsent(uniqueId, k -> new HashMap<>()).put(targetUUID,
System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**
* Set a cool down - can be set by other commands on this one
* @param uniqueId - the UUID that is having the cooldown
* @param targetUUID - the target UUID (if any)
*
* @param uniqueId - the UUID that is having the cooldown
* @param targetUUID - the target UUID (if any)
* @param timeInSeconds - time in seconds to cool down
*/
public void setCooldown(UUID uniqueId, UUID targetUUID, int timeInSeconds) {
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000);
cooldowns.computeIfAbsent(uniqueId.toString(), k -> new HashMap<>()).put(
targetUUID == null ? null : targetUUID.toString(), System.currentTimeMillis() + timeInSeconds * 1000L);
}
/**
* Set a cool down for a user - can be set by other commands on this one
* @param uniqueId - the UUID that is having the cooldown
*
* @param uniqueId - the UUID that is having the cooldown
* @param timeInSeconds - time in seconds to cool down
* @since 1.5.0
*/
@ -725,7 +853,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress for user
* @param user - the caller of the command
*
* @param user - the caller of the command
* @param targetUUID - the target (if any)
* @return true if cool down in place, false if not
*/
@ -735,6 +864,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress for user
*
* @param user - the user to check
* @return true if cool down in place, false if not
* @since 1.5.0
@ -745,14 +875,16 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if cool down is in progress
* @param user - the caller of the command
* @param uniqueId - the id that needs to be checked
*
* @param user - the caller of the command
* @param uniqueId - the id that needs to be checked
* @param targetUUID - the target (if any)
* @return true if cool down in place, false if not
* @since 1.5.0
*/
protected boolean checkCooldown(User user, String uniqueId, String targetUUID) {
if (!cooldowns.containsKey(uniqueId) || user.isOp() || user.hasPermission(getPermissionPrefix() + "mod.bypasscooldowns")) {
if (!cooldowns.containsKey(uniqueId) || user.isOp()
|| user.hasPermission(getPermissionPrefix() + "mod.bypasscooldowns")) {
return false;
}
cooldowns.putIfAbsent(uniqueId, new HashMap<>());
@ -761,7 +893,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
cooldowns.get(uniqueId).remove(targetUUID);
return false;
}
int timeToGo = (int) ((cooldowns.get(uniqueId).getOrDefault(targetUUID, 0L) - System.currentTimeMillis()) / 1000);
int timeToGo = (int) ((cooldowns.get(uniqueId).getOrDefault(targetUUID, 0L) - System.currentTimeMillis())
/ 1000);
user.sendMessage("general.errors.you-must-wait", TextVariables.NUMBER, String.valueOf(timeToGo));
return true;
}
@ -780,8 +913,29 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
this.configurableRankCommand = true;
}
/**
* Sets default command rank.
*
* @param rank the rank
* @since 1.20.0
*/
public void setDefaultCommandRank(int rank) {
this.defaultCommandRank = rank;
}
/**
* Gets default command rank.
*
* @return the default command rank
* @since 1.20.0
*/
public int getDefaultCommandRank() {
return this.defaultCommandRank;
}
/**
* Checks if a command is hidden
*
* @return the hidden
* @since 1.13.0
*/
@ -791,6 +945,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Sets a command and all its help and tab complete as hidden
*
* @param hidden whether command is hidden or not
* @since 1.13.0
*/

View File

@ -11,7 +11,8 @@ import world.bentobox.bentobox.api.user.User;
/**
* BentoBox Confirmable Command
* Adds ability to confirm a command before execution
* Adds ability to confirm a command before execution.
* See {@link #askConfirmation(User, Runnable)}, {@link #askConfirmation(User, String, Runnable)}
* @author tastybento
* @author Poslovitch
*/
@ -20,7 +21,7 @@ public abstract class ConfirmableCommand extends CompositeCommand {
/**
* Confirmation tracker
*/
private static Map<User, Confirmer> toBeConfirmed = new HashMap<>();
private static final Map<User, Confirmer> toBeConfirmed = new HashMap<>();
/**
* Top level command
@ -44,9 +45,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
/**
*
* @param parent
* @param label
* @param aliases
* @param parent - parent command
* @param label - command label
* @param aliases - command aliases
*/
protected ConfirmableCommand(CompositeCommand parent, String label, String... aliases) {
super(parent, label, aliases);
@ -61,9 +62,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
public void askConfirmation(User user, String message, Runnable confirmed) {
// Check for pending confirmations
if (toBeConfirmed.containsKey(user)) {
if (toBeConfirmed.get(user).getTopLabel().equals(getTopLabel()) && toBeConfirmed.get(user).getLabel().equalsIgnoreCase(getLabel())) {
toBeConfirmed.get(user).getTask().cancel();
Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).getRunnable());
if (toBeConfirmed.get(user).topLabel().equals(getTopLabel()) && toBeConfirmed.get(user).label().equalsIgnoreCase(getLabel())) {
toBeConfirmed.get(user).task().cancel();
Bukkit.getScheduler().runTask(getPlugin(), toBeConfirmed.get(user).runnable());
toBeConfirmed.remove(user);
return;
} else {
@ -88,7 +89,8 @@ public abstract class ConfirmableCommand extends CompositeCommand {
}
/**
* Tells user to confirm command by retyping it.
* Tells user to confirm command by retyping it. Uses the default message to retype it.<p>
* If you need a custom message, use {@link #askConfirmation(User, String, Runnable)}
* @param user User to ask confirmation to.
* @param confirmed Runnable to be executed if successfully confirmed.
*/
@ -97,51 +99,9 @@ public abstract class ConfirmableCommand extends CompositeCommand {
}
/**
* Holds the data to run once the confirmation is given
* @author tastybento
* Record to hold the data to run once the confirmation is given
*
*/
private class Confirmer {
private final String topLabel;
private final String label;
private final Runnable runnable;
private final BukkitTask task;
/**
* @param label - command label
* @param runnable - runnable to run when confirmed
* @param task - task ID to cancel when confirmed
*/
Confirmer(String topLabel, String label, Runnable runnable, BukkitTask task) {
this.topLabel = topLabel;
this.label = label;
this.runnable = runnable;
this.task = task;
}
/**
* @return the topLabel
*/
public String getTopLabel() {
return topLabel;
}
/**
* @return the label
*/
public String getLabel() {
return label;
}
/**
* @return the runnable
*/
public Runnable getRunnable() {
return runnable;
}
/**
* @return the task
*/
public BukkitTask getTask() {
return task;
}
}
private record Confirmer (String topLabel, String label, Runnable runnable, BukkitTask task) { }
}

View File

@ -7,8 +7,10 @@ import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.scheduler.BukkitTask;
import world.bentobox.bentobox.api.addons.Addon;
@ -24,21 +26,34 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
/**
* User monitor map
*/
private static Map<UUID, DelayedCommand> toBeMonitored = new HashMap<>();
private static final Map<UUID, DelayedCommand> toBeMonitored = new HashMap<>();
@EventHandler
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent e) {
UUID uuid = e.getPlayer().getUniqueId();
// Only check x,y,z
if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).getLocation().toVector())) {
// Player moved
toBeMonitored.get(uuid).getTask().cancel();
toBeMonitored.remove(uuid);
// Player has another outstanding confirmation request that will now be cancelled
User.getInstance(e.getPlayer()).notify("commands.delay.moved-so-command-cancelled");
if (toBeMonitored.containsKey(uuid) && !e.getTo().toVector().equals(toBeMonitored.get(uuid).location().toVector())) {
moved(uuid);
}
}
private void moved(UUID uuid) {
// Player moved
toBeMonitored.get(uuid).task().cancel();
toBeMonitored.remove(uuid);
// Player has another outstanding confirmation request that will now be cancelled
User.getInstance(uuid).notify("commands.delay.moved-so-command-cancelled");
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent e) {
UUID uuid = e.getPlayer().getUniqueId();
if (toBeMonitored.containsKey(uuid)) {
moved(uuid);
}
}
/**
* Top level command
* @param addon - addon creating the command
@ -63,9 +78,9 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
/**
*
* @param parent
* @param label
* @param aliases
* @param parent - parent command
* @param label - command label
* @param aliases - command aliases
*/
protected DelayedTeleportCommand(CompositeCommand parent, String label, String... aliases) {
super(parent, label, aliases);
@ -88,7 +103,7 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
UUID uuid = user.getUniqueId();
if (toBeMonitored.containsKey(uuid)) {
// A double request - clear out the old one
toBeMonitored.get(uuid).getTask().cancel();
toBeMonitored.get(uuid).task().cancel();
toBeMonitored.remove(uuid);
// Player has another outstanding confirmation request that will now be cancelled
user.sendMessage("commands.delay.previous-command-cancelled");
@ -101,7 +116,7 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
user.sendMessage("commands.delay.stand-still", "[seconds]", String.valueOf(getSettings().getDelayTime()));
// Set up the run task
BukkitTask task = Bukkit.getScheduler().runTaskLater(getPlugin(), () -> {
Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).getRunnable());
Bukkit.getScheduler().runTask(getPlugin(), toBeMonitored.get(uuid).runnable());
toBeMonitored.remove(uuid);
}, getPlugin().getSettings().getDelayTime() * 20L);
@ -120,42 +135,8 @@ public abstract class DelayedTeleportCommand extends CompositeCommand implements
/**
* Holds the data to run once the confirmation is given
* @author tastybento
*
*/
private class DelayedCommand {
private final Runnable runnable;
private final BukkitTask task;
private final Location location;
/**
* @param runnable - runnable to run when confirmed
* @param task - task ID to cancel when confirmed
* @param location - location
*/
DelayedCommand(Runnable runnable, BukkitTask task, Location location) {
this.runnable = runnable;
this.task = task;
this.location = location;
}
/**
* @return the runnable
*/
public Runnable getRunnable() {
return runnable;
}
/**
* @return the task
*/
public BukkitTask getTask() {
return task;
}
/**
* @return the location
*/
public Location getLocation() {
return location;
}
}
private record DelayedCommand(Runnable runnable, BukkitTask task, Location location) {}
}

View File

@ -1,11 +1,13 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
@ -18,6 +20,9 @@ import world.bentobox.bentobox.util.Util;
public class AdminDeleteCommand extends ConfirmableCommand {
private @Nullable UUID targetUUID;
private Island island;
public AdminDeleteCommand(CompositeCommand parent) {
super(parent, "delete");
}
@ -31,97 +36,157 @@ public class AdminDeleteCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 1) {
showHelp(this, user);
if (args.isEmpty()) {
this.showHelp(this, user);
return false;
}
// Get target
UUID targetUUID = Util.getUUID(args.get(0));
// Convert name to a UUID
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!getIslands().hasIsland(getWorld(), targetUUID)) {
// Check island exists
if (!getIslands().hasIsland(getWorld(), targetUUID) && !getIslands().inTeam(getWorld(), targetUUID)) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (args.size() == 1) {
// Check if player is owner of any islands
if (getIslands().getIslands(getWorld(), targetUUID).stream().filter(Island::hasTeam)
.anyMatch(is -> targetUUID.equals(is.getOwner()))) {
user.sendMessage("commands.admin.delete.cannot-delete-owner");
return false;
}
// This is a delete everything request
return true;
}
// Get the island
User target = User.getInstance(targetUUID);
// They named the island to go to
Map<String, IslandInfo> names = getNameIslandMap(target);
final String name = String.join(" ", args.subList(1, args.size()));
if (!names.containsKey(name)) {
// Failed home name check
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
names.keySet()
.forEach(n -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, n));
return false;
} else {
IslandInfo info = names.get(name);
island = info.island;
}
// Team members should be kicked before deleting otherwise the whole team will become weird
if (getIslands().inTeam(getWorld(), targetUUID) && getIslands().getOwner(getWorld(), targetUUID).equals(targetUUID)) {
if (island.hasTeam() && targetUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.delete.cannot-delete-owner");
return false;
}
if (names.size() == 1) {
// This is the only island they have so, no need to specify it
island = null;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
showHelp(this, user);
return false;
}
// Get target
UUID targetUUID = getPlayers().getUUID(args.get(0));
// Confirm
askConfirmation(user, () -> deletePlayer(user, targetUUID));
if (island == null) {
// Delete the player entirely
askConfirmation(user, () -> deletePlayer(user));
} else {
// Just delete the player's island
askConfirmation(user, () -> deleteIsland(user, island));
}
return true;
}
private void deletePlayer(User user, UUID targetUUID) {
private void deleteIsland(User user, Island oldIsland) {
// Fire island preclear event
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ,
Util.xyz(oldIsland.getCenter().toVector()));
getIslands().deleteIsland(oldIsland, true, targetUUID);
}
private void deletePlayer(User user) {
// Delete player and island
// Get the target's island
Island oldIsland = getIslands().getIsland(getWorld(), targetUUID);
Vector vector = null;
if (oldIsland != null) {
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(Reason.PRECLEAR)
.island(oldIsland)
.oldIsland(oldIsland)
.location(oldIsland.getCenter())
.build();
// Check if player is online and on the island
User target = User.getInstance(targetUUID);
// Remove them from this island (it still exists and will be deleted later)
getIslands().removePlayer(getWorld(), targetUUID);
if (target.isOnline()) {
// Execute commands when leaving
Util.runCommands(user, getIWM().getOnLeaveCommands(getWorld()), "leave");
// Remove money inventory etc.
if (getIWM().isOnLeaveResetEnderChest(getWorld())) {
target.getPlayer().getEnderChest().clear();
}
if (getIWM().isOnLeaveResetInventory(getWorld())) {
target.getPlayer().getInventory().clear();
}
if (getSettings().isUseEconomy() && getIWM().isOnLeaveResetMoney(getWorld())) {
getPlugin().getVault().ifPresent(vault -> vault.withdraw(target, vault.getBalance(target)));
}
// Reset the health
if (getIWM().isOnLeaveResetHealth(getWorld())) {
Util.resetHealth(target.getPlayer());
}
for (Island oldIsland : getIslands().getIslands(getWorld(), targetUUID)) {
deleteIsland(user, oldIsland);
}
// Check if player is online and on the island
User target = User.getInstance(targetUUID);
// Remove target from any and all islands in the world
getIslands().removePlayer(getWorld(), targetUUID);
if (target.isPlayer() && target.isOnline()) {
cleanUp(target);
}
user.sendMessage("general.success");
}
// Reset the hunger
if (getIWM().isOnLeaveResetHunger(getWorld())) {
target.getPlayer().setFoodLevel(20);
}
private void cleanUp(User target) {
// Remove money inventory etc.
if (getIWM().isOnLeaveResetEnderChest(getWorld())) {
target.getPlayer().getEnderChest().clear();
}
if (getIWM().isOnLeaveResetInventory(getWorld())) {
target.getPlayer().getInventory().clear();
}
if (getSettings().isUseEconomy() && getIWM().isOnLeaveResetMoney(getWorld())) {
getPlugin().getVault().ifPresent(vault -> vault.withdraw(target, vault.getBalance(target)));
}
// Reset the health
if (getIWM().isOnLeaveResetHealth(getWorld())) {
Util.resetHealth(target.getPlayer());
}
// Reset the XP
if (getIWM().isOnLeaveResetXP(getWorld())) {
target.getPlayer().setTotalExperience(0);
}
// Reset the hunger
if (getIWM().isOnLeaveResetHunger(getWorld())) {
target.getPlayer().setFoodLevel(20);
}
// Reset the XP
if (getIWM().isOnLeaveResetXP(getWorld())) {
// Player collected XP (displayed)
target.getPlayer().setLevel(0);
target.getPlayer().setExp(0);
// Player total XP (not displayed)
target.getPlayer().setTotalExperience(0);
}
// Execute commands when leaving
Util.runCommands(target, target.getName(), getIWM().getOnLeaveCommands(getWorld()), "leave");
}
private record IslandInfo(Island island, boolean islandName) {
}
private Map<String, IslandInfo> getNameIslandMap(User target) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : getIslands().getIslands(getWorld(), target.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = target.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
vector = oldIsland.getCenter().toVector();
getIslands().deleteIsland(oldIsland, true, targetUUID);
}
getPlayers().clearHomeLocations(getWorld(), targetUUID);
if (vector == null) {
user.sendMessage("general.success");
} else {
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(vector));
// Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
@Override
@ -131,7 +196,16 @@ public class AdminDeleteCommand extends ConfirmableCommand {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
if (args.size() == 2) {
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
}
if (args.size() == 3) {
UUID target = Util.getUUID(args.get(1));
return target == null ? Optional.empty()
: Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(User.getInstance(target)).keySet()),
lastArg));
}
return Optional.empty();
}
}
}

View File

@ -0,0 +1,78 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* Deletes all named homes from an island
* @author tastybento
*
*/
public class AdminDeleteHomesCommand extends ConfirmableCommand {
public AdminDeleteHomesCommand(CompositeCommand parent) {
super(parent, "deletehomes");
}
@Override
public void setup() {
setPermission("mod.deletehomes");
setOnlyPlayer(false);
setParametersHelp("commands.admin.deletehomes.parameters");
setDescription("commands.admin.deletehomes.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 1) {
showHelp(this, user);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Get target player
UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Get island
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Confirm
askConfirmation(user, user.getTranslation("commands.admin.deletehomes.warning"), () -> deleteHomes(user, island));
return true;
}
private boolean deleteHomes(User user, Island island) {
island.removeHomes();
user.sendMessage("general.success");
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -1,61 +0,0 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
public class AdminEmptyTrashCommand extends ConfirmableCommand {
/**
* Clear trash for player, or all unowned islands in trash
* @param parent - admin command
* @since 1.3.0
*/
public AdminEmptyTrashCommand(CompositeCommand parent) {
super(parent, "emptytrash");
}
@Override
public void setup() {
setPermission("admin.trash");
setOnlyPlayer(false);
setParametersHelp("commands.admin.emptytrash.parameters");
setDescription("commands.admin.emptytrash.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() > 1) {
// Show help
showHelp(this, user);
return false;
}
// Get target player
UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0));
if (!args.isEmpty() && targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Remove trash for this player
final List<Island> islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
if (islands.isEmpty()) {
if (args.isEmpty()) {
user.sendMessage("commands.admin.trash.no-unowned-in-trash");
} else {
user.sendMessage("commands.admin.trash.no-islands-in-trash");
}
return false;
} else {
this.askConfirmation(user, () -> {
getIslands().deleteQuarantinedIslandByUser(getWorld(), targetUUID);
user.sendMessage("commands.admin.emptytrash.success");
});
return true;
}
}
}

View File

@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -17,6 +16,7 @@ import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* Tells the rank of the player
* @author tastybento
*
*/
@ -83,10 +83,10 @@ public class AdminGetrankCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
// Get rank
RanksManager rm = getPlugin().getRanksManager();
User target = User.getInstance(targetUUID);
int currentRank = island.getRank(target);
user.sendMessage("commands.admin.getrank.rank-is", TextVariables.RANK, user.getTranslation(rm.getRank(currentRank)),
user.sendMessage("commands.admin.getrank.rank-is", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(currentRank)),
TextVariables.NAME, getPlayers().getName(island.getOwner()));
return true;
}
@ -98,7 +98,7 @@ public class AdminGetrankCommand extends CompositeCommand {
return Optional.empty();
}
String lastArg = args.get(args.size() - 1);
List<String> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
List<String> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -9,6 +9,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
public class AdminInfoCommand extends CompositeCommand {
@ -34,10 +35,8 @@ public class AdminInfoCommand extends CompositeCommand {
}
// If there are no args, then the player wants info on the island at this location
if (args.isEmpty()) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) {
user.sendMessage("commands.admin.info.no-island");
return false;
}
getIslands().getIslandAt(user.getLocation()).ifPresentOrElse(i -> new IslandInfo(i).showAdminInfo(user, getAddon()), () ->
user.sendMessage("commands.admin.info.no-island"));
return true;
}
// Get target player
@ -49,10 +48,7 @@ public class AdminInfoCommand extends CompositeCommand {
// Show info for this player
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island != null) {
island.showInfo(user);
if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) {
user.sendMessage("commands.admin.info.islands-in-trash");
}
new IslandInfo(island).showAdminInfo(user, getAddon());
return true;
} else {
user.sendMessage("general.errors.player-has-no-island");

View File

@ -2,11 +2,13 @@ package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
@ -19,6 +21,10 @@ import world.bentobox.bentobox.util.Util;
public class AdminRegisterCommand extends ConfirmableCommand {
private Island island;
private Location closestIsland;
private @Nullable UUID targetUUID;
public AdminRegisterCommand(CompositeCommand parent) {
super(parent, "register");
}
@ -32,104 +38,108 @@ public class AdminRegisterCommand extends ConfirmableCommand {
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
showHelp(this, user);
return false;
}
// Check world
if (!getWorld().equals(user.getWorld())) {
user.sendMessage("general.errors.wrong-world");
return false;
}
// Get target
UUID targetUUID = Util.getUUID(args.get(0));
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (getIslands().hasIsland(getWorld(), targetUUID)) {
user.sendMessage("general.errors.player-has-island");
return false;
}
if (getIslands().inTeam(getWorld(), targetUUID)) {
user.sendMessage("commands.admin.register.cannot-register-team-player");
return false;
}
// Check if this spot is still being deleted
Location closestIsland = Util.getClosestIsland(user.getLocation());
closestIsland = Util.getClosestIsland(user.getLocation());
if (getPlugin().getIslandDeletionManager().inDeletion(closestIsland)) {
user.sendMessage("commands.admin.register.in-deletion");
return false;
}
// Check if island is owned
Optional<Island> island = getIslands().getIslandAt(user.getLocation());
if (island.filter(Island::isOwned)
.filter(i -> !i.getOwner().equals(targetUUID))
.isPresent()) {
Optional<Island> opIsland = getIslands().getIslandAt(user.getLocation());
if (opIsland.isEmpty()) {
// Reserve spot
this.askConfirmation(user, user.getTranslation("commands.admin.register.no-island-here"),
() -> reserve(user, args.get(0)));
return false;
}
island = opIsland.get();
if (targetUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.register.already-owned");
return false;
}
// Check if island is spawn
if (island.map(Island::isSpawn).orElse(false)) {
askConfirmation(user, user.getTranslation("commands.admin.register.island-is-spawn"), () -> register(user, args.get(0), targetUUID, island, closestIsland));
if (island.isSpawn()) {
askConfirmation(user, user.getTranslation("commands.admin.register.island-is-spawn"),
() -> register(user, args.get(0)));
return false;
}
return register(user, args.get(0),targetUUID, island, closestIsland);
return true;
}
private boolean register(User user, String targetName, UUID targetUUID, Optional<Island> island, Location closestIsland) {
// Register island if it exists
if (!island.map(i -> {
// Island exists
getIslands().setOwner(user, targetUUID, i);
if (i.isSpawn()) {
getIslands().clearSpawn(i.getWorld());
}
user.sendMessage("commands.admin.register.registered-island", TextVariables.XYZ, Util.xyz(i.getCenter().toVector()),
TextVariables.NAME, targetName);
user.sendMessage("general.success");
// Build and call event
IslandEvent.builder()
.island(i)
.location(i.getCenter())
.reason(IslandEvent.Reason.REGISTERED)
.involvedPlayer(targetUUID)
.admin(true)
.build();
IslandEvent.builder()
.island(i)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.VISITOR_RANK, RanksManager.OWNER_RANK)
.build();
return true;
}).orElse(false)) {
// Island does not exist - this is a reservation
this.askConfirmation(user, user.getTranslation("commands.admin.register.no-island-here"), () -> {
// Make island here
Island i = getIslands().createIsland(closestIsland, targetUUID);
if (i == null) {
user.sendMessage("commands.admin.register.cannot-make-island");
return;
}
getIslands().setOwner(user, targetUUID, i);
i.setReserved(true);
i.getCenter().getBlock().setType(Material.BEDROCK);
user.sendMessage("commands.admin.register.reserved-island", TextVariables.XYZ, Util.xyz(i.getCenter().toVector()),
TextVariables.NAME, targetName);
// Build and fire event
IslandEvent.builder()
.island(i)
.location(i.getCenter())
.reason(IslandEvent.Reason.RESERVED)
.involvedPlayer(targetUUID)
.admin(true)
.build();
});
return false;
}
@Override
public boolean execute(User user, String label, List<String> args) {
register(user, args.get(0));
return true;
}
/**
* Reserve a spot for a target
* @param user user doing the reserving
* @param targetName target name
*/
void reserve(User user, String targetName) {
Objects.requireNonNull(closestIsland);
Objects.requireNonNull(targetUUID);
// Island does not exist - this is a reservation
// Make island here
Island i = getIslands().createIsland(closestIsland, targetUUID);
if (i == null) {
user.sendMessage("commands.admin.register.cannot-make-island");
return;
}
getIslands().setOwner(user, targetUUID, i, RanksManager.VISITOR_RANK);
i.setReserved(true);
i.getCenter().getBlock().setType(Material.BEDROCK);
user.sendMessage("commands.admin.register.reserved-island", TextVariables.XYZ,
Util.xyz(i.getCenter().toVector()), TextVariables.NAME, targetName);
// Build and fire event
IslandEvent.builder().island(i).location(i.getCenter()).reason(IslandEvent.Reason.RESERVED)
.involvedPlayer(targetUUID).admin(true).build();
}
/**
* Register the island to a target
* @param user user doing the registering
* @param targetName name of target
*/
void register(User user, String targetName) {
Objects.requireNonNull(closestIsland);
Objects.requireNonNull(targetUUID);
Objects.requireNonNull(island);
// Island exists
getIslands().setOwner(user, targetUUID, island, RanksManager.VISITOR_RANK);
if (island.isSpawn()) {
getIslands().clearSpawn(island.getWorld());
}
user.sendMessage("commands.admin.register.registered-island", TextVariables.XYZ,
Util.xyz(island.getCenter().toVector()), TextVariables.NAME, targetName);
user.sendMessage("general.success");
// Build and call event
IslandEvent.builder().island(island).location(island.getCenter()).reason(IslandEvent.Reason.REGISTERED)
.involvedPlayer(targetUUID).admin(true).build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(RanksManager.VISITOR_RANK, RanksManager.OWNER_RANK)
.build();
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";

View File

@ -3,7 +3,6 @@ package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
@ -20,13 +19,13 @@ import world.bentobox.bentobox.util.Util;
*/
public class AdminResetFlagsCommand extends ConfirmableCommand {
private List<String> options;
private final List<String> options;
public AdminResetFlagsCommand(CompositeCommand parent) {
super(parent, "resetflags");
options = getPlugin().getFlagsManager().getFlags().stream()
.filter(f -> f.getType().equals(Type.PROTECTION) || f.getType().equals(Type.SETTING))
.map(Flag::getID).collect(Collectors.toList());
.map(Flag::getID).toList();
}
@Override

View File

@ -0,0 +1,130 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* This command resets players island name.
* @author BONNe
*/
public class AdminResetNameCommand extends CompositeCommand
{
/**
* Default constructor.
* @param command Parent command.
*/
public AdminResetNameCommand(CompositeCommand command)
{
super(command, "resetname");
}
/**
* {@inheritDoc}
*/
@Override
public void setup()
{
this.setPermission("mod.resetname");
this.setOnlyPlayer(true);
this.setDescription("commands.admin.resetname.description");
}
/**
* @param user the {@link User} who is executing this command.
* @param label the label which has been used to execute this command.
* It can be {@link CompositeCommand#getLabel()} or an alias.
* @param args the command arguments.
* @return {@code true} if name can be reset, {@code false} otherwise.
*/
@Override
public boolean canExecute(User user, String label, List<String> args)
{
if (args.size() == 1)
{
UUID playerUUID = Util.getUUID(args.get(0));
if (playerUUID == null)
{
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
this.island = this.getIslandsManager().getIsland(this.getWorld(), playerUUID);
}
else
{
this.island = this.getIslandsManager().getIslandAt(user.getLocation()).orElse(null);
}
if (this.island == null)
{
user.sendMessage("general.errors.player-has-no-island");
return false;
}
return true;
}
/**
* @param user the {@link User} who is executing this command.
* @param label the label which has been used to execute this command.
* It can be {@link CompositeCommand#getLabel()} or an alias.
* @param args the command arguments.
* @return {@code true}
*/
@Override
public boolean execute(User user, String label, List<String> args)
{
if (this.island == null)
{
this.showHelp(this, user);
return true;
}
// Resets the island name
this.island.setName(null);
user.sendMessage("commands.admin.resetname.success", TextVariables.NAME, this.getPlayers().getName(this.island.getOwner()));
return true;
}
/**
* @param user the {@link User} who is executing this command.
* @param alias alias for command
* @param args command arguments
* @return Optional of possible values.
*/
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
// Return the player names
if (args.size() == 1)
{
return Optional.of(Util.getOnlinePlayerList(user));
}
else
{
return Optional.empty();
}
}
/**
* Island which name must be changed.
*/
@Nullable
private Island island;
}

View File

@ -50,7 +50,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() == 3) {
// Get location
targetLoc = getLocation(user, args);
targetLoc = getLocation(args);
} else {
targetLoc = new Location(getWorld(), user.getLocation().getBlockX(), user.getLocation().getBlockY(), user.getLocation().getBlockZ());
}
@ -59,7 +59,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
return false;
}
Optional<Island> optionalIsland = getIslands().getIslandAt(targetLoc);
if (!optionalIsland.isPresent()) {
if (optionalIsland.isEmpty()) {
user.sendMessage("commands.admin.setspawnpoint.no-island-here");
return false;
}
@ -67,7 +67,7 @@ public class AdminSetProtectionCenterCommand extends ConfirmableCommand
return true;
}
private Location getLocation(User user, List<String> args) {
private Location getLocation(List<String> args) {
try {
int x = Integer.parseInt(args.get(0));
int y = Integer.parseInt(args.get(1));

View File

@ -1,6 +1,5 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -12,84 +11,70 @@ import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* This command sets spawn point for island at admin location for island on which admin is located.
* This command is only for player entity.
* @author BONNe
* @since 1.13.0
*/
public class AdminSetSpawnPointCommand extends ConfirmableCommand
{
public class AdminSetSpawnPointCommand extends ConfirmableCommand {
/**
* Sub-command constructor
*
* @param parent - the parent composite command
*/
public AdminSetSpawnPointCommand(CompositeCommand parent)
{
public AdminSetSpawnPointCommand(CompositeCommand parent) {
super(parent, "setspawnpoint");
}
/**
* {@inheritDoc}
*/
@Override
public void setup()
{
public void setup() {
this.setPermission("admin.setspawnpoint");
this.setOnlyPlayer(true);
this.setDescription("commands.admin.setspawnpoint.description");
}
/**
* This method finds an island in user location and asks confirmation if spawn point
* must be changed to that location.
* This method finds an island in user location and asks confirmation if spawn point
* must be changed to that location.
* {@inheritDoc}
*/
@Override
public boolean execute(User user, String label, List<String> args)
{
public boolean execute(User user, String label, List<String> args) {
Optional<Island> optionalIsland = this.getIslands().getIslandAt(user.getLocation());
if (optionalIsland.isPresent() &&
(optionalIsland.get().hasNetherIsland() ||
!World.Environment.NETHER.equals(user.getLocation().getWorld().getEnvironment())) &&
(optionalIsland.get().hasEndIsland() ||
!World.Environment.THE_END.equals(user.getLocation().getWorld().getEnvironment())))
{
if (optionalIsland.isPresent()
&& (optionalIsland.get().hasNetherIsland()
|| !World.Environment.NETHER.equals(user.getLocation().getWorld().getEnvironment()))
&& (optionalIsland.get().hasEndIsland()
|| !World.Environment.THE_END.equals(user.getLocation().getWorld().getEnvironment()))) {
// Everything's fine, we can set the location as spawn point for island :)
this.askConfirmation(user, user.getTranslation("commands.admin.setspawnpoint.confirmation"),
() -> this.setSpawnPoint(user, optionalIsland.get()));
() -> this.setSpawnPoint(user, optionalIsland.get()));
return true;
}
else
{
} else {
user.sendMessage("commands.admin.setspawnpoint.no-island-here");
return false;
}
}
/**
* This method changes spawn point for island at given user location.
* @param user User who initiate spawn point change.
* @param island Island which is targeted by user.
*/
private void setSpawnPoint(User user, Island island)
{
private void setSpawnPoint(User user, Island island) {
island.setSpawnPoint(Objects.requireNonNull(user.getLocation().getWorld()).getEnvironment(),
user.getLocation());
user.getLocation());
user.sendMessage("commands.admin.setspawnpoint.success");
if (!island.isSpawn())
{
island.getPlayersOnIsland().forEach(player ->
User.getInstance(player).sendMessage("commands.admin.setspawnpoint.island-spawnpoint-changed",
"[user]", user.getName()));
if (!island.isSpawn()) {
island.getPlayersOnIsland().forEach(player -> User.getInstance(player)
.sendMessage("commands.admin.setspawnpoint.island-spawnpoint-changed", "[user]", user.getName()));
}
}
}

View File

@ -4,7 +4,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
@ -25,7 +24,6 @@ public class AdminSetrankCommand extends CompositeCommand {
private int rankValue;
private @Nullable UUID targetUUID;
private @Nullable UUID ownerUUID;
private RanksManager rm;
public AdminSetrankCommand(CompositeCommand adminCommand) {
super(adminCommand, "setrank");
@ -37,7 +35,6 @@ public class AdminSetrankCommand extends CompositeCommand {
setOnlyPlayer(false);
setParametersHelp("commands.admin.setrank.parameters");
setDescription("commands.admin.setrank.description");
rm = getPlugin().getRanksManager();
}
@Override
@ -54,7 +51,7 @@ public class AdminSetrankCommand extends CompositeCommand {
return false;
}
// Get rank
rankValue = rm.getRanks().entrySet().stream()
rankValue = RanksManager.getInstance().getRanks().entrySet().stream()
.filter(r -> user.getTranslation(r.getKey()).equalsIgnoreCase(args.get(1))).findFirst()
.map(Map.Entry::getValue).orElse(-999);
if (rankValue < RanksManager.BANNED_RANK) {
@ -101,6 +98,10 @@ public class AdminSetrankCommand extends CompositeCommand {
} else {
island = getIslands().getIsland(getWorld(), targetUUID);
}
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
int currentRank = island.getRank(target);
island.setRank(target, rankValue);
IslandEvent.builder()
@ -118,8 +119,8 @@ public class AdminSetrankCommand extends CompositeCommand {
ownerName = target.getName();
}
user.sendMessage("commands.admin.setrank.rank-set",
"[from]", user.getTranslation(rm.getRank(currentRank)),
"[to]", user.getTranslation(rm.getRank(rankValue)),
"[from]", user.getTranslation(RanksManager.getInstance().getRank(currentRank)), "[to]",
user.getTranslation(RanksManager.getInstance().getRank(rankValue)),
TextVariables.NAME, ownerName);
return true;
}
@ -133,10 +134,10 @@ public class AdminSetrankCommand extends CompositeCommand {
// Return the ranks
if (args.size() == 3) {
return Optional.of(getPlugin().getRanksManager().getRanks()
return Optional.of(RanksManager.getInstance().getRanks()
.entrySet().stream()
.filter(entry -> entry.getValue() > RanksManager.VISITOR_RANK)
.map(entry -> user.getTranslation(entry.getKey())).collect(Collectors.toList()));
.map(entry -> user.getTranslation(entry.getKey())).toList());
}
// Return the player names again for the optional island owner argument

View File

@ -65,10 +65,8 @@ public class AdminSetspawnCommand extends ConfirmableCommand {
.build();
}
// If island is owned, then unregister the owner and any members
new ImmutableSet.Builder<UUID>().addAll(i.getMembers().keySet()).build().forEach(m -> {
getIslands().removePlayer(getWorld(), m);
getPlayers().clearHomeLocations(getWorld(), m);
});
new ImmutableSet.Builder<UUID>().addAll(i.getMembers().keySet()).build()
.forEach(m -> getIslands().removePlayer(i, m));
}
getIslands().setSpawn(i);
i.setSpawnPoint(World.Environment.NORMAL, user.getLocation());

View File

@ -8,7 +8,6 @@ import java.util.Locale;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.ChatColor;
import org.eclipse.jdt.annotation.NonNull;
@ -17,7 +16,6 @@ import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.Flag.Mode;
import world.bentobox.bentobox.api.flags.Flag.Type;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.builders.TabbedPanelBuilder;
@ -34,33 +32,37 @@ import world.bentobox.bentobox.util.Util;
*/
public class AdminSettingsCommand extends CompositeCommand {
private final List<String> PROTECTION_FLAG_NAMES;
private static final String SPAWN_ISLAND = "spawn-island";
private List<String> protectionFlagNames;
private Island island;
private final List<String> SETTING_FLAG_NAMES;
private List<String> WORLD_SETTING_FLAG_NAMES;
private @NonNull Optional<Flag> flag;
private List<String> settingFlagNames;
private List<String> worldSettingFlagNames;
private @NonNull Optional<Flag> flag = Optional.empty();
private boolean activeState;
private int rank;
private final GameModeAddon gameMode;
public AdminSettingsCommand(CompositeCommand islandCommand) {
super(islandCommand, "settings", "flags", "options");
// make constants
GameModeAddon gameMode = getPlugin().getIWM().getAddon(getWorld()).orElse(null);
PROTECTION_FLAG_NAMES = getPlugin().getFlagsManager().getFlags().stream()
gameMode = getPlugin().getIWM().getAddon(getWorld()).orElse(null);
}
private void makeLists() {
protectionFlagNames = getPlugin().getFlagsManager().getFlags().stream()
.filter(f -> f.getType().equals(Type.PROTECTION))
.filter(f -> f.getGameModes().isEmpty() || gameMode == null || f.getGameModes().contains(gameMode))
.map(Flag::getID)
.collect(Collectors.toList());
SETTING_FLAG_NAMES = getPlugin().getFlagsManager().getFlags().stream()
.toList();
settingFlagNames = getPlugin().getFlagsManager().getFlags().stream()
.filter(f -> f.getType().equals(Type.SETTING))
.filter(f -> f.getGameModes().isEmpty() || gameMode == null || f.getGameModes().contains(gameMode))
.map(Flag::getID)
.collect(Collectors.toList());
WORLD_SETTING_FLAG_NAMES = getPlugin().getFlagsManager().getFlags().stream()
.toList();
worldSettingFlagNames = getPlugin().getFlagsManager().getFlags().stream()
.filter(f -> f.getType().equals(Type.WORLD_SETTING))
.filter(f -> f.getGameModes().isEmpty() || gameMode == null || f.getGameModes().contains(gameMode))
.map(Flag::getID)
.collect(Collectors.toList());
.toList();
}
@Override
@ -78,12 +80,16 @@ public class AdminSettingsCommand extends CompositeCommand {
}
if (args.size() > 1) {
// Command
return checkSyntax(user, args);
return checkSyntax(user, args);
}
return getIsland(user, args);
}
private boolean getIsland(User user, List<String> args) {
if (args.get(0).equalsIgnoreCase(SPAWN_ISLAND) && getIslands().getSpawn(getWorld()).isPresent()) {
island = getIslands().getSpawn(getWorld()).get();
return true;
}
// Get target player
@Nullable UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
@ -100,49 +106,66 @@ public class AdminSettingsCommand extends CompositeCommand {
/**
* Check that this command is correct to set a setting
*
* @param user - user
* @param args - args
* @return true if the syntax is correct
*/
private boolean checkSyntax(User user, List<String> args) {
if (args.size() == 2) {
// Should be a world setting
// If world settings, then active/disabled, otherwise player flags
if (WORLD_SETTING_FLAG_NAMES.contains(args.get(0).toUpperCase(Locale.ENGLISH))) {
if (checkActiveDisabled(user, args.get(1))) {
flag = getPlugin().getFlagsManager().getFlag(args.get(0).toUpperCase(Locale.ENGLISH));
return true;
}
} else {
this.showHelp(this, user);
return false;
}
} else if (args.size() > 2) {
// Get island
if (!getIsland(user, args)) {
return false;
}
this.makeLists();
if (!SETTING_FLAG_NAMES.contains(args.get(1).toUpperCase(Locale.ENGLISH))
&& !PROTECTION_FLAG_NAMES.contains(args.get(1).toUpperCase(Locale.ENGLISH))) {
user.sendMessage("commands.admin.settings.unknown-flag", TextVariables.NAME, args.get(2));
return false;
}
// Set flag
flag = getPlugin().getFlagsManager().getFlag(args.get(1).toUpperCase(Locale.ENGLISH));
// Check settings
if (flag.isPresent()) {
if (flag.get().getType().equals(Type.SETTING)) {
return checkActiveDisabled(user, args.get(2));
} else {
// Protection flag
return checkRank(user, String.join(" ", args.subList(2, args.size())));
}
int argSize = args.size();
if (argSize == 2) {
return checkWorldSetting(user, args);
} else if (argSize > 2) {
return checkIslandSetting(user, args);
}
return false;
}
private boolean checkWorldSetting(User user, List<String> args) {
String arg0 = args.get(0).toUpperCase(Locale.ENGLISH);
if (worldSettingFlagNames.contains(arg0)) {
if (checkActiveDisabled(user, args.get(1))) {
flag = getPlugin().getFlagsManager().getFlag(args.get(0).toUpperCase(Locale.ENGLISH));
return true;
}
} else {
this.showHelp(this, user);
return false;
}
return false;
}
private boolean checkIslandSetting(User user, List<String> args) {
// Get island
if (!getIsland(user, args)) {
return false;
}
String arg1 = args.get(1).toUpperCase(Locale.ENGLISH);
if (!settingFlagNames.contains(arg1) && !protectionFlagNames.contains(arg1)) {
user.sendMessage("commands.admin.settings.unknown-flag", TextVariables.NAME, args.get(2));
return false;
}
// Set flag
flag = getPlugin().getFlagsManager().getFlag(arg1);
// Check settings
if (flag.isPresent()) {
if (flag.get().getType().equals(Type.SETTING)) {
return checkActiveDisabled(user, args.get(2));
} else {
// Protection flag
return checkRank(user, String.join(" ", args.subList(2, args.size())));
}
}
return false;
}
/**
* Check the rank given.
@ -151,7 +174,7 @@ public class AdminSettingsCommand extends CompositeCommand {
* @return true if rank is valid
*/
private boolean checkRank(User user, String string) {
for (Entry<String, Integer> en : getPlugin().getRanksManager().getRanks().entrySet()) {
for (Entry<String, Integer> en : RanksManager.getInstance().getRanks().entrySet()) {
if (en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK
&& string.equalsIgnoreCase(ChatColor.stripColor(user.getTranslation(en.getKey())))) {
// We have a winner
@ -168,7 +191,7 @@ public class AdminSettingsCommand extends CompositeCommand {
String disabled = ChatColor.stripColor(user.getTranslation("protection.panel.flag-item.setting-disabled"));
if (!string.equalsIgnoreCase(active) && !string.equalsIgnoreCase(disabled)) {
user.sendMessage("commands.admin.settings.unknown-setting", TextVariables.NAME, string);
return false;
return false;
}
activeState = string.equalsIgnoreCase(active);
return true;
@ -180,19 +203,16 @@ public class AdminSettingsCommand extends CompositeCommand {
// Command line setting
flag.ifPresent(f -> {
switch (f.getType()) {
case PROTECTION:
case PROTECTION -> {
island.setFlag(f, rank);
getIslands().save(island);
break;
case SETTING:
}
case SETTING -> {
island.setSettingsFlag(f, activeState);
getIslands().save(island);
break;
case WORLD_SETTING:
f.setSetting(getWorld(), activeState);
break;
default:
break;
}
case WORLD_SETTING -> f.setSetting(getWorld(), activeState);
default -> {
// Do nothing
}
}
});
user.sendMessage("general.success");
@ -200,15 +220,14 @@ public class AdminSettingsCommand extends CompositeCommand {
}
// GUI requires in-game
if (!user.isPlayer()) {
user.sendMessage("general.errors.use-in-game");
return false;
user.sendMessage("general.errors.use-in-game");
return false;
}
getPlayers().setFlagsDisplayMode(user.getUniqueId(), Mode.EXPERT);
if (args.isEmpty()) {
new TabbedPanelBuilder()
.user(user)
.world(getWorld())
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING))
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.WORLD_SETTING, Flag.Mode.EXPERT))
.tab(2, new WorldDefaultSettingsTab(getWorld(), user))
.startingSlot(1)
.size(54)
@ -219,8 +238,8 @@ public class AdminSettingsCommand extends CompositeCommand {
new TabbedPanelBuilder()
.user(user)
.world(island.getWorld())
.tab(1, new SettingsTab(user, island, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(user, island, Flag.Type.SETTING))
.island(island).tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION, Flag.Mode.EXPERT))
.tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING, Flag.Mode.EXPERT))
.startingSlot(1)
.size(54)
.build().openPanel();
@ -229,39 +248,37 @@ public class AdminSettingsCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
// Update with the latest lists
this.makeLists();
String active = ChatColor.stripColor(user.getTranslation("protection.panel.flag-item.setting-active"));
String disabled = ChatColor.stripColor(user.getTranslation("protection.panel.flag-item.setting-disabled"));
List<String> options = new ArrayList<>();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.size() == 2) {
// Player names or world settings
// Player names or world settings
options = Util.tabLimit(Util.getOnlinePlayerList(user), lastArg);
options.addAll(WORLD_SETTING_FLAG_NAMES);
options.addAll(worldSettingFlagNames);
if (getIslands().getSpawn(getWorld()).isPresent()) {
options.add(SPAWN_ISLAND);
}
} else if (args.size() == 3) {
// If world settings, then active/disabled, otherwise player flags
if (WORLD_SETTING_FLAG_NAMES.contains(args.get(1).toUpperCase(Locale.ENGLISH))) {
if (worldSettingFlagNames.contains(args.get(1).toUpperCase(Locale.ENGLISH))) {
options = Arrays.asList(active, disabled);
} else {
// Flag IDs
options.addAll(PROTECTION_FLAG_NAMES);
options.addAll(SETTING_FLAG_NAMES);
options.addAll(protectionFlagNames);
options.addAll(settingFlagNames);
}
} else if (args.size() == 4) {
// Get flag in previous argument
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> {
switch (f.getType()) {
case PROTECTION:
return getPlugin().getRanksManager()
.getRanks().entrySet().stream()
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.map(Entry::getKey)
.map(user::getTranslation).collect(Collectors.toList());
case SETTING:
return Arrays.asList(active, disabled);
default:
return Collections.<String>emptyList();
}
options = getPlugin().getFlagsManager().getFlag(args.get(2).toUpperCase(Locale.ENGLISH)).map(f -> switch (f.getType()) {
case PROTECTION -> RanksManager.getInstance().getRanks().entrySet().stream()
.filter(en -> en.getValue() > RanksManager.BANNED_RANK && en.getValue() <= RanksManager.OWNER_RANK)
.map(Entry::getKey)
.map(user::getTranslation).toList();
case SETTING -> Arrays.asList(active, disabled);
default -> Collections.<String>emptyList();
}).orElse(Collections.emptyList());
}
return Optional.of(Util.tabLimit(options, lastArg));

View File

@ -44,15 +44,15 @@ public class AdminSwitchCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
boolean switchState = user.getMetaData("AdminCommandSwitch").map(MetaDataValue::asBoolean).orElse(false);
boolean switchState = user.getMetaData(META_TAG).map(MetaDataValue::asBoolean).orElse(false);
if (switchState) {
// Turn off
user.putMetaData("AdminCommandSwitch", new MetaDataValue(false));
user.putMetaData(META_TAG, new MetaDataValue(false));
user.sendMessage("commands.admin.switch.adding"); // Adding protection bypass
user.sendMessage("general.success");
} else {
// Turn on
user.putMetaData("AdminCommandSwitch", new MetaDataValue(true));
user.putMetaData(META_TAG, new MetaDataValue(true));
user.sendMessage("commands.admin.switch.removing"); // Removing protection bypass
user.sendMessage("general.success");
}

View File

@ -1,87 +0,0 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang.math.NumberUtils;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class AdminSwitchtoCommand extends ConfirmableCommand {
private UUID targetUUID;
private @NonNull List<Island> islands;
/**
* Switch player's island to the numbered one in trash
* @param parent - admin command
* @since 1.3.0
*/
public AdminSwitchtoCommand(CompositeCommand parent) {
super(parent, "switchto");
islands = new ArrayList<>();
}
@Override
public void setup() {
setPermission("admin.switchto");
setOnlyPlayer(false);
setParametersHelp("commands.admin.switchto.parameters");
setDescription("commands.admin.switchto.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 2) {
// Show help
showHelp(this, user);
return false;
}
// Get target player
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Check island number
islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
if (islands.isEmpty()) {
user.sendMessage("commands.admin.trash.no-islands-in-trash");
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (NumberUtils.isDigits(args.get(1))) {
try {
Integer n = Integer.valueOf(args.get(1));
if (n < 1 || n > islands.size()) {
user.sendMessage("commands.admin.switchto.out-of-range", TextVariables.NUMBER, String.valueOf(islands.size()), TextVariables.LABEL, getTopLabel());
return false;
}
this.askConfirmation(user, () -> {
if (getIslands().switchIsland(getWorld(), targetUUID, islands.get(n -1))) {
user.sendMessage("commands.admin.switchto.success");
} else {
user.sendMessage("commands.admin.switchto.cannot-switch");
}
});
return true;
} catch (Exception e) {
showHelp(this, user);
return false;
}
}
return true;
}
}

View File

@ -1,24 +1,36 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
/**
* Enables admins to teleport to a player's island, nether or end islands,
*
* For example /acid tp tastybento [island name] would teleport to tastybento's [named] island
*
*/
public class AdminTeleportCommand extends CompositeCommand {
private static final String NOT_SAFE = "general.errors.no-safe-location-found";
private @Nullable UUID targetUUID;
private @Nullable User userToTeleport;
private Location warpSpot;
/**
* @param parent - parent command
@ -38,12 +50,12 @@ public class AdminTeleportCommand extends CompositeCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 1 && args.size() != 2) {
if (args.isEmpty()) {
this.showHelp(this, user);
return false;
}
// Check for console or not
if (!user.isPlayer() && args.size() != 2) {
if (!user.isPlayer()) {
user.sendMessage("general.errors.use-in-game");
return false;
}
@ -59,39 +71,56 @@ public class AdminTeleportCommand extends CompositeCommand {
return false;
}
if (args.size() == 2) {
// We are trying to teleport another player
UUID playerToTeleportUUID = Util.getUUID(args.get(1));
if (playerToTeleportUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
return false;
} else {
userToTeleport = User.getInstance(playerToTeleportUUID);
if (!userToTeleport.isOnline()) {
user.sendMessage("general.errors.offline-player");
return false;
}
}
World world = getWorld();
if (getLabel().equals("tpnether")) {
world = getPlugin().getIWM().getNetherWorld(getWorld());
} else if (getLabel().equals("tpend")) {
world = getPlugin().getIWM().getEndWorld(getWorld());
}
if (world == null) {
user.sendMessage(NOT_SAFE);
return false;
}
// Get default location if there are no arguments
warpSpot = getSpot(world);
if (warpSpot == null) {
user.sendMessage(NOT_SAFE);
return false;
}
if (args.size() == 1) {
return true;
}
// They named the island to go to
Map<String, IslandInfo> names = getNameIslandMap(User.getInstance(targetUUID));
final String name = String.join(" ", args.subList(1, args.size()));
if (!names.containsKey(name)) {
// Failed home name check
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
names.keySet()
.forEach(n -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, n));
return false;
} else if (names.size() > 1) {
IslandInfo info = names.get(name);
Island island = info.island;
warpSpot = island.getSpawnPoint(world.getEnvironment()) != null
? island.getSpawnPoint(world.getEnvironment())
: island.getProtectionCenter().toVector().toLocation(world);
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
Location warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getWorld());
if (getLabel().equals("tpnether")) {
warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getPlugin().getIWM().getNetherWorld(getWorld()));
} else if (getLabel().equals("tpend")) {
warpSpot = getIslands().getIslandLocation(getWorld(), targetUUID).toVector().toLocation(getPlugin().getIWM().getEndWorld(getWorld()));
}
Objects.requireNonNull(warpSpot);
// Otherwise, ask the admin to go to a safe spot
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "
+ warpSpot.getBlockZ());
Player player = user.getPlayer();
// Set the player
Player player = args.size() == 2 ? user.getPlayer() : user.getPlayer();
if (args.size() == 2) {
player = userToTeleport.getPlayer();
failureMessage = userToTeleport.getTranslation("general.errors.no-safe-location-found");
failureMessage = user.getTranslation(NOT_SAFE);
}
// Teleport
@ -99,10 +128,44 @@ public class AdminTeleportCommand extends CompositeCommand {
.entity(player)
.location(warpSpot)
.failureMessage(failureMessage)
.thenRun(() -> user.sendMessage("general.success"))
.build();
return true;
}
private Location getSpot(World world) {
Island island = getIslands().getIsland(world, targetUUID);
if (island == null) {
return null;
}
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
}
private record IslandInfo(Island island, boolean islandName) {
}
private Map<String, IslandInfo> getNameIslandMap(User target) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : getIslands().getIslands(getWorld(), target.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = target.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
// Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
@ -110,8 +173,17 @@ public class AdminTeleportCommand extends CompositeCommand {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
if (args.size() == 2) {
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
}
if (args.size() == 3) {
UUID target = Util.getUUID(args.get(1));
return target == null ? Optional.empty()
: Optional
.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(User.getInstance(target)).keySet()), lastArg));
}
return Optional.empty();
}
}

View File

@ -0,0 +1,199 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
/**
* Enables admins to teleport to a player's island, nether or end islands, or to teleport another player
* to a player's island
*
* For example /acid tp tastybento boxmanager would teleport BoxManager to tastybento's overwold island
*
* If the user has multiple islands, then the format is:
* [admin_command] [user with island] [island to go to]
*/
public class AdminTeleportUserCommand extends CompositeCommand {
private static final String NOT_SAFE = "general.errors.no-safe-location-found";
private @Nullable UUID targetUUID;
private @Nullable User userToTeleport;
/**
* @param parent - parent command
* @param tpCommand - should be "tp", "tpnether" or "tpend"
*/
public AdminTeleportUserCommand(CompositeCommand parent, String tpCommand) {
super(parent, tpCommand);
}
@Override
public void setup() {
// Permission
setPermission("admin.tp");
setParametersHelp("commands.admin.tp.parameters");
setDescription("commands.admin.tp.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.isEmpty() || args.size() > 3) {
this.showHelp(this, user);
return false;
}
// Check for console or not
if (!user.isPlayer() && args.size() == 1) {
user.sendMessage("general.errors.use-in-game");
return false;
}
// Convert name to a UUID
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Check island exists
if (!getIslands().hasIsland(getWorld(), targetUUID) && !getIslands().inTeam(getWorld(), targetUUID)) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (args.size() == 2) {
// We are trying to teleport another player
UUID playerToTeleportUUID = Util.getUUID(args.get(1));
if (playerToTeleportUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
return false;
} else {
userToTeleport = User.getInstance(playerToTeleportUUID);
if (!userToTeleport.isOnline()) {
user.sendMessage("general.errors.offline-player");
return false;
}
}
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
World world = getWorld();
if (getLabel().equals("tpnether")) {
world = getPlugin().getIWM().getNetherWorld(getWorld());
} else if (getLabel().equals("tpend")) {
world = getPlugin().getIWM().getEndWorld(getWorld());
}
if (world == null) {
user.sendMessage(NOT_SAFE);
return false;
}
// Get default location if there are no arguments
Location warpSpot = getSpot(world);
if (warpSpot == null) {
user.sendMessage(NOT_SAFE);
return false;
}
// See if there is a quoted island name
if (args.size() == 2) {
Map<String, IslandInfo> names = getNameIslandMap(user);
final String name = String.join(" ", args);
if (!names.containsKey(name)) {
// Failed home name check
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
names.keySet().forEach(
n -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, n));
return false;
} else {
IslandInfo info = names.get(name);
Island island = info.island;
warpSpot = island.getSpawnPoint(world.getEnvironment()) != null
? island.getSpawnPoint(world.getEnvironment())
: island.getProtectionCenter().toVector().toLocation(world);
}
}
// Otherwise, ask the admin to go to a safe spot
String failureMessage = user.getTranslation("commands.admin.tp.manual", "[location]", warpSpot.getBlockX() + " " + warpSpot.getBlockY() + " "
+ warpSpot.getBlockZ());
// Set the player
Player player = args.size() == 2 ? userToTeleport.getPlayer() : user.getPlayer();
if (args.size() == 2) {
failureMessage = userToTeleport.getTranslation(NOT_SAFE);
}
// Teleport
new SafeSpotTeleport.Builder(getPlugin())
.entity(player)
.location(warpSpot)
.failureMessage(failureMessage)
.thenRun(() -> user.sendMessage("general.success"))
.build();
return true;
}
private Location getSpot(World world) {
Island island = getIslands().getIsland(world, targetUUID);
if (island == null) {
return null;
}
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
}
private record IslandInfo(Island island, boolean islandName) {
}
private Map<String, IslandInfo> getNameIslandMap(User user) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
// Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
if (args.size() == 1) {
return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg));
}
if (args.size() == 2) {
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
}
return Optional.empty();
}
}

View File

@ -1,72 +0,0 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
public class AdminTrashCommand extends CompositeCommand {
/**
* A command for viewing islands in the database trash
* @param parent - admin command
* @since 1.3.0
*/
public AdminTrashCommand(CompositeCommand parent) {
super(parent, "trash");
}
@Override
public void setup() {
setPermission("admin.trash");
setOnlyPlayer(false);
setParametersHelp("commands.admin.trash.parameters");
setDescription("commands.admin.trash.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() > 1) {
// Show help
showHelp(this, user);
return false;
}
// Get target player
UUID targetUUID = args.isEmpty() ? null : getPlayers().getUUID(args.get(0));
if (!args.isEmpty() && targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Show trash can info for this player
List<Island> islands = getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID);
if (islands.isEmpty()) {
if (args.isEmpty()) {
user.sendMessage("commands.admin.trash.no-unowned-in-trash");
} else {
user.sendMessage("commands.admin.trash.no-islands-in-trash");
}
return false;
} else {
if (targetUUID == null) {
showTrash(user, islands);
} else {
getIslands().getQuarantineCache().values().forEach(v -> showTrash(user, v));
}
return true;
}
}
private void showTrash(User user, List<Island> islands) {
user.sendMessage("commands.admin.trash.title");
for (int i = 0; i < islands.size(); i++) {
user.sendMessage("commands.admin.trash.count", TextVariables.NUMBER, String.valueOf(i+1));
islands.get(i).showInfo(user);
}
user.sendMessage("commands.admin.trash.use-switch", TextVariables.LABEL, getTopLabel());
user.sendMessage("commands.admin.trash.use-emptytrash", TextVariables.LABEL, getTopLabel());
}
}

View File

@ -2,13 +2,19 @@ package world.bentobox.bentobox.api.commands.admin;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.logs.LogEntry;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
@ -16,6 +22,9 @@ import world.bentobox.bentobox.util.Util;
public class AdminUnregisterCommand extends ConfirmableCommand {
private Island targetIsland;
private @Nullable UUID targetUUID;
public AdminUnregisterCommand(CompositeCommand parent) {
super(parent, "unregister");
}
@ -30,12 +39,12 @@ public class AdminUnregisterCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
if (args.isEmpty()) {
showHelp(this, user);
return false;
}
// Get target
UUID targetUUID = Util.getUUID(args.get(0));
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
@ -44,46 +53,71 @@ public class AdminUnregisterCommand extends ConfirmableCommand {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Check if the player has more than one island
Map<String, Island> islands = getIslandsXYZ(targetUUID);
if (islands.size() == 0) {
user.sendMessage("general.errors.player-has-no-island");
return false;
} else if (args.size() == 1) {
if (islands.size() == 1) {
targetIsland = islands.values().iterator().next();
return true;
} else {
// They need to specify which island
user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island");
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
return false;
}
} else if (args.size() != 2) {
// Check if the name given works
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
return false;
} else if (!islands.containsKey(args.get(1))) {
if (args.get(1).equalsIgnoreCase("help")) {
this.showHelp(this, user);
return false;
}
user.sendMessage("commands.admin.unregister.errors.unknown-island-location");
return false;
}
targetIsland = islands.get(args.get(1));
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Get target
UUID targetUUID = getPlayers().getUUID(args.get(0));
Objects.requireNonNull(targetIsland);
Objects.requireNonNull(targetUUID);
// Everything's fine, we can set the island as spawn :)
askConfirmation(user, () -> unregisterPlayer(user, args.get(0), targetUUID));
askConfirmation(user, () -> unregisterIsland(user));
return true;
}
void unregisterPlayer(User user, String targetName, UUID targetUUID) {
void unregisterIsland(User user) {
// Unregister island
Island oldIsland = getIslands().getIsland(getWorld(), targetUUID);
if (oldIsland == null) return;
IslandEvent.builder()
.island(oldIsland)
.location(oldIsland.getCenter())
.island(targetIsland)
.location(targetIsland.getCenter())
.reason(IslandEvent.Reason.UNREGISTERED)
.involvedPlayer(targetUUID)
.admin(true)
.build();
IslandEvent.builder()
.island(oldIsland)
.island(targetIsland)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, RanksManager.VISITOR_RANK)
.build();
targetIsland.setOwner(null);
// Remove all island members
oldIsland.getMemberSet().forEach(m -> {
getIslands().removePlayer(getWorld(), m);
getPlayers().clearHomeLocations(getWorld(), m);
});
targetIsland.getMemberSet().forEach(m -> getIslands().removePlayer(targetIsland, m));
// Remove all island players that reference this island
oldIsland.getMembers().clear();
getIslands().save(oldIsland);
user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(oldIsland.getCenter().toVector()),
TextVariables.NAME, targetName);
targetIsland.getMembers().clear();
targetIsland.log(new LogEntry.Builder("UNREGISTER").data("player", targetUUID.toString())
.data("admin", user.getUniqueId().toString()).build());
user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(targetIsland.getCenter().toVector()),
TextVariables.NAME, getPlayers().getName(targetUUID));
}
@Override
@ -92,8 +126,22 @@ public class AdminUnregisterCommand extends ConfirmableCommand {
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
} else if (args.size() == 2) {
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
} else if (args.size() > 2) {
// Find out which user
UUID uuid = getPlayers().getUUID(args.get(1));
if (uuid != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(getIslandsXYZ(uuid).keySet()), lastArg));
}
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
return Optional.empty();
}
private Map<String, Island> getIslandsXYZ(UUID target) {
return getIslands().getOwnedIslands(getWorld(), target).stream()
.collect(Collectors.toMap(island -> Util.xyz(island.getCenter().toVector()), island -> island));
}
}

View File

@ -22,7 +22,7 @@ public class AdminVersionCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.bentobox.version.addon-syntax", TextVariables.NAME, getAddon().getDescription().getName(),
TextVariables.VERSION, getAddon().getDescription().getVersion());
TextVariables.VERSION, getAddon().getDescription().getVersion(), "[state]", getAddon().getState().name());
return true;
}

View File

@ -10,8 +10,8 @@ import world.bentobox.bentobox.api.commands.admin.purge.AdminPurgeCommand;
import world.bentobox.bentobox.api.commands.admin.range.AdminRangeCommand;
import world.bentobox.bentobox.api.commands.admin.resets.AdminResetsCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamAddCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamDisbandCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamFixCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamKickCommand;
import world.bentobox.bentobox.api.commands.admin.team.AdminTeamSetownerCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
@ -29,8 +29,9 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
*
* @param addon - GameMode addon
*/
public DefaultAdminCommand(GameModeAddon addon) {
protected DefaultAdminCommand(GameModeAddon addon) {
// Register command with alias from config.
// The first command listed is the "label" and the others are aliases.
super(addon,
addon.getWorldSettings().getAdminCommandAliases().split(" ")[0],
addon.getWorldSettings().getAdminCommandAliases().split(" "));
@ -42,7 +43,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
*/
@Override
public void setup() {
this.setPermission("admin.*");
this.setPermission("admin");
this.setOnlyPlayer(false);
this.setParametersHelp("commands.admin.help.parameters");
@ -56,12 +57,12 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminSetrankCommand(this);
new AdminInfoCommand(this);
// Team commands
new AdminTeamCommand(this);
new AdminTeamAddCommand(this);
new AdminTeamKickCommand(this);
new AdminTeamDisbandCommand(this);
new AdminTeamSetownerCommand(this);
new AdminTeamFixCommand(this);
// Schems
// Blueprints
new AdminBlueprintCommand(this);
// Register/unregister islands
new AdminRegisterCommand(this);
@ -92,11 +93,15 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminSettingsCommand(this);
// Location
new AdminSetProtectionCenterCommand(this);
// Delete homes
new AdminDeleteHomesCommand(this);
// Reset name
new AdminResetNameCommand(this);
}
/**
* Defines what will be executed when this command is run.
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List&lt;String&gt;)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(User, String, List)
*/
@Override
public boolean execute(User user, String label, List<String> args) {

View File

@ -12,6 +12,7 @@ import org.bukkit.Particle;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.commands.admin.range.AdminRangeDisplayCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
import world.bentobox.bentobox.managers.BlueprintsManager;
@ -23,7 +24,6 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
// Map containing selection cuboid display tasks
private Map<User, Integer> displayClipboards;
private static final Particle PARTICLE = Particle.REDSTONE;
private static final Particle.DustOptions PARTICLE_DUST_OPTIONS = new Particle.DustOptions(Color.RED, 1.0F);
public AdminBlueprintCommand(CompositeCommand parent) {
@ -62,23 +62,34 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
return clipboards;
}
protected void showClipboard(User user) {
displayClipboards.putIfAbsent(user, Bukkit.getScheduler().scheduleSyncRepeatingTask(getPlugin(), () -> {
if (!user.getPlayer().isOnline()) {
hideClipboard(user);
}
if (clipboards.containsKey(user.getUniqueId())) {
BlueprintClipboard clipboard = clipboards.get(user.getUniqueId());
if (clipboard.getPos1() != null && clipboard.getPos2() != null) {
paintAxis(user, clipboard);
/**
* This method shows clipboard for requested user.
* @param user User who need to see clipboard.
*/
protected void showClipboard(User user)
{
this.displayClipboards.computeIfAbsent(user,
key -> Bukkit.getScheduler().scheduleSyncRepeatingTask(this.getPlugin(), () ->
{
if (!key.isPlayer() || !key.getPlayer().isOnline())
{
this.hideClipboard(key);
}
}
}, 20, 20));
if (this.clipboards.containsKey(key.getUniqueId()))
{
BlueprintClipboard clipboard = this.clipboards.get(key.getUniqueId());
this.paintAxis(key, clipboard);
}
}, 20, 20));
}
private void paintAxis(User user, BlueprintClipboard clipboard) {
if (clipboard.getPos1() == null || clipboard.getPos2() == null) {
return;
}
int minX = Math.min(clipboard.getPos1().getBlockX(), clipboard.getPos2().getBlockX());
int minY = Math.min(clipboard.getPos1().getBlockY(), clipboard.getPos2().getBlockY());
int minZ = Math.min(clipboard.getPos1().getBlockZ(), clipboard.getPos2().getBlockZ());
@ -88,31 +99,38 @@ public class AdminBlueprintCommand extends ConfirmableCommand {
// Drawing x-axes
for (int x = minX; x <= maxX; x++) {
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5, minZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5, minZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5, maxZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5, maxZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5,
minZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5,
minZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, minY + 0.5,
maxZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, x + 0.5, maxY + 0.5,
maxZ + 0.5);
}
// Drawing y-axes
for (int y = minY; y <= maxY; y++) {
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5, minZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5, minZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5, maxZ + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5, maxZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5,
minZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5,
minZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, y + 0.5,
maxZ + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, y + 0.5,
maxZ + 0.5);
}
// Drawing z-axes
for (int z = minZ; z <= maxZ; z++) {
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, minY + 0.5, z + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, minY + 0.5, z + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, maxY + 0.5, z + 0.5);
user.spawnParticle(PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, maxY + 0.5, z + 0.5);
}
// Drawing origin
if (clipboard.getOrigin() != null) {
user.spawnParticle(Particle.VILLAGER_HAPPY, null, clipboard.getOrigin().getBlockX() + 0.5, clipboard.getOrigin().getBlockY() + 0.5, clipboard.getOrigin().getBlockZ() + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, minY + 0.5,
z + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, minY + 0.5,
z + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, minX + 0.5, maxY + 0.5,
z + 0.5);
user.spawnParticle(AdminRangeDisplayCommand.PARTICLE, PARTICLE_DUST_OPTIONS, maxX + 0.5, maxY + 0.5,
z + 0.5);
}
}

View File

@ -1,35 +1,54 @@
package world.bentobox.bentobox.api.commands.admin.blueprints;
import java.util.List;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
public class AdminBlueprintCopyCommand extends CompositeCommand {
public AdminBlueprintCopyCommand(AdminBlueprintCommand parent) {
public class AdminBlueprintCopyCommand extends CompositeCommand
{
public AdminBlueprintCopyCommand(AdminBlueprintCommand parent)
{
super(parent, "copy");
}
@Override
public void setup() {
inheritPermission();
public void setup()
{
setPermission("admin.blueprint.copy");
setParametersHelp("commands.admin.blueprint.copy.parameters");
setDescription("commands.admin.blueprint.copy.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() > 1) {
showHelp(this, user);
public boolean execute(User user, String label, List<String> args)
{
if (args.size() > 2)
{
this.showHelp(this, user);
return false;
}
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
BlueprintClipboard clipboard = parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
boolean copyAir = (args.size() == 1 && args.get(0).equalsIgnoreCase("air"));
return clipboard.copy(user, copyAir);
BlueprintClipboard clipboard =
parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
boolean copyAir = args.stream().anyMatch(key -> key.equalsIgnoreCase("air"));
boolean copyBiome = args.stream().anyMatch(key -> key.equalsIgnoreCase("biome"));
return clipboard.copy(user, copyAir, copyBiome);
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
{
return Optional.of(List.of("air", "biome"));
}
}

View File

@ -2,55 +2,68 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
/**
* Command that deletes a Blueprint.
* @author Poslovitch
* @since 1.9.0
*/
public class AdminBlueprintDeleteCommand extends ConfirmableCommand {
public AdminBlueprintDeleteCommand(AdminBlueprintCommand parent) {
public class AdminBlueprintDeleteCommand extends ConfirmableCommand
{
public AdminBlueprintDeleteCommand(AdminBlueprintCommand parent)
{
super(parent, "delete", "remove");
}
@Override
public void setup() {
inheritPermission();
setParametersHelp("commands.admin.blueprint.delete.parameters");
setDescription("commands.admin.blueprint.delete.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() != 1) {
showHelp(this, user);
public void setup()
{
setPermission("admin.blueprint.delete");
this.setParametersHelp("commands.admin.blueprint.delete.parameters");
this.setDescription("commands.admin.blueprint.delete.description");
}
@Override
public boolean execute(User user, String label, List<String> args)
{
if (args.size() != 1)
{
this.showHelp(this, user);
return false;
}
String blueprintName = args.get(0).toLowerCase(Locale.ENGLISH);
String blueprintName = Util.sanitizeInput(args.get(0));
// Check if blueprint exist
if (getPlugin().getBlueprintsManager().getBlueprints(getAddon()).containsKey(blueprintName)) {
askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"), () -> {
getPlugin().getBlueprintsManager().deleteBlueprint(getAddon(), blueprintName);
user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName);
});
if (this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).containsKey(blueprintName))
{
this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.delete.confirmation"),
() -> {
this.getPlugin().getBlueprintsManager().deleteBlueprint(this.getAddon(), blueprintName);
user.sendMessage("commands.admin.blueprint.delete.success", TextVariables.NAME, blueprintName);
});
return true;
} else {
}
else
{
user.sendMessage("commands.admin.blueprint.delete.no-blueprint", TextVariables.NAME, blueprintName);
return false;
}
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
return Optional.of(new LinkedList<>(getPlugin().getBlueprintsManager().getBlueprints(getAddon()).keySet()));
public Optional<List<String>> tabComplete(User user, String alias, List<String> args)
{
return Optional.of(new LinkedList<>(this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).keySet()));
}
}
}

View File

@ -5,51 +5,66 @@ import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.BlueprintsManager;
public class AdminBlueprintListCommand extends CompositeCommand {
public class AdminBlueprintListCommand extends CompositeCommand
{
public AdminBlueprintListCommand(AdminBlueprintCommand parent) {
public AdminBlueprintListCommand(AdminBlueprintCommand parent)
{
super(parent, "list");
}
@Override
public void setup() {
inheritPermission();
setDescription("commands.admin.blueprint.list.description");
public void setup()
{
setPermission("admin.blueprint.list");
this.setDescription("commands.admin.blueprint.list.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (!args.isEmpty()) {
showHelp(this, user);
public boolean canExecute(User user, String label, List<String> args)
{
if (!args.isEmpty())
{
this.showHelp(this, user);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
File blueprints = new File(getAddon().getDataFolder(), BlueprintsManager.FOLDER_NAME);
if (!blueprints.exists()) {
public boolean execute(User user, String label, List<String> args)
{
File blueprints = new File(this.getAddon().getDataFolder(), BlueprintsManager.FOLDER_NAME);
if (!blueprints.exists())
{
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
return false;
}
FilenameFilter blueprintFilter = (File dir, String name) -> name.toLowerCase(java.util.Locale.ENGLISH).endsWith(BlueprintsManager.BLUEPRINT_SUFFIX);
List<String> blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))).map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())).collect(Collectors.toList());
if (blueprintList.isEmpty()) {
FilenameFilter blueprintFilter = (File dir, String name) -> name.endsWith(BlueprintsManager.BLUEPRINT_SUFFIX);
List<String> blueprintList = Arrays.stream(Objects.requireNonNull(blueprints.list(blueprintFilter))).
map(name -> name.substring(0, name.length() - BlueprintsManager.BLUEPRINT_SUFFIX.length())).
toList();
if (blueprintList.isEmpty())
{
user.sendMessage("commands.admin.blueprint.list.no-blueprints");
return false;
}
user.sendMessage("commands.admin.blueprint.list.available-blueprints");
blueprintList.forEach(user::sendRawMessage);
return true;
}
}

View File

@ -17,7 +17,7 @@ public class AdminBlueprintLoadCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.blueprint.load");
setParametersHelp("commands.admin.blueprint.load.parameters");
setDescription("commands.admin.blueprint.load.description");
}
@ -32,7 +32,7 @@ public class AdminBlueprintLoadCommand extends CompositeCommand {
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
BlueprintClipboardManager bp = new BlueprintClipboardManager(getPlugin(), parent.getBlueprintsFolder());
if (bp.load(user, args.get(0))) {
if (bp.load(user, Util.sanitizeInput(args.get(0)))) {
parent.getClipboards().put(user.getUniqueId(), bp.getClipboard());
return true;
}

View File

@ -1,6 +1,7 @@
package world.bentobox.bentobox.api.commands.admin.blueprints;
import java.util.List;
import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@ -18,7 +19,7 @@ public class AdminBlueprintOriginCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.blueprint.origin");
setParametersHelp("commands.admin.blueprint.origin.parameters");
setDescription("commands.admin.blueprint.origin.description");
}
@ -34,7 +35,7 @@ public class AdminBlueprintOriginCommand extends CompositeCommand {
}
// Get the block player is looking at
Block b = user.getPlayer().getLineOfSight(null, 20).stream().filter(x -> !x.getType().equals(Material.AIR)).findFirst().orElse(null);
Block b = Objects.requireNonNull(user.getPlayer()).getLineOfSight(null, 20).stream().filter(x -> !x.getType().equals(Material.AIR)).findFirst().orElse(null);
if (b != null) {
clipboard.setOrigin(b.getLocation().toVector());
user.getPlayer().sendBlockChange(b.getLocation(), Material.REDSTONE_BLOCK.createBlockData());

View File

@ -15,7 +15,7 @@ public class AdminBlueprintPasteCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.blueprint.paste");
setParametersHelp("commands.admin.blueprint.paste.parameters");
setDescription("commands.admin.blueprint.paste.description");
}
@ -25,7 +25,7 @@ public class AdminBlueprintPasteCommand extends CompositeCommand {
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
BlueprintClipboard clipboard = parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
if (clipboard.isFull()) {
new BlueprintPaster(getPlugin(), clipboard, user.getLocation()).paste().thenAccept(b -> {
new BlueprintPaster(getPlugin(), clipboard, user.getLocation()).paste(false).thenAccept(b -> {
user.sendMessage("general.success");
parent.showClipboard(user);
});

View File

@ -15,7 +15,7 @@ public class AdminBlueprintPos1Command extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.blueprint.pos1");
setParametersHelp("commands.admin.blueprint.pos1.parameters");
setDescription("commands.admin.blueprint.pos1.description");
}
@ -30,7 +30,7 @@ public class AdminBlueprintPos1Command extends CompositeCommand {
return false;
}
clipboard.setPos1(user.getLocation());
user.sendMessage("commands.admin.blueprint.set-pos1", "[vector]", Util.xyz(clipboard.getPos1().toVector()));
user.sendMessage("commands.admin.blueprint.set-pos1", "[vector]", Util.xyz(user.getLocation().toVector()));
parent.getClipboards().put(user.getUniqueId(), clipboard);
parent.showClipboard(user);
return true;

View File

@ -15,7 +15,7 @@ public class AdminBlueprintPos2Command extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.blueprint.pos2");
setParametersHelp("commands.admin.blueprint.pos2.parameters");
setDescription("commands.admin.blueprint.pos2.description");
}
@ -30,7 +30,7 @@ public class AdminBlueprintPos2Command extends CompositeCommand {
return false;
}
clipboard.setPos2(user.getLocation());
user.sendMessage("commands.admin.blueprint.set-pos2", "[vector]", Util.xyz((clipboard.getPos2()).toVector()));
user.sendMessage("commands.admin.blueprint.set-pos2", "[vector]", Util.xyz(user.getLocation().toVector()));
parent.getClipboards().put(user.getUniqueId(), clipboard);
parent.showClipboard(user);
return true;

View File

@ -2,72 +2,111 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
import java.io.File;
import java.util.List;
import java.util.Locale;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.Blueprint;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.util.Util;
/**
* Renames an existing blueprint.
* @author Poslovitch
* @since 1.10.0
*/
public class AdminBlueprintRenameCommand extends ConfirmableCommand {
public AdminBlueprintRenameCommand(AdminBlueprintCommand parent) {
public class AdminBlueprintRenameCommand extends ConfirmableCommand
{
public AdminBlueprintRenameCommand(AdminBlueprintCommand parent)
{
super(parent, "rename");
}
@Override
public void setup() {
inheritPermission();
setParametersHelp("commands.admin.blueprint.rename.parameters");
setDescription("commands.admin.blueprint.rename.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() != 2) {
showHelp(this, user);
public void setup()
{
setPermission("admin.blueprint.rename");
this.setParametersHelp("commands.admin.blueprint.rename.parameters");
this.setDescription("commands.admin.blueprint.rename.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args)
{
if (args.size() != 2)
{
// Blueprint must have a name.
this.showHelp(this, user);
return false;
}
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
String from = Util.sanitizeInput(args.get(0));
String to = Util.sanitizeInput(args.get(1));
// Check if the names are the same
String from = args.get(0).toLowerCase(Locale.ENGLISH);
String to = args.get(1).toLowerCase(Locale.ENGLISH);
if (from.equals(to)) {
// Check if name is changed.
if (from.equals(to))
{
user.sendMessage("commands.admin.blueprint.rename.pick-different-name");
return false;
}
// Check if the 'from' file exists
AdminBlueprintCommand parent = (AdminBlueprintCommand) this.getParent();
File fromFile = new File(parent.getBlueprintsFolder(), from + BlueprintsManager.BLUEPRINT_SUFFIX);
if (!fromFile.exists()) {
if (!fromFile.exists())
{
user.sendMessage("commands.admin.blueprint.no-such-file");
return false;
}
// Check if the 'to' file exists
File toFile = new File(parent.getBlueprintsFolder(), to + BlueprintsManager.BLUEPRINT_SUFFIX);
if (toFile.exists()) {
// Ask for confirmation to overwrite
askConfirmation(user, user.getTranslation("commands.admin.blueprint.file-exists"), () -> rename(user, from, to));
} else {
askConfirmation(user, () -> rename(user, from, to));
}
return true;
}
private void rename(User user, String blueprintName, String newName) {
Blueprint blueprint = getPlugin().getBlueprintsManager().getBlueprints(getAddon()).get(blueprintName);
getPlugin().getBlueprintsManager().renameBlueprint(getAddon(), blueprint, newName);
user.sendMessage("commands.admin.blueprint.rename.success", "[old]", blueprintName, TextVariables.NAME, blueprint.getName());
@Override
public boolean execute(User user, String label, List<String> args)
{
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
// Check if the names are the same
String from = Util.sanitizeInput(args.get(0));
String to = Util.sanitizeInput(args.get(1));
// Check if the 'to' file exists
File toFile = new File(parent.getBlueprintsFolder(), to + BlueprintsManager.BLUEPRINT_SUFFIX);
if (toFile.exists())
{
// Ask for confirmation to overwrite
this.askConfirmation(user,
user.getTranslation("commands.admin.blueprint.file-exists"),
() -> this.rename(user, from, to, args.get(1)));
}
else
{
this.askConfirmation(user, () -> this.rename(user, from, to, args.get(1)));
}
return true;
}
private void rename(User user, String blueprintName, String fileName, String displayName)
{
Blueprint blueprint = this.getPlugin().getBlueprintsManager().getBlueprints(this.getAddon()).get(blueprintName);
this.getPlugin().getBlueprintsManager().renameBlueprint(this.getAddon(), blueprint, fileName, displayName);
user.sendMessage("commands.admin.blueprint.rename.success",
"[old]",
blueprintName,
TextVariables.NAME,
blueprint.getName(),
"[display]",
blueprint.getDisplayName());
}
}

View File

@ -2,62 +2,115 @@ package world.bentobox.bentobox.api.commands.admin.blueprints;
import java.io.File;
import java.util.List;
import java.util.Locale;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.BlueprintClipboard;
import world.bentobox.bentobox.managers.BlueprintClipboardManager;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.util.Util;
public class AdminBlueprintSaveCommand extends ConfirmableCommand {
public AdminBlueprintSaveCommand(AdminBlueprintCommand parent) {
/**
* This method allows to save blueprint from the clipboard.
*/
public class AdminBlueprintSaveCommand extends ConfirmableCommand
{
public AdminBlueprintSaveCommand(AdminBlueprintCommand parent)
{
super(parent, "save");
}
@Override
public void setup() {
inheritPermission();
setParametersHelp("commands.admin.blueprint.save.parameters");
setDescription("commands.admin.blueprint.save.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() != 1) {
showHelp(this, user);
public void setup()
{
setPermission("admin.blueprint.save");
this.setParametersHelp("commands.admin.blueprint.save.parameters");
this.setDescription("commands.admin.blueprint.save.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args)
{
if (args.size() != 1)
{
// Blueprint must have a name.
this.showHelp(this, user);
return false;
}
AdminBlueprintCommand parent = (AdminBlueprintCommand) getParent();
BlueprintClipboard clipboard = parent.getClipboards().computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
String fileName = args.get(0).toLowerCase(Locale.ENGLISH);
if (clipboard.isFull()) {
// Check if blueprint had bedrock
if (clipboard.getBlueprint().getBedrock() == null) {
user.sendMessage("commands.admin.blueprint.bedrock-required");
return false;
}
// Check if file exists
File newFile = new File(parent.getBlueprintsFolder(), fileName + BlueprintsManager.BLUEPRINT_SUFFIX);
if (newFile.exists()) {
this.askConfirmation(user, user.getTranslation("commands.admin.blueprint.file-exists"), () -> hideAndSave(user, parent, clipboard, fileName));
return false;
}
return hideAndSave(user, parent, clipboard, fileName);
} else {
BlueprintClipboard clipboard = ((AdminBlueprintCommand) this.getParent()).getClipboards().
computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
if (!clipboard.isFull())
{
// Clipboard is not set up.
user.sendMessage("commands.admin.blueprint.copy-first");
return false;
}
if (clipboard.getBlueprint() != null && clipboard.getBlueprint().getBedrock() == null)
{
// Bedrock is required for all blueprints.
user.sendMessage("commands.admin.blueprint.bedrock-required");
return false;
}
return true;
}
private boolean hideAndSave(User user, AdminBlueprintCommand parent, BlueprintClipboard clipboard, String name) {
parent.hideClipboard(user);
boolean result = new BlueprintClipboardManager(getPlugin(), parent.getBlueprintsFolder(), clipboard).save(user, name);
if (result) {
getPlugin().getBlueprintsManager().addBlueprint(getAddon(), clipboard.getBlueprint());
@Override
public boolean execute(User user, String label, List<String> args)
{
AdminBlueprintCommand parent = (AdminBlueprintCommand) this.getParent();
BlueprintClipboard clipboard = parent.getClipboards().
computeIfAbsent(user.getUniqueId(), v -> new BlueprintClipboard());
String fileName = Util.sanitizeInput(args.get(0));
// Check if file exists
File newFile = new File(parent.getBlueprintsFolder(), fileName + BlueprintsManager.BLUEPRINT_SUFFIX);
if (newFile.exists())
{
this.askConfirmation(user,
user.getTranslation("commands.admin.blueprint.file-exists"),
() -> this.hideAndSave(user, parent, clipboard, fileName, args.get(0)));
return false;
}
return this.hideAndSave(user, parent, clipboard, fileName, args.get(0));
}
/**
* This method saves given blueprint.
* @param user User that triggers blueprint save.
* @param parent Parent command that contains clipboard.
* @param clipboard Active clipboard.
* @param name Filename for the blueprint
* @param displayName Display name for the blueprint.
* @return {@code true} if blueprint is saved, {@code false} otherwise.
*/
private boolean hideAndSave(User user,
AdminBlueprintCommand parent,
BlueprintClipboard clipboard,
String name,
String displayName)
{
parent.hideClipboard(user);
boolean result = new BlueprintClipboardManager(this.getPlugin(),
parent.getBlueprintsFolder(), clipboard).
save(user, name, displayName);
if (result && clipboard.isFull())
{
this.getPlugin().getBlueprintsManager().addBlueprint(this.getAddon(), clipboard.getBlueprint());
}
return result;
}
}

View File

@ -17,12 +17,14 @@ import world.bentobox.bentobox.database.objects.Island;
*/
public class NamePrompt extends StringPrompt {
private @NonNull final Island island;
private @NonNull final User user;
@NonNull
private final Island island;
@NonNull
private final User user;
private final String oldName;
private final BentoBox plugin;
public NamePrompt(BentoBox plugin, @NonNull Island island, User user, String oldName) {
public NamePrompt(BentoBox plugin, @NonNull Island island, @NonNull User user, String oldName) {
this.plugin = plugin;
this.island = island;
this.user = user;
@ -30,14 +32,14 @@ public class NamePrompt extends StringPrompt {
}
@Override
public String getPromptText(ConversationContext context) {
@NonNull
public String getPromptText(@NonNull ConversationContext context) {
return user.getTranslation("commands.island.renamehome.enter-new-name");
}
@Override
public Prompt acceptInput(ConversationContext context, String input) {
public Prompt acceptInput(@NonNull ConversationContext context, String input) {
if (island.renameHome(oldName, input)) {
plugin.getIslands().save(island);
Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("general.success"));
} else {
Bukkit.getScheduler().runTask(plugin, () -> user.sendMessage("commands.island.renamehome.already-exists"));

View File

@ -22,7 +22,7 @@ public class AdminDeathsAddCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.deaths.add");
setDescription("commands.admin.deaths.add.description");
setParametersHelp("commands.admin.deaths.add.parameters");
}

View File

@ -22,7 +22,7 @@ public class AdminDeathsRemoveCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.deaths.remove");
setDescription("commands.admin.deaths.remove.description");
setParametersHelp("commands.admin.deaths.remove.parameters");
}

View File

@ -21,7 +21,7 @@ public class AdminDeathsResetCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.deaths.reset");
setDescription("commands.admin.deaths.reset.description");
setParametersHelp("commands.admin.deaths.reset.parameters");
}

View File

@ -21,7 +21,7 @@ public class AdminDeathsSetCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.deaths.set");
setDescription("commands.admin.deaths.set.description");
setParametersHelp("commands.admin.deaths.set.parameters");
}

View File

@ -5,7 +5,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
@ -18,6 +17,7 @@ import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class AdminPurgeCommand extends CompositeCommand implements Listener {
@ -70,7 +70,7 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
islands.clear();
this.user = user;
try {
Integer days = Integer.parseInt(args.get(0));
int days = Integer.parseInt(args.get(0));
if (days < 1) {
user.sendMessage("commands.admin.purge.days-one-or-more");
return false;
@ -82,7 +82,7 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel());
return false;
}
} catch(Exception e) {
} catch (NumberFormatException e) {
user.sendMessage("commands.admin.purge.number-error");
return false;
}
@ -120,29 +120,42 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
}
}
/**
* Gets a set of islands that are older than the parameter in days
* @param days days
* @return set of islands
*/
Set<String> getOldIslands(int days) {
long currentTimeMillis = System.currentTimeMillis();
double daysInMilliseconds = days * 1000 * 3600 * 24;
Set<String> oldIslands = new HashSet<>();
// Process islands in one pass, logging and adding to the set if applicable
getPlugin().getIslands().getIslands().stream()
.filter(i -> !i.isSpawn())
.filter(i -> !i.getPurgeProtected())
.filter(i -> i.getWorld().equals(this.getWorld()))
.filter(Island::isOwned)
.filter(i -> i.getMembers().size() == 1)
.filter(i -> ((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) > days)
.forEach(i -> {
Date date = new Date(Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed());
BentoBox.getInstance().log("Will purge " +
BentoBox.getInstance().getPlayers().getName(i.getOwner()) +
" last logged in " + (int)((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) + " days ago. " + date);
});
return getPlugin().getIslands().getIslands().stream()
.filter(i -> !i.isSpawn())
.filter(i -> !i.getPurgeProtected())
.filter(i -> i.getWorld().equals(this.getWorld()))
.filter(Island::isOwned)
.filter(i -> i.getMembers().size() == 1)
.filter(i -> ((double)(System.currentTimeMillis() - Bukkit.getOfflinePlayer(i.getOwner()).getLastPlayed()) / 1000 / 3600 / 24) > days)
.map(Island::getUniqueId)
.collect(Collectors.toSet());
.filter(i -> !i.isSpawn()).filter(i -> !i.getPurgeProtected())
.filter(i -> i.getWorld().equals(this.getWorld())).filter(Island::isOwned).filter(
i -> i.getMemberSet().stream()
.allMatch(member -> (currentTimeMillis
- Bukkit.getOfflinePlayer(member).getLastPlayed()) > daysInMilliseconds))
.forEach(i -> {
// Add the unique island ID to the set
oldIslands.add(i.getUniqueId());
BentoBox.getInstance().log("Will purge island at " + Util.xyz(i.getCenter().toVector()) + " in "
+ i.getWorld().getName());
// Log each member's last login information
i.getMemberSet().forEach(member -> {
Date lastLogin = new Date(Bukkit.getOfflinePlayer(member).getLastPlayed());
BentoBox.getInstance()
.log("Player " + BentoBox.getInstance().getPlayers().getName(member)
+ " last logged in "
+ (int) ((currentTimeMillis - Bukkit.getOfflinePlayer(member).getLastPlayed())
/ 1000 / 3600 / 24)
+ " days ago. " + lastLogin);
});
BentoBox.getInstance().log("+-----------------------------------------+");
});
return oldIslands;
}
/**

View File

@ -16,7 +16,7 @@ public class AdminPurgeProtectCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.purge.protect");
setOnlyPlayer(true);
setDescription("commands.admin.purge.protect.description");
}

View File

@ -19,7 +19,7 @@ public class AdminPurgeStatusCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.purge.status");
setOnlyPlayer(false);
setParametersHelp("commands.admin.purge.status.parameters");
setDescription("commands.admin.purge.status.description");

View File

@ -13,7 +13,7 @@ public class AdminPurgeStopCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.purge.stop");
setOnlyPlayer(false);
setDescription("commands.admin.purge.stop.description");
}

View File

@ -17,7 +17,7 @@ public class AdminPurgeUnownedCommand extends ConfirmableCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.purge.unowned");
setOnlyPlayer(false);
setParametersHelp("commands.admin.purge.unowned.parameters");
setDescription("commands.admin.purge.unowned.description");

View File

@ -0,0 +1,103 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* @author Poslovitch
*/
public abstract class AbstractAdminRangeCommand extends CompositeCommand {
protected @Nullable UUID targetUUID;
protected Island targetIsland;
public AbstractAdminRangeCommand(CompositeCommand parent, String string) {
super(parent, string);
}
@Override
public boolean canExecute(User user, String label, @NonNull List<String> args) {
if (args.size() <= 1) {
showHelp(this, user);
return false;
}
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
return false;
}
// Check if the player has more than one island
Map<String, Island> islands = getIslandsXYZ(targetUUID);
if (islands.size() == 0) {
user.sendMessage("general.errors.player-has-no-island");
return false;
} else if (args.size() == 2) {
// If they only have one island, 2 args are fine
if (islands.size() == 1) {
targetIsland = islands.values().iterator().next();
return true;
} else {
// They need to specify which island
user.sendMessage("commands.admin.unregister.errors.player-has-more-than-one-island");
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
return false;
}
} else if (args.size() != 3) {
// No location
user.sendMessage("commands.admin.unregister.errors.specify-island-location");
return false;
} else if (!islands.containsKey(args.get(2))) {
if (args.get(2).equalsIgnoreCase("help")) {
this.showHelp(this, user);
return false;
}
user.sendMessage("commands.admin.unregister.errors.unknown-island-location");
return false;
}
targetIsland = islands.get(args.get(2));
return true;
}
protected Map<String, Island> getIslandsXYZ(UUID target) {
return getIslands().getOwnedIslands(getWorld(), target).stream()
.collect(Collectors.toMap(island -> Util.xyz(island.getCenter().toVector()), island -> island));
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
} else if (args.size() == 3) {
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
} else if (args.size() > 4) {
// Find out which user
UUID uuid = getPlayers().getUUID(args.get(2));
if (uuid != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(getIslandsXYZ(uuid).keySet()), lastArg));
}
}
return Optional.empty();
}
}

View File

@ -1,22 +1,18 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.List;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* @since 1.10.0
* @author Poslovitch
*/
public class AdminRangeAddCommand extends CompositeCommand {
public class AdminRangeAddCommand extends AbstractAdminRangeCommand {
public AdminRangeAddCommand(AdminRangeCommand parent) {
super(parent, "add");
@ -24,60 +20,35 @@ public class AdminRangeAddCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.range.add");
setDescription("commands.admin.range.add.description");
setParametersHelp("commands.admin.range.add.parameters");
}
@Override
public boolean execute(User user, String label, @NonNull List<String> args) {
if (args.size() != 2) {
showHelp(this, user);
return false;
}
int newRange = targetIsland.getProtectionRange() + Integer.parseInt(args.get(1));
UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
if (newRange > targetIsland.getRange()) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER,
String.valueOf(targetIsland.getRange()));
return false;
}
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
return false;
}
Island island = getIslands().getIsland(getWorld(), targetUUID);
int newRange = island.getProtectionRange() + Integer.parseInt(args.get(1));
if (newRange > island.getRange() * 2) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange()));
return false;
} else if (newRange == island.getProtectionRange()) {
} else if (newRange == targetIsland.getProtectionRange()) {
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
return false;
}
// Get old range for event
int oldRange = island.getProtectionRange();
int oldRange = targetIsland.getProtectionRange();
// Well, now it can be applied without taking any risks!
island.setProtectionRange(newRange);
targetIsland.setProtectionRange(newRange);
// Call Protection Range Change event. Does not support cancelling.
IslandEvent.builder()
.island(island)
.location(island.getCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE)
.involvedPlayer(targetUUID)
.admin(true)
.protectionRange(newRange, oldRange)
.build();
.island(targetIsland).location(targetIsland.getCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE).involvedPlayer(targetUUID).admin(true)
.protectionRange(newRange, oldRange).build();
user.sendMessage("commands.admin.range.add.success",
TextVariables.NAME, args.get(0), TextVariables.NUMBER, args.get(1),
@ -85,4 +56,6 @@ public class AdminRangeAddCommand extends CompositeCommand {
return true;
}
}

View File

@ -31,4 +31,5 @@ public class AdminRangeCommand extends CompositeCommand {
showHelp(this, user);
return true;
}
}
}

View File

@ -7,11 +7,13 @@ import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* @author Poslovitch
@ -22,9 +24,12 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
private static final String DISPLAY = "display";
private static final String SHOW = "show";
private static final String HIDE = "hide";
public static final Particle PARTICLE = Util.findFirstMatchingEnum(Particle.class, "REDSTONE", "DUST");
private static final Particle PARTICLE2 = Util.findFirstMatchingEnum(Particle.class, "VILLAGER_HAPPY",
"HAPPY_VILLAGER");
// Map of users to which ranges must be displayed
private Map<User, Integer> displayRanges = new HashMap<>();
private final Map<User, Integer> displayRanges = new HashMap<>();
public AdminRangeDisplayCommand(CompositeCommand parent) {
super(parent, DISPLAY, SHOW, HIDE);
@ -46,29 +51,15 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
if (!displayRanges.containsKey(user)) {
switch (label) {
case DISPLAY:
case SHOW:
showZones(user);
break;
case HIDE:
user.sendMessage("commands.admin.range.display.already-off");
break;
default:
showHelp(this, user);
break;
case DISPLAY, SHOW -> showZones(user);
case HIDE -> user.sendMessage("commands.admin.range.display.already-off");
default -> showHelp(this, user);
}
} else {
switch (label) {
case DISPLAY:
case HIDE:
hideZones(user);
break;
case SHOW:
user.sendMessage("commands.admin.range.display.already-on");
break;
default:
showHelp(this, user);
break;
case DISPLAY, HIDE -> hideZones(user);
case SHOW -> user.sendMessage("commands.admin.range.display.already-on");
default -> showHelp(this, user);
}
}
@ -85,15 +76,15 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
getIslands().getIslandAt(user.getLocation()).ifPresent(island -> {
// Draw the island protected area
drawZone(user, Particle.BARRIER, null, island, island.getProtectionRange());
drawZone(user, Particle.BLOCK_MARKER, Material.BARRIER.createBlockData(), island, island.getProtectionRange());
// Draw the default protected area if island protected zone is different
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
drawZone(user, Particle.VILLAGER_HAPPY, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
drawZone(user, PARTICLE2, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
}
// Draw the island area
drawZone(user, Particle.REDSTONE, new Particle.DustOptions(Color.GRAY, 1.0F), island, island.getRange());
drawZone(user, PARTICLE, new Particle.DustOptions(Color.GRAY, 1.0F), island, island.getRange());
});
}, 20, 30));
}
@ -104,7 +95,7 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
displayRanges.remove(user);
}
private void drawZone(User user, Particle particle, Particle.DustOptions dustOptions, Island island, int range) {
private void drawZone(User user, Particle particle, Object dustOptions, Island island, int range) {
Location center = island.getProtectionCenter();
// Get player Y coordinate
int playerY = user.getPlayer().getLocation().getBlockY() + 1;
@ -112,10 +103,10 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
// Draw 3 "stages" (one line below, at and above player's y coordinate)
for (int stage = -1 ; stage <= 1 ; stage++) {
for (int i = -range ; i <= range ; i++) {
user.spawnParticle(particle, dustOptions, center.getBlockX() + i, playerY + stage, center.getBlockZ() + range);
user.spawnParticle(particle, dustOptions, center.getBlockX() + i, playerY + stage, center.getBlockZ() - range);
user.spawnParticle(particle, dustOptions, center.getBlockX() + range, playerY + stage, center.getBlockZ() + i);
user.spawnParticle(particle, dustOptions, center.getBlockX() - range, playerY + stage, center.getBlockZ() + i);
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + i, (double)playerY + stage, (double)center.getBlockZ() + range);
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + i, (double)playerY + stage, (double)center.getBlockZ() - range);
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() + range, (double)playerY + stage, (double)center.getBlockZ() + i);
user.spawnParticle(particle, dustOptions, (double)center.getBlockX() - range, (double)playerY + stage, (double)center.getBlockZ() + i);
}
}
}

View File

@ -1,22 +1,19 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.List;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* @since 1.10.0
* @author Poslovitch
*/
public class AdminRangeRemoveCommand extends CompositeCommand {
public class AdminRangeRemoveCommand extends AbstractAdminRangeCommand {
public AdminRangeRemoveCommand(AdminRangeCommand parent) {
super(parent, "remove");
@ -24,55 +21,38 @@ public class AdminRangeRemoveCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.range.remove");
setDescription("commands.admin.range.remove.description");
setParametersHelp("commands.admin.range.remove.parameters");
}
@Override
public boolean execute(User user, String label, @NonNull List<String> args) {
if (args.size() != 2) {
showHelp(this, user);
return false;
}
UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
return false;
}
Island island = getIslands().getIsland(getWorld(), targetUUID);
int newRange = island.getProtectionRange() - Integer.parseInt(args.get(1));
int newRange = targetIsland.getProtectionRange() - Integer.parseInt(args.get(1));
if (newRange <= 1) {
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, String.valueOf(island.getRange()));
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER,
String.valueOf(targetIsland.getRange()));
return false;
} else if (newRange == island.getProtectionRange()) {
} else if (newRange == targetIsland.getProtectionRange()) {
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
return false;
}
// Get old range for event
int oldRange = island.getProtectionRange();
int oldRange = targetIsland.getProtectionRange();
// Well, now it can be applied without taking any risks!
island.setProtectionRange(newRange);
targetIsland.setProtectionRange(newRange);
// Call Protection Range Change event. Does not support cancelling.
IslandEvent.builder()
.island(island)
.location(island.getCenter())
.island(targetIsland).location(targetIsland.getCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE)
.involvedPlayer(targetUUID)
.admin(true)

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.commands.admin.range;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@ -45,7 +46,7 @@ public class AdminRangeResetCommand extends CompositeCommand {
}
// Get island
Island island = getIslands().getIsland(getWorld(), targetUUID);
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
// Get old range for event
int oldRange = island.getProtectionRange();

View File

@ -1,18 +1,14 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class AdminRangeSetCommand extends CompositeCommand {
public class AdminRangeSetCommand extends AbstractAdminRangeCommand {
public AdminRangeSetCommand(CompositeCommand parent) {
super(parent, "set");
@ -27,23 +23,6 @@ public class AdminRangeSetCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() != 2) {
// Show help
showHelp(this, user);
return false;
}
// Get target player
UUID targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!(getIslands().hasIsland(getWorld(), targetUUID) || getIslands().inTeam(getWorld(), targetUUID))) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Get new range
if (!Util.isInteger(args.get(1), true) || Integer.parseInt(args.get(1)) < 0) {
user.sendMessage("general.errors.must-be-positive-number", TextVariables.NUMBER, args.get(1));
@ -51,33 +30,30 @@ public class AdminRangeSetCommand extends CompositeCommand {
}
int range = Integer.parseInt(args.get(1));
// Get island
Island island = getIslands().getIsland(getWorld(), targetUUID);
// Do some sanity checks to make sure the new protection range won't cause problems
if (range < 1) {
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, args.get(1));
return false;
}
if (range > island.getRange() * 2) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(2 * island.getRange()));
if (range > targetIsland.getRange() * 2) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER,
String.valueOf(2 * targetIsland.getRange()));
return false;
}
if (range == island.getProtectionRange()) {
if (range == targetIsland.getProtectionRange()) {
user.sendMessage("commands.admin.range.invalid-value.same-as-before", TextVariables.NUMBER, args.get(1));
return false;
}
// Get old range for event
int oldRange = island.getProtectionRange();
int oldRange = targetIsland.getProtectionRange();
// Well, now it can be applied without taking any risks!
island.setProtectionRange(range);
targetIsland.setProtectionRange(range);
// Call Protection Range Change event. Does not support canceling.
IslandEvent.builder()
.island(island)
.location(island.getCenter())
.island(targetIsland).location(targetIsland.getCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE)
.involvedPlayer(targetUUID)
.admin(true)
@ -89,14 +65,4 @@ public class AdminRangeSetCommand extends CompositeCommand {
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -22,7 +22,7 @@ public class AdminResetsAddCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.resets.add");
setDescription("commands.admin.resets.add.description");
setParametersHelp("commands.admin.resets.add.parameters");
}

View File

@ -22,7 +22,7 @@ public class AdminResetsRemoveCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.resets.remove");
setDescription("commands.admin.resets.remove.description");
setParametersHelp("commands.admin.resets.remove.parameters");
}

View File

@ -22,7 +22,7 @@ public class AdminResetsResetCommand extends ConfirmableCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.resets.remove");
setDescription("commands.admin.resets.reset.description");
setParametersHelp("commands.admin.resets.reset.parameters");
}

View File

@ -16,7 +16,7 @@ public class AdminResetsSetCommand extends CompositeCommand {
@Override
public void setup() {
inheritPermission();
setPermission("admin.resets.set");
setDescription("commands.admin.resets.set.description");
setParametersHelp("commands.admin.resets.set.parameters");
}

View File

@ -10,6 +10,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
public class AdminTeamAddCommand extends CompositeCommand {
@ -20,7 +21,7 @@ public class AdminTeamAddCommand extends CompositeCommand {
@Override
public void setup() {
setPermission("mod.team");
setPermission("mod.team.add");
setParametersHelp("commands.admin.team.add.parameters");
setDescription("commands.admin.team.add.description");
}
@ -43,16 +44,17 @@ public class AdminTeamAddCommand extends CompositeCommand {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(1));
return false;
}
if (!getIslands().hasIsland(getWorld(), ownerUUID)) {
Island island = getIslands().getPrimaryIsland(getWorld(), ownerUUID);
if (island == null || !getIslands().hasIsland(getWorld(), ownerUUID)) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (getIslands().inTeam(getWorld(), ownerUUID) && !getIslands().getOwner(getWorld(), ownerUUID).equals(ownerUUID)) {
if (getIslands().inTeam(getWorld(), ownerUUID) && !ownerUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.team.add.name-not-owner", TextVariables.NAME, args.get(0));
getIslands().getIsland(getWorld(), ownerUUID).showMembers(user);
new IslandInfo(island).showMembers(user);
return false;
}
if (getIslands().inTeam(getWorld(), targetUUID)) {
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands() && island.inTeam(targetUUID)) {
user.sendMessage("commands.island.team.invite.errors.already-on-team");
return false;
}
@ -63,25 +65,19 @@ public class AdminTeamAddCommand extends CompositeCommand {
// Success
User target = User.getInstance(targetUUID);
User owner = User.getInstance(ownerUUID);
owner.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, getPlugin().getPlayers().getName(targetUUID));
owner.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME,
getPlugin().getPlayers().getName(targetUUID));
target.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
Island teamIsland = getIslands().getIsland(getWorld(), ownerUUID);
if (teamIsland != null) {
getIslands().setJoinTeam(teamIsland, targetUUID);
user.sendMessage("commands.admin.team.add.success", TextVariables.NAME, target.getName(), "[owner]", owner.getName());
TeamEvent.builder()
.island(teamIsland)
.reason(TeamEvent.Reason.JOINED)
.involvedPlayer(targetUUID)
.admin(true)
.build();
IslandEvent.builder()
.island(teamIsland)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(target), RanksManager.MEMBER_RANK)
.build();
user.sendMessage("commands.admin.team.add.success", TextVariables.NAME, target.getName(), "[owner]",
owner.getName());
TeamEvent.builder().island(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(targetUUID)
.admin(true).build();
IslandEvent.builder().island(teamIsland).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(target), RanksManager.MEMBER_RANK).build();
return true;
} else {
user.sendMessage("general.errors.player-has-no-island");

View File

@ -0,0 +1,47 @@
//
// Created by BONNe
// Copyright - 2022
//
package world.bentobox.bentobox.api.commands.admin.team;
import java.util.List;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
/**
* Parent command for all Admin Team commands.
*/
public class AdminTeamCommand extends CompositeCommand
{
public AdminTeamCommand(CompositeCommand parent)
{
super(parent, "team");
}
@Override
public void setup()
{
this.setPermission("mod.team");
this.setDescription("commands.admin.team.description");
new AdminTeamAddCommand(this);
new AdminTeamDisbandCommand(this);
new AdminTeamKickCommand(this);
new AdminTeamSetownerCommand(this);
}
@Override
public boolean execute(User user, String label, List<String> args)
{
this.showHelp(this, user);
return true;
}
}

View File

@ -1,7 +1,14 @@
package world.bentobox.bentobox.api.commands.admin.team;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -14,66 +21,110 @@ import world.bentobox.bentobox.util.Util;
public class AdminTeamDisbandCommand extends CompositeCommand {
private Island island;
private @Nullable UUID targetUUID;
/**
* Disbands a team
* @param parent parent command
*/
public AdminTeamDisbandCommand(CompositeCommand parent) {
super(parent, "disband");
}
@Override
public void setup() {
setPermission("mod.team");
setPermission("mod.team.disband");
setParametersHelp("commands.admin.team.disband.parameters");
setDescription("commands.admin.team.disband.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
if (args.isEmpty() || args.size() > 2) {
showHelp(this, user);
return false;
}
// Get target
UUID targetUUID = Util.getUUID(args.get(0));
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!getIslands().hasIsland(getWorld(), targetUUID)) {
user.sendMessage("general.errors.no-island");
// Find the island the player is an owner of
Map<String, Island> islands = getIslandsXYZ(targetUUID);
if (islands.isEmpty()) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
if (!getIslands().inTeam(getWorld(), targetUUID)) {
user.sendMessage("general.errors.not-in-team");
return false;
}
if (!getIslands().getOwner(getWorld(), targetUUID).equals(targetUUID)) {
user.sendMessage("commands.admin.team.disband.use-disband-owner", "[owner]", getPlayers().getName(getIslands().getOwner(getWorld(), targetUUID)));
if (islands.size() > 1) {
if (args.size() != 2 || !islands.containsKey(args.get(1))) {
user.sendMessage("commands.admin.team.disband.more-than-one-island", TextVariables.NAME,
getPlayers().getName(island.getOwner()));
islands.keySet().forEach(coords -> user.sendMessage("commands.admin.team.disband.more-than-one-island",
TextVariables.XYZ, coords));
return false;
}
// Get the named island
island = islands.get(args.get(1));
} else {
// Get the only island
island = islands.values().iterator().next();
}
// Check that the target owns the island
if (island.getOwner() == null || !island.getOwner().equals(targetUUID)) {
user.sendMessage("general.errors.player-is-not-owner", TextVariables.NAME, args.get(0));
return false;
}
return true;
}
private Map<String, Island> getIslandsXYZ(UUID target) {
return getIslands().getOwnedIslands(getWorld(), target).stream().filter(is -> is.getMemberSet().size() > 1) // Filter for teams
.collect(Collectors.toMap(is -> Util.xyz(is.getCenter().toVector()), is -> is));
}
@Override
public boolean execute(User user, String label, List<String> args) {
Objects.requireNonNull(island);
Objects.requireNonNull(targetUUID);
// Disband team
Island island = getIslands().getIsland(getWorld(), targetUUID);
getIslands().getMembers(getWorld(), targetUUID).forEach(m -> {
island.getMemberSet().forEach(m -> {
User mUser = User.getInstance(m);
mUser.sendMessage("commands.admin.team.disband.disbanded");
// The owner gets to keep the island
if (!m.equals(targetUUID)) {
getIslands().setLeaveTeam(getWorld(), m);
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.KICK)
.involvedPlayer(m)
.admin(true)
.build();
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(mUser), RanksManager.VISITOR_RANK)
.build();
getIslands().removePlayer(island, m);
TeamEvent.builder().island(island).reason(TeamEvent.Reason.KICK).involvedPlayer(m).admin(true).build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(mUser), RanksManager.VISITOR_RANK).build();
}
});
user.sendMessage("commands.admin.team.disband.success", TextVariables.NAME, args.get(0));
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
} else if (args.size() == 3) {
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
} else if (args.size() > 3) {
// Find out which user
UUID uuid = getPlayers().getUUID(args.get(1));
if (uuid != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(getIslandsXYZ(uuid).keySet()), lastArg));
}
}
return Optional.empty();
}
}

View File

@ -1,35 +0,0 @@
package world.bentobox.bentobox.api.commands.admin.team;
import java.util.List;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
public class AdminTeamFixCommand extends CompositeCommand {
public AdminTeamFixCommand(CompositeCommand parent) {
super(parent, "fix");
}
@Override
public void setup() {
setPermission("mod.team");
setDescription("commands.admin.team.fix.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (!args.isEmpty()) {
showHelp(this, user);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
getIslands().checkTeams(user, getWorld());
return true;
}
}

View File

@ -13,6 +13,7 @@ import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
/**
@ -29,7 +30,7 @@ public class AdminTeamKickCommand extends CompositeCommand {
@Override
public void setup() {
setPermission("mod.team");
setPermission("mod.team.kick");
setParametersHelp("commands.admin.team.kick.parameters");
setDescription("commands.admin.team.kick.description");
}
@ -59,17 +60,18 @@ public class AdminTeamKickCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, @NonNull List<String> args) {
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island == null) {
return false;
}
if (targetUUID.equals(island.getOwner())) {
user.sendMessage("commands.admin.team.kick.cannot-kick-owner");
island.showMembers(user);
new IslandInfo(island).showMembers(user);
return false;
}
User target = User.getInstance(targetUUID);
target.sendMessage("commands.admin.team.kick.admin-kicked");
getIslands().removePlayer(getWorld(), targetUUID);
getPlayers().clearHomeLocations(getWorld(), targetUUID);
getIslands().removePlayer(island, targetUUID);
user.sendMessage("commands.admin.team.kick.success", TextVariables.NAME, target.getName(), "[owner]", getPlayers().getName(island.getOwner()));
// Fire event so add-ons know

View File

@ -1,9 +1,17 @@
package world.bentobox.bentobox.api.commands.admin.team;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
@ -14,9 +22,14 @@ import world.bentobox.bentobox.util.Util;
/**
* Sets the owner of an island.
*
* @author tastybento
*/
public class AdminTeamSetownerCommand extends CompositeCommand {
public class AdminTeamSetownerCommand extends ConfirmableCommand {
private @Nullable UUID targetUUID;
private Island island;
private @Nullable UUID previousOwnerUUID;
public AdminTeamSetownerCommand(CompositeCommand parent) {
super(parent, "setowner");
@ -24,73 +37,95 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
@Override
public void setup() {
setPermission("mod.team");
setPermission("mod.team.setowner");
setParametersHelp("commands.admin.team.setowner.parameters");
setDescription("commands.admin.team.setowner.description");
this.setOnlyPlayer(true);
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
// If args are not right, show help
if (args.size() != 1) {
showHelp(this, user);
return false;
}
// Get target
UUID targetUUID = Util.getUUID(args.get(0));
targetUUID = Util.getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (!getIslands().inTeam(getWorld(), targetUUID)) {
user.sendMessage("general.errors.not-in-team");
// Check that user is on an island
Optional<Island> opIsland = getIslands().getIslandAt(user.getLocation());
if (opIsland.isEmpty()) {
user.sendMessage("commands.admin.team.setowner.must-be-on-island");
return false;
}
UUID previousOwnerUUID = getIslands().getOwner(getWorld(), targetUUID);
island = opIsland.get();
previousOwnerUUID = island.getOwner();
if (targetUUID.equals(previousOwnerUUID)) {
user.sendMessage("commands.admin.team.setowner.already-owner", TextVariables.NAME, args.get(0));
return false;
}
// Get the User corresponding to the current owner
User previousOwner = User.getInstance(previousOwnerUUID);
User target = User.getInstance(targetUUID);
// Fire event so add-ons know
Island island = getIslands().getIsland(getWorld(), targetUUID);
// Call the setowner event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID)
.admin(true)
.build();
// Call the rank change event for the new island owner
// We need to call it BEFORE the actual change, in order to retain the player's previous rank.
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(target), RanksManager.OWNER_RANK)
.build();
// Make new owner
getIslands().setOwner(getWorld(), user, targetUUID);
user.sendMessage("commands.admin.team.setowner.success", TextVariables.NAME, args.get(0));
// Call the rank change event for the old island owner
// We need to call it AFTER the actual change.
IslandEvent.builder()
.island(island)
.involvedPlayer(previousOwnerUUID)
.admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwner))
.build();
return true;
}
public boolean execute(User user, String label, List<String> args) {
Objects.requireNonNull(island);
Objects.requireNonNull(targetUUID);
this.askConfirmation(user, user.getTranslation("commands.admin.team.setowner.confirmation", TextVariables.NAME,
args.get(0), TextVariables.XYZ, Util.xyz(island.getCenter().toVector())), () -> changeOwner(user));
return true;
}
protected void changeOwner(User user) {
User target = User.getInstance(targetUUID);
// Fire event so add-ons know
// Call the setowner event
TeamEvent.builder().island(island).reason(TeamEvent.Reason.SETOWNER).involvedPlayer(targetUUID).admin(true)
.build();
// Call the rank change event for the new island owner
// We need to call it BEFORE the actual change, in order to retain the player's
// previous rank.
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(target), RanksManager.OWNER_RANK)
.build();
// Make new owner
getIslands().setOwner(user, targetUUID, island, RanksManager.MEMBER_RANK);
user.sendMessage("commands.admin.team.setowner.success", TextVariables.NAME, target.getName());
// Report if this made player have more islands than expected
// Get how many islands this player has
int num = this.getIslands().getNumberOfConcurrentIslands(targetUUID, getWorld());
int max = target.getPermissionValue(
this.getIWM().getAddon(getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.number",
this.getIWM().getWorldSettings(getWorld()).getConcurrentIslands());
if (num > max) {
// You cannot make an island
user.sendMessage("commands.admin.team.setowner.extra-islands", TextVariables.NUMBER, String.valueOf(num),
"[max]", String.valueOf(max));
}
// Call the rank change event for the old island owner
if (previousOwnerUUID != null) {
// We need to call it AFTER the actual change.
IslandEvent.builder().island(island).involvedPlayer(previousOwnerUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwnerUUID)).build();
}
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
List<String> options = Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -23,6 +23,7 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
*/
protected DefaultPlayerCommand(GameModeAddon addon) {
// Register command with alias from config.
// The first command listed is the "label" and the others are aliases.
super(addon,
addon.getWorldSettings().getPlayerCommandAliases().split(" ")[0],
addon.getWorldSettings().getPlayerCommandAliases().split(" "));
@ -78,6 +79,7 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
new IslandSethomeCommand(this);
new IslandDeletehomeCommand(this);
new IslandRenamehomeCommand(this);
new IslandHomesCommand(this);
}
@ -99,31 +101,30 @@ public abstract class DefaultPlayerCommand extends CompositeCommand {
// Check if user has an island.
if (this.getIslands().getIsland(this.getWorld(), user.getUniqueId()) != null) {
// Default command if user has an island.
String command = this.<GameModeAddon>getAddon().getWorldSettings().getDefaultPlayerAction();
// Perform command or use "go" command.
if (command != null && user.performCommand(label + " " + command)) {
return true;
}
else {
return this.getSubCommand("go").
map(goCmd -> goCmd.call(user, goCmd.getLabel(), Collections.emptyList())).
orElse(false);
}
}
else {
return runCommand(user, label, this.<GameModeAddon>getAddon().getWorldSettings().getDefaultPlayerAction(), "go");
} else {
// Default command if user does not have an island.
String command = this.<GameModeAddon>getAddon().getWorldSettings().getDefaultNewPlayerAction();
return runCommand(user, label, this.<GameModeAddon>getAddon().getWorldSettings().getDefaultNewPlayerAction(), "create");
}
}
// Perform command or use "create" command.
if (command != null && user.performCommand(label + " " + command)) {
return true;
}
else {
return this.getSubCommand("create").
map(createCmd -> createCmd.call(user, createCmd.getLabel(), Collections.emptyList())).
orElse(false);
private boolean runCommand(User user, String label, String command, String defaultSubCommand) {
if (command == null || command.isEmpty()) {
command = defaultSubCommand;
}
// Call sub command or perform command if it does not exist
if (this.getSubCommand(command).isPresent()) {
return this.getSubCommand(command).
map(c -> c.call(user, c.getLabel(), Collections.emptyList())).
orElse(false);
} else {
// Command is not a known sub command - try to perform it directly - some plugins trap these commands, like Deluxe menus
if (command.startsWith("/")) {
// If commands starts with Slash, don't append the prefix
return user.performCommand(command.substring(1));
} else {
return user.performCommand(label + " " + command);
}
}
}
}
}

View File

@ -4,7 +4,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
@ -18,6 +17,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandBanCommand extends CompositeCommand {
@ -46,15 +46,17 @@ public class IslandBanCommand extends CompositeCommand {
}
UUID playerUUID = user.getUniqueId();
// Player issuing the command must have an island or be in a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId()) && !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())
&& !getIslands().hasIsland(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-island");
return false;
}
// Check rank to use command
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), user));
int rank = island.getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -68,11 +70,11 @@ public class IslandBanCommand extends CompositeCommand {
user.sendMessage("commands.island.ban.cannot-ban-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
user.sendMessage("commands.island.ban.cannot-ban-member");
return false;
}
if (getIslands().getIsland(getWorld(), playerUUID).isBanned(targetUUID)) {
if (island.isBanned(targetUUID)) {
user.sendMessage("commands.island.ban.player-already-banned");
return false;
}
@ -95,28 +97,29 @@ public class IslandBanCommand extends CompositeCommand {
}
private boolean ban(@NonNull User issuer, User target) {
Island island = getIslands().getIsland(getWorld(), issuer.getUniqueId());
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), issuer.getUniqueId()));
// Check if player can ban any more players
int banLimit = issuer.getPermissionValue(getPermissionPrefix() + "ban.maxlimit", getIWM().getBanLimit(getWorld()));
int banLimit = issuer.getPermissionValue(getPermissionPrefix() + "ban.maxlimit",
getIWM().getBanLimit(getWorld()));
if (banLimit <= -1 || island.getBanned().size() < banLimit) {
// Run the event
IslandBaseEvent banEvent = IslandEvent.builder()
.island(island)
.involvedPlayer(target.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.BAN)
.build();
if (banEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(banEvent.isCancelled()) ) {
IslandBaseEvent banEvent = IslandEvent.builder().island(island).involvedPlayer(target.getUniqueId())
.admin(false).reason(IslandEvent.Reason.BAN).build();
if (banEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(banEvent.isCancelled())) {
// Banning was blocked due to an event cancellation. Fail silently.
return false;
}
// Event is not cancelled
if (island.ban(issuer.getUniqueId(), target.getUniqueId())) {
issuer.sendMessage("commands.island.ban.player-banned", TextVariables.NAME, target.getName());
target.sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, issuer.getName());
// If the player is online, has an island and on the banned island, move them home immediately
if (target.isOnline() && getIslands().hasIsland(getWorld(), target.getUniqueId()) && island.onIsland(target.getLocation())) {
issuer.sendMessage("commands.island.ban.player-banned", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.ban.owner-banned-you", TextVariables.NAME, issuer.getName(),
TextVariables.DISPLAY_NAME, issuer.getDisplayName());
// If the player is online, has an island and on the banned island, move them
// home immediately
if (target.isOnline() && getIslands().hasIsland(getWorld(), target.getUniqueId())
&& island.onIsland(target.getLocation())) {
getIslands().homeTeleportAsync(getWorld(), target.getPlayer());
island.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F);
}
@ -131,7 +134,7 @@ public class IslandBanCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (lastArg.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
@ -140,9 +143,8 @@ public class IslandBanCommand extends CompositeCommand {
if (island != null) {
List<String> options = Bukkit.getOnlinePlayers().stream()
.filter(p -> !p.getUniqueId().equals(user.getUniqueId()))
.filter(p -> !island.isBanned(p.getUniqueId()))
.filter(p -> user.getPlayer().canSee(p))
.map(Player::getName).collect(Collectors.toList());
.filter(p -> !island.isBanned(p.getUniqueId())).filter(p -> user.getPlayer().canSee(p))
.map(Player::getName).toList();
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -3,12 +3,12 @@ package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
public class IslandBanlistCommand extends CompositeCommand {
@ -41,7 +41,8 @@ public class IslandBanlistCommand extends CompositeCommand {
island = getIslands().getIsland(getWorld(), user.getUniqueId());
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
return true;
@ -57,7 +58,7 @@ public class IslandBanlistCommand extends CompositeCommand {
// Title
user.sendMessage("commands.island.banlist.the-following");
// Create a nicely formatted list
List<String> names = island.getBanned().stream().map(u -> getPlayers().getName(u)).sorted().collect(Collectors.toList());
List<String> names = island.getBanned().stream().map(u -> getPlayers().getName(u)).sorted().toList();
List<String> lines = new ArrayList<>();
StringBuilder line = new StringBuilder();
// Put the names into lines of no more than 40 characters long, separated by commas

View File

@ -5,13 +5,15 @@ import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**
* /island create - Create an island.
@ -22,6 +24,7 @@ public class IslandCreateCommand extends CompositeCommand {
/**
* Command to create an island
*
* @param islandCommand - parent command
*/
public IslandCreateCommand(CompositeCommand islandCommand) {
@ -40,14 +43,29 @@ public class IslandCreateCommand extends CompositeCommand {
public boolean canExecute(User user, String label, List<String> args) {
// Check if the island is reserved
@Nullable
Island island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island != null) {
// Reserved islands can be made
if (island.isReserved()) {
return true;
}
}
// Check if this player is on a team in this world
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
&& getIslands().inTeam(getWorld(), user.getUniqueId()) && island != null
&& !user.getUniqueId().equals(island.getOwner())) {
// Team members who are not owners cannot make additional islands
user.sendMessage("commands.island.create.you-cannot-make-team");
return false;
}
// Get how many islands this player has
int num = this.getIslands().getNumberOfConcurrentIslands(user.getUniqueId(), getWorld());
int max = user.getPermissionValue(
this.getIWM().getAddon(getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.number",
this.getIWM().getWorldSettings(getWorld()).getConcurrentIslands());
if (num >= max) {
// You cannot make an island
user.sendMessage("general.errors.already-have-island");
user.sendMessage("commands.island.create.you-cannot-make");
return false;
}
if (getIWM().getMaxIslands(getWorld()) > 0
@ -63,44 +81,67 @@ public class IslandCreateCommand extends CompositeCommand {
public boolean execute(User user, String label, List<String> args) {
// Permission check if the name is not the default one
if (!args.isEmpty()) {
String name = getPlugin().getBlueprintsManager().validate(getAddon(), args.get(0).toLowerCase(java.util.Locale.ENGLISH));
String name = getPlugin().getBlueprintsManager().validate(getAddon(), Util.sanitizeInput(args.get(0)));
if (name == null) {
// The blueprint name is not valid.
user.sendMessage("commands.island.create.unknown-blueprint");
return false;
}
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, args.get(0))) {
// Check perm
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) {
return false;
}
// Check maximum uses
if (checkMaxUses(user, name)) {
return false;
}
// Make island
return makeIsland(user, name);
} else {
if (getPlugin().getSettings().getIslandNumber() > 1
&& checkMaxUses(user, BlueprintsManager.DEFAULT_BUNDLE_NAME)) {
return false;
}
// Show panel only if there are multiple bundles available
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) {
// Show panel
IslandCreationPanel.openPanel(this, user, label);
IslandCreationPanel.openPanel(this, user, label, false);
return true;
}
return makeIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME);
}
}
private boolean checkMaxUses(User user, String name) {
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).containsKey(name)) {
int maxTimes = getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).get(name).getTimes();
// Check how many times this player has used this bundle
if (maxTimes > 0 && getBundleUses(user, name) >= maxTimes) {
user.sendMessage("commands.island.create.max-uses");
return true;
}
}
return false;
}
private long getBundleUses(User user, String name) {
return getIslands().getIslands(getWorld(), user).stream()
.filter(is -> is.getMetaData("bundle").map(mdv -> name.equalsIgnoreCase(mdv.asString())).orElse(false))
.count();
}
private boolean makeIsland(User user, String name) {
user.sendMessage("commands.island.create.creating-island");
try {
NewIsland.builder()
.player(user)
.addon(getAddon())
.reason(Reason.CREATE)
.name(name)
.build();
NewIsland.builder().player(user).addon(getAddon()).reason(Reason.CREATE).name(name).build();
} catch (IOException e) {
getPlugin().logError("Could not create island for player. " + e.getMessage());
user.sendMessage(e.getMessage());
return false;
}
if (getSettings().isResetCooldownOnCreate()) {
getParent().getSubCommand("reset").ifPresent(resetCommand -> resetCommand.setCooldown(user.getUniqueId(), getSettings().getResetCooldown()));
getParent().getSubCommand("reset").ifPresent(
resetCommand -> resetCommand.setCooldown(user.getUniqueId(), getSettings().getResetCooldown()));
}
return true;
}

View File

@ -1,16 +1,18 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
@ -20,8 +22,10 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandDeletehomeCommand extends ConfirmableCommand {
private @Nullable Island island;
/**
* Deletes a home
* @param islandCommand parent command
*/
public IslandDeletehomeCommand(CompositeCommand islandCommand) {
super(islandCommand, "deletehome");
}
@ -32,6 +36,8 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
setOnlyPlayer(true);
setParametersHelp("commands.island.deletehome.parameters");
setDescription("commands.island.deletehome.description");
setConfigurableRankCommand();
setDefaultCommandRank(RanksManager.MEMBER_RANK);
}
@Override
@ -40,25 +46,36 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
this.showHelp(this, user);
return false;
}
island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getIsland(getWorld(), user);
// Check island
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
// Check if the name is known
if (!getIslands().isHomeLocation(island, String.join(" ", args))) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("home-list-syntax", TextVariables.NAME, s));
// check command ranks
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
this.askConfirmation(user, () -> delete(island, user, String.join(" ", args)));
// Check if the name is known
Map<String, Island> map = getNameIslandMap(user);
String name = String.join(" ", args);
if (!map.containsKey(name)) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
map.keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
return false;
}
this.askConfirmation(user, () -> delete(map.get(name), user, name));
return true;
}
@ -71,11 +88,19 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
} else {
return Optional.empty();
}
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
}
private Map<String, Island> getNameIslandMap(User user) {
Map<String, Island> islandMap = new HashMap<>();
for (Island isle : getIslands().getIslands(getWorld(), user.getUniqueId())) {
// Add homes.
isle.getHomes().keySet().forEach(name -> islandMap.put(name, isle));
}
return islandMap;
}
}

View File

@ -4,7 +4,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
@ -17,6 +16,7 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
@ -60,7 +60,8 @@ public class IslandExpelCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target player
@ -75,7 +76,7 @@ public class IslandExpelCommand extends CompositeCommand {
return false;
}
// Or team member
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (island.inTeam(targetUUID)) {
user.sendMessage("commands.island.expel.cannot-expel-member");
return false;
}
@ -91,53 +92,48 @@ public class IslandExpelCommand extends CompositeCommand {
return false;
}
// Cannot ban ops
if (target.isOp() ||
target.hasPermission(this.getPermissionPrefix() + "admin.noexpel") ||
target.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")) {
if (target.isOp() || target.hasPermission(this.getPermissionPrefix() + "admin.noexpel")
|| target.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")) {
user.sendMessage(CANNOT_EXPEL);
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Finished error checking - expel player
Island island = getIslands().getIsland(getWorld(), user);
// Fire event
IslandBaseEvent expelEvent = IslandEvent.builder()
.island(island)
.involvedPlayer(target.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.EXPEL)
.build();
IslandBaseEvent expelEvent = IslandEvent.builder().island(island).involvedPlayer(target.getUniqueId())
.admin(false).reason(IslandEvent.Reason.EXPEL).build();
if (expelEvent.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(expelEvent.isCancelled())) {
user.sendMessage(CANNOT_EXPEL);
return false;
}
target.sendMessage("commands.island.expel.player-expelled-you", TextVariables.NAME, user.getName());
target.sendMessage("commands.island.expel.player-expelled-you", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
island.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F);
if (getIslands().hasIsland(getWorld(), target) || getIslands().inTeam(getWorld(), target.getUniqueId())) {
// Success
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
// Teleport home
getIslands().homeTeleportAsync(getWorld(), target.getPlayer());
return true;
} else if (getIslands().getSpawn(getWorld()).isPresent()){
} else if (getIslands().getSpawn(getWorld()).isPresent()) {
// Success
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
getIslands().spawnTeleport(getWorld(), target.getPlayer());
return true;
} else if (getIWM().getAddon(getWorld())
.map(gm -> gm.getPlayerCommand()
.map(pc -> pc.getSubCommand("create").isPresent())
.orElse(false))
.orElse(false)
&& target.performCommand(this.getTopLabel() + " create")) {
.map(gm -> gm.getPlayerCommand().map(pc -> pc.getSubCommand("create").isPresent()).orElse(false))
.orElse(false) && target.performCommand(this.getTopLabel() + " create")) {
getAddon().logWarning("Expel: " + target.getName() + " had no island, so one was created");
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
return true;
}
@ -150,15 +146,15 @@ public class IslandExpelCommand extends CompositeCommand {
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
Island island = getIslands().getIsland(getWorld(), user);
if (island != null) {
List<String> options = island.getPlayersOnIsland().stream()
.filter(p -> !p.equals(user.getPlayer())) // Not self
List<String> options = island.getPlayersOnIsland().stream().filter(p -> !p.equals(user.getPlayer())) // Not
// self
.filter(p -> user.getPlayer().canSee(p)) // Not invisible
.filter(p -> !p.isOp()) // Not op
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "admin.noexpel"))
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel"))
.map(Player::getName).collect(Collectors.toList());
.filter(p -> !p.hasPermission(this.getPermissionPrefix() + "mod.bypassexpel")).map(Player::getName)
.toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -2,7 +2,9 @@ package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
@ -32,49 +34,98 @@ public class IslandGoCommand extends DelayedTeleportCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
// Check if the island is reserved
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {
// Check if mid-teleport
if (getIslands().isGoingHome(user)) {
// Tell them again that it's in progress
user.sendMessage("commands.island.go.teleport");
return false;
}
List<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
if (islands.isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
if (island.isReserved()) {
// Send player to create an island
getParent().getSubCommand("create").ifPresent(createCmd -> createCmd.call(user, createCmd.getLabel(), Collections.emptyList()));
// Check if the island is reserved
if (checkReserved(user, islands)) {
return false;
}
// Prevent command if player is falling and its not allowed
if ((getIWM().inWorld(user.getWorld()) && Flags.PREVENT_TELEPORT_WHEN_FALLING.isSetForWorld(user.getWorld()))
&& user.getPlayer().getFallDistance() > 0) {
// We're sending the "hint" to the player to tell them they cannot teleport while falling.
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
return false;
}
if (!args.isEmpty()) {
if (!getIslands().isHomeLocation(island, String.join(" ", args))) {
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s));
return false;
}
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), String.join(" ", args)));
// Check if the home is known
if (!args.isEmpty()) {
Map<String, IslandInfo> names = getNameIslandMap(user);
final String name = String.join(" ", args);
if (!names.containsKey(name)) {
// Failed home name check
user.sendMessage("commands.island.go.unknown-home");
user.sendMessage("commands.island.sethome.homes-are");
names.keySet().forEach(n -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, n));
return false;
} else {
IslandInfo info = names.get(name);
getIslands().setPrimaryIsland(user.getUniqueId(), info.island);
if (!info.islandName) {
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer(), name)
.thenAccept((r) -> getIslands().setPrimaryIsland(user.getUniqueId(), info.island)));
return true;
}
}
}
this.delayCommand(user, () -> getIslands().homeTeleportAsync(getWorld(), user.getPlayer()));
return true;
}
private boolean checkReserved(User user, List<Island> islands) {
for (Island island : islands) {
if (island.isReserved()) {
// Send player to create an island
getParent().getSubCommand("create").ifPresent(createCmd -> createCmd.call(user, createCmd.getLabel(), Collections.emptyList()));
return true;
}
}
return false;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
} else {
return Optional.empty();
return Optional.of(Util.tabLimit(new ArrayList<>(getNameIslandMap(user).keySet()), lastArg));
}
private record IslandInfo(Island island, boolean islandName) {}
private Map<String, IslandInfo> getNameIslandMap(User user) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
// Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
}

View File

@ -0,0 +1,60 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class IslandHomesCommand extends ConfirmableCommand {
private List<Island> islands;
public IslandHomesCommand(CompositeCommand islandCommand) {
super(islandCommand, "homes");
}
@Override
public void setup() {
setPermission("island.homes");
setOnlyPlayer(true);
setDescription("commands.island.homes.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
islands = getIslands().getIslands(getWorld(), user);
// Check island
if (islands.isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.island.sethome.homes-are");
islands.forEach(island ->
island.getHomes().keySet().stream().filter(s -> !s.isEmpty())
.forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
List<String> result = new ArrayList<>();
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
result.addAll(island.getHomes().keySet());
}
return Optional.of(Util.tabLimit(result, lastArg));
}
}

View File

@ -9,6 +9,7 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.IslandInfo;
import world.bentobox.bentobox.util.Util;
/**
@ -37,7 +38,7 @@ public class IslandInfoCommand extends CompositeCommand {
}
// If there are no args, then the player wants info on the island at this location
if (args.isEmpty()) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> i.showInfo(user)).orElse(false)) {
if (!getIslands().getIslandAt(user.getLocation()).map(i -> new IslandInfo(i).showInfo(user)).orElse(false)) {
user.sendMessage("commands.admin.info.no-island");
return false;
}
@ -56,10 +57,10 @@ public class IslandInfoCommand extends CompositeCommand {
return false;
}
// Show info for this player
island.showInfo(user);
new IslandInfo(island).showInfo(user);
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";

View File

@ -7,7 +7,7 @@ import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.panels.LanguagePanel;
import world.bentobox.bentobox.panels.customizable.LanguagePanel;
import world.bentobox.bentobox.util.Util;
/**
@ -46,7 +46,7 @@ public class IslandLanguageCommand extends CompositeCommand {
return false;
}
} else {
LanguagePanel.openPanel(user);
LanguagePanel.openPanel(this, user);
}
return true;
}

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.bukkit.conversations.ConversationFactory;
@ -14,6 +15,7 @@ import world.bentobox.bentobox.api.commands.admin.conversations.NamePrompt;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
@ -35,6 +37,8 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
setOnlyPlayer(true);
setParametersHelp("commands.island.renamehome.parameters");
setDescription("commands.island.renamehome.description");
setConfigurableRankCommand();
setDefaultCommandRank(RanksManager.MEMBER_RANK);
}
@Override
@ -57,6 +61,15 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
this.showHelp(this, user);
return false;
}
// check command permission
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
return true;
}
@ -75,9 +88,9 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(island.getHomes().keySet()), lastArg));
Island is = getIslands().getIsland(getWorld(), user.getUniqueId());
if (is != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(is.getHomes().keySet()), lastArg));
} else {
return Optional.empty();
}

View File

@ -16,7 +16,8 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.BlueprintsManager;
import world.bentobox.bentobox.managers.island.NewIsland;
import world.bentobox.bentobox.managers.island.NewIsland.Builder;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.panels.customizable.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**
* @author tastybento
@ -31,8 +32,9 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Creates the island reset command
*
* @param islandCommand - parent command
* @param noPaste - true if resetting should not paste a new island
* @param noPaste - true if resetting should not paste a new island
*/
public IslandResetCommand(CompositeCommand islandCommand, boolean noPaste) {
super(islandCommand, "reset", "restart");
@ -78,20 +80,21 @@ public class IslandResetCommand extends ConfirmableCommand {
public boolean execute(User user, String label, List<String> args) {
// Permission check if the name is not the default one
if (!args.isEmpty()) {
String name = getPlugin().getBlueprintsManager().validate(getAddon(), args.get(0).toLowerCase(java.util.Locale.ENGLISH));
String name = getPlugin().getBlueprintsManager().validate(getAddon(), Util.sanitizeInput(args.get(0)));
if (name == null || name.isEmpty()) {
// The blueprint name is not valid.
user.sendMessage("commands.island.create.unknown-blueprint");
return false;
}
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, args.get(0))) {
if (!getPlugin().getBlueprintsManager().checkPerm(getAddon(), user, Util.sanitizeInput(args.get(0)))) {
return false;
}
return resetIsland(user, name);
} else {
// Show panel after confirmation
if (getPlugin().getSettings().isResetConfirmation()) {
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"), () -> selectBundle(user, label));
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"),
() -> selectBundle(user, label));
} else {
selectBundle(user, label);
}
@ -101,52 +104,38 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Either selects the bundle to use or asks the user to choose.
*
* @since 1.5.1
*/
private void selectBundle(@NonNull User user, @NonNull String label) {
// Show panel only if there are multiple bundles available
if (getPlugin().getBlueprintsManager().getBlueprintBundles(getAddon()).size() > 1) {
// Show panel - once the player selected a bundle, this will re-run this command
IslandCreationPanel.openPanel(this, user, label);
IslandCreationPanel.openPanel(this, user, label, true);
} else {
resetIsland(user, BlueprintsManager.DEFAULT_BUNDLE_NAME);
}
}
/**
* Reset island
*
* @param user user
* @param name name of Blueprint Bundle
* @return true if successful
*/
private boolean resetIsland(User user, String name) {
// Get the player's old island
Island oldIsland = getIslands().getIsland(getWorld(), user);
deleteOldIsland(user, oldIsland);
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(Reason.PRECLEAR)
.island(oldIsland)
.oldIsland(oldIsland)
.location(oldIsland.getCenter())
.build();
// Reset the island
user.sendMessage("commands.island.create.creating-island");
// Kick all island members (including the owner)
kickMembers(oldIsland);
// Add a reset
getPlayers().addReset(getWorld(), user.getUniqueId());
// Reset the homes of the player
getPlayers().clearHomeLocations(getWorld(), user.getUniqueId());
// Create new island and then delete the old one
try {
Builder builder = NewIsland.builder()
.player(user)
.reason(Reason.RESET)
.addon(getAddon())
.oldIsland(oldIsland)
.name(name);
if (noPaste) builder.noPaste();
Builder builder = NewIsland.builder().player(user).reason(Reason.RESET).addon(getAddon())
.oldIsland(oldIsland).name(name);
if (noPaste)
builder.noPaste();
builder.build();
} catch (IOException e) {
getPlugin().logError("Could not create island for player. " + e.getMessage());
@ -157,35 +146,49 @@ public class IslandResetCommand extends ConfirmableCommand {
return true;
}
private void deleteOldIsland(User user, Island oldIsland) {
// Fire island preclear event
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
// Reset the island
// Kick all island members (including the owner)
kickMembers(oldIsland);
// Add a reset
getPlayers().addReset(getWorld(), user.getUniqueId());
}
/**
* Kicks the members (incl. owner) of the island.
*
* @since 1.7.0
*/
private void kickMembers(Island island) {
/*
* We cannot assume the island owner can run /[cmd] team kick (it might be disabled, or there could be permission restrictions...)
* Therefore, we need to do it manually.
* Plus, a more specific team event (TeamDeleteEvent) is called by this method.
* We cannot assume the island owner can run /[cmd] team kick (it might be
* disabled, or there could be permission restrictions...) Therefore, we need to
* do it manually. Plus, a more specific team event (TeamDeleteEvent) is called
* by this method.
*/
island.getMemberSet().forEach(memberUUID -> {
User member = User.getInstance(memberUUID);
// Send a "you're kicked" message if the member is not the island owner (send before removing!)
// Send a "you're kicked" message if the member is not the island owner (send
// before removing!)
if (!memberUUID.equals(island.getOwner())) {
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE, getAddon().getDescription().getName());
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE,
getAddon().getDescription().getName());
}
// Remove player
getIslands().removePlayer(getWorld(), memberUUID);
getIslands().removePlayer(island, memberUUID);
// Clean player
getPlayers().cleanLeavingPlayer(getWorld(), member, false);
getPlayers().cleanLeavingPlayer(getWorld(), member, false, island);
// Fire event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.DELETE)
.involvedPlayer(memberUUID)
.build();
TeamEvent.builder().island(island).reason(TeamEvent.Reason.DELETE).involvedPlayer(memberUUID).build();
});
}
}

View File

@ -1,10 +1,14 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.List;
import java.util.UUID;
import java.util.Objects;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
/**
* @author tastybento
@ -23,22 +27,34 @@ public class IslandResetnameCommand extends CompositeCommand {
setDescription("commands.island.resetname.description");
}
@Override
public boolean execute(User user, String label, List<String> args) {
UUID playerUUID = user.getUniqueId();
if (!getIslands().hasIsland(getWorld(), playerUUID)) {
@Override
public boolean canExecute(User user, String label, List<String> args)
{
Island island = getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
if (!getIslands().isOwner(getWorld(), playerUUID)) {
user.sendMessage("general.errors.not-owner");
// Check command rank.
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Resets the island name
getIslands().getIsland(getWorld(), playerUUID).setName(null);
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Resets the island name
Objects.requireNonNull(getIslands().getIsland(getWorld(), user)).setName(null);
user.sendMessage("commands.island.resetname.success");
return true;
}

View File

@ -1,14 +1,18 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.List;
import java.util.Objects;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
public class IslandSethomeCommand extends ConfirmableCommand {
@ -23,6 +27,8 @@ public class IslandSethomeCommand extends ConfirmableCommand {
setPermission("island.sethome");
setOnlyPlayer(true);
setDescription("commands.island.sethome.description");
setConfigurableRankCommand();
setDefaultCommandRank(RanksManager.MEMBER_RANK);
}
@Override
@ -37,12 +43,22 @@ public class IslandSethomeCommand extends ConfirmableCommand {
user.sendMessage("commands.island.sethome.must-be-on-your-island");
return false;
}
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
// Check number of homes
int maxHomes = getIslands().getMaxHomes(island);
if (getIslands().getNumberOfHomesIfAdded(island, String.join(" ", args)) > maxHomes) {
user.sendMessage("commands.island.sethome.too-many-homes", TextVariables.NUMBER, String.valueOf(island.getMaxHomes()));
user.sendMessage("commands.island.sethome.too-many-homes", TextVariables.NUMBER, String.valueOf(maxHomes));
user.sendMessage("commands.island.sethome.homes-are");
island.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("home-list-syntax", TextVariables.NAME, s));
getIslands().getIslands(getWorld(), user).forEach(is ->
is.getHomes().keySet().stream().filter(s -> !s.isEmpty()).forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
return false;
}
return true;
@ -51,29 +67,30 @@ public class IslandSethomeCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
String number = String.join(" ", args);
WorldSettings ws = getIWM().getWorldSettings(user.getWorld());
// Check if the player is in the Nether
if (getIWM().isNether(user.getWorld())) {
// Check if he is (not) allowed to set his home here
if (!getIWM().getWorldSettings(user.getWorld()).isAllowSetHomeInNether()) {
if (!ws.isAllowSetHomeInNether()) {
user.sendMessage("commands.island.sethome.nether.not-allowed");
return false;
}
// Check if a confirmation is required
if (getIWM().getWorldSettings(user.getWorld()).isRequireConfirmationToSetHomeInNether()) {
if (ws.isRequireConfirmationToSetHomeInNether()) {
askConfirmation(user, user.getTranslation("commands.island.sethome.nether.confirmation"), () -> doSetHome(user, number));
} else {
doSetHome(user, number);
}
} else if (getIWM().isEnd(user.getWorld())) { // Check if the player is in the End
// Check if he is (not) allowed to set his home here
if (!getIWM().getWorldSettings(user.getWorld()).isAllowSetHomeInTheEnd()) {
if (!ws.isAllowSetHomeInTheEnd()) {
user.sendMessage("commands.island.sethome.the-end.not-allowed");
return false;
}
// Check if a confirmation is required
if (getIWM().getWorldSettings(user.getWorld()).isRequireConfirmationToSetHomeInTheEnd()) {
if (ws.isRequireConfirmationToSetHomeInTheEnd()) {
askConfirmation(user, user.getTranslation("commands.island.sethome.the-end.confirmation"), () -> doSetHome(user, number));
} else {
doSetHome(user, number);

View File

@ -1,13 +1,17 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.List;
import java.util.UUID;
import java.util.Objects;
import org.bukkit.ChatColor;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
/**
* @author tastybento
@ -25,24 +29,31 @@ public class IslandSetnameCommand extends CompositeCommand {
setOnlyPlayer(true);
setParametersHelp("commands.island.setname.parameters");
setDescription("commands.island.setname.description");
setConfigurableRankCommand();
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args)
{
// Explain command
if (args.isEmpty()) {
showHelp(this, user);
return false;
}
UUID playerUUID = user.getUniqueId();
Island island = getIslands().getIsland(getWorld(), user);
if (!getIslands().hasIsland(getWorld(), playerUUID)) {
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
if (!getIslands().isOwner(getWorld(), playerUUID)) {
user.sendMessage("general.errors.not-owner");
// Check command rank.
int rank = Objects.requireNonNull(island).getRank(user);
if (rank < island.getRankCommand(getUsage())) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
@ -64,15 +75,33 @@ public class IslandSetnameCommand extends CompositeCommand {
name = ChatColor.translateAlternateColorCodes('&', name);
}
// Check if the name doesn't already exist in the gamemode
if (getSettings().isNameUniqueness() && getIslands().nameExists(getWorld(), name)) {
user.sendMessage("commands.island.setname.name-already-exists");
return false;
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Naming the island - join all the arguments with spaces.
String name = String.join(" ", args);
// Apply colors
if (user.hasPermission(getPermissionPrefix() + "island.name.format")) {
name = ChatColor.translateAlternateColorCodes('&', name);
}
// Everything's good!
getIslands().getIsland(getWorld(), playerUUID).setName(name);
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), user));
String previousName = island.getName();
island.setName(name);
user.sendMessage("commands.island.setname.success", TextVariables.NAME, name);
// Fire the IslandNameEvent
new IslandEvent.IslandEventBuilder()
.island(island)
.involvedPlayer(user.getUniqueId())
.reason(IslandEvent.Reason.NAME)
.previousName(previousName)
.admin(false)
.build();
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More