Compare commits

...

262 Commits

Author SHA1 Message Date
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
327 changed files with 28505 additions and 12543 deletions

View File

@ -13,11 +13,11 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
java-version: '21'
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:

View File

@ -112,9 +112,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

178
pom.xml
View File

@ -73,10 +73,10 @@
<postgresql.version>42.2.18</postgresql.version>
<hikaricp.version>5.0.1</hikaricp.version>
<!-- More visible way to change dependency versions -->
<spigot.version>1.20.1-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.20.1-R0.1-SNAPSHOT</paper.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>
@ -88,7 +88,7 @@
<!-- 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.24.1</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>
@ -145,6 +145,10 @@
</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>
@ -153,10 +157,6 @@
<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>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
@ -189,44 +189,28 @@
<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>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<!-- AuthLib. Used for Head Getter. -->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>3.16.29</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.11.1</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>
@ -238,6 +222,56 @@
<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>
@ -280,6 +314,12 @@
<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 -->
<dependency>
<groupId>com.github.TheBusyBiscuit</groupId>
@ -321,6 +361,27 @@
<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>
@ -378,29 +439,23 @@
--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.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/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/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.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>
@ -449,9 +504,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.1-SNAPSHOT</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>
@ -463,9 +519,13 @@
</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>
@ -514,6 +574,8 @@
<!-- 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>

View File

@ -59,7 +59,6 @@ public class BStats {
registerGameModeAddonsChart();
registerHooksChart();
registerPlayersPerServerChart();
registerFlagsDisplayModeChart();
// Single Line charts
registerIslandsCountChart();
@ -171,27 +170,6 @@ public class BStats {
}));
}
/**
* Sends the "flags display mode" of all the online players.
* @since 1.6.0
*/
private void registerFlagsDisplayModeChart() {
metrics.addCustomChart(new AdvancedPie("flagsDisplayMode", () -> {
Map<String, Integer> values = new HashMap<>();
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);
}
});
return values;
}));
}
/**
* Sends the enabled addons (except GameModeAddons) of this server as bar chart.
* @since 1.17.1

View File

@ -1,5 +1,7 @@
package world.bentobox.bentobox;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
@ -22,8 +24,12 @@ 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.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.placeholders.PlaceholderAPIHook;
import world.bentobox.bentobox.listeners.BannedCommands;
@ -31,6 +37,8 @@ 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.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;
@ -68,7 +76,6 @@ public class BentoBox extends JavaPlugin implements Listener {
private AddonsManager addonsManager;
private FlagsManager flagsManager;
private IslandWorldManager islandWorldManager;
private RanksManager ranksManager;
private BlueprintsManager blueprintsManager;
private HooksManager hooksManager;
private PlaceholdersManager placeholdersManager;
@ -136,7 +143,6 @@ public class BentoBox extends JavaPlugin implements Listener {
return;
}
islandsManager = new IslandsManager(this);
ranksManager = new RanksManager();
// Start head getter
headGetter = new HeadGetter(this);
@ -180,8 +186,14 @@ public class BentoBox extends JavaPlugin implements Listener {
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);
@ -203,20 +215,6 @@ public class BentoBox extends JavaPlugin implements Listener {
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();
@ -230,6 +228,12 @@ public class BentoBox extends JavaPlugin implements Listener {
hooksManager.registerHook(new MyWorldsHook());
islandWorldManager.registerWorldsToMultiverse(true);
// 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
@ -251,6 +255,8 @@ public class BentoBox extends JavaPlugin implements Listener {
// 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.");
@ -306,6 +312,10 @@ public class BentoBox extends JavaPlugin implements Listener {
// 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
@ -328,10 +338,14 @@ public class BentoBox extends JavaPlugin implements Listener {
@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);
}
//islandWorldManager.registerWorldsToMultiverse(false);
}*/
}
/**
@ -410,9 +424,11 @@ public class BentoBox extends JavaPlugin implements Listener {
/**
* @return the ranksManager
* @deprecated Just use {@code RanksManager.getInstance()}
*/
@Deprecated(since = "2.0.0", forRemoval = true)
public RanksManager getRanksManager() {
return ranksManager;
return RanksManager.getInstance();
}
/**
@ -446,6 +462,17 @@ public class BentoBox extends JavaPlugin implements Listener {
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,50 +1,35 @@
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;
import world.bentobox.bentobox.api.configuration.StoreAt;
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.")
@ -56,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.")
@ -70,7 +62,7 @@ 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).")
@ -197,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")
@ -287,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.")
@ -402,6 +387,7 @@ public class Settings implements ConfigObject {
/**
* This method returns the useSSL value.
*
* @return the value of useSSL.
* @since 1.12.0
*/
@ -411,6 +397,7 @@ public class Settings implements ConfigObject {
/**
* This method sets the useSSL value.
*
* @param useSSL the useSSL new value.
* @since 1.12.0
*/
@ -630,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;
}
@ -725,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;
}
@ -733,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;
}
@ -757,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]", "_");
}
@ -770,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() {
@ -779,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) {
@ -787,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() {
@ -799,6 +772,7 @@ public class Settings implements ConfigObject {
/**
* Set the MongoDB client connection URI.
*
* @param mongodbConnectionUri connection URI.
* @since 1.14.0
*/
@ -807,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() {
@ -816,106 +793,96 @@ 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()
{
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)
{
public void setPlayerHeadCacheTime(long playerHeadCacheTime) {
this.playerHeadCacheTime = playerHeadCacheTime;
}
/**
* Is use cache server boolean.
*
* @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;
}
@ -933,7 +900,6 @@ public class Settings implements ConfigObject {
this.minPortalSearchRadius = minPortalSearchRadius;
}
/**
* Gets safe spot search vertical range.
*
@ -943,7 +909,6 @@ public class Settings implements ConfigObject {
return safeSpotSearchVerticalRange;
}
/**
* Sets safe spot search vertical range.
*
@ -953,7 +918,6 @@ public class Settings implements ConfigObject {
this.safeSpotSearchVerticalRange = safeSpotSearchVerticalRange;
}
/**
* Is slow deletion boolean.
*
@ -963,7 +927,6 @@ public class Settings implements ConfigObject {
return slowDeletion;
}
/**
* Sets slow deletion.
*
@ -973,69 +936,102 @@ public class Settings implements ConfigObject {
this.slowDeletion = slowDeletion;
}
/**
* Gets maximum pool size.
*
* @return the maximum pool size
*/
public int getMaximumPoolSize()
{
public int getMaximumPoolSize() {
return maximumPoolSize;
}
/**
* Gets safe spot search range.
*
* @return the safe spot search range
*/
public int getSafeSpotSearchRange()
{
public int getSafeSpotSearchRange() {
return safeSpotSearchRange;
}
/**
* Sets maximum pool size.
*
* @param maximumPoolSize the maximum pool size
*/
public void setMaximumPoolSize(int maximumPoolSize)
{
public void setMaximumPoolSize(int maximumPoolSize) {
this.maximumPoolSize = maximumPoolSize;
}
/**
* Gets custom pool properties.
*
* @return the custom pool properties
*/
public Map<String, String> getCustomPoolProperties()
{
public Map<String, String> getCustomPoolProperties() {
return customPoolProperties;
}
/**
* Sets custom pool properties.
*
* @param customPoolProperties the custom pool properties
*/
public void setCustomPoolProperties(Map<String, String> customPoolProperties)
{
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)
{
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;
@ -44,6 +47,8 @@ public abstract class Addon {
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());
}

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;
@ -129,7 +131,10 @@ 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

View File

@ -7,6 +7,8 @@ import org.bukkit.plugin.java.JavaPlugin;
import com.google.common.io.Files;
import world.bentobox.bentobox.BentoBox;
/**
* Provides a shell for addons to become Plugins so that other Plugins
* can tap into their API more easily. Plugin + addon = Pladdon
@ -16,6 +18,8 @@ import com.google.common.io.Files;
public abstract class Pladdon extends JavaPlugin {
private static final String ADDONS_FOLDER = "BentoBox" + File.separator + "addons";
private static final String PAPER_REMAPPED = "plugins" + File.separator + ".paper-remapped" + File.separator
+ "unknown-origin";
/**
* This must return a new instance of the addon. It is called when the Pladdon is loaded.
@ -26,9 +30,10 @@ public abstract class Pladdon extends JavaPlugin {
@Override
public void onLoad() {
String parentFolder = getFile().getParent();
BentoBox.getInstance().logDebug("LOOK HERE: " + parentFolder);
if (parentFolder == null || !parentFolder.endsWith(ADDONS_FOLDER)) {
// Jar is in the wrong place. Let's move it
moveJar();
//moveJar();
}
}

View File

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

View File

@ -26,6 +26,7 @@ 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;
@ -34,6 +35,7 @@ import world.bentobox.bentobox.util.Util;
/**
* BentoBox composite command. Provides an abstract implementation of a command.
*
* @author tastybento
* @author Poslovitch
*/
@ -50,11 +52,11 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* True if the command is only for the console
*
* @since 1.24.0
*/
private boolean onlyConsole = false;
/**
* True if command is a configurable rank
*/
@ -62,22 +64,26 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* 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;
/**
@ -109,8 +115,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
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;
@ -131,8 +138,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* 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) {
@ -164,17 +172,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* 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) {
@ -182,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();
@ -220,7 +232,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
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 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);
}
@ -235,29 +248,25 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
// 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);
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
*/
@ -273,8 +282,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
return false;
}
if (!this.runPermissionCheck(user))
{
if (!this.runPermissionCheck(user)) {
// Error message is displayed by permission check.
return false;
}
@ -284,22 +292,18 @@ 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.
* 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.
* @return {@code true} is user can execute given command, {@code false}
* otherwise.
*/
private boolean runPermissionCheck(User user)
{
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()))
{
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;
}
@ -308,9 +312,9 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
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
*/
@ -339,6 +343,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslands() {
@ -347,6 +352,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the island manager
*
* @return IslandsManager
*/
protected IslandsManager getIslandsManager() {
@ -354,8 +360,8 @@ 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.
* @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;
@ -369,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() {
@ -396,6 +408,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Convenience method to get the player manager
*
* @return PlayersManager
*/
protected PlayersManager getPlayers() {
@ -409,11 +422,13 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Get the island worlds manager
*
* @return island worlds manager
*/
public IslandWorldManager getIWM() {
return plugin.getIWM();
}
/**
* @return Settings object
*/
@ -423,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
*/
@ -446,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)
*/
@ -461,17 +480,6 @@ 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 @NonNull String getUsage() {
return "/" + usage;
@ -479,6 +487,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command has a specific sub command.
*
* @param subCommand - sub command
* @return true if this command has this sub command
*/
@ -488,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() {
@ -495,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)
*/
@ -507,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) {
@ -517,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() {
@ -525,6 +541,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Check if this command is only for consoles.
*
* @return true or false
*/
public boolean isOnlyConsole() {
@ -532,11 +549,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* 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) {
@ -544,11 +564,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* 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.
* 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) {
@ -556,28 +579,33 @@ 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 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}.
@ -589,28 +617,33 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
}
/**
* 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.
*/
@ -618,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;
}
/**
@ -652,7 +688,8 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
@Override
@NonNull
public List<String> tabComplete(final @NonNull CommandSender sender, final @NonNull String alias, final String[] args) {
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
@ -660,16 +697,22 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
|| (command.isOnlyConsole() && sender instanceof Player)) {
return List.of();
}
if (command.getPermission() != null && !command.getPermission().isEmpty() && !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
if (command.getPermission() != null && !command.getPermission().isEmpty()
&& !sender.hasPermission(command.getPermission()) && !sender.isOp()) {
return List.of();
}
// Add any tab completion from the subcommand
List<String> options = new ArrayList<>(command.tabComplete(User.getInstance(sender), alias, new LinkedList<>(Arrays.asList(args))).orElseGet(ArrayList::new));
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.
@ -686,17 +729,19 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Returns a list containing all the labels of the subcommands for the provided
* CompositeCommand excluding any hidden commands
* @param sender the CommandSender
*
* @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) {
List<String> result = new ArrayList<>();
for (CompositeCommand cc: command.getSubCommands().values()) {
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()))) {
if (!cc.isHidden() && !cc.isOnlyConsole()
&& (cc.getPermission().isEmpty() || sender.hasPermission(cc.getPermission()))) {
result.add(cc.getLabel());
}
} else if (!cc.isOnlyPlayer()) {
@ -708,12 +753,14 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* 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);
}
/**
@ -724,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
@ -734,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() {
@ -750,6 +800,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Get the parental addon
*
* @return the addon
*/
@SuppressWarnings("unchecked")
@ -766,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 * 1000L);
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 * 1000L);
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
*/
@ -797,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
*/
@ -807,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
@ -817,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<>());
@ -833,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;
}
@ -874,6 +935,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
/**
* Checks if a command is hidden
*
* @return the hidden
* @since 1.13.0
*/
@ -883,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

@ -5,8 +5,6 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.util.Vector;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -41,14 +39,14 @@ public class AdminDeleteCommand extends ConfirmableCommand {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
UUID owner = getIslands().getOwner(getWorld(), targetUUID);
if (owner == null) {
Island island = getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
// Team members should be kicked before deleting otherwise the whole team will become weird
if (getIslands().inTeam(getWorld(), targetUUID) && owner.equals(targetUUID)) {
if (island.hasTeam() && user.getUniqueId().equals(island.getOwner())) {
user.sendMessage("commands.admin.delete.cannot-delete-owner");
return false;
}
@ -66,10 +64,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
private void deletePlayer(User user, UUID targetUUID) {
// Delete player and island
// Get the target's island
Island oldIsland = getIslands().getIsland(getWorld(), targetUUID);
Vector vector = null;
if (oldIsland != null) {
for (Island oldIsland : getIslands().getIslands(getWorld(), targetUUID)) {
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
@ -78,21 +73,17 @@ public class AdminDeleteCommand extends ConfirmableCommand {
.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.isPlayer() && target.isOnline()) {
cleanUp(target);
}
vector = oldIsland.getCenter().toVector();
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(oldIsland.getCenter().toVector()));
getIslands().deleteIsland(oldIsland, true, targetUUID);
}
if (vector == null) {
user.sendMessage("general.success");
} else {
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(vector));
// 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.isPlayer() && target.isOnline()) {
cleanUp(target);
}
user.sendMessage("general.success");
}
private void cleanUp(User target) {
@ -118,6 +109,10 @@ public class AdminDeleteCommand extends ConfirmableCommand {
// 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);
}

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

@ -16,6 +16,7 @@ import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* Tells the rank of the player
* @author tastybento
*
*/
@ -82,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;
}

View File

@ -49,9 +49,6 @@ public class AdminInfoCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), targetUUID);
if (island != null) {
new IslandInfo(island).showAdminInfo(user, getAddon());
if (!getIslands().getQuarantinedIslandByUser(getWorld(), targetUUID).isEmpty()) {
user.sendMessage("commands.admin.info.islands-in-trash");
}
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

@ -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

@ -24,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");
@ -36,7 +35,6 @@ public class AdminSetrankCommand extends CompositeCommand {
setOnlyPlayer(false);
setParametersHelp("commands.admin.setrank.parameters");
setDescription("commands.admin.setrank.description");
rm = getPlugin().getRanksManager();
}
@Override
@ -53,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) {
@ -121,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;
}
@ -136,7 +134,7 @@ 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())).toList());

View File

@ -65,8 +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));
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

@ -16,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;
@ -175,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
@ -206,11 +205,9 @@ public class AdminSettingsCommand extends CompositeCommand {
switch (f.getType()) {
case PROTECTION -> {
island.setFlag(f, rank);
getIslands().save(island);
}
case SETTING -> {
island.setSettingsFlag(f, activeState);
getIslands().save(island);
}
case WORLD_SETTING -> f.setSetting(getWorld(), activeState);
default -> {
@ -226,12 +223,11 @@ public class AdminSettingsCommand extends CompositeCommand {
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)
@ -242,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();
@ -277,8 +273,7 @@ public class AdminSettingsCommand extends CompositeCommand {
} 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 -> getPlugin().getRanksManager()
.getRanks().entrySet().stream()
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();

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 {
int n = Integer.parseInt(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

@ -118,12 +118,10 @@ public class AdminTeleportCommand extends CompositeCommand {
private Location getSpot(World world) {
Island island = getIslands().getIsland(world, targetUUID);
if (island != null && island.getSpawnPoint(world.getEnvironment()) != null) {
// Return the defined spawn point
return island.getSpawnPoint(world.getEnvironment());
if (island == null) {
return null;
}
// Return the default island protection center
return island.getProtectionCenter().toVector().toLocation(world);
return island.getSpawnPoint(world.getEnvironment()) != null ? island.getSpawnPoint(world.getEnvironment()) : island.getProtectionCenter().toVector().toLocation(world);
}
@Override

View File

@ -1,73 +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;
import world.bentobox.bentobox.util.IslandInfo;
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));
new IslandInfo(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,43 +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));
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
@ -89,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

@ -12,7 +12,6 @@ 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;
@ -32,6 +31,7 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
*/
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(" "));
@ -62,7 +62,6 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminTeamKickCommand(this);
new AdminTeamDisbandCommand(this);
new AdminTeamSetownerCommand(this);
new AdminTeamFixCommand(this);
// Blueprints
new AdminBlueprintCommand(this);
// Register/unregister islands

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) {
@ -99,26 +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);
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

@ -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

@ -40,7 +40,6 @@ public class NamePrompt extends StringPrompt {
@Override
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

@ -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 {
@ -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

@ -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,23 +1,18 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.List;
import java.util.Objects;
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");
@ -32,53 +27,28 @@ public class AdminRangeAddCommand extends CompositeCommand {
@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 = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
int newRange = island.getProtectionRange() + Integer.parseInt(args.get(1));
if (newRange > island.getRange()) {
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),
@ -86,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

@ -13,6 +13,7 @@ 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
@ -23,6 +24,9 @@ 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 final Map<User, Integer> displayRanges = new HashMap<>();
@ -76,11 +80,11 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
// 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));
}

View File

@ -1,23 +1,19 @@
package world.bentobox.bentobox.api.commands.admin.range;
import java.util.List;
import java.util.Objects;
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");
@ -32,48 +28,31 @@ public class AdminRangeRemoveCommand extends CompositeCommand {
@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 = Objects.requireNonNull(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

@ -1,19 +1,14 @@
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;
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");
@ -28,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));
@ -52,33 +30,30 @@ public class AdminRangeSetCommand extends CompositeCommand {
}
int range = Integer.parseInt(args.get(1));
// Get island
Island island = Objects.requireNonNull(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)
@ -90,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

@ -44,19 +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));
Island island = getIslands().getIsland(getWorld(), ownerUUID);
if (island != null) {
new IslandInfo(island).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;
}
@ -67,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

@ -32,7 +32,7 @@ public class AdminTeamCommand extends CompositeCommand
new AdminTeamAddCommand(this);
new AdminTeamDisbandCommand(this);
new AdminTeamFixCommand(this);
new AdminTeamKickCommand(this);
new AdminTeamSetownerCommand(this);
}

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,6 +21,13 @@ 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");
}
@ -26,54 +40,91 @@ public class AdminTeamDisbandCommand extends CompositeCommand {
}
@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.fix");
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

@ -71,7 +71,7 @@ public class AdminTeamKickCommand extends CompositeCommand {
User target = User.getInstance(targetUUID);
target.sendMessage("commands.admin.team.kick.admin-kicked");
getIslands().removePlayer(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

@ -2,9 +2,16 @@ 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;
@ -15,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");
@ -28,70 +40,92 @@ public class AdminTeamSetownerCommand extends CompositeCommand {
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;
}
return true;
}
// Get the User corresponding to the current owner
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
Island island = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID));
// Call the setowner event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID)
.admin(true)
.build();
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();
// 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));
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();
IslandEvent.builder().island(island).involvedPlayer(previousOwnerUUID).admin(true)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(previousOwnerUUID)).build();
}
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> 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(" "));

View File

@ -17,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 {
@ -45,7 +46,8 @@ 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;
}
@ -53,7 +55,8 @@ public class IslandBanCommand extends CompositeCommand {
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
@ -67,7 +70,7 @@ 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;
}
@ -97,25 +100,26 @@ public class IslandBanCommand extends CompositeCommand {
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(), 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())) {
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);
}
@ -130,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();
@ -139,8 +143,7 @@ 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))
.filter(p -> !island.isBanned(p.getUniqueId())).filter(p -> user.getPlayer().canSee(p))
.map(Player::getName).toList();
return Optional.of(Util.tabLimit(options, lastArg));
} else {

View File

@ -8,6 +8,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.managers.RanksManager;
public class IslandBanlistCommand extends CompositeCommand {
@ -40,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;

View File

@ -5,16 +5,16 @@ 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.
*
@ -24,6 +24,7 @@ public class IslandCreateCommand extends CompositeCommand {
/**
* Command to create an island
*
* @param islandCommand - parent command
*/
public IslandCreateCommand(CompositeCommand islandCommand) {
@ -42,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
@ -71,38 +87,61 @@ public class IslandCreateCommand extends CompositeCommand {
user.sendMessage("commands.island.create.unknown-blueprint");
return false;
}
// 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,12 +1,12 @@
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;
@ -22,8 +22,6 @@ import world.bentobox.bentobox.util.Util;
*/
public class IslandDeletehomeCommand extends ConfirmableCommand {
private @Nullable Island island;
/**
* Deletes a home
* @param islandCommand parent command
@ -48,7 +46,7 @@ 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");
@ -59,23 +57,25 @@ public class IslandDeletehomeCommand extends ConfirmableCommand {
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)));
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
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));
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;
}
@ -88,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 is = getIslands().getIsland(getWorld(), user.getUniqueId());
if (is != null) {
return Optional.of(Util.tabLimit(new ArrayList<>(is.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

@ -16,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;
/**
@ -59,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
@ -74,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;
}
@ -90,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(), TextVariables.DISPLAY_NAME, user.getDisplayName());
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(), TextVariables.DISPLAY_NAME, target.getDisplayName());
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(), TextVariables.DISPLAY_NAME, target.getDisplayName());
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(), TextVariables.DISPLAY_NAME, target.getDisplayName());
user.sendMessage(SUCCESS, TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME,
target.getDisplayName());
return true;
}
@ -149,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).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;
@ -38,47 +40,92 @@ public class IslandGoCommand extends DelayedTeleportCommand {
user.sendMessage("commands.island.go.teleport");
return false;
}
// Check if the island is reserved
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {
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() && !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

@ -1,18 +1,19 @@
package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
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 @Nullable Island island;
private List<Island> islands;
public IslandHomesCommand(CompositeCommand islandCommand) {
super(islandCommand, "homes");
@ -27,9 +28,9 @@ public class IslandHomesCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
island = getIslands().getIsland(getWorld(), user);
islands = getIslands().getIslands(getWorld(), user);
// Check island
if (island == null || island.getOwner() == null) {
if (islands.isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
@ -39,9 +40,21 @@ public class IslandHomesCommand extends ConfirmableCommand {
@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));
.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

@ -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

@ -65,7 +65,8 @@ public class IslandRenamehomeCommand extends ConfirmableCommand {
// 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(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -16,10 +16,9 @@ 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
*/
@ -33,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");
@ -93,7 +93,8 @@ public class IslandResetCommand extends ConfirmableCommand {
} 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);
}
@ -103,13 +104,14 @@ 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);
}
@ -117,6 +119,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Reset island
*
* @param user user
* @param name name of Blueprint Bundle
* @return true if successful
@ -124,19 +127,15 @@ public class IslandResetCommand extends ConfirmableCommand {
private boolean resetIsland(User user, String name) {
// Get the player's old island
Island oldIsland = getIslands().getIsland(getWorld(), user);
if (oldIsland != null) {
deleteOldIsland(user, oldIsland);
}
deleteOldIsland(user, oldIsland);
user.sendMessage("commands.island.create.creating-island");
// 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());
@ -149,13 +148,8 @@ public class IslandResetCommand extends ConfirmableCommand {
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();
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
// Reset the island
@ -168,33 +162,33 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* 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, 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

@ -7,6 +7,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.managers.RanksManager;
/**
@ -41,7 +42,7 @@ public class IslandResetnameCommand extends CompositeCommand {
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)));
TextVariables.RANK, user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -46,16 +46,19 @@ public class IslandSethomeCommand extends ConfirmableCommand {
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;
}
// 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(maxHomes));
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));
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;

View File

@ -10,6 +10,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;
/**
@ -51,7 +52,8 @@ public class IslandSetnameCommand extends CompositeCommand {
// 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(getPlugin().getRanksManager().getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}

View File

@ -47,9 +47,10 @@ public class IslandSettingsCommand extends CompositeCommand {
public boolean execute(User user, String label, List<String> args) {
new TabbedPanelBuilder()
.user(user)
.island(island)
.world(island.getWorld())
.tab(1, new SettingsTab(user, island, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(user, island, Flag.Type.SETTING))
.tab(1, new SettingsTab(getWorld(), user, Flag.Type.PROTECTION))
.tab(2, new SettingsTab(getWorld(), user, Flag.Type.SETTING))
.startingSlot(1)
.size(54)
.hideIfEmpty()

View File

@ -13,6 +13,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;
/**
@ -54,7 +55,8 @@ public class IslandUnbanCommand 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

View File

@ -1,83 +0,0 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.util.Objects;
import java.util.UUID;
/**
* Represents an invite
* @author tastybento
* @since 1.8.0
*/
public class Invite {
/**
* Type of invitation
*
*/
public enum Type {
COOP,
TEAM,
TRUST
}
private final Type type;
private final UUID inviter;
private final UUID invitee;
/**
* @param type - invitation type, e.g., coop, team, trust
* @param inviter - UUID of inviter
* @param invitee - UUID of invitee
*/
public Invite(Type type, UUID inviter, UUID invitee) {
this.type = type;
this.inviter = inviter;
this.invitee = invitee;
}
/**
* @return the type
*/
public Type getType() {
return type;
}
/**
* @return the inviter
*/
public UUID getInviter() {
return inviter;
}
/**
* @return the invitee
*/
public UUID getInvitee() {
return invitee;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(invitee, inviter, type);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Invite other)) {
return false;
}
return Objects.equals(invitee, other.invitee) && Objects.equals(inviter, other.inviter) && type == other.type;
}
}

View File

@ -1,15 +1,10 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@ -17,22 +12,44 @@ import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.TeamInvite;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandTeamCommand extends CompositeCommand {
/**
* Invited list. Key is the invited party, value is the invite.
* @since 1.8.0
*/
private final Map<UUID, Invite> inviteMap;
private IslandTeamKickCommand kickCommand;
private IslandTeamLeaveCommand leaveCommand;
private IslandTeamSetownerCommand setOwnerCommand;
private IslandTeamUncoopCommand uncoopCommand;
private IslandTeamUntrustCommand unTrustCommand;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
private IslandTeamInviteAcceptCommand acceptCommand;
private IslandTeamInviteRejectCommand rejectCommand;
private IslandTeamInviteCommand inviteCommand;
private IslandTeamCoopCommand coopCommand;
private IslandTeamTrustCommand trustCommand;
private final Database<TeamInvite> handler;
public IslandTeamCommand(CompositeCommand parent) {
super(parent, "team");
inviteMap = new HashMap<>();
handler = new Database<>(parent.getAddon(), TeamInvite.class);
}
@Override
@ -41,143 +58,74 @@ public class IslandTeamCommand extends CompositeCommand {
setOnlyPlayer(true);
setDescription("commands.island.team.description");
// Register commands
new IslandTeamInviteCommand(this);
new IslandTeamLeaveCommand(this);
new IslandTeamSetownerCommand(this);
new IslandTeamKickCommand(this);
new IslandTeamInviteAcceptCommand(this);
new IslandTeamInviteRejectCommand(this);
new IslandTeamCoopCommand(this);
new IslandTeamUncoopCommand(this);
new IslandTeamTrustCommand(this);
new IslandTeamUntrustCommand(this);
inviteCommand = new IslandTeamInviteCommand(this);
leaveCommand = new IslandTeamLeaveCommand(this);
setOwnerCommand = new IslandTeamSetownerCommand(this);
kickCommand = new IslandTeamKickCommand(this);
acceptCommand = new IslandTeamInviteAcceptCommand(this);
rejectCommand = new IslandTeamInviteRejectCommand(this);
if (RanksManager.getInstance().rankExists(RanksManager.COOP_RANK_REF)) {
coopCommand = new IslandTeamCoopCommand(this);
uncoopCommand = new IslandTeamUncoopCommand(this);
}
if (RanksManager.getInstance().rankExists(RanksManager.TRUSTED_RANK_REF)) {
trustCommand = new IslandTeamTrustCommand(this);
unTrustCommand = new IslandTeamUntrustCommand(this);
}
new IslandTeamPromoteCommand(this, "promote");
new IslandTeamPromoteCommand(this, "demote");
// Panels
if (!new File(getPlugin().getDataFolder() + File.separator + "panels", "team_panel.yml").exists()) {
getPlugin().saveResource("panels/team_panel.yml", false);
}
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
// Player issuing the command must have an island
UUID ownerUUID = getOwner(getWorld(), user);
if (ownerUUID == null) {
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (island == null) {
if (isInvited(user.getUniqueId())) {
// Player has an invite, so show the invite
new IslandTeamGUI(getPlugin(), this, user, island).build();
return true;
}
user.sendMessage("general.errors.no-island");
return false;
}
UUID playerUUID = user.getUniqueId();
// Fire event so add-ons can run commands, etc.
if (fireEvent(user)) {
if (fireEvent(user, island)) {
// Cancelled
return false;
}
Island island = getIslands().getIsland(getWorld(), playerUUID);
if (island == null) {
return false;
}
Set<UUID> teamMembers = getMembers(getWorld(), user);
if (ownerUUID.equals(playerUUID)) {
if (playerUUID.equals(island.getOwner())) {
int maxSize = getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK);
if (teamMembers.size() < maxSize) {
user.sendMessage("commands.island.team.invite.you-can-invite", TextVariables.NUMBER, String.valueOf(maxSize - teamMembers.size()));
user.sendMessage("commands.island.team.invite.you-can-invite", TextVariables.NUMBER,
String.valueOf(maxSize - teamMembers.size()));
} else {
user.sendMessage("commands.island.team.invite.errors.island-is-full");
}
}
// Show members of island
showMembers(island, user);
return true;
}
private void showMembers(Island island, User user) {
// Gather online members
long count = island
.getMemberSet(RanksManager.MEMBER_RANK)
.stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
.count();
// List of ranks that we will loop through
Integer[] ranks = new Integer[]{RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK, RanksManager.MEMBER_RANK, RanksManager.TRUSTED_RANK, RanksManager.COOP_RANK};
// Show header:
user.sendMessage("commands.island.team.info.header",
"[max]", String.valueOf(getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)),
"[total]", String.valueOf(island.getMemberSet().size()),
"[online]", String.valueOf(count));
// We now need to get all online "members" of the island - incl. Trusted and coop
List<UUID> onlineMembers = island.getMemberSet(RanksManager.COOP_RANK).stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName())).toList();
for (int rank : ranks) {
Set<UUID> players = island.getMemberSet(rank, false);
if (!players.isEmpty()) {
if (rank == RanksManager.OWNER_RANK) {
// Slightly special handling for the owner rank
user.sendMessage("commands.island.team.info.rank-layout.owner",
TextVariables.RANK, user.getTranslation(RanksManager.OWNER_RANK_REF));
} else {
user.sendMessage("commands.island.team.info.rank-layout.generic",
TextVariables.RANK, user.getTranslation(getPlugin().getRanksManager().getRank(rank)),
TextVariables.NUMBER, String.valueOf(island.getMemberSet(rank, false).size()));
}
displayOnOffline(user, rank, island, onlineMembers);
}
}
}
private void displayOnOffline(User user, int rank, Island island, List<UUID> onlineMembers) {
for (UUID member : island.getMemberSet(rank, false)) {
OfflinePlayer offlineMember = Bukkit.getOfflinePlayer(member);
if (onlineMembers.contains(member)) {
// the player is online
user.sendMessage("commands.island.team.info.member-layout.online",
TextVariables.NAME, offlineMember.getName());
} else {
// A bit of handling for the last joined date
Instant lastJoined = Instant.ofEpochMilli(offlineMember.getLastPlayed());
Instant now = Instant.now();
Duration duration = Duration.between(lastJoined, now);
String lastSeen;
final String reference = "commands.island.team.info.last-seen.layout";
if (duration.toMinutes() < 60L) {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toMinutes()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.minutes"));
} else if (duration.toHours() < 24L) {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toHours()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.hours"));
} else {
lastSeen = user.getTranslation(reference,
TextVariables.NUMBER, String.valueOf(duration.toDays()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.days"));
}
if(island.getMemberSet(RanksManager.MEMBER_RANK, true).contains(member)) {
user.sendMessage("commands.island.team.info.member-layout.offline",
TextVariables.NAME, offlineMember.getName(),
"[last_seen]", lastSeen);
} else {
// This will prevent anyone that is trusted or below to not have a last-seen status
user.sendMessage("commands.island.team.info.member-layout.offline-not-last-seen",
TextVariables.NAME, offlineMember.getName());
}
}
}
}
private boolean fireEvent(User user) {
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands()
.getIsland(getWorld(), user.getUniqueId()))
.reason(TeamEvent.Reason.INFO)
.involvedPlayer(user.getUniqueId())
@Override
public boolean execute(User user, String label, List<String> args) {
// Show the panel
new IslandTeamGUI(getPlugin(), this, user, getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()))
.build();
return e.getNewEvent().map(IslandBaseEvent::isCancelled)
.orElse(e.isCancelled());
return true;
}
private boolean fireEvent(User user, Island island) {
IslandBaseEvent e = TeamEvent.builder().island(island).reason(TeamEvent.Reason.INFO)
.involvedPlayer(user.getUniqueId()).build();
return e.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(e.isCancelled());
}
/**
@ -187,8 +135,8 @@ public class IslandTeamCommand extends CompositeCommand {
* @param invitee - uuid of invitee
* @since 1.8.0
*/
public void addInvite(Invite.Type type, @NonNull UUID inviter, @NonNull UUID invitee) {
inviteMap.put(invitee, new Invite(type, inviter, invitee));
public void addInvite(TeamInvite.Type type, @NonNull UUID inviter, @NonNull UUID invitee, @NonNull Island island) {
handler.saveObjectAsync(new TeamInvite(type, inviter, invitee, island.getUniqueId()));
}
/**
@ -198,7 +146,7 @@ public class IslandTeamCommand extends CompositeCommand {
* @since 1.8.0
*/
public boolean isInvited(@NonNull UUID invitee) {
return inviteMap.containsKey(invitee);
return handler.objectExists(invitee.toString());
}
/**
@ -209,7 +157,7 @@ public class IslandTeamCommand extends CompositeCommand {
*/
@Nullable
public UUID getInviter(UUID invitee) {
return isInvited(invitee) ? inviteMap.get(invitee).getInviter() : null;
return isInvited(invitee) ? handler.loadObject(invitee.toString()).getInviter() : null;
}
/**
@ -219,8 +167,8 @@ public class IslandTeamCommand extends CompositeCommand {
* @since 1.8.0
*/
@Nullable
public Invite getInvite(UUID invitee) {
return inviteMap.get(invitee);
public TeamInvite getInvite(UUID invitee) {
return handler.loadObject(invitee.toString());
}
/**
@ -229,6 +177,68 @@ public class IslandTeamCommand extends CompositeCommand {
* @since 1.8.0
*/
public void removeInvite(@NonNull UUID invitee) {
inviteMap.remove(invitee);
handler.deleteID(invitee.toString());
}
/**
* @return the coopCommand
*/
protected IslandTeamCoopCommand getCoopCommand() {
return coopCommand;
}
/**
* @return the trustCommand
*/
protected IslandTeamTrustCommand getTrustCommand() {
return trustCommand;
}
public IslandTeamInviteCommand getInviteCommand() {
return inviteCommand;
}
public IslandTeamInviteAcceptCommand getAcceptCommand() {
return acceptCommand;
}
public IslandTeamInviteRejectCommand getRejectCommand() {
return rejectCommand;
}
/**
* @return the kickCommand
*/
public IslandTeamKickCommand getKickCommand() {
return kickCommand;
}
/**
* @return the leaveCommand
*/
public IslandTeamLeaveCommand getLeaveCommand() {
return leaveCommand;
}
/**
* @return the setOwnerCommand
*/
public IslandTeamSetownerCommand getSetOwnerCommand() {
return setOwnerCommand;
}
/**
* @return the uncoopCommand
*/
public IslandTeamUncoopCommand getUncoopCommand() {
return uncoopCommand;
}
/**
* @return the unTrustCommand
*/
public IslandTeamUntrustCommand getUnTrustCommand() {
return unTrustCommand;
}
}

View File

@ -8,15 +8,16 @@ import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
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.database.objects.TeamInvite.Type;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* Command to coop another player
*
* @author tastybento
*
*/
@ -47,7 +48,8 @@ public class IslandTeamCoopCommand extends CompositeCommand {
return false;
}
// 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;
}
@ -55,7 +57,8 @@ public class IslandTeamCoopCommand 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
@ -73,11 +76,13 @@ public class IslandTeamCoopCommand extends CompositeCommand {
user.sendMessage("commands.island.team.coop.cannot-coop-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId(), RanksManager.COOP_RANK).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet(RanksManager.COOP_RANK)
.contains(targetUUID)) {
user.sendMessage("commands.island.team.coop.already-has-rank");
return false;
}
if (itc.isInvited(targetUUID) && itc.getInviter(targetUUID).equals(user.getUniqueId()) && itc.getInvite(targetUUID).getType().equals(Type.COOP)) {
if (itc.isInvited(targetUUID) && user.getUniqueId().equals(itc.getInviter(targetUUID))
&& itc.getInvite(targetUUID) != null && itc.getInvite(targetUUID).getType().equals(Type.COOP)) {
// Prevent spam
user.sendMessage("commands.island.team.invite.errors.you-have-already-invited");
return false;
@ -92,21 +97,27 @@ public class IslandTeamCoopCommand extends CompositeCommand {
if (island != null) {
if (getPlugin().getSettings().isInviteConfirmation()) {
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Invite.Type.COOP, user.getUniqueId(), target.getUniqueId());
// If someone else has invited a player, then this invite will overwrite the
// previous invite!
itc.addInvite(Type.COOP, user.getUniqueId(), target.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, target.getName());
// Send message to online player
target.sendMessage("commands.island.team.coop.name-has-invited-you", TextVariables.NAME, user.getName());
target.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel());
target.sendMessage("commands.island.team.coop.name-has-invited-you", TextVariables.NAME,
user.getName());
target.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL,
getTopLabel());
} else {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() >= getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() >= getIslands().getMaxMembers(island,
RanksManager.COOP_RANK)) {
user.sendMessage("commands.island.team.coop.is-full");
return false;
}
island.setRank(target, RanksManager.COOP_RANK);
user.sendMessage("commands.island.team.coop.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
user.sendMessage("commands.island.team.coop.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
return true;
} else {
@ -118,7 +129,7 @@ public class IslandTeamCoopCommand 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();

View File

@ -0,0 +1,568 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Sound;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord.ActionRecords;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.TeamInvite;
import world.bentobox.bentobox.database.objects.TeamInvite.Type;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandTeamGUI {
/**
* List of ranks that we will loop through in order
*/
private static final List<Integer> RANKS = List.of(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK,
RanksManager.MEMBER_RANK, RanksManager.TRUSTED_RANK, RanksManager.COOP_RANK);
private static final String NAME = ".name";
private static final String TIPS = "commands.island.team.gui.tips.";
private final User user;
private final Island island;
private int rankView = RanksManager.OWNER_RANK;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
private final IslandTeamCommand parent;
private final BentoBox plugin;
/**
* Displays the team management GUI
* @param plugin BentoBox
* @param parent IslandTeamCommand object
* @param user user who is opening the GUI
* @param island island that the GUI is managing
*/
public IslandTeamGUI(BentoBox plugin, IslandTeamCommand parent, User user, Island island) {
this.parent = parent;
this.plugin = plugin;
this.user = user;
this.island = island;
// Panels
if (!new File(plugin.getDataFolder() + File.separator + "panels", "team_panel.yml").exists()) {
plugin.saveResource("panels/team_panel.yml", false);
}
}
/**
* This method builds this GUI.
*/
public void build() {
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
panelBuilder.user(user);
panelBuilder.world(user.getWorld());
panelBuilder.template("team_panel", new File(plugin.getDataFolder(), "panels"));
panelBuilder.parameters("[name]", user.getName(), "[display_name]", user.getDisplayName());
panelBuilder.registerTypeBuilder("STATUS", this::createStatusButton);
panelBuilder.registerTypeBuilder("MEMBER", this::createMemberButton);
panelBuilder.registerTypeBuilder("INVITED", this::createInvitedButton);
panelBuilder.registerTypeBuilder("RANK", this::createRankButton);
panelBuilder.registerTypeBuilder("INVITE", this::createInviteButton);
border = panelBuilder.getPanelTemplate().border();
background = panelBuilder.getPanelTemplate().background();
// Register unknown type builder.
panelBuilder.build();
}
private PanelItem createInviteButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
if (island == null || !user.hasPermission(this.parent.getInviteCommand().getPermission())
|| island.getRank(user) < island.getRankCommand(parent.getLabel() + " invite")) {
return this.getBlankBorder();
}
PanelItemBuilder builder = new PanelItemBuilder();
builder.icon(Material.PLAYER_HEAD);
builder.name(user.getTranslation("commands.island.team.gui.buttons.invite.name"));
builder.description(user.getTranslation("commands.island.team.gui.buttons.invite.description"));
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (template.actions().stream().noneMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
user.closeInventory();
new IslandTeamInviteGUI(parent, false, island).build(user);
}
return true;
});
return builder.build();
}
private PanelItem createRankButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// If there is no island, the do not show this icon
if (island == null) {
return this.getBlankBorder();
}
PanelItemBuilder builder = new PanelItemBuilder();
builder.name(user.getTranslation("commands.island.team.gui.buttons.rank-filter.name"));
builder.icon(Material.AMETHYST_SHARD);
// Create description
createDescription(builder);
createClickHandler(builder, template.actions());
return builder.build();
}
private void createClickHandler(PanelItemBuilder builder, @NonNull List<ActionRecords> actions) {
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (actions.stream().noneMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
rankView = RanksManager.getInstance().getRankDownValue(rankView);
if (rankView <= RanksManager.VISITOR_RANK) {
rankView = RanksManager.OWNER_RANK;
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
} else {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
}
}
if (clickType.equals(ClickType.RIGHT)) {
rankView = RanksManager.getInstance().getRankUpValue(rankView);
if (rankView >= RanksManager.OWNER_RANK) {
rankView = RanksManager.getInstance().getRankUpValue(RanksManager.VISITOR_RANK);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
} else {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
}
}
// Update panel after click
build();
return true;
});
}
private void createDescription(PanelItemBuilder builder) {
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
if (rankView == RanksManager.OWNER_RANK && score > RanksManager.VISITOR_RANK
&& score <= RanksManager.OWNER_RANK) {
builder.description(user.getTranslation("protection.panel.flag-item.allowed-rank")
+ user.getTranslation(reference));
} else if (score > RanksManager.VISITOR_RANK && score < rankView) {
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
+ user.getTranslation(reference));
} else if (score <= RanksManager.OWNER_RANK && score > rankView) {
builder.description(user.getTranslation("protection.panel.flag-item.blocked-rank")
+ user.getTranslation(reference));
} else if (score == rankView) {
builder.description(user.getTranslation("protection.panel.flag-item.allowed-rank")
+ user.getTranslation(reference));
}
});
builder.description(user.getTranslation("commands.island.team.gui.buttons.rank-filter.description"));
}
/**
* Create invited button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createInvitedButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
PanelItemBuilder builder = new PanelItemBuilder();
if (parent.isInvited(user.getUniqueId()) && user.hasPermission(parent.getAcceptCommand().getPermission())) {
TeamInvite invite = parent.getInvite(user.getUniqueId());
if (invite == null) {
return this.getBlankBorder();
}
User inviter = User.getInstance(invite.getInviter());
String name = inviter.getName();
builder.icon(inviter.getName());
builder.name(user.getTranslation("commands.island.team.gui.buttons.invitation"));
createInviteDescription(builder, invite.getType(), name, template.actions());
createInviteClickHandler(builder, invite, template.actions());
} else {
return this.getBlankBorder();
}
return builder.build();
}
private void createInviteClickHandler(PanelItemBuilder builder, TeamInvite invite,
@NonNull List<ActionRecords> list) {
Type type = invite.getType();
builder.clickHandler((panel, user, clickType, clickSlot) -> {
if (list.stream().noneMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.SHIFT_LEFT)
&& user.hasPermission(parent.getAcceptCommand().getPermission())) {
plugin.log("Invite accepted: " + user.getName() + " accepted " + type);
// Accept
switch (type) {
case COOP -> parent.getAcceptCommand().acceptCoopInvite(user, invite);
case TRUST -> parent.getAcceptCommand().acceptTrustInvite(user, invite);
default -> parent.getAcceptCommand().acceptTeamInvite(user, invite);
}
user.closeInventory();
}
if (clickType.equals(ClickType.SHIFT_RIGHT)
&& user.hasPermission(parent.getRejectCommand().getPermission())) {
// Reject
plugin.log("Invite rejected: " + user.getName() + " rejected " + type + " invite.");
parent.getRejectCommand().execute(user, "", List.of());
user.closeInventory();
}
return true;
});
}
private void createInviteDescription(PanelItemBuilder builder, Type type, String name,
@NonNull List<ActionRecords> list) {
builder.description(switch (type) {
case COOP -> List.of(
user.getTranslation("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name));
case TRUST -> List.of(user.getTranslation("commands.island.team.invite.name-has-invited-you.trust",
TextVariables.NAME, name));
default ->
List.of(user.getTranslation("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name),
user.getTranslation("commands.island.team.invite.accept.confirmation"));
});
// Add all the tool tips
builder.description(list.stream()
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME) + " "
+ user.getTranslation(ar.tooltip()))
.toList());
}
/**
* Create status button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createStatusButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
PanelItemBuilder builder = new PanelItemBuilder();
// Player issuing the command must have an island
Island is = plugin.getIslands().getPrimaryIsland(parent.getWorld(), user.getUniqueId());
if (is == null) {
return getBlankBorder();
}
return builder.icon(user.getName()).name(user.getTranslation("commands.island.team.gui.buttons.status.name"))
.description(showMembers()).build();
}
private PanelItem getBlankBorder() {
return new PanelItemBuilder().icon(Objects.requireNonNullElse(border.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(border.title(), ""))).build();
}
private PanelItem getBlankBackground() {
return new PanelItemBuilder()
.icon(Objects.requireNonNullElse(background.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(background.title(), ""))).build();
}
/**
* Create member button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createMemberButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// Player issuing the command must have an island
Island is = plugin.getIslands().getPrimaryIsland(parent.getWorld(), user.getUniqueId());
if (is == null) {
return this.getBlankBackground();
}
int minimumRank = RanksManager.getInstance().getRankUpValue(RanksManager.VISITOR_RANK); // Get the rank above Visitor.
Optional<User> opMember = is.getMemberSet(minimumRank).stream().map(User::getInstance)
.filter((User usr) -> rankView == RanksManager.OWNER_RANK || is.getRank(usr) == rankView) // If rankView is owner then show all ranks
.sorted(Comparator.comparingInt((User usr) -> is.getRank(usr)).reversed()) // Show owner on left, then descending ranks
.skip(slot.slot()) // Get the head for this slot
.limit(1L).findFirst(); // Get just one head
if (opMember.isEmpty()) {
return this.getBlankBackground();
}
User member = opMember.get();
int rank = is.getRank(member);
String rankRef = RanksManager.getInstance().getRank(rank);
@NonNull
List<ActionRecords> actions = template.actions();
// Make button description depending on viewer
List<String> desc = new ArrayList<>();
int userRank = Objects.requireNonNull(is).getRank(user);
// Add the tooltip for kicking
if (user.hasPermission(parent.getKickCommand().getPermission())
&& userRank >= is.getRankCommand(parent.getLabel() + " kick") && !user.equals(member)) {
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("kick"))
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME)
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
// Set Owner
if (user.hasPermission(parent.getSetOwnerCommand().getPermission()) && !user.equals(member)
&& userRank >= RanksManager.OWNER_RANK && rank >= RanksManager.MEMBER_RANK) {
// Add the tooltip for setowner
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("setowner"))
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME)
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
// Leave
if (user.hasPermission(parent.getLeaveCommand().getPermission()) && user.equals(member)
&& userRank < RanksManager.OWNER_RANK) {
// Add the tooltip for leave
actions.stream().filter(ar -> ar.actionType().equalsIgnoreCase("leave"))
.map(ar -> user.getTranslation(TIPS + ar.clickType().name() + NAME)
+ " " + user.getTranslation(ar.tooltip()))
.findFirst().ifPresent(desc::add);
}
if (member.isOnline()) {
desc.add(0, user.getTranslation(rankRef));
return new PanelItemBuilder().icon(member.getName()).name(member.getDisplayName()).description(desc)
.clickHandler(
(panel, user, clickType, i) -> clickListener(panel, user, clickType, i, member, actions))
.build();
} else {
// Offline player
desc.add(0, user.getTranslation(rankRef));
return new PanelItemBuilder().icon(member.getName())
.name(offlinePlayerStatus(Bukkit.getOfflinePlayer(member.getUniqueId()))).description(desc)
.clickHandler(
(panel, user, clickType, i) -> clickListener(panel, user, clickType, i, member, actions))
.build();
}
}
/**
* Click listener
* @param panel panel
* @param clickingUser clicking user
* @param clickType click type
* @param i slot
* @param target target user
* @param actions actions
* @return true if the inventory item should not be removed - always true
*/
private boolean clickListener(Panel panel, User clickingUser, ClickType clickType, int i, User target,
List<ActionRecords> actions) {
if (actions.stream().noneMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
int rank = Objects.requireNonNull(island).getRank(clickingUser);
for (ItemTemplateRecord.ActionRecords action : actions) {
if (clickType.equals(action.clickType())) {
switch (action.actionType().toUpperCase(Locale.ENGLISH)) {
case "KICK" -> kickPlayer(clickingUser, target, rank);
case "SETOWNER" -> setOwner(clickingUser, target);
case "LEAVE" -> leave(clickingUser, target);
default -> {
// Do nothing
}
}
}
}
return true;
}
private void leave(User clickingUser, User target) {
if (clickingUser.hasPermission(parent.getLeaveCommand().getPermission()) && target.equals(clickingUser)
&& !clickingUser.getUniqueId().equals(island.getOwner())) {
plugin.log("Leave: " + clickingUser.getName() + " trying to leave island at " + island.getCenter());
clickingUser.closeInventory();
if (parent.getLeaveCommand().leave(clickingUser)) {
plugin.log("Leave: success");
} else {
plugin.log("Leave: failed");
}
}
}
private void setOwner(User clickingUser, User target) {
// Make the player the leader of the island
if (clickingUser.hasPermission(parent.getSetOwnerCommand().getPermission()) && !target.equals(clickingUser)
&& clickingUser.getUniqueId().equals(island.getOwner())
&& island.getRank(target) >= RanksManager.MEMBER_RANK) {
plugin.log("Set Owner: " + clickingUser.getName() + " trying to make " + target.getName()
+ " owner of island at " + island.getCenter());
clickingUser.closeInventory();
if (parent.getSetOwnerCommand().setOwner(clickingUser, target.getUniqueId())) {
plugin.log("Set Owner: success");
} else {
plugin.log("Set Owner: failed");
}
}
}
private void kickPlayer(User clickingUser, User target, int rank) {
// Kick the player, or uncoop, or untrust
if (clickingUser.hasPermission(parent.getKickCommand().getPermission()) && !target.equals(clickingUser)
&& rank >= island.getRankCommand(parent.getLabel() + " kick")) {
plugin.log("Kick: " + clickingUser.getName() + " kicked " + target.getName() + " from island at "
+ island.getCenter());
clickingUser.closeInventory();
if (removePlayer(clickingUser, target)) {
clickingUser.getPlayer().playSound(clickingUser.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
plugin.log("Kick: success");
} else {
plugin.log("Kick: failed");
}
}
}
private boolean removePlayer(User clicker, User member) {
// If member then kick, if coop, uncoop, if trusted, then untrust
return switch (island.getRank(member)) {
case RanksManager.COOP_RANK -> parent.getUncoopCommand().unCoopCmd(user, member.getUniqueId());
case RanksManager.TRUSTED_RANK -> parent.getUnTrustCommand().unTrustCmd(user, member.getUniqueId());
default -> {
if (parent.getKickCommand().canExecute(user, parent.getKickCommand().getLabel(),
List.of(member.getName()))) {
yield parent.getKickCommand().kick(clicker, member.getUniqueId());
} else {
yield false;
}
}
};
}
private List<String> showMembers() {
List<String> message = new ArrayList<>();
// Gather online members
long onlineMemberCount = island.getMemberSet(RanksManager.MEMBER_RANK).stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
.count();
// Show header:
message.add(user.getTranslation("commands.island.team.info.header", "[max]",
String.valueOf(plugin.getIslands().getMaxMembers(island, RanksManager.MEMBER_RANK)), "[total]",
String.valueOf(island.getMemberSet().size()), "[online]", String.valueOf(onlineMemberCount)));
// We now need to get all online "members" of the island - incl. Trusted and coop
List<UUID> onlineMembers = island.getMemberSet(RanksManager.COOP_RANK).stream()
.filter(uuid -> Util.getOnlinePlayerList(user).contains(Bukkit.getOfflinePlayer(uuid).getName()))
.toList();
for (int rank : RANKS) {
Set<UUID> players = island.getMemberSet(rank, false);
if (!players.isEmpty()) {
if (rank == RanksManager.OWNER_RANK) {
// Slightly special handling for the owner rank
message.add(user.getTranslation("commands.island.team.info.rank-layout.owner", TextVariables.RANK,
user.getTranslation(RanksManager.OWNER_RANK_REF)));
} else {
message.add(user.getTranslation("commands.island.team.info.rank-layout.generic", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)), TextVariables.NUMBER,
String.valueOf(island.getMemberSet(rank, false).size())));
}
message.addAll(displayOnOffline(rank, island, onlineMembers));
}
}
return message;
}
private List<String> displayOnOffline(int rank, Island island, List<UUID> onlineMembers) {
List<String> message = new ArrayList<>();
for (UUID member : island.getMemberSet(rank, false)) {
message.add(getMemberStatus(member, onlineMembers.contains(member)));
}
return message;
}
private String getMemberStatus(UUID member, boolean online) {
OfflinePlayer offlineMember = Bukkit.getOfflinePlayer(member);
if (online) {
return user.getTranslation("commands.island.team.info.member-layout.online", TextVariables.NAME,
offlineMember.getName());
} else {
return offlinePlayerStatus(offlineMember);
}
}
/**
* Creates text to describe the status of the player
* @param offlineMember member of the team
* @return string
*/
private String offlinePlayerStatus(OfflinePlayer offlineMember) {
String lastSeen = lastSeen(offlineMember);
if (island.getMemberSet(RanksManager.MEMBER_RANK, true).contains(offlineMember.getUniqueId())) {
return user.getTranslation("commands.island.team.info.member-layout.offline", TextVariables.NAME,
offlineMember.getName(), "[last_seen]", lastSeen);
} else {
// This will prevent anyone that is trusted or below to not have a last-seen status
return user.getTranslation("commands.island.team.info.member-layout.offline-not-last-seen",
TextVariables.NAME, offlineMember.getName());
}
}
private String lastSeen(OfflinePlayer offlineMember) {
// A bit of handling for the last joined date
Instant lastJoined = Instant.ofEpochMilli(offlineMember.getLastPlayed());
Instant now = Instant.now();
Duration duration = Duration.between(lastJoined, now);
String lastSeen;
final String reference = "commands.island.team.info.last-seen.layout";
if (duration.toMinutes() < 60L) {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toMinutes()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.minutes"));
} else if (duration.toHours() < 24L) {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toHours()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.hours"));
} else {
lastSeen = user.getTranslation(reference, TextVariables.NUMBER, String.valueOf(duration.toDays()),
TextVariables.UNIT, user.getTranslation("commands.island.team.info.last-seen.days"));
}
return lastSeen;
}
}

View File

@ -5,13 +5,14 @@ import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
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.database.objects.TeamInvite;
import world.bentobox.bentobox.database.objects.TeamInvite.Type;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
@ -22,8 +23,6 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
private static final String INVALID_INVITE = "commands.island.team.invite.errors.invalid-invite";
private final IslandTeamCommand itc;
private UUID playerUUID;
private UUID prospectiveOwnerUUID;
public IslandTeamInviteAcceptCommand(IslandTeamCommand islandTeamCommand) {
super(islandTeamCommand, "accept");
@ -39,19 +38,19 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
@Override
public boolean canExecute(User user, String label, List<String> args) {
playerUUID = user.getUniqueId();
UUID playerUUID = user.getUniqueId();
// Check if player has been invited
if (!itc.isInvited(playerUUID)) {
user.sendMessage("commands.island.team.invite.errors.none-invited-you");
return false;
}
// Get the island owner
prospectiveOwnerUUID = itc.getInviter(playerUUID);
UUID prospectiveOwnerUUID = itc.getInviter(playerUUID);
if (prospectiveOwnerUUID == null) {
user.sendMessage(INVALID_INVITE);
return false;
}
Invite invite = itc.getInvite(playerUUID);
TeamInvite invite = itc.getInvite(playerUUID);
if (invite.getType().equals(Type.TEAM)) {
// Check rank to of inviter
Island island = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
@ -63,16 +62,14 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
}
// Check if player is already in a team
if (getIslands().inTeam(getWorld(), playerUUID)) {
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
&& getIslands().inTeam(getWorld(), playerUUID)) {
user.sendMessage("commands.island.team.invite.errors.you-already-are-in-team");
return false;
}
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOIN)
.involvedPlayer(playerUUID)
.build();
IslandBaseEvent e = TeamEvent.builder().island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOIN).involvedPlayer(playerUUID).build();
return !e.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(e.isCancelled());
}
@ -82,96 +79,104 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
// Get the invite
Invite invite = itc.getInvite(playerUUID);
TeamInvite invite = itc.getInvite(user.getUniqueId());
switch (invite.getType()) {
case COOP -> askConfirmation(user, () -> acceptCoopInvite(user, invite));
case TRUST -> askConfirmation(user, () -> acceptTrustInvite(user, invite));
default -> askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"),
() -> acceptTeamInvite(user, invite));
default -> {
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
askConfirmation(user, user.getTranslation("commands.island.team.invite.accept.confirmation"),
() -> acceptTeamInvite(user, invite));
} else {
acceptTeamInvite(user, invite);
}
}
}
return true;
}
private void acceptTrustInvite(User user, Invite invite) {
void acceptTrustInvite(User user, TeamInvite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
User inviter = User.getInstance(invite.getInviter());
Island island = getIslands().getIsland(getWorld(), inviter);
Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null);
if (island != null) {
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.TRUSTED_RANK)) {
if (island.getMemberSet(RanksManager.TRUSTED_RANK, false).size() > getIslands().getMaxMembers(island,
RanksManager.TRUSTED_RANK)) {
user.sendMessage("commands.island.team.trust.is-full");
return;
}
island.setRank(user, RanksManager.TRUSTED_RANK);
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.TRUSTED_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.TRUSTED_RANK)
.build();
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.trust.success", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.trust.success", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
}
if (inviter.isPlayer()) {
user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
user.sendMessage("commands.island.team.trust.you-are-trusted", TextVariables.NAME, inviter.getName(),
TextVariables.DISPLAY_NAME, inviter.getDisplayName());
}
}
}
private void acceptCoopInvite(User user, Invite invite) {
void acceptCoopInvite(User user, TeamInvite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
User inviter = User.getInstance(invite.getInviter());
Island island = getIslands().getIsland(getWorld(), inviter);
Island island = getIslands().getIslandById(invite.getIslandID()).orElse(null);
if (island != null) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island, RanksManager.COOP_RANK)) {
if (island.getMemberSet(RanksManager.COOP_RANK, false).size() > getIslands().getMaxMembers(island,
RanksManager.COOP_RANK)) {
user.sendMessage("commands.island.team.coop.is-full");
return;
}
island.setRank(user, RanksManager.COOP_RANK);
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.COOP_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.COOP_RANK)
.build();
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.coop.success", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.coop.success", TextVariables.NAME, user.getName(),
TextVariables.DISPLAY_NAME, user.getDisplayName());
}
if (inviter.isPlayer()) {
user.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME, inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
user.sendMessage("commands.island.team.coop.you-are-a-coop-member", TextVariables.NAME,
inviter.getName(), TextVariables.DISPLAY_NAME, inviter.getDisplayName());
}
}
}
private void acceptTeamInvite(User user, Invite invite) {
void acceptTeamInvite(User user, TeamInvite invite) {
// Remove the invite
itc.removeInvite(playerUUID);
itc.removeInvite(user.getUniqueId());
// Get the player's island - may be null if the player has no island
Island island = getIslands().getIsland(getWorld(), playerUUID);
List<Island> islands = getIslands().getIslands(getWorld(), user.getUniqueId());
// Get the team's island
Island teamIsland = getIslands().getIsland(getWorld(), prospectiveOwnerUUID);
Island teamIsland = getIslands().getIslandById(invite.getIslandID()).orElse(null);
if (teamIsland == null) {
user.sendMessage(INVALID_INVITE);
return;
}
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() >= getIslands().getMaxMembers(teamIsland, RanksManager.MEMBER_RANK)) {
if (teamIsland.getMemberSet(RanksManager.MEMBER_RANK, true).size() >= getIslands().getMaxMembers(teamIsland,
RanksManager.MEMBER_RANK)) {
user.sendMessage("commands.island.team.invite.errors.island-is-full");
return;
}
// Remove player as owner of the old island
getIslands().removePlayer(getWorld(), playerUUID);
// Remove money inventory etc. for leaving
cleanPlayer(user);
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
// Remove the player's other islands
getIslands().removePlayer(getWorld(), user.getUniqueId());
// Remove money inventory etc. for leaving
cleanPlayer(user);
}
// Add the player as a team member of the new island
getIslands().setJoinTeam(teamIsland, playerUUID);
getIslands().setJoinTeam(teamIsland, user.getUniqueId());
// Move player to team's island
getIslands().setPrimaryIsland(user.getUniqueId(), teamIsland);
getIslands().homeTeleportAsync(getWorld(), user.getPlayer()).thenRun(() -> {
// Delete the old island
if (island != null) {
getIslands().deleteIsland(island, true, user.getUniqueId());
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()) {
// Delete the old islands
islands.forEach(island -> getIslands().deleteIsland(island, true, user.getUniqueId()));
}
// Put player back into normal mode
user.setGameMode(getIWM().getDefaultGameMode(getWorld()));
@ -181,29 +186,23 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
Util.runCommands(user, ownerName, getIWM().getOnJoinCommands(getWorld()), "join");
});
// Reset deaths
if (getIWM().isTeamJoinDeathReset(getWorld())) {
getPlayers().setDeaths(getWorld(), playerUUID, 0);
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
&& getIWM().isTeamJoinDeathReset(getWorld())) {
// Reset deaths
getPlayers().setDeaths(getWorld(), user.getUniqueId(), 0);
}
user.sendMessage("commands.island.team.invite.accept.you-joined-island", TextVariables.LABEL, getTopLabel());
User inviter = User.getInstance(invite.getInviter());
if (inviter.isOnline()) {
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
inviter.sendMessage("commands.island.team.invite.accept.name-joined-your-island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
getIslands().save(teamIsland);
// Fire event
TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), prospectiveOwnerUUID))
.reason(TeamEvent.Reason.JOINED)
.involvedPlayer(playerUUID)
.build();
IslandEvent.builder()
.island(teamIsland)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(teamIsland.getRank(user), RanksManager.MEMBER_RANK)
.build();
TeamEvent.builder().island(teamIsland).reason(TeamEvent.Reason.JOINED).involvedPlayer(user.getUniqueId())
.build();
IslandEvent.builder().island(teamIsland).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(teamIsland.getRank(user), RanksManager.MEMBER_RANK)
.build();
}
private void cleanPlayer(User user) {
@ -213,7 +212,8 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
if (getIWM().isOnLeaveResetInventory(getWorld()) || getIWM().isOnJoinResetInventory(getWorld())) {
user.getPlayer().getInventory().clear();
}
if (getSettings().isUseEconomy() && (getIWM().isOnLeaveResetMoney(getWorld()) || getIWM().isOnJoinResetMoney(getWorld()))) {
if (getSettings().isUseEconomy()
&& (getIWM().isOnLeaveResetMoney(getWorld()) || getIWM().isOnJoinResetMoney(getWorld()))) {
getPlugin().getVault().ifPresent(vault -> vault.withdraw(user, vault.getBalance(user)));
}
@ -229,6 +229,10 @@ public class IslandTeamInviteAcceptCommand extends ConfirmableCommand {
// Reset the XP
if (getIWM().isOnJoinResetXP(getWorld())) {
// Player collected XP (displayed)
user.getPlayer().setLevel(0);
user.getPlayer().setExp(0);
// Player total XP (not displayed)
user.getPlayer().setTotalExperience(0);
}

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -9,12 +10,13 @@ import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.team.TeamEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.database.objects.TeamInvite.Type;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.managers.RanksManager;
@ -24,6 +26,8 @@ public class IslandTeamInviteCommand extends CompositeCommand {
private final IslandTeamCommand itc;
private @Nullable User invitedPlayer;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
public IslandTeamInviteCommand(IslandTeamCommand parent) {
super(parent, "invite");
@ -36,6 +40,10 @@ public class IslandTeamInviteCommand extends CompositeCommand {
setOnlyPlayer(true);
setDescription("commands.island.team.invite.description");
setConfigurableRankCommand();
// Panels
if (!new File(getPlugin().getDataFolder() + File.separator + "panels", "team_invite_panel.yml").exists()) {
getPlugin().saveResource("panels/team_invite_panel.yml", false);
}
}
@ -49,44 +57,27 @@ public class IslandTeamInviteCommand extends CompositeCommand {
user.sendMessage("general.errors.no-island");
return false;
}
Island island = islandsManager.getIsland(getWorld(), user);
if (args.size() != 1) {
return handleCommandWithNoArgs(user);
new IslandTeamInviteGUI(itc, true, island).build(user);
return true;
}
Island island = islandsManager.getIsland(getWorld(), user);
int rank = Objects.requireNonNull(island).getRank(user);
return checkRankAndInvitePlayer(user, island, rank, args.get(0));
}
private boolean handleCommandWithNoArgs(User user) {
UUID playerUUID = user.getUniqueId();
Type inviteType = getInviteType(playerUUID);
if (inviteType != null) {
String name = getPlayers().getName(playerUUID);
switch (inviteType) {
case COOP -> user.sendMessage("commands.island.team.invite.name-has-invited-you.coop", TextVariables.NAME, name);
case TRUST -> user.sendMessage("commands.island.team.invite.name-has-invited-you.trust", TextVariables.NAME, name);
default -> user.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, name);
}
return true;
}
showHelp(this, user);
return false;
}
private boolean checkRankAndInvitePlayer(User user, Island island, int rank, String playerName) {
RanksManager ranksManager = getPlugin().getRanksManager();
PlayersManager playersManager = getPlayers();
UUID playerUUID = user.getUniqueId();
// Check rank to use command
int requiredRank = island.getRankCommand(getUsage());
if (rank < requiredRank) {
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK, user.getTranslation(ranksManager.getRank(rank)));
user.sendMessage("general.errors.insufficient-rank", TextVariables.RANK,
user.getTranslation(RanksManager.getInstance().getRank(rank)));
return false;
}
@ -114,7 +105,8 @@ public class IslandTeamInviteCommand extends CompositeCommand {
}
// Player cannot invite someone already on a team
if (getIslands().inTeam(getWorld(), invitedPlayerUUID)) {
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
&& getIslands().inTeam(getWorld(), invitedPlayerUUID)) {
user.sendMessage("commands.island.team.invite.errors.already-on-team");
return false;
}
@ -127,14 +119,6 @@ public class IslandTeamInviteCommand extends CompositeCommand {
return true;
}
private Type getInviteType(UUID playerUUID) {
if (itc.isInvited(playerUUID)) {
Invite invite = itc.getInvite(playerUUID);
return invite.getType();
}
return null;
}
private boolean canInvitePlayer(User user, User invitedPlayer) {
UUID playerUUID = user.getUniqueId();
if (!invitedPlayer.isOnline() || !user.getPlayer().canSee(invitedPlayer.getPlayer())) {
@ -166,9 +150,14 @@ public class IslandTeamInviteCommand extends CompositeCommand {
itc.removeInvite(invitedPlayer.getUniqueId());
user.sendMessage("commands.island.team.invite.removing-invite");
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(getIslands().getIsland(getWorld(), user.getUniqueId()))
.island(island)
.reason(TeamEvent.Reason.INVITE)
.involvedPlayer(invitedPlayer.getUniqueId())
.build();
@ -177,12 +166,13 @@ public class IslandTeamInviteCommand extends CompositeCommand {
}
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Invite.Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId());
itc.addInvite(Type.TEAM, user.getUniqueId(), invitedPlayer.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, invitedPlayer.getName(), TextVariables.DISPLAY_NAME, invitedPlayer.getDisplayName());
// Send message to online player
invitedPlayer.sendMessage("commands.island.team.invite.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
invitedPlayer.sendMessage("commands.island.team.invite.to-accept-or-reject", TextVariables.LABEL, getTopLabel());
if (getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) {
if (getIWM().getWorldSettings(getWorld()).isDisallowTeamMemberIslands()
&& getIslands().hasIsland(getWorld(), invitedPlayer.getUniqueId())) {
invitedPlayer.sendMessage("commands.island.team.invite.you-will-lose-your-island");
}
return true;

View File

@ -0,0 +1,286 @@
package world.bentobox.bentobox.api.commands.island.team;
import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.conversations.ConversationContext;
import org.bukkit.conversations.ConversationFactory;
import org.bukkit.conversations.Prompt;
import org.bukkit.conversations.StringPrompt;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord.ActionRecords;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord.TemplateItem;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
public class IslandTeamInviteGUI {
private final IslandTeamInviteCommand itic;
private final IslandTeamCommand itc;
private @Nullable TemplateItem border;
private @Nullable TemplateItem background;
private User user;
private long page = 0; // This number by 35
private final boolean inviteCmd;
private static final long PER_PAGE = 35;
private String searchName = "";
private final BentoBox plugin;
private final Island island;
public IslandTeamInviteGUI(IslandTeamCommand itc, boolean invitedCmd, Island island) {
this.island = island;
this.plugin = itc.getPlugin();
this.inviteCmd = invitedCmd;
itic = itc.getInviteCommand();
this.itc = itc;
// Panels
if (!new File(plugin.getDataFolder() + File.separator + "panels", "team_invite_panel.yml")
.exists()) {
plugin.saveResource("panels/team_invite_panel.yml", false);
}
}
/**
* Build the invite panel
* @param user use of the panel
*/
void build(User user) {
this.user = user;
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
panelBuilder.user(user);
panelBuilder.world(user.getWorld());
panelBuilder.template("team_invite_panel", new File(plugin.getDataFolder(), "panels"));
panelBuilder.parameters("[name]", user.getName(), "[display_name]", user.getDisplayName());
panelBuilder.registerTypeBuilder("PROSPECT", this::createProspectButton);
panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
panelBuilder.registerTypeBuilder("SEARCH", this::createSearchButton);
panelBuilder.registerTypeBuilder("BACK", this::createBackButton);
// Stash the backgrounds for later use
border = panelBuilder.getPanelTemplate().border();
background = panelBuilder.getPanelTemplate().background();
// Register unknown type builder.
panelBuilder.build();
}
private PanelItem createBackButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.closeInventory();
if (!inviteCmd) {
new IslandTeamGUI(plugin, itc, user, island).build();
}
return true;
}).build();
}
private PanelItem createSearchButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
PanelItemBuilder pib = new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.closeInventory();
new ConversationFactory(BentoBox.getInstance()).withLocalEcho(false).withTimeout(90)
.withModality(false).withFirstPrompt(new InviteNamePrompt())
.buildConversation(user.getPlayer()).begin();
return true;
});
if (!this.searchName.isBlank()) {
pib.description(user.getTranslation(Objects
.requireNonNullElse(template.description(),
"commands.island.team.invite.gui.button.searching"),
TextVariables.NAME, searchName));
}
return pib.build();
}
private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
long count = itc.getWorld().getPlayers().stream().filter(player -> user.getPlayer().canSee(player))
.filter(player -> !player.equals(user.getPlayer())).count();
if (count > page * PER_PAGE) {
// We need to show a next button
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
page++;
build(user);
return true;
}).build();
}
return getBlankBorder();
}
private void checkTemplate(ItemTemplateRecord template) {
if (template.icon() == null) {
plugin.logError("Icon in template is missing or unknown! " + template.toString());
}
if (template.title() == null) {
plugin.logError("Title in template is missing! " + template.toString());
}
}
private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
checkTemplate(template);
if (page > 0) {
// We need to show a next button
return new PanelItemBuilder().name(user.getTranslation(template.title())).icon(template.icon())
.clickHandler((panel, user, clickType, clickSlot) -> {
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
page--;
build(user);
return true;
}).build();
}
return getBlankBorder();
}
private PanelItem getBlankBorder() {
return new PanelItemBuilder().icon(Objects.requireNonNullElse(border.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(border.title(), ""))).build();
}
/**
* Create member button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
private PanelItem createProspectButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) {
// Player issuing the command must have an island
Island is = plugin.getIslands().getPrimaryIsland(itc.getWorld(), user.getUniqueId());
if (is == null) {
return this.getBlankBackground();
}
if (page < 0) {
page = 0;
}
// Stream of all players that the user can see
Stream<Player> visiblePlayers = itc.getWorld().getPlayers().stream().filter(user.getPlayer()::canSee);
// Filter players based on searchName if it's not blank, and ensure they're not the user
Stream<Player> filteredPlayers = visiblePlayers
.filter(player -> this.searchName.isBlank()
|| player.getName().toLowerCase().contains(searchName.toLowerCase()))
.filter(player -> !player.equals(user.getPlayer()));
// Skipping to the correct pagination slot, then finding the first player
Optional<Player> playerOptional = filteredPlayers.skip(slot.slot() + page * PER_PAGE).findFirst();
// Map the player to a prospect or return a blank background if not found
return playerOptional.map(player -> getProspect(player, template)).orElse(this.getBlankBackground());
}
private PanelItem getProspect(Player player, ItemTemplateRecord template) {
// Check if the prospect has already been invited
if (this.itc.isInvited(player.getUniqueId())
&& user.getUniqueId().equals(this.itc.getInvite(player.getUniqueId()).getInviter())) {
return new PanelItemBuilder().icon(player.getName()).name(player.getDisplayName())
.description(user.getTranslation("commands.island.team.invite.gui.button.already-invited")).build();
}
List<String> desc = template.actions().stream().map(ar -> user
.getTranslation("commands.island.team.invite.gui.tips." + ar.clickType().name() + ".name")
+ " " + user.getTranslation(ar.tooltip())).toList();
return new PanelItemBuilder().icon(player.getName()).name(player.getDisplayName()).description(desc)
.clickHandler(
(panel, user, clickType, clickSlot) -> clickHandler(user, clickType, player,
template.actions()))
.build();
}
private boolean clickHandler(User user, ClickType clickType, Player player, @NonNull List<ActionRecords> list) {
if (list.stream().noneMatch(ar -> clickType.equals(ar.clickType()))) {
// If the click type is not in the template, don't do anything
return true;
}
if (clickType.equals(ClickType.LEFT)) {
user.closeInventory();
if (itic.canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Invite sent to: " + player.getName() + " by " + user.getName() + " to join island in "
+ itc.getWorld().getName());
itic.execute(user, itic.getLabel(), List.of(player.getName()));
} else {
plugin.log("Invite failed: " + player.getName() + " by " + user.getName() + " to join island in "
+ itc.getWorld().getName());
}
} else if (clickType.equals(ClickType.RIGHT)) {
user.closeInventory();
if (this.itc.getCoopCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Coop: " + player.getName() + " cooped " + user.getName() + " to island in "
+ itc.getWorld().getName());
this.itc.getCoopCommand().execute(user, itic.getLabel(), List.of(player.getName()));
} else {
plugin.log(
"Coop failed: " + player.getName() + "'s coop to " + user.getName() + " failed for island in "
+ itc.getWorld().getName());
}
} else if (clickType.equals(ClickType.SHIFT_LEFT)) {
user.closeInventory();
if (this.itc.getTrustCommand().canExecute(user, itic.getLabel(), List.of(player.getName()))) {
plugin.log("Trust: " + player.getName() + " trusted " + user.getName() + " to island in "
+ itc.getWorld().getName());
this.itc.getTrustCommand().execute(user, itic.getLabel(), List.of(player.getName()));
} else {
plugin.log("Trust failed: " + player.getName() + "'s trust failed for " + user.getName()
+ " for island in "
+ itc.getWorld().getName());
}
}
return true;
}
private PanelItem getBlankBackground() {
return new PanelItemBuilder()
.icon(Objects.requireNonNullElse(background.icon(), new ItemStack(Material.BARRIER)))
.name((Objects.requireNonNullElse(background.title(), ""))).build();
}
class InviteNamePrompt extends StringPrompt {
@Override
@NonNull
public String getPromptText(@NonNull ConversationContext context) {
return user.getTranslation("commands.island.team.invite.gui.enter-name");
}
@Override
public Prompt acceptInput(@NonNull ConversationContext context, String input) {
if (itic.canExecute(user, itic.getLabel(), List.of(input))
&& itic.execute(user, itic.getLabel(), List.of(input))) {
return Prompt.END_OF_CONVERSATION;
}
// Set the search item to what was entered
searchName = input;
// Return to the GUI but give a second for the error to show
// TODO: return the failed input and display the options in the GUI.
Bukkit.getScheduler().runTaskLater(BentoBox.getInstance(), () -> build(user), 20L);
return Prompt.END_OF_CONVERSATION;
}
}
}

View File

@ -19,7 +19,6 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandTeamKickCommand extends ConfirmableCommand {
public IslandTeamKickCommand(CompositeCommand islandTeamCommand) {
@ -36,7 +35,7 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
}
@Override
public boolean execute(User user, String label, List<String> args) {
public boolean canExecute(User user, String label, List<String> args) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
@ -45,7 +44,8 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
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;
}
// If args are not right, show help
@ -63,18 +63,24 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
user.sendMessage("commands.island.team.kick.cannot-kick");
return false;
}
if (!getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (!getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
user.sendMessage("general.errors.not-in-team");
return false;
}
int targetRank = Objects.requireNonNull(island).getRank(targetUUID);
if (rank <= targetRank) {
user.sendMessage("commands.island.team.kick.cannot-kick-rank",
TextVariables.NAME, getPlayers().getName(targetUUID));
user.sendMessage("commands.island.team.kick.cannot-kick-rank", TextVariables.NAME,
getPlayers().getName(targetUUID));
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
// Get target
UUID targetUUID = getPlayers().getUUID(args.get(0));
if (!getSettings().isKickConfirmation()) {
kick(user, targetUUID);
return true;
@ -84,43 +90,41 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
}
}
private void kick(User user, UUID targetUUID) {
User target = User.getInstance(targetUUID);
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), targetUUID)); // Should never be null because of checks above
// Fire event
IslandBaseEvent event = TeamEvent.builder()
.island(oldIsland)
.reason(TeamEvent.Reason.KICK)
.involvedPlayer(targetUUID)
.build();
if (event.isCancelled()) {
return;
protected boolean kick(User user, UUID targetUUID) {
if (targetUUID == null) {
return false;
}
target.sendMessage("commands.island.team.kick.player-kicked",
TextVariables.GAMEMODE, getAddon().getDescription().getName(),
TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
User target = User.getInstance(targetUUID);
Island oldIsland = Objects.requireNonNull(getIslands().getIsland(getWorld(), user)); // Should never be
// null because of
// checks above
// Fire event
IslandBaseEvent event = TeamEvent.builder().island(oldIsland).reason(TeamEvent.Reason.KICK)
.involvedPlayer(targetUUID).build();
if (event.isCancelled()) {
return false;
}
target.sendMessage("commands.island.team.kick.player-kicked", TextVariables.GAMEMODE,
getAddon().getDescription().getName(), TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME,
user.getDisplayName());
getIslands().removePlayer(getWorld(), targetUUID);
getIslands().removePlayer(oldIsland, targetUUID);
// Clean the target player
getPlayers().cleanLeavingPlayer(getWorld(), target, true, oldIsland);
user.sendMessage("commands.island.team.kick.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(oldIsland)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(oldIsland.getRank(user), RanksManager.VISITOR_RANK)
.build();
user.sendMessage("commands.island.team.kick.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder().island(oldIsland).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(oldIsland.getRank(user), RanksManager.VISITOR_RANK)
.build();
// Add cooldown for this player and target
if (getSettings().getInviteCooldown() > 0 && getParent() != null) {
// Get the invite class from the parent
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(
oldIsland.getUniqueId(),
targetUUID.toString(),
getSettings().getInviteCooldown() * 60));
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(oldIsland.getUniqueId(),
targetUUID.toString(), getSettings().getInviteCooldown() * 60));
}
return true;
}
@Override
@ -128,11 +132,10 @@ public class IslandTeamKickCommand extends ConfirmableCommand {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
List<String> options = island.getMemberSet().stream()
.filter(uuid -> island.getRank(uuid) >= RanksManager.MEMBER_RANK)
.map(Bukkit::getOfflinePlayer)
.filter(uuid -> island.getRank(uuid) >= RanksManager.MEMBER_RANK).map(Bukkit::getOfflinePlayer)
.map(OfflinePlayer::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

@ -32,7 +32,7 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
user.sendMessage("general.errors.no-team");
return false;
}
if (getIslands().hasIsland(getWorld(), user.getUniqueId())) {
if (user.getUniqueId().equals(getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getOwner())) {
user.sendMessage("commands.island.team.leave.cannot-leave");
return false;
}
@ -65,29 +65,32 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
}
private void leave(User user) {
protected boolean leave(User user) {
Island island = getIslands().getIsland(getWorld(), user);
if (island == null) {
user.sendMessage("general.errors.no-island");
return false;
}
// Fire event
IslandBaseEvent event = TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.LEAVE)
.involvedPlayer(user.getUniqueId())
.build();
IslandBaseEvent event = TeamEvent.builder().island(island).reason(TeamEvent.Reason.LEAVE)
.involvedPlayer(user.getUniqueId()).build();
if (event.isCancelled()) {
return;
return false;
}
UUID ownerUUID = getIslands().getOwner(getWorld(), user.getUniqueId());
UUID ownerUUID = island.getOwner();
if (ownerUUID != null) {
User.getInstance(ownerUUID).sendMessage("commands.island.team.leave.left-your-island", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
User.getInstance(ownerUUID).sendMessage("commands.island.team.leave.left-your-island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
}
getIslands().setLeaveTeam(getWorld(), user.getUniqueId());
getIslands().removePlayer(island, user.getUniqueId());
// Clean the player
getPlayers().cleanLeavingPlayer(getWorld(), user, false, island);
// Add cooldown for this player and target
if (getSettings().getInviteCooldown() > 0 && getParent() != null) {
// Get the invite class from the parent
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(island.getUniqueId(), user.getUniqueId().toString(), getSettings().getInviteCooldown() * 60));
getParent().getSubCommand("invite").ifPresent(c -> c.setCooldown(island.getUniqueId(),
user.getUniqueId().toString(), getSettings().getInviteCooldown() * 60));
}
// Remove reset if required
if (getIWM().isLeaversLoseReset(getWorld())) {
@ -97,12 +100,9 @@ public class IslandTeamLeaveCommand extends ConfirmableCommand {
showResets(user);
}
user.sendMessage("commands.island.team.leave.success");
IslandEvent.builder()
.island(island)
.involvedPlayer(user.getUniqueId())
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(user), RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(island.getRank(user), RanksManager.VISITOR_RANK)
.build();
return true;
}
}

View File

@ -15,8 +15,13 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
/**
* Handle promotion and demotion
*/
public class IslandTeamPromoteCommand extends CompositeCommand {
private User target;
public IslandTeamPromoteCommand(CompositeCommand islandTeamCommand, String string) {
super(islandTeamCommand, string);
}
@ -35,53 +40,78 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
this.setConfigurableRankCommand();
}
@Override
public boolean execute(User user, String label, List<String> args) {
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return true;
}
// Check rank to use command
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)));
return false;
}
@Override
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
User target = getPlayers().getUser(args.get(0));
if (target == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return true;
// Check if the user has a team
if (!getIslands().inTeam(getWorld(), user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
}
// Check if the user is not trying to promote/ demote himself
if (target == user) {
user.sendMessage("commands.island.team.demote.errors.cant-demote-yourself");
return true;
}
if (!inTeam(getWorld(), target) || !Objects.requireNonNull(getOwner(getWorld(), user), "Island has no owner!").equals(getOwner(getWorld(), target))) {
user.sendMessage("general.errors.not-in-team");
return true;
// Check rank to use command
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(RanksManager.getInstance().getRank(rank)));
return false;
}
// Get target
target = getPlayers().getUser(args.get(0));
if (target == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Check that target is a member of this island
if (!island.inTeam(target.getUniqueId())) {
user.sendMessage("commands.island.team.promote.errors.must-be-member");
return false;
}
// Check if the user is not trying to promote/ demote himself
if (target.equals(user)) {
if (this.getLabel().equals("promote")) {
user.sendMessage("commands.island.team.promote.errors.cant-promote-yourself");
} else {
user.sendMessage("commands.island.team.demote.errors.cant-demote-yourself");
}
return false;
}
// Check that user is not trying to promote above their own rank
// Check that user is not trying to demote ranks higher than them
if (island.getRank(target) >= island.getRank(user)) {
if (this.getLabel().equals("promote")) {
user.sendMessage("commands.island.team.promote.errors.cant-promote");
} else {
user.sendMessage("commands.island.team.demote.errors.cant-demote");
}
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
return change(user, target);
}
private boolean change(User user, User target) {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
Island island = getIslands().getIsland(getWorld(), user);
int currentRank = island.getRank(target);
if (this.getLabel().equals("promote")) {
int nextRank = getPlugin().getRanksManager().getRankUpValue(currentRank);
int nextRank = RanksManager.getInstance().getRankUpValue(currentRank);
// Stop short of owner
if (nextRank != RanksManager.OWNER_RANK && nextRank > currentRank) {
getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, nextRank);
String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(nextRank));
if (nextRank < RanksManager.OWNER_RANK && currentRank >= RanksManager.MEMBER_RANK
&& nextRank > currentRank) {
island.setRank(target, nextRank);
String rankName = user.getTranslation(RanksManager.getInstance().getRank(nextRank));
user.sendMessage("commands.island.team.promote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName, TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(island)
@ -97,11 +127,11 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
}
} else {
// Demote
int prevRank = getPlugin().getRanksManager().getRankDownValue(currentRank);
int prevRank = RanksManager.getInstance().getRankDownValue(currentRank);
// Lowest is Member
if (prevRank >= RanksManager.MEMBER_RANK && prevRank < currentRank) {
getIslands().getIsland(getWorld(), user.getUniqueId()).setRank(target, prevRank);
String rankName = user.getTranslation(getPlugin().getRanksManager().getRank(prevRank));
island.setRank(target, prevRank);
String rankName = user.getTranslation(RanksManager.getInstance().getRank(prevRank));
user.sendMessage("commands.island.team.demote.success", TextVariables.NAME, target.getName(), TextVariables.RANK, rankName, TextVariables.DISPLAY_NAME, target.getDisplayName());
IslandEvent.builder()
.island(island)
@ -120,7 +150,7 @@ public class IslandTeamPromoteCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
Island island = getIslands().getIsland(getWorld(), user);
if (island != null) {
List<String> options = island.getMemberSet().stream()
.map(Bukkit::getOfflinePlayer)

View File

@ -4,6 +4,9 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.IslandBaseEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent;
@ -11,11 +14,14 @@ import world.bentobox.bentobox.api.events.team.TeamEvent;
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.IslandsManager;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
public class IslandTeamSetownerCommand extends CompositeCommand {
private @Nullable UUID targetUUID;
public IslandTeamSetownerCommand(CompositeCommand islandTeamCommand) {
super(islandTeamCommand, "setowner");
}
@ -29,73 +35,76 @@ public class IslandTeamSetownerCommand extends CompositeCommand {
}
@Override
public boolean execute(User user, String label, List<String> args) {
UUID playerUUID = user.getUniqueId();
// Can use if in a team
boolean inTeam = getIslands().inTeam(getWorld(), playerUUID);
if (!inTeam) {
user.sendMessage("general.errors.no-team");
return false;
}
UUID ownerUUID = getOwner(getWorld(), user);
if (ownerUUID == null || !ownerUUID.equals(playerUUID)) {
user.sendMessage("general.errors.not-owner");
return false;
}
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;
}
UUID targetUUID = getPlayers().getUUID(args.get(0));
// Can use if in a team
Island is = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
if (is == null || !is.inTeam(user.getUniqueId())) {
user.sendMessage("general.errors.no-team");
return false;
}
UUID ownerUUID = is.getOwner();
if (ownerUUID == null || !ownerUUID.equals(user.getUniqueId())) {
user.sendMessage("general.errors.not-owner");
return false;
}
targetUUID = getPlayers().getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
if (targetUUID.equals(playerUUID)) {
if (targetUUID.equals(user.getUniqueId())) {
user.sendMessage("commands.island.team.setowner.errors.cant-transfer-to-yourself");
return false;
}
if (!getIslands().getMembers(getWorld(), playerUUID).contains(targetUUID)) {
if (!is.inTeam(targetUUID)) {
user.sendMessage("commands.island.team.setowner.errors.target-is-not-member");
return false;
}
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
return setOwner(user, targetUUID);
}
protected boolean setOwner(User user, @NonNull UUID targetUUID2) {
// Fire event so add-ons can run commands, etc.
Island island = getIslands().getIsland(getWorld(), user);
Island island = getIslands().getPrimaryIsland(getWorld(), user.getUniqueId());
// Fire event so add-ons can run commands, etc.
IslandBaseEvent e = TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID)
.build();
IslandBaseEvent e = TeamEvent.builder().island(island).reason(TeamEvent.Reason.SETOWNER)
.involvedPlayer(targetUUID2).build();
if (e.isCancelled()) {
return false;
}
getIslands().setOwner(getWorld(), user, targetUUID);
getIslands().setOwner(getWorld(), user, targetUUID2);
// Call the event for the new owner
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(User.getInstance(targetUUID)), RanksManager.OWNER_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID2).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(island.getRank(User.getInstance(targetUUID2)), RanksManager.OWNER_RANK).build();
// Call the event for the previous owner
IslandEvent.builder()
.island(island)
.involvedPlayer(playerUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.OWNER_RANK, island.getRank(user))
.build();
getIslands().save(island);
IslandEvent.builder().island(island).involvedPlayer(user.getUniqueId()).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE).rankChange(RanksManager.OWNER_RANK, RanksManager.SUB_OWNER_RANK)
.build();
return true;
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
return Optional.of(Util.tabLimit(getIslands().getMembers(getWorld(), user.getUniqueId()).stream().map(getPlayers()::getName).toList(), lastArg));
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()) == null) {
return Optional.empty();
}
return Optional.of(Util.tabLimit(
getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).getMemberSet().stream()
.filter(uuid -> !user.getUniqueId().equals(uuid)).map(getPlayers()::getName).toList(),
lastArg));
}
}
}

View File

@ -8,10 +8,10 @@ import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.island.team.Invite.Type;
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.database.objects.TeamInvite.Type;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
@ -55,7 +55,8 @@ public class IslandTeamTrustCommand 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
@ -95,7 +96,7 @@ public class IslandTeamTrustCommand extends CompositeCommand {
if (getPlugin().getSettings().isInviteConfirmation()) {
// Put the invited player (key) onto the list with inviter (value)
// If someone else has invited a player, then this invite will overwrite the previous invite!
itc.addInvite(Type.TRUST, user.getUniqueId(), target.getUniqueId());
itc.addInvite(Type.TRUST, user.getUniqueId(), target.getUniqueId(), island);
user.sendMessage("commands.island.team.invite.invitation-sent", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
// Send message to online player
target.sendMessage("commands.island.team.trust.name-has-invited-you", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());

View File

@ -18,6 +18,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Command to uncoop a player
*
* @author tastybento
*
*/
@ -44,7 +45,8 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
return false;
}
// 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;
}
@ -52,7 +54,8 @@ public class IslandTeamUncoopCommand 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
@ -65,13 +68,13 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
return unCoopCmd(user, targetUUID);
}
private boolean unCoopCmd(User user, UUID targetUUID) {
protected boolean unCoopCmd(User user, UUID targetUUID) {
// Player cannot uncoop themselves
if (user.getUniqueId().equals(targetUUID)) {
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
user.sendMessage("commands.island.team.uncoop.cannot-uncoop-member");
return false;
}
@ -83,21 +86,19 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
island.removeMember(targetUUID);
user.sendMessage("commands.island.team.uncoop.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.uncoop.you-are-no-longer-a-coop-member", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
getIslands().removePlayer(island, targetUUID);
user.sendMessage("commands.island.team.uncoop.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.uncoop.you-are-no-longer-a-coop-member", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
// Set cooldown
if (getSettings().getCoopCooldown() > 0 && getParent() != null) {
getParent().getSubCommand("coop").ifPresent(subCommand ->
subCommand.setCooldown(island.getUniqueId(), targetUUID.toString(), getSettings().getCoopCooldown() * 60));
getParent().getSubCommand("coop").ifPresent(subCommand -> subCommand.setCooldown(island.getUniqueId(),
targetUUID.toString(), getSettings().getCoopCooldown() * 60));
}
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.COOP_RANK, RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.COOP_RANK, RanksManager.VISITOR_RANK).build();
return true;
} else {
// Should not happen
@ -111,10 +112,9 @@ public class IslandTeamUncoopCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
List<String> options = island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.COOP_RANK)
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.filter(e -> e.getValue() == RanksManager.COOP_RANK).map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.map(OfflinePlayer::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

@ -18,6 +18,7 @@ import world.bentobox.bentobox.util.Util;
/**
* Command to untrust a player
*
* @author tastybento
*
*/
@ -44,7 +45,8 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
return false;
}
// 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;
}
@ -52,7 +54,8 @@ public class IslandTeamUntrustCommand 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
@ -65,13 +68,13 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
return unTrustCmd(user, targetUUID);
}
private boolean unTrustCmd(User user, UUID targetUUID) {
protected boolean unTrustCmd(User user, UUID targetUUID) {
// Player cannot untrust themselves
if (user.getUniqueId().equals(targetUUID)) {
user.sendMessage("commands.island.team.untrust.cannot-untrust-yourself");
return false;
}
if (getIslands().getMembers(getWorld(), user.getUniqueId()).contains(targetUUID)) {
if (getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()).inTeam(targetUUID)) {
user.sendMessage("commands.island.team.untrust.cannot-untrust-member");
return false;
}
@ -83,21 +86,19 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
}
Island island = getIslands().getIsland(getWorld(), user.getUniqueId());
if (island != null) {
island.removeMember(targetUUID);
user.sendMessage("commands.island.team.untrust.success", TextVariables.NAME, target.getName(), TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.untrust.you-are-no-longer-trusted", TextVariables.NAME, user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
getIslands().removePlayer(island, targetUUID);
user.sendMessage("commands.island.team.untrust.success", TextVariables.NAME, target.getName(),
TextVariables.DISPLAY_NAME, target.getDisplayName());
target.sendMessage("commands.island.team.untrust.you-are-no-longer-trusted", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName());
// Set cooldown
if (getSettings().getTrustCooldown() > 0 && getParent() != null) {
getParent().getSubCommand("trust").ifPresent(subCommand ->
subCommand.setCooldown(island.getUniqueId(), targetUUID.toString(), getSettings().getTrustCooldown() * 60));
getParent().getSubCommand("trust").ifPresent(subCommand -> subCommand.setCooldown(island.getUniqueId(),
targetUUID.toString(), getSettings().getTrustCooldown() * 60));
}
IslandEvent.builder()
.island(island)
.involvedPlayer(targetUUID)
.admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.TRUSTED_RANK, RanksManager.VISITOR_RANK)
.build();
IslandEvent.builder().island(island).involvedPlayer(targetUUID).admin(false)
.reason(IslandEvent.Reason.RANK_CHANGE)
.rankChange(RanksManager.TRUSTED_RANK, RanksManager.VISITOR_RANK).build();
return true;
} else {
// Should not happen
@ -112,9 +113,8 @@ public class IslandTeamUntrustCommand extends CompositeCommand {
if (island != null) {
List<String> options = island.getMembers().entrySet().stream()
.filter(e -> e.getValue() == RanksManager.TRUSTED_RANK)
.map(e -> Bukkit.getOfflinePlayer(e.getKey()))
.map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
.map(e -> Bukkit.getOfflinePlayer(e.getKey())).map(OfflinePlayer::getName).toList();
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
return Optional.of(Util.tabLimit(options, lastArg));
} else {
return Optional.empty();

View File

@ -13,6 +13,7 @@ import org.bukkit.GameMode;
import org.bukkit.entity.EntityType;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.lists.Flags;
@ -549,6 +550,7 @@ public interface WorldSettings extends ConfigObject {
* Returns all aliases for main admin command.
* It is assumed that all aliases are split with whitespace between them.
* String cannot be empty.
* The first command listed is the "label" in the API, and after that are the aliases
* Default value: {@code getFriendlyName() + "admin"} (to retain backward compatibility).
* @return String value
* @since 1.13.0
@ -563,6 +565,7 @@ public interface WorldSettings extends ConfigObject {
* Returns all aliases for main player command.
* It is assumed that all aliases are split with whitespace between them.
* String cannot be empty.
* The first command listed is the "label" in the API, and after that are the aliases
* Default value: {@code getFriendlyName()} (to retain backward compatibility).
* @return String value
* @since 1.13.0
@ -632,4 +635,22 @@ public interface WorldSettings extends ConfigObject {
default boolean isCheckForBlocks() {
return true;
}
/**
* Get the number of concurrent islands a player can have in the world
* @return 1 by default
* @since 2.0.0
*/
default int getConcurrentIslands() {
return BentoBox.getInstance().getSettings().getIslandNumber();
}
/**
* Remove islands when players join a team and not allow players to have other islands if they are in a team.
* @return true or false
* @since 2.3.0
*/
default boolean isDisallowTeamMemberIslands() {
return true;
}
}

View File

@ -33,40 +33,53 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
}
/**
* @param island - island
* @param island - island
* @param playerUUID - the player's UUID
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param admin - true if ths is due to an admin event
* @param location - the location
*/
public IslandBaseEvent(Island island, UUID playerUUID, boolean admin, Location location) {
super();
this.island = island;
this.playerUUID = playerUUID;
this.admin = admin;
this.location = location;
if (location != null) {
this.location = location;
} else if (island != null) {
this.location = island.getCenter();
} else {
this.location = null;
}
rawEvent = null;
}
/**
* @param island - island
* @param island - island
* @param playerUUID - the player's UUID
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param rawEvent - the raw event
* @param admin - true if ths is due to an admin event
* @param location - the location
* @param rawEvent - the raw event
*/
public IslandBaseEvent(Island island, UUID playerUUID, boolean admin, Location location, Event rawEvent) {
super();
this.island = island;
this.playerUUID = playerUUID;
this.admin = admin;
this.location = location;
if (location != null) {
this.location = location;
} else if (island != null) {
this.location = island.getCenter();
} else {
this.location = null;
}
this.rawEvent = rawEvent;
}
/**
* @return the island involved in this event. This may be null in the case of deleted islands, so use location instead
* @return the island involved in this event. This may be null in the case of
* deleted islands, so use location instead
*/
public Island getIsland(){
public Island getIsland() {
return island;
}
@ -94,6 +107,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* @return the location
*/
@Nullable
public Location getLocation() {
return location;
}
@ -118,6 +132,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* Get new event if this event is deprecated
*
* @return optional newEvent or empty if there is none
*/
public Optional<IslandBaseEvent> getNewEvent() {
@ -126,6 +141,7 @@ public abstract class IslandBaseEvent extends BentoBoxEvent implements Cancellab
/**
* Set the newer event so it can be obtained if this event is deprecated
*
* @param newEvent the newEvent to set
*/
public void setNewEvent(IslandBaseEvent newEvent) {

View File

@ -0,0 +1,50 @@
package world.bentobox.bentobox.api.events.flags;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import world.bentobox.bentobox.api.events.BentoBoxEvent;
/**
* This event is fired just before damage is prevented to visitors on an island, if that protection is provided.
* @author tastybento
*
*/
public class InvincibleVistorFlagDamageRemovalEvent extends BentoBoxEvent implements Cancellable {
private final Player player;
private final DamageCause cause;
private boolean cancel;
/**
* This event is fired just before damage is prevented to visitors on an island, if that protection is provided.
* @param player player being protected
* @param cause damage cause
*/
public InvincibleVistorFlagDamageRemovalEvent(Player player, DamageCause cause) {
this.player = player;
this.cause = cause;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
/**
* @return the player
*/
public Player getPlayer() {
return player;
}
/**
* @return the cause
*/
public DamageCause getCause() {
return cause;
}
}

View File

@ -176,7 +176,7 @@ public class IslandEvent extends IslandBaseEvent {
* Event that will fire when an island is named or renamed
* @since 1.24.0
*/
NAME,
NAME,
/**
* Event that will fire when the info command is executed. Allows addons to add to it
* @since 1.24.0
@ -334,7 +334,7 @@ public class IslandEvent extends IslandBaseEvent {
this.previousName = previousName;
return this;
}
/**
* Addon that triggered this event, e.g. BSkyBlock
* @param addon Addon.
@ -389,4 +389,4 @@ public class IslandEvent extends IslandBaseEvent {
}
}
}
}

View File

@ -377,35 +377,40 @@ public class Flag implements Comparable<Flag> {
* Converts a flag to a panel item. The content of the flag will change depending on who the user is and where they are.
* @param plugin - plugin
* @param user - user that will see this flag
* @param world - the world this flag is being shown for. If island is present, then world is the same as the island.
* @param island - target island, if any
* @param invisible - true if this flag is not visible to players
* @return - PanelItem for this flag or null if item is invisible to user
*/
@Nullable
public PanelItem toPanelItem(BentoBox plugin, User user, @Nullable Island island, boolean invisible) {
public PanelItem toPanelItem(BentoBox plugin, User user, World world, @Nullable Island island, boolean invisible) {
// Invisibility
if (!user.isOp() && invisible) {
return null;
}
// Start the flag conversion
PanelItemBuilder pib = new PanelItemBuilder()
.icon(ItemParser.parse(user.getTranslationOrNothing(this.getIconReference()), new ItemStack(icon)))
.name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME, user.getTranslation(getNameReference())))
.name(user.getTranslation("protection.panel.flag-item.name-layout", TextVariables.NAME,
user.getTranslation(getNameReference())))
.clickHandler(clickHandler)
.invisible(invisible);
if (hasSubPanel()) {
pib.description(user.getTranslation("protection.panel.flag-item.menu-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
return pib.build();
}
return switch (getType()) {
case PROTECTION -> createProtectionFlag(plugin, user, island, pib).build();
case SETTING -> createSettingFlag(user, island, pib).build();
case WORLD_SETTING -> createWorldSettingFlag(user, pib).build();
case WORLD_SETTING -> createWorldSettingFlag(user, world, pib).build();
};
}
private PanelItemBuilder createWorldSettingFlag(User user, PanelItemBuilder pib) {
String worldSetting = this.isSetForWorld(user.getWorld()) ? user.getTranslation("protection.panel.flag-item.setting-active")
private PanelItemBuilder createWorldSettingFlag(User user, World world, PanelItemBuilder pib) {
String worldSetting = this.isSetForWorld(world)
? user.getTranslation("protection.panel.flag-item.setting-active")
: user.getTranslation("protection.panel.flag-item.setting-disabled");
pib.description(user.getTranslation("protection.panel.flag-item.setting-layout", TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())
, "[setting]", worldSetting));
@ -427,19 +432,24 @@ public class Flag implements Comparable<Flag> {
private PanelItemBuilder createProtectionFlag(BentoBox plugin, User user, Island island, PanelItemBuilder pib) {
if (island != null) {
int y = island.getFlag(this);
// Protection flag
pib.description(user.getTranslation("protection.panel.flag-item.description-layout",
TextVariables.DESCRIPTION, user.getTranslation(getDescriptionReference())));
plugin.getRanksManager().getRanks().forEach((reference, score) -> {
if (score > RanksManager.BANNED_RANK && score < island.getFlag(this)) {
RanksManager.getInstance().getRanks().forEach((reference, score) -> {
if (score > RanksManager.BANNED_RANK && score < y) {
pib.description(user.getTranslation("protection.panel.flag-item.blocked-rank") + user.getTranslation(reference));
} else if (score <= RanksManager.OWNER_RANK && score > island.getFlag(this)) {
} else if (score <= RanksManager.OWNER_RANK && score > y) {
pib.description(user.getTranslation("protection.panel.flag-item.allowed-rank") + user.getTranslation(reference));
} else if (score == island.getFlag(this)) {
} else if (score == y) {
pib.description(user.getTranslation("protection.panel.flag-item.minimal-rank") + user.getTranslation(reference));
}
});
}
return pib;
}
@ -467,7 +477,7 @@ public class Flag implements Comparable<Flag> {
public Set<Flag> getSubflags() {
return subflags;
}
/**
* Set the name of this flag for a specified locale. This enables the flag's name to be assigned via API. It will not be stored anywhere
* and must be rewritten using this call every time the flag is built.
@ -480,7 +490,7 @@ public class Flag implements Comparable<Flag> {
public boolean setTranslatedName(Locale locale, String name) {
return BentoBox.getInstance().getLocalesManager().setTranslation(locale, getNameReference(), name);
}
/**
* Set the name of this flag for a specified locale. This enables the flag's name to be assigned via API. It will not be stored anywhere
* and must be rewritten using this call every time the flag is built.
@ -679,11 +689,11 @@ public class Flag implements Comparable<Flag> {
public Flag build() {
// If no clickHandler has been set, then apply default ones
if (clickHandler == null) {
switch (type) {
case SETTING -> clickHandler = new IslandToggleClick(id);
case WORLD_SETTING -> clickHandler = new WorldToggleClick(id);
default -> clickHandler = new CycleClick(id);
}
clickHandler = switch (type) {
case SETTING -> new IslandToggleClick(id);
case WORLD_SETTING -> new WorldToggleClick(id);
default -> new CycleClick(id);
};
}
return new Flag(this);

View File

@ -4,6 +4,7 @@ import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.event.inventory.ClickType;
import world.bentobox.bentobox.BentoBox;
@ -59,6 +60,11 @@ public class CycleClick implements PanelItem.ClickHandler {
@Override
public boolean onClick(Panel panel, User user2, ClickType click, int slot) {
if (panel.getWorld().isEmpty()) {
plugin.logError("Panel " + panel.getName()
+ " has no world associated with it. Please report this bug to the author.");
return true;
}
// This click listener is used with TabbedPanel and SettingsTabs only
TabbedPanel tp = (TabbedPanel)panel;
SettingsTab st = (SettingsTab)tp.getActiveTab();
@ -67,7 +73,7 @@ public class CycleClick implements PanelItem.ClickHandler {
this.user = user2;
changeOccurred = false;
// Permission prefix
String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld()));
String prefix = plugin.getIWM().getPermissionPrefix(Util.getWorld(panel.getWorld().get()));
String reqPerm = prefix + "settings." + id;
String allPerms = prefix + "settings.*";
if (!user.hasPermission(reqPerm) && !user.hasPermission(allPerms)
@ -81,18 +87,17 @@ public class CycleClick implements PanelItem.ClickHandler {
// Shift Left Click toggles player visibility
if (island != null && (user.isOp() || island.isAllowed(user, Flags.CHANGE_SETTINGS) || user.hasPermission(prefix + "admin.settings"))) {
changeOccurred = true;
RanksManager rm = plugin.getRanksManager();
plugin.getFlagsManager().getFlag(id).ifPresent(flag -> {
// Rank
int currentRank = island.getFlag(flag);
if (click.equals(ClickType.LEFT)) {
leftClick(flag, rm, currentRank);
leftClick(flag, currentRank);
} else if (click.equals(ClickType.RIGHT)) {
rightClick(flag, rm, currentRank);
rightClick(flag, currentRank);
} else if (click.equals(ClickType.SHIFT_LEFT) && user2.isOp()) {
leftShiftClick(flag);
leftShiftClick(flag, panel.getWorld().get());
}
});
} else {
@ -109,16 +114,16 @@ public class CycleClick implements PanelItem.ClickHandler {
// Player is not the allowed to change settings.
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK,
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
user.getTranslation(RanksManager.getInstance().getRank(Objects.requireNonNull(island).getRank(user))));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
}
private void leftClick(Flag flag, RanksManager rm, int currentRank) {
private void leftClick(Flag flag, int currentRank) {
if (currentRank >= maxRank) {
island.setFlag(flag, minRank);
} else {
island.setFlag(flag, rm.getRankUpValue(currentRank));
island.setFlag(flag, RanksManager.getInstance().getRankUpValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_OFF, 1F, 1F);
// Fire event
@ -132,11 +137,11 @@ public class CycleClick implements PanelItem.ClickHandler {
}
private void rightClick(Flag flag, RanksManager rm, int currentRank) {
private void rightClick(Flag flag, int currentRank) {
if (currentRank <= minRank) {
island.setFlag(flag, maxRank);
} else {
island.setFlag(flag, rm.getRankDownValue(currentRank));
island.setFlag(flag, RanksManager.getInstance().getRankDownValue(currentRank));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event
@ -150,16 +155,16 @@ public class CycleClick implements PanelItem.ClickHandler {
}
private void leftShiftClick(Flag flag) {
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
private void leftShiftClick(Flag flag, World world) {
if (!plugin.getIWM().getHiddenFlags(world).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(world).add(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
} else {
plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID());
plugin.getIWM().getHiddenFlags(world).remove(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F);
}
// Save changes
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
plugin.getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings);
}

View File

@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.panels.TabbedPanel;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.panels.settings.SettingsTab;
import world.bentobox.bentobox.util.Util;
@ -112,7 +113,7 @@ public class IslandToggleClick implements ClickHandler {
// Player is not the allowed to change settings.
user.sendMessage("general.errors.insufficient-rank",
TextVariables.RANK,
user.getTranslation(plugin.getRanksManager().getRank(Objects.requireNonNull(island).getRank(user))));
user.getTranslation(RanksManager.getInstance().getRank(Objects.requireNonNull(island).getRank(user))));
}
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);

View File

@ -2,6 +2,7 @@ package world.bentobox.bentobox.api.flags.clicklisteners;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.event.inventory.ClickType;
import world.bentobox.bentobox.BentoBox;
@ -32,12 +33,8 @@ public class WorldToggleClick implements ClickHandler {
@Override
public boolean onClick(Panel panel, User user, ClickType click, int slot) {
// Get the world
if (!plugin.getIWM().inWorld(user.getLocation())) {
user.sendMessage("general.errors.wrong-world");
return true;
}
String reqPerm = plugin.getIWM().getPermissionPrefix(Util.getWorld(user.getWorld())) + "admin.world.settings." + id;
World world = panel.getWorld().orElseThrow(); // The panel must have a world
String reqPerm = plugin.getIWM().getPermissionPrefix(world) + "admin.world.settings." + id;
if (!user.hasPermission(reqPerm)) {
user.sendMessage("general.errors.no-permission", TextVariables.PERMISSION, reqPerm);
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_METAL_HIT, 1F, 1F);
@ -46,31 +43,34 @@ public class WorldToggleClick implements ClickHandler {
// Get flag
plugin.getFlagsManager().getFlag(id).ifPresent(flag -> {
if (click.equals(ClickType.SHIFT_LEFT) && user.isOp()) {
if (!plugin.getIWM().getHiddenFlags(user.getWorld()).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(user.getWorld()).add(flag.getID());
if (!plugin.getIWM().getHiddenFlags(world).contains(flag.getID())) {
plugin.getIWM().getHiddenFlags(world).add(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_GLASS_BREAK, 1F, 1F);
} else {
plugin.getIWM().getHiddenFlags(user.getWorld()).remove(flag.getID());
plugin.getIWM().getHiddenFlags(world).remove(flag.getID());
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME, 1F, 1F);
}
// Save changes
plugin.getIWM().getAddon(user.getWorld()).ifPresent(GameModeAddon::saveWorldSettings);
plugin.getIWM().getAddon(world).ifPresent(GameModeAddon::saveWorldSettings);
} else {
// Toggle flag
flag.setSetting(user.getWorld(), !flag.isSetForWorld(user.getWorld()));
flag.setSetting(world, !flag.isSetForWorld(world));
user.getPlayer().playSound(user.getLocation(), Sound.BLOCK_STONE_BUTTON_CLICK_ON, 1F, 1F);
// Fire event
Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), flag, flag.isSetForWorld(user.getWorld())));
Bukkit.getPluginManager().callEvent(
new FlagWorldSettingChangeEvent(world, user.getUniqueId(), flag, flag.isSetForWorld(world)));
// Subflag support
if (flag.hasSubflags()) {
// Fire events for all subflags as well
flag.getSubflags().forEach(subflag -> Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(user.getWorld(), user.getUniqueId(), subflag, subflag.isSetForWorld(user.getWorld()))));
flag.getSubflags().forEach(
subflag -> Bukkit.getPluginManager().callEvent(new FlagWorldSettingChangeEvent(world,
user.getUniqueId(), subflag, subflag.isSetForWorld(world))));
}
}
// Save world settings
plugin.getIWM().getAddon(Util.getWorld(user.getWorld())).ifPresent(GameModeAddon::saveWorldSettings);
plugin.getIWM().getAddon(Util.getWorld(world)).ifPresent(GameModeAddon::saveWorldSettings);
});
return true;
}

View File

@ -69,5 +69,7 @@ public abstract class Hook {
* Returns an explanation that will be sent to the user to tell them why the hook process did not succeed.
* @return the probable causes why the hook process did not succeed.
*/
public abstract String getFailureCause();
public String getFailureCause() {
return "";
}
}

View File

@ -13,6 +13,7 @@ import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.listeners.PanelListenerManager;
import world.bentobox.bentobox.util.heads.HeadGetter;
import world.bentobox.bentobox.util.heads.HeadRequester;
@ -30,18 +31,27 @@ public class Panel implements HeadRequester, InventoryHolder {
private User user;
private String name;
private World world;
private Island island;
/**
* Various types of Panel that can be created.
* Various types of Panels that can be created that use InventoryTypes.
* <br>
* The current list of inventories that cannot be created are:<br>
* <blockquote>
* {@link Type#INVENTORY}, {@link Type#HOPPER},
* {@link Type#DROPPER}, {@link Type#ANVIL}
* </blockquote>
*
* These relate to the Bukkit inventories with INVENTORY being the standard CHEST inventory.
* See {@link org.bukkit.event.inventory.InventoryType}.
* @since 1.7.0
*/
public enum Type {
INVENTORY,
HOPPER,
DROPPER
INVENTORY, HOPPER, DROPPER, ANVIL
}
public Panel() {}
public Panel() {
}
public Panel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener) {
this(name, items, size, user, listener, Type.INVENTORY);
@ -65,28 +75,28 @@ public class Panel implements HeadRequester, InventoryHolder {
pb.getUser(), pb.getListener(), pb.getPanelType());
}
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user,
PanelListener listener) {
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener) {
this.makePanel(name, items, size, user, listener, Type.INVENTORY);
}
/**
* @since 1.7.0
*/
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user,
PanelListener listener, Type type) {
protected void makePanel(String name, Map<Integer, PanelItem> items, int size, User user, PanelListener listener,
Type type) {
this.name = name;
this.items = items;
// Create panel
switch (type) {
case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
case INVENTORY -> inventory = Bukkit.createInventory(null, fixSize(size), name);
case HOPPER -> inventory = Bukkit.createInventory(null, InventoryType.HOPPER, name);
case DROPPER -> inventory = Bukkit.createInventory(null, InventoryType.DROPPER, name);
case ANVIL -> inventory = Bukkit.createInventory(null, InventoryType.ANVIL, name);
}
// Fill the inventory and return
for (Map.Entry<Integer, PanelItem> en: items.entrySet()) {
for (Map.Entry<Integer, PanelItem> en : items.entrySet()) {
if (en.getKey() < 54) {
inventory.setItem(en.getKey(), en.getValue().getItem());
// Get player head async
@ -97,11 +107,13 @@ public class Panel implements HeadRequester, InventoryHolder {
}
this.listener = listener;
// If the listener is defined, then run setup
if (listener != null) listener.setup();
if (listener != null)
listener.setup();
// If the user is defined, then open panel immediately
this.user = user;
if (user != null) this.open(user);
if (user != null)
this.open(user);
}
private int fixSize(int size) {
@ -113,7 +125,8 @@ public class Panel implements HeadRequester, InventoryHolder {
// Make sure size is a multiple of 9 and is 54 max.
size = size + 8;
size -= (size % 9);
if (size > 54) size = 54;
if (size > 54)
size = 54;
} else {
return 9;
}
@ -194,12 +207,10 @@ public class Panel implements HeadRequester, InventoryHolder {
public void setHead(PanelItem item) {
// Update the panel item
// Find panel item index in items and replace it once more in inventory to update it.
this.items.entrySet().stream().
filter(entry -> entry.getValue() == item).
mapToInt(Map.Entry::getKey).findFirst()
.ifPresent(index ->
// Update item inside inventory to change icon only if item is inside panel.
this.inventory.setItem(index, item.getItem()));
this.items.entrySet().stream().filter(entry -> entry.getValue() == item).mapToInt(Map.Entry::getKey).findFirst()
.ifPresent(index ->
// Update item inside inventory to change icon only if item is inside panel.
this.inventory.setItem(index, item.getItem()));
}
/**
@ -226,5 +237,18 @@ public class Panel implements HeadRequester, InventoryHolder {
this.world = world;
}
/**
* @return the island
*/
public Island getIsland() {
return island;
}
/**
* @param island the island to set
*/
protected void setIsland(Island island) {
this.island = island;
}
}

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.api.panels;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@ -47,7 +48,6 @@ public class PanelItem {
meta.addItemFlags(ItemFlag.HIDE_DESTROYS);
meta.addItemFlags(ItemFlag.HIDE_PLACED_ON);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
icon.setItemMeta(meta);
}
@ -88,7 +88,6 @@ public class PanelItem {
this.name = name;
if (meta != null) {
meta.setDisplayName(name);
meta.setLocalizedName(name); //Localized name cannot be overridden by the player using an anvils
icon.setItemMeta(meta);
}
}
@ -99,7 +98,7 @@ public class PanelItem {
public void setInvisible(boolean invisible) {
this.invisible = invisible;
if (meta != null) {
if (meta != null && !inTest()) {
if (invisible) {
meta.addEnchant(Enchantment.VANISHING_CURSE, 1, true);
meta.removeItemFlags(ItemFlag.HIDE_ENCHANTS);
@ -129,17 +128,29 @@ public class PanelItem {
public void setGlow(boolean glow) {
this.glow = glow;
if (inTest()) {
return;
}
if (meta != null) {
if (glow) {
meta.addEnchant(Enchantment.ARROW_DAMAGE, 0, glow);
meta.addEnchant(Enchantment.LURE, 0, glow);
} else {
meta.removeEnchant(Enchantment.ARROW_DAMAGE);
meta.removeEnchant(Enchantment.LURE);
}
icon.setItemMeta(meta);
}
}
/**
* This checks the stack trace for @Test to determine if a test is calling the code and skips.
* TODO: when we find a way to mock Enchantment, remove this.
* @return true if it's a test.
*/
private boolean inTest() {
return Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().endsWith("Test"));
}
/**
* @return the playerHead
*/

View File

@ -15,6 +15,19 @@ import org.eclipse.jdt.annotation.Nullable;
*/
public interface Tab {
/**
* @return the tabbed panel that owns this tab
*/
default TabbedPanel getParentPanel() {
return null;
}
/**
* @param parent set the tabbed panel that owns this tab
*/
default void setParentPanel(TabbedPanel parent) {
}
// The icon that should be shown at the top of the tabbed panel
PanelItem getIcon();

View File

@ -43,7 +43,10 @@ public class TabbedPanel extends Panel implements PanelListener {
*/
public TabbedPanel(TabbedPanelBuilder tpb) {
this.tpb = tpb;
// Set world
this.setWorld(tpb.getWorld());
// Set island context in Panel
this.setIsland(tpb.getIsland());
}
/* (non-Javadoc)
@ -71,6 +74,7 @@ public class TabbedPanel extends Panel implements PanelListener {
* @param page - the page of the tab to show (if multi paged)
*/
public void openPanel(int activeTab, int page) {
if (!tpb.getTabs().containsKey(activeTab)) {
// Request to open a non-existent tab
throw new InvalidParameterException("Attempt to open a non-existent tab in a tabbed panel. Missing tab #" + activeTab);
@ -85,21 +89,17 @@ public class TabbedPanel extends Panel implements PanelListener {
TreeMap<Integer, PanelItem> items = new TreeMap<>();
// Get the tab
Tab tab = tpb.getTabs().get(activeTab);
// Remove any tabs that have no items, if required
if (tpb.isHideIfEmpty()) {
tpb.getTabs().values().removeIf(t -> !t.equals(tab) && t.getPanelItems().stream().noneMatch(Objects::nonNull));
}
// Set up the tabbed header
setupHeader(tab, items);
// Show the active tab
if (tpb.getTabs().containsKey(activeTab)) {
List<PanelItem> panelItems = tab.getPanelItems();
// Adds the flag items
panelItems.stream().filter(Objects::nonNull).skip(page * ITEMS_PER_PAGE).limit(page * ITEMS_PER_PAGE + ITEMS_PER_PAGE).forEach(i -> items.put(items.lastKey() + 1, i));
// set up the footer
setupFooter(items);
// Add forward and backward icons
@ -179,6 +179,7 @@ public class TabbedPanel extends Panel implements PanelListener {
// Reset the closed flag
closed = false;
}
}
/**
@ -208,4 +209,5 @@ public class TabbedPanel extends Panel implements PanelListener {
public void setActiveTab(int activeTab) {
this.activeTab = activeTab;
}
}

View File

@ -3,10 +3,8 @@
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@ -22,20 +20,20 @@ import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.panels.reader.PanelTemplateRecord;
import world.bentobox.bentobox.api.user.User;
/**
* This class creates a new Panel from the template record.
*
* @author BONNe
* @since 1.17.3
*/
public class TemplatedPanel extends Panel
{
public class TemplatedPanel extends Panel {
/**
* TemplatedPanel constructor class which generates functional panel.
* @param builder Builder that contains all information about the panel that must be generated.
*
* @param builder Builder that contains all information about the panel that
* must be generated.
*/
public TemplatedPanel(@NonNull TemplatedPanelBuilder builder)
{
public TemplatedPanel(@NonNull TemplatedPanelBuilder builder) {
this.user = builder.getUser();
this.setWorld(builder.getWorld());
this.setListener(builder.getListener());
@ -48,218 +46,63 @@ public class TemplatedPanel extends Panel
this.parameters = builder.getParameters().toArray(new String[0]);
if (this.panelTemplate == null)
{
if (this.panelTemplate == null) {
BentoBox.getInstance().logError("Cannot generate panel because template is not loaded.");
}
else
{
} else {
this.generatePanel();
}
}
/**
* This method generates the panel from the template.
*/
private void generatePanel()
{
Map<Integer, PanelItem> items = switch (this.panelTemplate.type())
{
case INVENTORY -> this.populateInventoryPanel();
case HOPPER -> this.populateHopperPanel();
case DROPPER -> this.populateDropperPanel();
};
private void generatePanel() {
Map<Integer, PanelItem> items = switch (this.panelTemplate.type()) {
case INVENTORY -> this.populateInventoryPanel(new PanelItem[6][9]);
case HOPPER -> this.populateInventoryPanel(new PanelItem[1][5]);
case DROPPER -> this.populateInventoryPanel(new PanelItem[3][3]);
case ANVIL -> this.populateInventoryPanel(new PanelItem[4][9]);
};
super.makePanel(this.user.getTranslation(this.panelTemplate.title(), this.parameters),
items,
items.keySet().stream().max(Comparator.naturalOrder()).orElse(9),
this.user,
this.getListener().orElse(null),
this.panelTemplate.type());
super.makePanel(this.user.getTranslation(this.panelTemplate.title(), this.parameters), items,
items.keySet().stream().max(Comparator.naturalOrder()).orElse(9), this.user,
this.getListener().orElse(null), this.panelTemplate.type());
}
/**
* This method creates map with item indexes and their icons that will be added into
* Inventory Panel.
* This method creates map with item indexes and their icons that will be added
* into Inventory Panel.
*
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateInventoryPanel() {
PanelItem[][] itemArray = new PanelItem[6][9];
processItemData(itemArray);
removeEmptyLines(itemArray);
fillBorder(itemArray);
fillBackground(itemArray);
return createItemMap(itemArray);
private Map<Integer, PanelItem> populateInventoryPanel(PanelItem[][] itemArray) {
this.preProcessPanelTemplate(itemArray);
this.processItemData(itemArray);
this.removeEmptyLines(itemArray);
this.fillBorder(itemArray);
this.fillBackground(itemArray);
return this.createItemMap(itemArray);
}
private void processItemData(PanelItem[][] itemArray) {
/**
* This method processes what items should be added into the panel. It counts
* how many same type buttons should be generated. This cannot be done in the
* same step as creating button.
*
* @param itemArray The double array with items into panel
*/
private void preProcessPanelTemplate(PanelItem[][] itemArray) {
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
// Analyze the GUI button layout a bit.
for (int i = 0; i < panelTemplate.content().length; i++) {
for (int k = 0; k < panelTemplate.content()[i].length; k++) {
ItemTemplateRecord rec = panelTemplate.content()[i][k];
if (rec != null && rec.dataMap().containsKey("type")) {
String type = String.valueOf(rec.dataMap().get("type"));
int counter = typeSlotMap.computeIfAbsent(type, key -> 0);
typeSlotMap.put(type, counter + 1);
}
// Make buttons for the GUI
itemArray[i][k] = makeButton(panelTemplate.content()[i][k]);
}
}
}
private void removeEmptyLines(PanelItem[][] itemArray) {
boolean[] showLine = panelTemplate.forcedRows();
for (int i = 0; i < panelTemplate.content().length; i++) {
boolean emptyLine = true;
for (int k = 0; emptyLine && k < panelTemplate.content()[i].length; k++) {
emptyLine = itemArray[i][k] == null;
}
showLine[i] = showLine[i] || !emptyLine;
}
}
private void fillBorder(PanelItem[][] itemArray) {
if (panelTemplate.border() == null) {
return;
}
PanelItem template = makeTemplate(panelTemplate.border());
int numRows = itemArray.length;
int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
if (i == 0 || i == numRows - 1) {
// Fill first and last row completely with border.
for (int k = 0; k < numCols; k++) {
if (itemArray[i][k] == null) {
itemArray[i][k] = template;
}
}
} else {
// Fill first and last element in row with border.
if (itemArray[i][0] == null) {
itemArray[i][0] = template;
}
if (itemArray[i][numCols - 1] == null) {
itemArray[i][numCols - 1] = template;
}
}
}
panelTemplate.forcedRows()[0] = true;
panelTemplate.forcedRows()[numRows - 1] = true;
}
private void fillBackground(PanelItem[][] itemArray) {
if (panelTemplate.background() != null) {
PanelItem template = makeTemplate(panelTemplate.background());
for (int i = 0; i < 6; i++) {
for (int k = 0; k < 9; k++) {
if (itemArray[i][k] == null) {
itemArray[i][k] = template;
}
}
}
}
}
private Map<Integer, PanelItem> createItemMap(PanelItem[][] itemArray) {
Map<Integer, PanelItem> itemMap = new HashMap<>(6 * 9);
int correctIndex = 0;
for (int i = 0; i < itemArray.length; i++) {
final boolean iterate = panelTemplate.forcedRows()[i];
for (int k = 0; iterate && k < itemArray[i].length; k++) {
if (itemArray[i][k] != null) {
itemMap.put(correctIndex, itemArray[i][k]);
}
correctIndex++;
}
}
return itemMap;
}
/**
* This method creates map with item indexes and their icons that will be added into
* hopper Panel.
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateHopperPanel()
{
// Init item array with the max available size.
PanelItem[] itemArray = new PanelItem[5];
// Analyze the template
for (int i = 0; i < 5; i++)
{
ItemTemplateRecord rec = this.panelTemplate.content()[0][i];
if (rec != null && rec.dataMap().containsKey("type"))
{
String type = String.valueOf(rec.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
this.typeSlotMap.put(type, counter + 1);
}
}
// Make buttons
for (int i = 0; i < 5; i++)
{
itemArray[i] = this.makeButton(this.panelTemplate.content()[0][i]);
}
// Now fill the background.
if (this.panelTemplate.background() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.background());
for (int i = 0; i < 5; i++)
{
if (itemArray[i] == null)
{
itemArray[i] = template;
}
}
}
// Now place all panel items with their indexes into item map.
Map<Integer, PanelItem> itemMap = new HashMap<>(5);
int correctIndex = 0;
for (PanelItem panelItem : itemArray)
{
if (panelItem != null)
{
itemMap.put(correctIndex, panelItem);
}
correctIndex++;
}
return itemMap;
}
/**
* This method creates map with item indexes and their icons that will be added into
* dropper Panel.
* @return Map that contains indexes linked to the correct panel item.
*/
@NonNull
private Map<Integer, PanelItem> populateDropperPanel()
{
// Analyze the template
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
for (int k = 0; k < numCols; k++) {
ItemTemplateRecord rec = this.panelTemplate.content()[i][k];
if (rec != null && rec.dataMap().containsKey("type"))
{
if (rec != null && rec.dataMap().containsKey("type")) {
String type = String.valueOf(rec.dataMap().get("type"));
int counter = this.typeSlotMap.computeIfAbsent(type, key -> 0);
@ -267,47 +110,114 @@ public class TemplatedPanel extends Panel
}
}
}
}
// Init item array with the max available size.
PanelItem[][] itemArray = new PanelItem[3][3];
/**
* This method populates item array with all buttons.
*
* @param itemArray The double array with items into panel
*/
private void processItemData(PanelItem[][] itemArray) {
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
// Make buttons
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
for (int i = 0; i < numRows; i++) {
for (int k = 0; k < numCols; k++) {
itemArray[i][k] = this.makeButton(this.panelTemplate.content()[i][k]);
}
}
}
// Now fill the background.
if (this.panelTemplate.background() != null)
{
PanelItem template = this.makeTemplate(this.panelTemplate.background());
/**
* This method removes all empty lines if they are not forced to be showed.
*
* @param itemArray The double array with items into panel
*/
private void removeEmptyLines(PanelItem[][] itemArray) {
// After items are created, remove empty lines.
boolean[] showLine = this.panelTemplate.forcedRows();
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] == null)
{
itemArray[i][k] = template;
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
boolean emptyLine = true;
for (int k = 0; emptyLine && k < numCols; k++) {
emptyLine = itemArray[i][k] == null;
}
// Do not generate fallback for "empty" lines.
showLine[i] = showLine[i] || !emptyLine;
}
}
/**
* Fills the border of a panel with a template item.
*
* @param itemArray 2D array of panel items
*/
private void fillBorder(PanelItem[][] itemArray) {
if (this.panelTemplate.border() == null)
return;
PanelItem template = makeTemplate(this.panelTemplate.border());
int numRows = itemArray.length;
int numCols = itemArray[0].length;
for (int row = 0; row < numRows; row++) {
for (int col = 0; col < numCols; col++) {
// Fill border rows completely, and first/last columns of other rows
if (row == 0 || row == numRows - 1 || col == 0 || col == numCols - 1) {
if (itemArray[row][col] == null) {
itemArray[row][col] = template;
}
}
}
}
// Init item map with the max available size.
Map<Integer, PanelItem> itemMap = new HashMap<>(9);
this.panelTemplate.forcedRows()[0] = true;
this.panelTemplate.forcedRows()[numRows - 1] = true;
}
/**
* This method fills background elements with item from template.
*
* @param itemArray The double array with items into panel
*/
private void fillBackground(PanelItem[][] itemArray) {
if (this.panelTemplate.background() == null) {
return;
}
PanelItem template = this.makeTemplate(this.panelTemplate.background());
final int numRows = itemArray.length;
final int numCols = itemArray[0].length;
for (int i = 0; i < numRows; i++) {
for (int k = 0; k < numCols; k++) {
if (itemArray[i][k] == null) {
itemArray[i][k] = template;
}
}
}
}
/**
* This method converts to PanelItem array to the correct item map.
*
* @param itemArray The double array with items into panel
* @return The map that links index of button to panel item.
*/
private Map<Integer, PanelItem> createItemMap(PanelItem[][] itemArray) {
Map<Integer, PanelItem> itemMap = new HashMap<>(itemArray.length * itemArray[0].length);
int correctIndex = 0;
for (int i = 0; i < 3; i++)
{
for (int k = 0; k < 3; k++)
{
if (itemArray[i][k] != null)
{
for (int i = 0; i < itemArray.length; i++) {
final boolean iterate = this.panelTemplate.forcedRows()[i];
for (int k = 0; iterate && k < itemArray[i].length; k++) {
if (itemArray[i][k] != null) {
itemMap.put(correctIndex, itemArray[i][k]);
}
@ -318,43 +228,36 @@ public class TemplatedPanel extends Panel
return itemMap;
}
/**
* This method passes button creation from given record template.
*
* @param rec Template of the button that must be created.
* @return PanelItem of the template, otherwise null.
*/
@Nullable
private PanelItem makeButton(@Nullable ItemTemplateRecord rec)
{
if (rec == null)
{
private PanelItem makeButton(@Nullable ItemTemplateRecord rec) {
if (rec == null) {
// Immediate exit if record is null.
return null;
}
if (rec.dataMap().containsKey("type"))
{
// If dataMap is not null, and it is not empty, then pass button to the object creator function.
if (rec.dataMap().containsKey("type")) {
// If dataMap is not null, and it is not empty, then pass button to the object
// creator function.
return this.makeAddonButton(rec);
}
else
{
} else {
PanelItemBuilder itemBuilder = new PanelItemBuilder();
if (rec.icon() != null)
{
if (rec.icon() != null) {
itemBuilder.icon(rec.icon().clone());
}
if (rec.title() != null)
{
if (rec.title() != null) {
itemBuilder.name(this.user.getTranslation(rec.title()));
}
if (rec.description() != null)
{
if (rec.description() != null) {
itemBuilder.description(this.user.getTranslation(rec.description()));
}
@ -366,20 +269,18 @@ public class TemplatedPanel extends Panel
}
}
/**
* This method passes button to the type creator, if that exists.
*
* @param rec Template of the button that must be created.
* @return PanelItem of the button created by typeCreator, otherwise null.
*/
@Nullable
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord rec)
{
private PanelItem makeAddonButton(@NonNull ItemTemplateRecord rec) {
// Get object type.
String type = String.valueOf(rec.dataMap().getOrDefault("type", ""));
if (!this.typeCreators.containsKey(type))
{
if (!this.typeCreators.containsKey(type)) {
// There are no object with a given type.
return this.makeFallBack(rec.fallback());
}
@ -387,9 +288,7 @@ public class TemplatedPanel extends Panel
BiFunction<ItemTemplateRecord, ItemSlot, PanelItem> buttonBuilder = this.typeCreators.get(type);
// Get next slot index.
ItemSlot itemSlot = this.typeIndex.containsKey(type) ?
this.typeIndex.get(type) :
new ItemSlot(0, this.typeSlotMap);
ItemSlot itemSlot = this.typeIndex.containsKey(type) ? this.typeIndex.get(type) : new ItemSlot(0, this);
this.typeIndex.put(type, itemSlot.nextItemSlot());
// Try to get next object.
@ -397,43 +296,38 @@ public class TemplatedPanel extends Panel
return item == null ? this.makeFallBack(rec.fallback()) : item;
}
/**
* This method creates a fall back button for given record.
*
* @param rec Record which fallback must be created.
* @return PanelItem if fallback was creates successfully, otherwise null.
*/
@Nullable
private PanelItem makeFallBack(@Nullable ItemTemplateRecord rec)
{
private PanelItem makeFallBack(@Nullable ItemTemplateRecord rec) {
return rec == null ? null : this.makeButton(rec.fallback());
}
/**
* This method translates template record into a panel item.
*
* @param rec Record that must be translated.
* @return PanelItem that contains all information from the record.
*/
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem rec)
{
private PanelItem makeTemplate(PanelTemplateRecord.TemplateItem rec) {
PanelItemBuilder itemBuilder = new PanelItemBuilder();
// Read icon only if it is not null.
if (rec.icon() != null)
{
if (rec.icon() != null) {
itemBuilder.icon(rec.icon().clone());
}
// Read title only if it is not null.
if (rec.title() != null)
{
if (rec.title() != null) {
itemBuilder.name(this.user.getTranslation(rec.title()));
}
// Read description only if it is not null.
if (rec.description() != null)
{
if (rec.description() != null) {
itemBuilder.description(this.user.getTranslation(rec.description()));
}
@ -441,37 +335,67 @@ public class TemplatedPanel extends Panel
return itemBuilder.build();
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* This record contains current slot object and map that links types with a number of slots in
* panel with it.
* Some buttons need information about all types, like previous/next.
* @param slot Index of object in current panel.
* @param amountMap Map that links types with number of objects in panel.
* This record contains current slot object and map that links types with a
* number of slots in panel with it. Some buttons need information about all
* types, like previous/next.
*
* @param slot Index of object in current panel.
* @param parentPanel The parent panel for current Item.
*/
public record ItemSlot(int slot, Map<String, Integer> amountMap)
{
public record ItemSlot(int slot, TemplatedPanel parentPanel) {
/**
* This method returns new record object with iterative slot index.
*
* @return New ItemSlot object that has increased slot index by 1.
*/
ItemSlot nextItemSlot()
{
return new ItemSlot(this.slot() + 1, this.amountMap());
ItemSlot nextItemSlot() {
return new ItemSlot(this.slot() + 1, this.parentPanel());
}
}
/**
* This method returns map that links button types with a number of slots that
* this button is present.
*
* @return Map that links button type to amount in the gui.
* @deprecated Use {@link #amount(String)} instead.
*/
@Deprecated
public Map<String, Integer> amountMap() {
return this.parentPanel.typeSlotMap;
}
/**
* This returns amount of slots for given button type.
*
* @param type Type of the button.
* @return Number of slots in panel.
*/
public int amount(String type) {
return this.amount(type, 0);
}
/**
* This returns amount of slots for given button type.
*
* @param type Type of the button.
* @param defaultValue The default value if the type is not found
* @return Number of slots in panel.
*/
public int amount(String type, int defaultValue) {
return this.parentPanel.typeSlotMap.getOrDefault(type, defaultValue);
}
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* The GUI template record.
*/
@ -499,6 +423,7 @@ public class TemplatedPanel extends Panel
/**
* Stores the parameters for panel title object.
*
* @since 1.20.0
*/
private final String[] parameters;

View File

@ -9,6 +9,7 @@ import org.bukkit.World;
import world.bentobox.bentobox.api.panels.Tab;
import world.bentobox.bentobox.api.panels.TabbedPanel;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* Builds {@link TabbedPanel}'s
@ -23,6 +24,17 @@ public class TabbedPanelBuilder {
private World world;
private User user;
private boolean hideIfEmpty;
private Island island;
/**
* Set the island related to this panel
* @param island island
* @return PanelBuilder - PanelBuilder
*/
public TabbedPanelBuilder island(Island island) {
this.island = island;
return this;
}
/**
* Forces panel to be a specific number of slots.
@ -97,7 +109,10 @@ public class TabbedPanelBuilder {
if (!tabs.isEmpty() && !tabs.containsKey(startingSlot)) {
startingSlot = ((TreeMap<Integer, Tab>)tabs).firstKey();
}
return new TabbedPanel(this);
TabbedPanel tp = new TabbedPanel(this);
// Set tab parents
tabs.values().forEach(tab -> tab.setParentPanel(tp));
return tp;
}
/**
@ -142,6 +157,12 @@ public class TabbedPanelBuilder {
return hideIfEmpty;
}
/**
* @return the island
*/
public Island getIsland() {
return island;
}
}

View File

@ -3,10 +3,8 @@
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -29,13 +27,32 @@ import org.eclipse.jdt.annotation.Nullable;
*
* @since 1.17.3
*/
public record ItemTemplateRecord(@Nullable ItemStack icon,
public record ItemTemplateRecord(
/**
* ItemStack of the Item
*/
@Nullable ItemStack icon,
/**
* Title of the item
*/
@Nullable String title,
/**
* Lore message of the item
*/
@Nullable String description,
/**
* List of Actions for a button
*/
@NonNull List<ActionRecords> actions,
/**
* DataMap that links additional objects for a button.
*/
@NonNull Map<String, Object> dataMap,
@Nullable ItemTemplateRecord fallback)
{
/**
* FallBack item if current one is not possible to generate.
*/
@Nullable ItemTemplateRecord fallback) {
/**
* Instantiates a new Item template record without actions and data map.
*
@ -44,39 +61,32 @@ public record ItemTemplateRecord(@Nullable ItemStack icon,
* @param description the description
* @param fallback the fallback
*/
public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord fallback)
{
public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord fallback) {
this(icon, title, description, new ArrayList<>(6), new HashMap<>(0), fallback);
}
/**
* This method adds given object associated with key into data map.
* @param key Key value of object.
* @param data Data that is associated with a key.
*/
public void addData(String key, Object data)
{
public void addData(String key, Object data) {
this.dataMap.put(key, data);
}
/**
* Add action to the actions list.
*
* @param actionData the action data
*/
public void addAction(ActionRecords actionData)
{
public void addAction(ActionRecords actionData) {
this.actions.add(actionData);
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* The Action Records holds data about each action.
*
@ -85,5 +95,22 @@ public record ItemTemplateRecord(@Nullable ItemStack icon,
* @param content the content of the action
* @param tooltip the tooltip of action
*/
public record ActionRecords(ClickType clickType, String actionType, String content, String tooltip) {}
public record ActionRecords(
/**
* the click type
*/
ClickType clickType,
/**
* the string that represents action type
*/
String actionType,
/**
* the content of the action
*/
String content,
/**
* the tooltip of action
*/
String tooltip) {
}
}

View File

@ -22,6 +22,7 @@ import org.eclipse.jdt.annotation.Nullable;
import com.google.common.base.Enums;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.util.ItemParser;
@ -83,6 +84,7 @@ public class TemplateReader
{
if (!panelLocation.exists())
{
BentoBox.getInstance().logError("Panel Template reader: Folder does not exist");
// Return null because folder does not exist.
return null;
}
@ -91,6 +93,7 @@ public class TemplateReader
if (!file.exists())
{
BentoBox.getInstance().logError(file.getAbsolutePath() + " does not exist for panel template");
// Return as file does not exist.
return null;
}
@ -117,6 +120,8 @@ public class TemplateReader
}
catch (IOException | InvalidConfigurationException e)
{
BentoBox.getInstance().logError("Error loading template");
BentoBox.getInstance().logStacktrace(e);
rec = null;
}
@ -133,6 +138,7 @@ public class TemplateReader
{
if (configurationSection == null)
{
BentoBox.getInstance().logError("No configuration section!");
// No data to return.
return null;
}

View File

@ -14,6 +14,7 @@ import java.util.UUID;
import org.apache.commons.lang.math.NumberUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
@ -33,6 +34,8 @@ import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import com.google.common.base.Enums;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.events.OfflineMessageEvent;
@ -42,13 +45,15 @@ import world.bentobox.bentobox.database.objects.Players;
import world.bentobox.bentobox.util.Util;
/**
* Combines {@link Player}, {@link OfflinePlayer} and {@link CommandSender} to provide convenience methods related to
* localization and generic interactions.
* Combines {@link Player}, {@link OfflinePlayer} and {@link CommandSender} to
* provide convenience methods related to localization and generic interactions.
* <br/>
* Therefore, a User could usually be a Player, an OfflinePlayer or the server's console.
* Preliminary checks should be performed before trying to run methods that relies on a specific implementation.
* <br/><br/>
* It is good practice to use the User instance whenever possible instead of Player or CommandSender.
* Therefore, a User could usually be a Player, an OfflinePlayer or the server's
* console. Preliminary checks should be performed before trying to run methods
* that relies on a specific implementation. <br/>
* <br/>
* It is good practice to use the User instance whenever possible instead of
* Player or CommandSender.
*
* @author tastybento
*/
@ -60,19 +65,23 @@ public class User implements MetaDataAble {
private static final Map<Particle, Class<?>> VALIDATION_CHECK;
static {
Map<Particle, Class<?>> v = new EnumMap<>(Particle.class);
v.put(Particle.REDSTONE, Particle.DustOptions.class);
v.put(Particle.ITEM_CRACK, ItemStack.class);
v.put(Particle.BLOCK_CRACK, BlockData.class);
v.put(Particle.BLOCK_DUST, BlockData.class);
v.put(Enums.getIfPresent(Particle.class, "DUST")
.or(Enums.getIfPresent(Particle.class, "REDSTONE").or(Particle.FLAME)), Particle.DustOptions.class);
if (Enums.getIfPresent(Particle.class, "ITEM").isPresent()) {
// 1.20.6 Particles
v.put(Particle.ITEM, ItemStack.class);
v.put(Particle.ITEM_COBWEB, ItemStack.class);
v.put(Particle.BLOCK, BlockData.class);
v.put(Particle.DUST_PILLAR, BlockData.class);
v.put(Particle.ENTITY_EFFECT, Color.class);
}
v.put(Particle.FALLING_DUST, BlockData.class);
v.put(Particle.BLOCK_MARKER, BlockData.class);
v.put(Particle.DUST_COLOR_TRANSITION, DustTransition.class);
v.put(Particle.VIBRATION, Vibration.class);
v.put(Particle.SCULK_CHARGE, Float.class);
v.put(Particle.SHRIEK, Integer.class);
v.put(Particle.LEGACY_BLOCK_CRACK, BlockData.class);
v.put(Particle.LEGACY_BLOCK_DUST, BlockData.class);
v.put(Particle.LEGACY_FALLING_DUST, BlockData.class);
VALIDATION_CHECK = Collections.unmodifiableMap(v);
}
@ -85,6 +94,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a CommandSender
*
* @param sender - command sender, e.g. console
* @return user - user
*/
@ -99,6 +109,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a Player object.
*
* @param player - the player
* @return user - user
*/
@ -113,6 +124,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from a UUID. This will always return a user object.
* If the player is offline then the getPlayer value will be null.
*
* @param uuid - UUID
* @return user - user
*/
@ -127,6 +139,7 @@ public class User implements MetaDataAble {
/**
* Gets an instance of User from an OfflinePlayer
*
* @param offlinePlayer offline Player
* @return user
* @since 1.3.0
@ -141,6 +154,7 @@ public class User implements MetaDataAble {
/**
* Removes this player from the User cache and player manager cache
*
* @param player the player
*/
public static void removePlayer(Player player) {
@ -193,6 +207,7 @@ public class User implements MetaDataAble {
/**
* Used for testing
*
* @param p - plugin
*/
public static void setPlugin(BentoBox p) {
@ -205,6 +220,7 @@ public class User implements MetaDataAble {
/**
* Get the user's inventory
*
* @return player's inventory
*/
@NonNull
@ -214,6 +230,7 @@ public class User implements MetaDataAble {
/**
* Get the user's location
*
* @return location
*/
@NonNull
@ -223,6 +240,7 @@ public class User implements MetaDataAble {
/**
* Get the user's name
*
* @return player's name
*/
@NonNull
@ -232,7 +250,9 @@ public class User implements MetaDataAble {
/**
* Get the user's display name
* @return player's display name if the player is online otherwise just their name
*
* @return player's display name if the player is online otherwise just their
* name
* @since 1.22.1
*/
@NonNull
@ -242,6 +262,7 @@ public class User implements MetaDataAble {
/**
* Check if the User is a player before calling this method. {@link #isPlayer()}
*
* @return the player
*/
@NonNull
@ -258,6 +279,7 @@ public class User implements MetaDataAble {
/**
* Use {@link #isOfflinePlayer()} before calling this method
*
* @return the offline player
* @since 1.3.0
*/
@ -285,7 +307,8 @@ public class User implements MetaDataAble {
/**
* @param permission permission string
* @return true if permission is empty or null or if the player has that permission or if the player is op.
* @return true if permission is empty or null or if the player has that
* permission or if the player is op.
*/
public boolean hasPermission(@Nullable String permission) {
return permission == null || permission.isEmpty() || isOp() || sender.hasPermission(permission);
@ -293,6 +316,7 @@ public class User implements MetaDataAble {
/**
* Removes permission from user
*
* @param name - Name of the permission to remove
* @return true if successful
* @since 1.5.0
@ -310,6 +334,7 @@ public class User implements MetaDataAble {
/**
* Add a permission to user
*
* @param name - Name of the permission to attach
* @return The PermissionAttachment that was just created
* @since 1.5.0
@ -324,6 +349,7 @@ public class User implements MetaDataAble {
/**
* Checks if user is Op
*
* @return true if user is Op
*/
public boolean isOp() {
@ -337,30 +363,42 @@ public class User implements MetaDataAble {
}
/**
* Get the maximum value of a numerical permission setting.
* If a player is given an explicit negative number then this is treated as "unlimited" and returned immediately.
* @param permissionPrefix the start of the perm, e.g., {@code plugin.mypermission}
* @param defaultValue the default value; the result may be higher or lower than this
* Get the maximum value of a numerical permission setting. If a player is given
* an explicit negative number then this is treated as "unlimited" and returned
* immediately.
*
* @param permissionPrefix the start of the perm, e.g.,
* {@code plugin.mypermission}
* @param defaultValue the default value; the result may be higher or lower
* than this
* @return max value
*/
public int getPermissionValue(String permissionPrefix, int defaultValue) {
// If requester is console, then return the default value
if (!isPlayer()) return defaultValue;
if (!isPlayer())
return defaultValue;
// If there is a dot at the end of the permissionPrefix, remove it
if (permissionPrefix.endsWith(".")) {
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length()-1);
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length() - 1);
}
final String permPrefix = permissionPrefix + ".";
List<String> permissions = player.getEffectivePermissions().stream()
.filter(PermissionAttachmentInfo::getValue) // Must be a positive permission, not a negative one
.map(PermissionAttachmentInfo::getPermission)
.filter(permission -> permission.startsWith(permPrefix))
List<String> permissions = player.getEffectivePermissions().stream().filter(PermissionAttachmentInfo::getValue) // Must
// be
// a
// positive
// permission,
// not
// a
// negative
// one
.map(PermissionAttachmentInfo::getPermission).filter(permission -> permission.startsWith(permPrefix))
.toList();
if (permissions.isEmpty()) return defaultValue;
if (permissions.isEmpty())
return defaultValue;
return iteratePerms(permissions, permPrefix, defaultValue);
@ -376,7 +414,8 @@ public class User implements MetaDataAble {
String[] spl = permission.split(permPrefix);
if (spl.length > 1) {
if (!NumberUtils.isNumber(spl[1])) {
plugin.logError("Player " + player.getName() + " has permission: '" + permission + "' <-- the last part MUST be a number! Ignoring...");
plugin.logError("Player " + player.getName() + " has permission: '" + permission
+ "' <-- the last part MUST be a number! Ignoring...");
} else {
int v = Integer.parseInt(spl[1]);
if (v < 0) {
@ -393,27 +432,32 @@ public class User implements MetaDataAble {
/**
* Gets a translation for a specific world
* @param world - world of translation
*
* @param world - world of translation
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing
* has been found
* @since 1.3.0
*/
public String getTranslation(World world, String reference, String... variables) {
// Get translation.
String addonPrefix = plugin.getIWM()
.getAddon(world).map(a -> a.getDescription().getName().toLowerCase(Locale.ENGLISH) + ".").orElse("");
String addonPrefix = plugin.getIWM().getAddon(world)
.map(a -> a.getDescription().getName().toLowerCase(Locale.ENGLISH) + ".").orElse("");
return Util.translateColorCodes(translate(addonPrefix, reference, variables));
}
/**
* Gets a translation of this reference for this user with colors converted. Translations may be overridden by Addons
* by using the same reference prefixed by the addon name (from the Addon Description) in lower case.
* Gets a translation of this reference for this user with colors converted.
* Translations may be overridden by Addons by using the same reference prefixed
* by the addon name (from the Addon Description) in lower case.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or the reference if nothing
* has been found
*/
public String getTranslation(String reference, String... variables) {
// Get addonPrefix
@ -422,11 +466,13 @@ public class User implements MetaDataAble {
}
/**
* Gets a translation of this reference for this user without colors translated. Translations may be overridden by Addons
* by using the same reference prefixed by the addon name (from the Addon Description) in lower case.
* Gets a translation of this reference for this user without colors translated.
* Translations may be overridden by Addons by using the same reference prefixed
* by the addon name (from the Addon Description) in lower case.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string or the reference if nothing has been found
* @since 1.17.4
*/
@ -461,9 +507,11 @@ public class User implements MetaDataAble {
for (String prefix : plugin.getLocalesManager().getAvailablePrefixes(this)) {
String prefixTranslation = getTranslation("prefixes." + prefix);
// Replace the [gamemode] text variable
prefixTranslation = prefixTranslation.replace("[gamemode]", addon != null ? addon.getDescription().getName() : "[gamemode]");
prefixTranslation = prefixTranslation.replace("[gamemode]",
addon != null ? addon.getDescription().getName() : "[gamemode]");
// Replace the [friendly_name] text variable
prefixTranslation = prefixTranslation.replace("[friendly_name]", isPlayer() ? plugin.getIWM().getFriendlyName(getWorld()) : "[friendly_name]");
prefixTranslation = prefixTranslation.replace("[friendly_name]",
isPlayer() ? plugin.getIWM().getFriendlyName(getWorld()) : "[friendly_name]");
// Replace the prefix in the actual message
translation = translation.replace("[prefix_" + prefix + "]", prefixTranslation);
@ -506,10 +554,12 @@ public class User implements MetaDataAble {
/**
* Gets a translation of this reference for this user.
*
* @param reference - reference found in a locale file
* @param variables - variables to insert into translated string. Variables go in pairs, for example
* "[name]", "tastybento"
* @return Translated string with colors converted, or a blank String if nothing has been found
* @param variables - variables to insert into translated string. Variables go
* in pairs, for example "[name]", "tastybento"
* @return Translated string with colors converted, or a blank String if nothing
* has been found
*/
public String getTranslationOrNothing(String reference, String... variables) {
String translation = getTranslation(reference, variables);
@ -518,6 +568,7 @@ public class User implements MetaDataAble {
/**
* Send a message to sender if message is not empty.
*
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*/
@ -529,7 +580,9 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender without any modification (colors, multi-lines, placeholders).
* Sends a message to sender without any modification (colors, multi-lines,
* placeholders).
*
* @param message - the message to send
*/
public void sendRawMessage(String message) {
@ -542,7 +595,9 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender if message is not empty and if the same wasn't sent within the previous Notifier.NOTIFICATION_DELAY seconds.
* Sends a message to sender if message is not empty and if the same wasn't sent
* within the previous Notifier.NOTIFICATION_DELAY seconds.
*
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*
@ -556,8 +611,10 @@ public class User implements MetaDataAble {
}
/**
* Sends a message to sender if message is not empty and if the same wasn't sent within the previous Notifier.NOTIFICATION_DELAY seconds.
* @param world - the world the translation should come from
* Sends a message to sender if message is not empty and if the same wasn't sent
* within the previous Notifier.NOTIFICATION_DELAY seconds.
*
* @param world - the world the translation should come from
* @param reference - language file reference
* @param variables - CharSequence target, replacement pairs
*
@ -573,6 +630,7 @@ public class User implements MetaDataAble {
/**
* Sets the user's game mode
*
* @param mode - GameMode
*/
public void setGameMode(GameMode mode) {
@ -580,7 +638,9 @@ public class User implements MetaDataAble {
}
/**
* Teleports user to this location. If the user is in a vehicle, they will exit first.
* Teleports user to this location. If the user is in a vehicle, they will exit
* first.
*
* @param location - the location
*/
public void teleport(Location location) {
@ -589,6 +649,7 @@ public class User implements MetaDataAble {
/**
* Gets the current world this entity resides in
*
* @return World - world
*/
@NonNull
@ -606,6 +667,7 @@ public class User implements MetaDataAble {
/**
* Get the user's locale
*
* @return Locale
*/
public Locale getLocale() {
@ -616,8 +678,8 @@ public class User implements MetaDataAble {
}
/**
* Forces an update of the user's complete inventory.
* Deprecated, but there is no current alternative.
* Forces an update of the user's complete inventory. Deprecated, but there is
* no current alternative.
*/
public void updateInventory() {
player.updateInventory();
@ -625,6 +687,7 @@ public class User implements MetaDataAble {
/**
* Performs a command as the player
*
* @param command - command to execute
* @return true if the command was successful, otherwise false
*/
@ -634,7 +697,8 @@ public class User implements MetaDataAble {
// only perform the command, if the event wasn't cancelled by an other plugin:
if (!event.isCancelled()) {
return getPlayer().performCommand(event.getMessage());
return getPlayer().performCommand(
event.getMessage().startsWith("/") ? event.getMessage().substring(1) : event.getMessage());
}
// Cancelled, but it was recognized, so return true
return true;
@ -642,6 +706,7 @@ public class User implements MetaDataAble {
/**
* Checks if a user is in one of the game worlds
*
* @return true if user is, false if not
*/
public boolean inWorld() {
@ -649,80 +714,72 @@ public class User implements MetaDataAble {
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* @param particle Particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z)
{
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z) {
Class<?> expectedClass = VALIDATION_CHECK.get(particle);
if (expectedClass == null) throw new IllegalArgumentException("Unexpected value: " + particle);
if (expectedClass == null)
throw new IllegalArgumentException("Unexpected value: " + particle);
if (!(expectedClass.isInstance(dustOptions))) {
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName() + " must be provided when using Particle." + particle + " as particle.");
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName()
+ " must be provided when using Particle." + particle + " as particle.");
}
// Check if this particle is beyond the viewing distance of the server
if (this.player != null
&& this.player.getLocation().toVector().distanceSquared(new Vector(x, y, z)) <
(Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance()))
{
if (particle.equals(Particle.REDSTONE))
{
if (this.player != null && this.player.getLocation().toVector().distanceSquared(new Vector(x, y,
z)) < (Bukkit.getServer().getViewDistance() * 256 * Bukkit.getServer().getViewDistance())) {
if (particle.equals(Enums.getIfPresent(Particle.class, "DUST")
.or(Enums.getIfPresent(Particle.class, "REDSTONE").or(Particle.FLAME)))) {
player.spawnParticle(particle, x, y, z, 1, 0, 0, 0, 1, dustOptions);
}
else if (dustOptions != null)
{
} else if (dustOptions != null) {
player.spawnParticle(particle, x, y, z, 1, dustOptions);
}
else
{
// This will never be called unless the value in VALIDATION_CHECK is null in the future
} else {
// This will never be called unless the value in VALIDATION_CHECK is null in the
// future
player.spawnParticle(particle, x, y, z, 1);
}
}
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* Compatibility method for older usages.
* @param particle Particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance. Compatibility method for older usages.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z)
{
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, double x, double y, double z) {
this.spawnParticle(particle, (Object) dustOptions, x, y, z);
}
/**
* Spawn particles to the player.
* They are only displayed if they are within the server's view distance.
* @param particle Particle to display.
* Spawn particles to the player. They are only displayed if they are within the
* server's view distance.
*
* @param particle Particle to display.
* @param dustOptions Particle.DustOptions for the particle to display.
* Cannot be null when particle is {@link Particle#REDSTONE}.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
* @param x X coordinate of the particle to display.
* @param y Y coordinate of the particle to display.
* @param z Z coordinate of the particle to display.
*/
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z)
{
public void spawnParticle(Particle particle, Particle.DustOptions dustOptions, int x, int y, int z) {
this.spawnParticle(particle, dustOptions, (double) x, (double) y, (double) z);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
@ -733,7 +790,9 @@ public class User implements MetaDataAble {
return result;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@ -749,11 +808,13 @@ public class User implements MetaDataAble {
}
if (playerUUID == null) {
return other.playerUUID == null;
} else return playerUUID.equals(other.playerUUID);
} else
return playerUUID.equals(other.playerUUID);
}
/**
* Set the addon context when a command is executed
*
* @param addon - the addon executing the command
*/
public void setAddon(Addon addon) {
@ -762,14 +823,13 @@ public class User implements MetaDataAble {
/**
* Get all the meta data for this user
*
* @return the metaData
* @since 1.15.4
*/
@Override
public Optional<Map<String, MetaDataValue>> getMetaData() {
Players p = plugin
.getPlayers()
.getPlayer(playerUUID);
Players p = plugin.getPlayers().getPlayer(playerUUID);
return Objects.requireNonNull(p, "Unknown player for " + playerUUID).getMetaData();
}
@ -779,9 +839,7 @@ public class User implements MetaDataAble {
*/
@Override
public void setMetaData(Map<String, MetaDataValue> metaData) {
Players p = plugin
.getPlayers()
.getPlayer(playerUUID);
Players p = plugin.getPlayers().getPlayer(playerUUID);
Objects.requireNonNull(p, "Unknown player for " + playerUUID).setMetaData(metaData);
}

View File

@ -8,6 +8,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -43,6 +44,7 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.hooks.MythicMobsHook;
/**
* The clipboard provides the holding spot for an active blueprint that is being
@ -67,6 +69,7 @@ public class BlueprintClipboard {
private final Map<Vector, BlueprintBlock> bpAttachable = new LinkedHashMap<>();
private final Map<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
private final BentoBox plugin = BentoBox.getInstance();
private Optional<MythicMobsHook> mmh;
/**
* Create a clipboard for blueprint
@ -74,9 +77,16 @@ public class BlueprintClipboard {
*/
public BlueprintClipboard(@NonNull Blueprint blueprint) {
this.blueprint = blueprint;
// MythicMobs
mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance)
.map(MythicMobsHook.class::cast);
}
public BlueprintClipboard() { }
public BlueprintClipboard() {
// MythicMobs
mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance)
.map(MythicMobsHook.class::cast);
}
/**
* Copy the blocks between pos1 and pos2 into the clipboard for a user.
@ -167,11 +177,11 @@ public class BlueprintClipboard {
* @param b - bounding box
* @return - list of vectors
*/
private List<Vector> getVectors(BoundingBox b) {
protected List<Vector> getVectors(BoundingBox b) {
List<Vector> r = new ArrayList<>();
for (int y = (int)b.getMinY(); y <= b.getMaxY(); y++) {
for (int x = (int)b.getMinX(); x <= b.getMaxX(); x++) {
for (int z = (int)b.getMinZ(); z <= b.getMaxZ(); z++) {
for (int y = (int) Math.floor(b.getMinY()); y <= b.getMaxY(); y++) {
for (int x = (int) Math.floor(b.getMinX()); x <= b.getMaxX(); x++) {
for (int z = (int) Math.floor(b.getMinZ()); z <= b.getMaxZ(); z++) {
r.add(new Vector(x,y,z));
}
}
@ -285,6 +295,7 @@ public class BlueprintClipboard {
List<BlueprintEntity> bpEnts = new ArrayList<>();
for (LivingEntity entity: entities) {
BlueprintEntity bpe = new BlueprintEntity();
bpe.setType(entity.getType());
bpe.setCustomName(entity.getCustomName());
if (entity instanceof Villager villager) {
@ -317,6 +328,10 @@ public class BlueprintClipboard {
if (entity instanceof Horse horse) {
bpe.setStyle(horse.getStyle());
}
mmh.filter(mm -> mm.isMythicMob(entity)).map(mm -> mm.getMythicMob(entity))
.ifPresent(bpe::setMythicMobsRecord);
bpEnts.add(bpe);
}
return bpEnts;

View File

@ -36,6 +36,9 @@ import world.bentobox.bentobox.util.Util;
*/
public class BlueprintPaster {
/**
* This tracks the stages of pasting from loading the chunk, pasting blocks, attachments, entities and then finishing.
*/
enum PasteState {
CHUNK_LOAD,
CHUNK_LOADING,
@ -55,6 +58,7 @@ public class BlueprintPaster {
private final BentoBox plugin;
private final PasteHandler paster = Util.getPasteHandler();
private final PasteHandler fallback = new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl();
private final World world;
// The minimum block position (x,y,z)
private Location pos1;
@ -115,6 +119,9 @@ public class BlueprintPaster {
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
// Calculate location for pasting
this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world);
// Ensure the y coordinate is within the world limits
int y = Math.min(world.getMaxHeight() - 1, Math.max(world.getMinHeight(), location.getBlockY()));
location.setY(y);
}
private record Bits(Map<Vector, BlueprintBlock> blocks,
@ -124,10 +131,20 @@ public class BlueprintPaster {
Iterator<Entry<Vector, BlueprintBlock>> it2,
Iterator<Entry<Vector, List<BlueprintEntity>>> it3,
int pasteSpeed) {}
/**
* The main pasting method
*/
public CompletableFuture<Boolean> paste() {
return this.paste(true);
}
/**
* Paste the clipboard
* @param useNMS if true, NMS pasting will be used, otherwise Bukkit API
* @return Future boolean where true is success
*/
public CompletableFuture<Boolean> paste(boolean useNMS) {
CompletableFuture<Boolean> result = new CompletableFuture<>();
// Iterators for the various maps to paste
final Map<Vector, BlueprintBlock> blocks = blueprint.getBlocks() == null ? Collections.emptyMap() : blueprint.getBlocks();
@ -145,12 +162,12 @@ public class BlueprintPaster {
Bits bits = new Bits(blocks, attached, entities,
blocks.entrySet().iterator(), attached.entrySet().iterator(), entities.entrySet().iterator(),
plugin.getSettings().getPasteSpeed());
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> pasterTask(result, owner, bits), 0L, 1L);
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> pasterTask(result, owner, bits, useNMS), 0L, 1L);
return result;
}
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits) {
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits, boolean useNMS) {
if (!currentTask.isDone()) return;
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
@ -160,10 +177,10 @@ public class BlueprintPaster {
loadChunk();
}
else if (pasteState.equals(PasteState.BLOCKS) || pasteState.equals(PasteState.ATTACHMENTS)) {
pasteBlocks(bits, count, owner, pasteSpeed);
pasteBlocks(bits, count, owner, pasteSpeed, useNMS);
}
else if (pasteState.equals(PasteState.ENTITIES)) {
pasteEntities(bits, count, owner, pasteSpeed);
pasteEntities(bits, count, owner, pasteSpeed, useNMS);
}
else if (pasteState.equals(PasteState.DONE)) {
// All done. Cancel task
@ -185,7 +202,7 @@ public class BlueprintPaster {
result.complete(true);
}
private void pasteEntities(Bits bits, int count, Optional<User> owner, int pasteSpeed) {
private void pasteEntities(Bits bits, int count, Optional<User> owner, int pasteSpeed, boolean useNMS) {
if (bits.it3().hasNext()) {
Map<Location, List<BlueprintEntity>> entityMap = new HashMap<>();
// Paste entities
@ -203,7 +220,8 @@ public class BlueprintPaster {
count++;
}
if (!entityMap.isEmpty()) {
currentTask = paster.pasteEntities(island, world, entityMap);
currentTask = useNMS ? paster.pasteEntities(island, world, entityMap)
: fallback.pasteEntities(island, world, entityMap);
}
} else {
pasteState = PasteState.DONE;
@ -219,27 +237,10 @@ public class BlueprintPaster {
}
private void pasteBlocks(Bits bits, int count, Optional<User> owner, int pasteSpeed) {
private void pasteBlocks(Bits bits, int count, Optional<User> owner, int pasteSpeed, boolean useNMS) {
Iterator<Entry<Vector, BlueprintBlock>> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2;
if (it.hasNext()) {
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
// Paste blocks
while (count < pasteSpeed) {
if (!it.hasNext()) {
break;
}
Entry<Vector, BlueprintBlock> entry = it.next();
Location pasteTo = location.clone().add(entry.getKey());
// pos1 and pos2 update
updatePos(pasteTo);
BlueprintBlock block = entry.getValue();
blockMap.put(pasteTo, block);
count++;
}
if (!blockMap.isEmpty()) {
currentTask = paster.pasteBlocks(island, world, blockMap);
}
pasteBlocksNow(it, count, pasteSpeed, useNMS);
} else {
if (pasteState.equals(PasteState.BLOCKS)) {
// Blocks done
@ -256,6 +257,29 @@ public class BlueprintPaster {
}
private void pasteBlocksNow(Iterator<Entry<Vector, BlueprintBlock>> it, int count, int pasteSpeed, boolean useNMS) {
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
// Paste blocks
while (count < pasteSpeed) {
if (!it.hasNext()) {
break;
}
Entry<Vector, BlueprintBlock> entry = it.next();
Location pasteTo = location.clone().add(entry.getKey());
// pos1 and pos2 update
updatePos(pasteTo);
BlueprintBlock block = entry.getValue();
blockMap.put(pasteTo, block);
count++;
}
if (!blockMap.isEmpty()) {
currentTask = useNMS ? paster.pasteBlocks(island, world, blockMap)
: fallback.pasteBlocks(island, world, blockMap);
}
}
private void loadChunk() {
long timer = System.currentTimeMillis();
pasteState = PasteState.CHUNK_LOADING;

View File

@ -12,6 +12,7 @@ import org.bukkit.inventory.ItemStack;
import com.google.gson.annotations.Expose;
/**
* Represents a block to be pasted
* @author tastybento
* @since 1.5.0
*/
@ -19,8 +20,14 @@ public class BlueprintBlock {
@Expose
private String blockData;
/**
* Front of sign
*/
@Expose
private List<String> signLines;
/**
* Back of sign
*/
@Expose
private List<String> signLines2;
@Expose
@ -37,8 +44,14 @@ public class BlueprintBlock {
*/
@Expose
private List<Pattern> bannerPatterns;
/**
* Front of sign
*/
@Expose
private boolean glowingText;
/**
* Back of sign
*/
@Expose
private boolean glowingText2;

View File

@ -61,6 +61,11 @@ public class BlueprintBundle implements DataObject {
@Expose
private int slot = 0;
/**
* Number of times this bundle can be used by a single player. 0 = unlimited
*/
@Expose
private int times = 0;
/**
@ -188,4 +193,18 @@ public class BlueprintBundle implements DataObject {
this.slot = slot;
}
/**
* @return the times
*/
public int getTimes() {
return times;
}
/**
* @param times the times to set
*/
public void setTimes(int times) {
this.times = times;
}
}

View File

@ -24,6 +24,19 @@ import com.google.gson.annotations.Expose;
*/
public class BlueprintEntity {
public record MythicMobRecord(String type, String displayName, double level, float power, String stance) {
}
// GSON can serialize records, but the record class needs to be know in advance. So this breaks out the record entries
@Expose
String MMtype;
@Expose
Double MMLevel;
@Expose
String MMStance;
@Expose
Float MMpower;
@Expose
private DyeColor color;
@Expose
@ -50,7 +63,6 @@ public class BlueprintEntity {
private Integer experience;
@Expose
private Villager.Type villagerType;
/**
* @since 1.8.0
@ -78,13 +90,13 @@ public class BlueprintEntity {
if (e instanceof AbstractHorse horse) {
if (domestication != null) horse.setDomestication(domestication);
if (inventory != null) {
inventory.forEach(horse.getInventory()::setItem);
inventory.forEach((index, item) -> horse.getInventory().setItem(index.intValue(), item));
}
}
if (style != null && e instanceof Horse horse) {
horse.setStyle(style);
}
}
/**
@ -269,5 +281,27 @@ public class BlueprintEntity {
public void setDomestication(Integer domestication) {
this.domestication = domestication;
}
/**
* @return the mythicMobsRecord
*/
public MythicMobRecord getMythicMobsRecord() {
if (this.MMtype == null || this.MMLevel == null || this.MMpower == null || this.MMStance == null) {
return null;
}
return new MythicMobRecord(this.MMtype, this.getCustomName(), this.MMLevel, this.MMpower, this.MMStance);
}
/**
* @param mmr the mythicMobsRecord to set
* @since 2.1.0
*/
public void setMythicMobsRecord(MythicMobRecord mmr) {
this.setCustomName(mmr.displayName());
this.MMtype = mmr.type();
this.MMLevel = mmr.level();
this.MMStance = mmr.stance();
this.MMpower = mmr.power();
}
}

View File

@ -25,6 +25,7 @@ public class BentoBoxCommand extends CompositeCommand {
new BentoBoxLocaleCommand(this);
new BentoBoxHelpCommand(this);
new BentoBoxPermsCommand(this);
new BentoBoxRankCommand(this);
// Database names with a 2 in them are migration databases
if (getPlugin().getSettings().getDatabaseType().name().contains("2")) {
new BentoBoxMigrateCommand(this);

View File

@ -1,6 +1,7 @@
package world.bentobox.bentobox.commands;
import java.util.List;
import java.util.Set;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
@ -8,8 +9,7 @@ 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.Database;
import world.bentobox.bentobox.database.objects.Names;
import world.bentobox.bentobox.database.objects.Players;
import world.bentobox.bentobox.database.objects.DataObject;
/**
* Forces migration from one database to another
@ -38,16 +38,10 @@ public class BentoBoxMigrateCommand extends ConfirmableCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
this.askConfirmation(user, () -> {
// Migrate BentoBox data
user.sendMessage("commands.bentobox.migrate.players");
new Database<>(getPlugin(), Players.class).loadObjects();
user.sendMessage(MIGRATED);
user.sendMessage("commands.bentobox.migrate.names");
new Database<>(getPlugin(), Names.class).loadObjects();
user.sendMessage(MIGRATED);
// Migrate addons data
user.sendMessage("commands.bentobox.migrate.addons");
getPlugin().getAddonsManager().getDataObjects().forEach(t -> {
Set<Class<? extends DataObject>> classSet = getPlugin().getAddonsManager().getDataObjects();
classSet.addAll(Database.getDataobjects());
classSet.forEach(t -> {
user.sendMessage("commands.bentobox.migrate.class", TextVariables.DESCRIPTION, BentoBox.getInstance().getSettings().getDatabasePrefix() + t.getCanonicalName());
new Database<>(getPlugin(), t).loadObjects();
user.sendMessage(MIGRATED);

View File

@ -0,0 +1,143 @@
package world.bentobox.bentobox.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
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.managers.RanksManager;
/**
* Manages ranks
*
* @author tastybento
* @since 2.0.0
*/
public class BentoBoxRankCommand extends CompositeCommand {
private static final String REMOVE = "remove";
private int rankValue;
private String firstElement;
/**
* Rank management. Add and remove
*
* @param parent command parent
*/
public BentoBoxRankCommand(CompositeCommand parent) {
super(parent, "rank");
}
@Override
public void setup() {
setPermission("bentobox.admin.rank");
setDescription("commands.bentobox.rank.description");
this.setParametersHelp("commands.bentobox.rank.parameters");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.isEmpty()) {
// Show help
showHelp(this, user);
return false;
}
// Check if the first element is "add" or REMOVE or "list"
firstElement = args.get(0);
if (!("list".equals(firstElement) || "add".equals(firstElement) || REMOVE.equals(firstElement))) {
// Show help
showHelp(this, user);
return false;
}
if (REMOVE.equals(firstElement) && args.size() != 2) {
// Show help
showHelp(this, user);
return false;
}
// If the first element is "add", then check if the third element is an integer
if ("add".equals(firstElement)) {
// Check if there is a third element
if (args.size() < 3) {
// Show help
showHelp(this, user);
return false;
}
// Check if the third element is an integer
String thirdElement = args.get(2);
try {
rankValue = Integer.parseInt(thirdElement);
} catch (NumberFormatException e) {
// Show help
showHelp(this, user);
return false;
}
}
// If all checks passed, return true
return true;
}
@Override
public boolean execute(User user, String label, List<String> args) {
if ("list".equals(firstElement)) {
showRanks(user);
return true;
}
if ("add".equals(firstElement)) {
if (RanksManager.getInstance().addRank(args.get(1), rankValue)) {
user.sendMessage("commands.bentobox.rank.add.success", TextVariables.RANK, args.get(1),
TextVariables.NUMBER, String.valueOf(rankValue));
showRanks(user);
} else {
user.sendMessage("commands.bentobox.rank.add.failure", TextVariables.RANK, args.get(1),
TextVariables.NUMBER, String.valueOf(rankValue));
return false;
}
} else {
if (RanksManager.getInstance().removeRank(args.get(1))) {
user.sendMessage("commands.bentobox.rank.remove.success", TextVariables.RANK, args.get(1));
showRanks(user);
} else {
user.sendMessage("commands.bentobox.rank.remove.failure", TextVariables.RANK, args.get(1));
return false;
}
}
return true;
}
private void showRanks(User user) {
user.sendMessage("commands.bentobox.rank.list");
RanksManager.getInstance().getRanks().forEach((ref, rank) -> {
user.sendRawMessage(user.getTranslation(ref) + ": " + ref + " " + String.valueOf(rank));
});
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
if (args.size() <= 1) {
return Optional.empty();
}
firstElement = args.get(1);
if (args.size() <= 2) {
return Optional.of(List.of("add", REMOVE, "list"));
}
if (args.size() > 1 && "add".equals(firstElement)) {
List<String> options = new ArrayList<>(RanksManager.DEFAULT_RANKS.keySet());
options.removeIf(RanksManager.getInstance().getRanks().keySet()::contains);
return Optional.of(options);
}
if (args.size() > 1 && REMOVE.equals(firstElement)) {
return Optional.of(new ArrayList<>(RanksManager.getInstance().getRanks().keySet()));
}
return Optional.empty();
}
}

View File

@ -2,11 +2,8 @@ package world.bentobox.bentobox.commands;
import java.util.List;
import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.panels.reader.TemplateReader;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand;
@ -40,7 +37,7 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
public boolean execute(User user, String label, List<String> args) {
if (args.isEmpty()) {
this.askConfirmation(user, user.getTranslation("commands.bentobox.reload.warning"), () -> {
// Unregister all placeholders
getPlugin().getPlaceholdersManager().unregisterAll();
@ -53,22 +50,13 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
getPlugin().loadSettings();
user.sendMessage("commands.bentobox.reload.settings-reloaded");
// Reload addons
getPlugin().getAddonsManager().reloadAddons();
user.sendMessage("commands.bentobox.reload.addons-reloaded");
// Reload locales
getPlugin().getLocalesManager().reloadLanguages();
user.sendMessage("commands.bentobox.reload.locales-reloaded");
// Register new default gamemode placeholders
getPlugin().getAddonsManager().getGameModeAddons().forEach(getPlugin().getPlaceholdersManager()::registerDefaultPlaceholders);
// Call the all Loaded method for addons
getPlugin().getAddonsManager().allLoaded();
// Fire ready event
Bukkit.getPluginManager().callEvent(new BentoBoxReadyEvent());
});
} else {
showHelp(this, user);

View File

@ -43,6 +43,7 @@ public class BentoBoxVersionCommand extends CompositeCommand {
user.sendMessage("commands.bentobox.version.server",
TextVariables.NAME, serverSoftware.equals(ServerSoftware.UNKNOWN) ? user.getTranslation("general.invalid") + " (" + serverSoftware.getName() + ")" : serverSoftware.toString(),
TextVariables.VERSION, serverVersion != null ? serverVersion.toString() : user.getTranslation("general.invalid"));
user.sendRawMessage("(" + Bukkit.getVersion() + ")");
user.sendMessage("commands.bentobox.version.plugin-version", TextVariables.VERSION, getPlugin().getDescription().getVersion());
user.sendMessage("commands.bentobox.version.database", "[database]", getSettings().getDatabaseType().toString());
user.sendMessage("commands.bentobox.version.loaded-game-worlds");

View File

@ -3,7 +3,9 @@ package world.bentobox.bentobox.database;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
@ -12,6 +14,7 @@ import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.database.objects.DataObject;
/**
* Handy class to store and load Java POJOs in the Database
@ -24,15 +27,19 @@ public class Database<T> {
private final AbstractDatabaseHandler<T> handler;
private final Logger logger;
private static DatabaseSetup databaseSetup = DatabaseSetup.getDatabase();
private static final Set<Class<? extends DataObject>> dataObjects = new HashSet<>();
/**
* Construct a database
* @param plugin - plugin
* @param type - to store this type
*/
@SuppressWarnings("unchecked")
public Database(BentoBox plugin, Class<T> type) {
this.logger = plugin.getLogger();
handler = databaseSetup.getHandler(type);
// Log any database classes
dataObjects.add((Class<? extends DataObject>) type);
}
/**
@ -40,9 +47,12 @@ public class Database<T> {
* @param addon - addon requesting
* @param type - to store this type
*/
@SuppressWarnings("unchecked")
public Database(Addon addon, Class<T> type) {
this.logger = addon.getLogger();
handler = databaseSetup.getHandler(type);
// Log any database classes
dataObjects.add((Class<? extends DataObject>) type);
}
/**
@ -149,6 +159,13 @@ public class Database<T> {
handler.close();
}
/**
* @return the dataobjects
*/
public static Set<Class<? extends DataObject>> getDataobjects() {
return dataObjects;
}
}

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