Compare commits

...

175 Commits

Author SHA1 Message Date
tastybento 519a2d05fc Version 1.15.0 2024-02-03 08:30:12 -08:00
tastybento 86c089b694 Update tipped arrows in GUI Panel 2024-01-21 09:09:17 -08:00
tastybento 3417633c4d Version 1.14.1 2024-01-21 09:09:12 -08:00
tastybento b16d5102b1
Merge pull request #129 from BentoBoxWorld/tastybento-patch-1
Update addon.yml
2024-01-12 14:37:29 -08:00
tastybento 4375eba030
Update addon.yml
Fix because the warps command got its own perm from a1e03af
2024-01-12 14:36:38 -08:00
tastybento bc1e92ee8d Add support for hanging signs.
Warps to below the sign.
2024-01-06 22:00:24 +09:00
tastybento b88497397f
Merge pull request #126 from BentoBoxWorld/gitlocalize-27267
Add Ukrainian locale
2023-11-26 10:00:25 -08:00
GIGABAIT b45c88025f Translate uk.yml via GitLocalize 2023-11-26 11:18:41 +00:00
tastybento 3c9515295d Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2023-11-12 13:25:11 -08:00
tastybento 82a0630ca3 BentoBox API 2.0.0 2023-11-12 13:25:03 -08:00
BONNe 6dbdb0f8aa
Update WarpsCommandTest.java 2023-11-05 22:00:28 +02:00
BONNe a1e03af13a
Update WarpsCommand.java 2023-11-05 21:35:59 +02:00
tastybento 2558d9d1ab
Merge pull request #124 from rchomczyk/fix-pl-translation
Improve polish translation
2023-09-04 07:46:24 -07:00
Rafał Chomczyk d346b7864e
Fix invalid placeholder name for warp's description; improve translations by a little, fix typo 2023-09-03 17:50:30 +02:00
BONNe 0e1cd1353c
Version Up 2023-07-26 15:37:35 +03:00
tastybento 9657d11af7
Update pom.xml 2023-07-10 21:39:57 -07:00
tastybento ab1a1e203c Update Jacoco 2023-07-10 21:24:40 -07:00
tastybento 1a90acb65f Update GitHub Action versions 2023-06-24 13:49:47 -07:00
tastybento ae172b9fdf Minor variable rename 2023-06-24 13:49:33 -07:00
tastybento ecebbcd039
Merge pull request #122 from BentoBoxWorld/gitlocalize-24193
Spanish
2023-05-29 09:40:06 -07:00
tastybento 3faed651aa
Merge pull request #123 from BentoBoxWorld/gitlocalize-24194
Indonesian
2023-05-29 09:39:49 -07:00
Dusty 0585fd7f3a Translate id.yml via GitLocalize 2023-05-29 16:35:40 +00:00
Espan 2f59225718 Translate es.yml via GitLocalize 2023-05-29 16:35:13 +00:00
tastybento 9e57851c62 Update GitHub build script to Java 17 2023-04-16 11:02:01 -07:00
tastybento b998b46fd5 User BentoBox release build 2023-04-16 11:00:06 -07:00
tastybento cb165e52b0 Updated POM; fixed tests. 2023-04-15 13:08:55 -07:00
BONNe ebb3f05d78
Merge pull request #119 from BentoBoxWorld/return-to-plugin
Return to plugin.yml
2023-04-08 19:10:40 +03:00
BONNe fe6b020ac8
Update WarpsPladdon.java 2023-04-08 19:10:32 +03:00
BONNe d2dde61d8b
Update pom.xml 2023-04-08 19:10:15 +03:00
BONNe e58010547b
Create plugin.yml 2023-04-08 19:09:38 +03:00
tastybento 5a9f6495ad Updated pladdon dependencies. Removed unused imports. 2023-03-25 10:12:07 -07:00
tastybento ff4850d32a Use addon name in annotation 2023-03-25 09:59:53 -07:00
DevSolaris 7a26acefd9
Flip bool expression (#114) 2023-02-12 10:44:49 -08:00
tastybento 2be57a66d2 Fix SonarCloud bugs and smells. 2023-02-09 17:44:25 -08:00
tastybento 67c5c9f8c3 Updated Jacoco POM 2023-02-09 17:38:25 -08:00
tastybento 62f1f2e966
Add ${argLine} to get jacoco coverage 2023-02-09 15:13:34 -08:00
BONNe 5a37c3da75
Merge pull request #118 from BentoBoxWorld/gitlocalize-22781
Update NL
2023-01-24 15:18:17 +02:00
DevSolaris 94e9208941 Translate nl.yml via GitLocalize 2023-01-24 13:18:06 +00:00
BONNe 225b4d66f8
Update Warp.java 2023-01-19 23:15:38 +02:00
BONNe d3b45396fd
Add missing import 2023-01-19 23:10:00 +02:00
BONNe d2b9540650
Update locale 2023-01-19 23:06:57 +02:00
BONNe 363a628244
Improve create warps flag.
Rename the flag to PLACE_WARP as it is a more proper name.
Also, add a click handler that limits setting value from member to owner, as visitors should not be allowed.

Also, set default value outside island range as false.
2023-01-19 23:06:45 +02:00
DevSolaris 8be844b59f
Closes #69 - Fix message from being sent when the user is banned. (#116) 2023-01-19 21:59:39 +01:00
DevSolaris b3a36c75bb
Create set warp flag (#117) 2023-01-19 21:56:31 +01:00
DevSolaris 7dbb5d39ad
Add gamemode placeholder to message (#115) 2023-01-18 22:14:50 +01:00
BONNe fb1090717b
Merge pull request #113 from BentoBoxWorld/gitlocalize-20471
Update Polish translation
2022-08-23 10:54:09 +03:00
mt-gitlocalize 39b94e09d8 Translate pl.yml via GitLocalize 2022-08-23 07:53:37 +00:00
wiktorm12 5716214527 Translate pl.yml via GitLocalize 2022-08-23 07:53:37 +00:00
BONNe 153e8be6a0
Init next version 2022-05-17 12:32:25 +03:00
BONNe 0dea651274
Fix build number for warps
1.11.2 was already released.
2022-05-17 12:20:13 +03:00
BONNe 63b51fb26e
Merge pull request #109 from BentoBoxWorld/gitlocalize-18727
Update German translation
2022-04-09 12:32:55 +03:00
Patrick 15a699037a Translate de.yml via GitLocalize 2022-04-09 09:30:14 +00:00
BONNe cbaf6ce16c Implement customizable Warps Panel.
Relates to #BentoBox/issues/1931
These changes includes to removing some obsolete config settings and locales string.
Also, moved some classes around to clean up directory.
2022-03-11 12:34:16 +02:00
Huynh Tien b12a6dcff9
Update id.yml (#108) 2022-01-28 07:13:25 -08:00
gitlocalize-app[bot] 89bcc54e23
Translate it.yml via GitLocalize (#107)
Co-authored-by: Pirlas <alexpirlas@outlook.it>
2022-01-01 16:47:50 -08:00
gitlocalize-app[bot] 6db80f73f8
Taiwan translation (#106)
* Translate zh-TW.yml via GitLocalize

* Translate zh-TW.yml via GitLocalize

Co-authored-by: Shaowing <lien78520@gmail.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
2022-01-01 16:47:40 -08:00
gitlocalize-app[bot] 87d04302d2
Indonesian Translation (#105)
* Translate id.yml via GitLocalize

* Translate id.yml via GitLocalize

* Translate id.yml via GitLocalize

Co-authored-by: Alunite? <xcionsspro30@gmail.com>
Co-authored-by: mt-gitlocalize <mt@gitlocalize.com>
Co-authored-by: gitlocalize-app[bot] <55277160+gitlocalize-app[bot]@users.noreply.github.com>
2022-01-01 16:47:29 -08:00
gitlocalize-app[bot] 9b0f00f2a0
Translate hu.yml via GitLocalize (#104)
Co-authored-by: driverdakid <tamascsiszar99@icloud.com>
2022-01-01 16:47:18 -08:00
gitlocalize-app[bot] 048f7237da
French translation (#103)
* Translate fr.yml via GitLocalize

* Translate fr.yml via GitLocalize

Co-authored-by: Aksel <afgameytb@gmail.com>
Co-authored-by: HipppB <hippolyte.bach@eleve.isep.fr>
2022-01-01 16:47:08 -08:00
gitlocalize-app[bot] 49d461d224
Translate tr.yml via GitLocalize (#102)
Co-authored-by: Over_Brave <soncesurlar@gmail.com>
2022-01-01 16:46:54 -08:00
Huynh Tien 6cfaa0e9c0
Fix null player on removeWarp (#100) 2021-12-31 08:25:05 -08:00
tastybento 9290503be3 Fix plugin.yml 2021-12-21 14:13:48 -08:00
tastybento 079dba0f41 Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2021-10-01 19:04:19 -07:00
tastybento 60c427c2a9 Build to BentoBox 1.18 API 2021-10-01 19:04:09 -07:00
KrazyxWolf 6caa652162
Update es.yml (#98) 2021-08-26 18:02:37 -07:00
tastybento 4ebaa22f37 Renamed plugin Pladdon 2021-08-12 18:48:39 -07:00
tastybento f0780d91ba Version 1.11.2 2021-08-12 18:48:29 -07:00
tastybento 55ec1ded61 Remove this deprecated event. 2021-08-08 18:28:29 -07:00
tastybento d936fbecc1 More code cleanup 2021-08-08 18:28:19 -07:00
tastybento 6c4c42ff64 Clean up tests. 2021-08-08 18:19:55 -07:00
tastybento 00938e67da Code smell reduction. 2021-08-08 18:09:28 -07:00
tastybento 3cd93f39dd Objects.requireNonNull for User 2021-08-08 17:36:03 -07:00
tastybento 0003c91cca Make Warps a Pladdon. 2021-08-08 16:40:13 -07:00
tastybento f6207ababd Update to Java 16 for GitHub Action building 2021-07-09 09:07:43 -07:00
tastybento 9333265e4d Fix PVP message when teleporting to an island. 2021-07-09 09:00:56 -07:00
tastybento 8c8f25f6d4 Version 1.11.1 2021-07-09 09:00:43 -07:00
tastybento afec2f56f5 Java 16 Minecraft 17 2021-06-20 11:29:45 -07:00
tastybento fc4f6c634a Return null if Level addon is not operating in world 2021-06-05 08:37:42 -07:00
tastybento 757a39e7f9 Github workflows sonar cloud 2021-03-06 12:37:51 -08:00
Huynh Tien 9db549ea0d
Add files via upload (#95) 2021-02-03 12:14:32 -08:00
tastybento 7fbe4b5b88 Fixes sync errors and blank warps
https://github.com/BentoBoxWorld/Warps/issues/93
https://github.com/BentoBoxWorld/Warps/issues/92
https://github.com/BentoBoxWorld/Warps/issues/75
2021-01-09 15:33:27 -08:00
tastybento 82bd4c8b31 Shows GUI correctly 2021-01-09 11:37:41 -08:00
tastybento 4ad5d864ab Use SignCacheItem
Indicate whether it is real or not.
2021-01-09 11:19:35 -08:00
tastybento dcd5bd1514 Run offlinePlayer code async to avoid blocking IO 2021-01-09 10:38:47 -08:00
tastybento 2506d2ac9e Deprecated and removed WarpListEvent
Cannot be fired because this part of the code needs to run async.

https://github.com/BentoBoxWorld/Warps/issues/93
2021-01-08 22:14:03 -08:00
tastybento c882ca3288 Added owner to WarpRemoveEvent
https://github.com/BentoBoxWorld/Warps/issues/91
2021-01-03 17:54:33 -08:00
tastybento 4817c2c913 Version 1.11.0 2021-01-03 17:54:05 -08:00
tastybento 86612b72e3 Move warp panel creation async and reworked test class. 2021-01-01 15:36:14 -08:00
tastybento 02a27f3df7 Version 1.15.6 2021-01-01 15:35:38 -08:00
tastybento 2d3d2f10d6 Use BentoBox 1.15.4 2020-12-30 22:58:49 -08:00
tastybento 36284d963b Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2020-12-27 11:14:43 -08:00
tastybento 73aab77c99 Updated to BentoBox 1.15.4 API 2020-12-27 11:14:19 -08:00
gitlocalize-app[bot] 1591bfc36d
Translate tr.yml via GitLocalize (#90)
Co-authored-by: Over_Brave <soncesurlar@gmail.com>
2020-12-09 11:51:43 -08:00
Yasin e459c53c77
WarpCreateEvent (#88) 2020-09-10 18:34:56 -07:00
Gabriele C 45b968820b
Don't validate unloaded signs on startup (#85)
* Don't validate unloaded signs in startup

* Actually stop loading chunks

* Fix crash issues due to unwanted chunk loads in the sign ChunkLoadEvent listener

* Delay chunk handling, fixes CMEs
2020-09-07 09:14:44 -07:00
tastybento bee85b0db1 Do not send warp message if player is hidden.
https://github.com/BentoBoxWorld/Warps/issues/87
2020-08-29 08:58:44 -07:00
tastybento abf4b882c0 Fixes test 2020-08-06 21:06:19 -07:00
wellnesscookie fd06c0cdd7
Fixes unwanted parsing when warping to an island (#86)
* Fixes incorrect parsing when warping to similiar players

* Removes debug lines
2020-08-06 12:14:50 -07:00
tastybento 27afafadce 1.14.0 BentoBox API 2020-07-07 18:43:34 -07:00
tastybento b482a1b3e4 Version 1.10.0 2020-06-26 16:24:32 -07:00
tastybento 4cb6b66bc7 BentoBox 1.14 API 2020-06-10 21:31:20 -07:00
NotMyFault 262889c49f
Fix typo (#82) 2020-06-04 07:26:57 -07:00
tastybento 619acc55cd Version 1.9.7 2020-06-02 14:35:17 -07:00
tastybento 8f38353b73 Fixes tests 2020-06-02 14:34:21 -07:00
tastybento 6d4b7c2a19 WIP 2020-06-02 13:00:09 -07:00
tastybento 94f0ff4e22 Sign Cache could get out of sync with signs.
https://github.com/BentoBoxWorld/Warps/issues/80
2020-05-27 16:48:20 -07:00
tastybento 74675abce6 Version 1.9.6 2020-05-27 16:47:55 -07:00
tastybento 9c4a224c97 Fixes bug where Warps would not hook with no game modes
https://github.com/BentoBoxWorld/Warps/issues/77
2020-05-20 18:57:56 -07:00
tastybento 75feedbec0 Version 1.9.5 2020-05-20 18:57:10 -07:00
tastybento 826b86ae64 Merge remote-tracking branch 'origin/develop' 2020-05-19 19:34:45 -07:00
tastybento df6936ff1f Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2020-05-19 19:34:09 -07:00
tastybento 77b834a7bd Version 1.9.4. 2020-05-19 19:33:59 -07:00
John 23b2d19b0d
Add german (de) translation (#74) 2020-04-29 20:40:06 -07:00
Krystian 45813ee39a
Add Polish translation (#73) 2020-04-20 07:36:28 -07:00
tastybento eea1ea25e1 Updated permissions 2020-04-19 08:36:17 -07:00
tastybento c62de9d021 Merge remote-tracking branch 'origin/develop'
Conflicts:
	.gitignore
2020-04-19 08:30:32 -07:00
tastybento 88bae57218 Updated gitignore 2020-04-19 08:29:18 -07:00
Clément P 6dab2f3ce7
Add Gitignore (#72) 2020-04-06 17:35:37 -07:00
tastybento 40c0b8d791 Fixes bug where database table existed but had no valid data in it.
Fixes https://github.com/BentoBoxWorld/Warps/issues/71
2020-04-02 12:54:55 -07:00
tastybento f3fe87c757
Czech translation. Credit @Polda18 2020-01-30 10:22:26 -08:00
tastybento 79b8c0cd87 Added saving of the warp sign cache to database.
Relates to https://github.com/BentoBoxWorld/Warps/issues/68
2020-01-28 17:11:23 -08:00
tastybento ef523d4332 Removed warp sign for players who leave a team or are kicked.
Fixes https://github.com/BentoBoxWorld/Warps/issues/67
2020-01-14 17:34:28 -08:00
tastybento a51930fa05 Version 1.9.3 2020-01-14 17:11:06 -08:00
tastybento 490caf098e Made listeners package 2020-01-14 17:03:06 -08:00
gitlocalize-app[bot] 685f2beb5f Translate lv.yml via GitLocalize (#66)
Co-authored-by: BONNe <bonne@bonne.id.lv>
2020-01-10 16:29:24 -08:00
gitlocalize-app[bot] a63a6c2372 Translate zh-CN.yml via GitLocalize (#65) 2019-12-27 12:28:24 -08:00
tastybento b2d5c145a5 Ties into the BentoBox delayed teleport settings.
https://github.com/BentoBoxWorld/Warps/issues/52
2019-12-08 16:02:55 -08:00
Florian CUNY d8b56f5448 Update name in pom from WelcomeWarpSigns to Warps (#64) 2019-12-08 15:19:28 -08:00
tastybento 46333baa52 Merge remote-tracking branch 'origin/master' into develop 2019-12-08 08:45:14 -08:00
tastybento bf6d7994dd Version 1.9.1
Changes stand alone command to /wwarp and /wwarps to match original
Welcome Warp Signs plugin.
2019-12-08 08:44:08 -08:00
András Marczinkó 0176019d19 Update hu.yml (#63)
Removed spaces after color codes.
2019-12-06 13:39:46 -08:00
András Marczinkó 2d796471c5 Update hu.yml (#62) 2019-12-06 08:30:38 -08:00
tastybento 51b37ff2c6 Version 1.9.0 2019-12-02 13:46:07 -08:00
tastybento a74037fd1b Merge remote-tracking branch 'origin/develop' 2019-12-02 13:45:49 -08:00
tastybento d2ef5d9400 Removed blank locale files. 2019-12-02 13:43:22 -08:00
tastybento 8ee9168a55 Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2019-12-02 13:23:33 -08:00
tastybento d14a6e1282 Cleaned up color codes and added spaces. 2019-12-02 13:23:24 -08:00
tastybento 9e172d3116 Merge branch 'develop' 2019-11-30 09:08:54 -08:00
gitlocalize-app[bot] 8f677e362b Translate ru.yml via GitLocalize (#60) 2019-11-28 15:03:30 -08:00
András Marczinkó cf0755e7ce Translated into Hungarian (#59) 2019-11-24 13:50:18 -08:00
tastybento 8083ce6f01 Merge remote-tracking branch 'origin/develop' 2019-11-22 13:34:47 -08:00
tastybento 037ca558cf
Update bug_report.md 2019-11-17 17:44:27 -08:00
tastybento 3dab96e546
Create id.yml 2019-11-16 18:32:16 -08:00
tastybento c361f0a1a3
Create ro.yml 2019-11-16 18:32:08 -08:00
tastybento ccc1f9c849 Merge branch 'develop' of https://github.com/BentoBoxWorld/Warps.git into develop 2019-11-11 07:58:05 -08:00
tastybento ae1c2fe96f Use BentoBox utility method for BlockFaceToFloat 2019-11-11 07:57:55 -08:00
tastybento eb0930757c Removed sonar_token reference from travis.yml 2019-11-07 16:52:39 -08:00
tastybento c73bf8bad7 Do not register top-level commands if config setting isn't true
https://github.com/BentoBoxWorld/Warps/issues/57
2019-11-07 15:52:49 -08:00
tastybento 455242396f Merge remote-tracking branch 'origin/develop' 2019-11-03 08:58:02 -08:00
tastybento a013a1e6a1 Version up 1.8.1 2019-11-03 08:52:29 -08:00
tastybento 9a234ef950 Added WarpsCommand test class 2019-10-31 17:33:34 -07:00
tastybento d7e0b36623 Added WarpCommand test class 2019-10-31 17:05:12 -07:00
tastybento 55558711cc Code smell - simplified listener 2019-10-31 14:49:26 -07:00
tastybento 6d788b6f66 Fixes potential null bugs 2019-10-31 14:22:32 -07:00
tastybento 85334079da Fixed code smells 2019-10-31 14:12:44 -07:00
tastybento 007f071234 Enabling sonarcloud 2019-10-31 10:18:13 -07:00
tastybento afb03e6d42 Enables warps to operate in any world. Commands can be changed.
https://github.com/BentoBoxWorld/Warps/issues/53
https://github.com/BentoBoxWorld/Warps/issues/54
2019-10-31 10:09:33 -07:00
gitlocalize-app[bot] 197957648b Translate fr.yml via GitLocalize (#55) 2019-10-28 22:11:56 -07:00
DuckSoft 5ee1520d8b Translate zh-CN.yml via GitLocalize (#51) 2019-09-20 07:14:47 -07:00
BONNe 0f00c7bd52 Translate lv.yml via GitLocalize (#50) 2019-09-19 17:08:35 -07:00
tastybento f8243e3035 Added test cases. 2019-09-19 14:45:15 -07:00
tastybento 38c5496118 Added random warp button to warps panel
https://github.com/BentoBoxWorld/Warps/issues/20
2019-09-19 14:23:29 -07:00
tastybento b98d83fffc Added WarpSignsManager test class 2019-09-18 16:48:54 -07:00
tastybento bbc748aae3 Enabled formatting of warp sign text
https://github.com/BentoBoxWorld/Warps/issues/48
2019-09-18 16:48:38 -07:00
tastybento 675f5791bd Version 1.8.0 2019-09-15 16:41:26 -07:00
tastybento d0355e8b9e Listener was not registered using BentoBox API so caused reload issues. 2019-09-15 16:41:17 -07:00
tastybento 6a6ac19ef0 Updated ReadMe 2019-09-13 18:32:40 -07:00
tastybento 0814f22424 Config file was not being read.
Signs removed by breaking blocks under them and then replaced were not
being correctly handled. Now they are deleted and remade. No need for
the "duplicate sign" text.

https://github.com/BentoBoxWorld/Warps/issues/47
https://github.com/BentoBoxWorld/Warps/issues/46
https://github.com/BentoBoxWorld/Warps/issues/32
2019-09-13 18:13:03 -07:00
Florian CUNY 8de53d1a81
Renamed addon to "Warps" to follow the repo's name in addon.yml
+ added icon
2019-09-07 14:21:46 +02:00
BONNe b001aad4d9 Translate lv.yml via GitLocalize (#42) 2019-09-03 07:55:40 -07:00
tastybento b29457cc97 Updated locale files for GitLocalize 2019-09-03 07:35:41 -07:00
BONNe 5dbc762027 Implement ability to change Warp Sign Icon in Warps panel via user permission. (#41)
Permission that will be checked is "[gamemode].island.warp.[material]"
This will not filter out invalid materials.
2019-09-02 09:16:54 -07:00
tastybento 4d1d9a5098 Removed inaccessible API. 2019-08-22 15:21:47 -07:00
tastybento c671e35661 Adds the missing support for PLAYER_HEAD icons
https://github.com/BentoBoxWorld/Warps/issues/38
2019-08-22 15:08:14 -07:00
tastybento 633f2a63e2 Adds an API to request data from the WarpSignsManager class
https://github.com/BentoBoxWorld/Warps/issues/39
2019-08-22 12:53:38 -07:00
54 changed files with 3819 additions and 1255 deletions

View File

@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Description**
A clear and concise description of what the bug is.
**Steps to reproduce the behavior:**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Server Information:**
[Please complete the following information:]
- Database being used (YAML, JSON, MySQL, MongoDB): []
- OS: [e.g. iOS]
- Java Version: [e.g. Java 8]
- BentoBox version: [e.g. 1.7.2.21]
- Addons installed? [Do '/bentobox version' and copy/paste from the console]
- Other plugins? [Do '/plugins' and copy/paste from the console]
**Additional context**
Add any other context about the problem here.

View File

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

38
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Build
on:
push:
branches:
- develop
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar

88
.gitignore vendored Normal file
View File

@ -0,0 +1,88 @@
# Git
*.orig
!.gitignore
/.settings/
# Windows
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.lnk
# Linux
*~
.fuse_hidden*
.directory
.Trash-*
.nfs*
# MacOS
.DS_Store
.AppleDouble
.LSOverride
._*
# Java
*.class
*.log
*.ctxt
.mtj.tmp/
*.jar
*.war
*.nar
*.ear
hs_err_pid*
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
# Intellij
*.iml
*.java___jb_tmp___
.idea/*
*.ipr
*.iws
/out/
.idea_modules/
# Eclipse
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.project
.externalToolBuilders/
*.launch
.cproject
.classpath
.buildpath
.target
# NetBeans
nbproject/private/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
nb-configuration.xml
.nb-gradle/

View File

@ -1,9 +1,12 @@
# addon-welcomewarpsigns
# Warps
Add-on for BentoBox to add personal warp signs for players of BSkyBlock or AcidIsland.
This add-on will work for both game modes.
This add-on will work for all game modes installed on a BentoBox server. Use config settings
to disable use by gamemode.
## How to use
### Note: Java 16 and Minecraft 17, or later are required.
1. Place the jar in the addons folder of the BentoBox plugin
2. Restart the server
3. The addon will create a data folder and inside the folder will be a config.yml
@ -12,14 +15,36 @@ This add-on will work for both game modes.
## Config.yml
There are only two options in the config:
These are options in the config:
### warplevelrestriction
This is the minimum island level needed to be able to create a warp.
0 or negative values will disable this restriction and 10 is default.
If you do not have the level addon installed, this will have no effect.
### Warp Sign Text - welcomeLine
This is the text a player must put on the first line of the sign to make it a warp sign.
### Icon
Icon that will be displayed in Warps list. SIGN counts for any kind of sign and the type of
wood used will be reflected in the panel if the server supports it.
It uses native Minecraft material strings, but using string 'PLAYER_HEAD', it is possible to
use player heads instead. Beware that Mojang API rate limiting may prevent heads from loading.
Default is 'SIGN'.
### Disabled Game Modes
This list stores GameModes in which Level addon should not work.
To disable addon it is necessary to write its name in new line that starts with -. Example:
```
disabled-gamemodes:
- BSkyBlock
```
Default is that all game modes can use Warps

259
pom.xml
View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>world.bentobox</groupId>
<artifactId>warps</artifactId>
<groupId>world.bentobox</groupId>
<artifactId>warps</artifactId>
<version>${revision}</version>
<name>WelcomeWarpSigns</name>
<description>WelcomeWarpSigns is an add-on for BentoBox, an expandable Minecraft Bukkit plugin for island-type games like ASkyBlock or AcidIsland.</description>
<url>https://github.com/BentoBoxWorld/addon-welcomewarpsigns</url>
<inceptionYear>2018</inceptionYear>
<name>Warps</name>
<description>Warps is an add-on for BentoBox, an expandable Minecraft Bukkit plugin for island-type games like ASkyBlock or AcidIsland.</description>
<url>https://github.com/BentoBoxWorld/Warps</url>
<inceptionYear>2018</inceptionYear>
<developers>
<developer>
<developer>
<id>tastybento</id>
<email>tastybento@bentobox.world</email>
<timezone>-8</timezone>
@ -24,50 +24,54 @@
</developer>
</developers>
<scm>
<connection>scm:git:https://github.com/BentoBoxWorld/addon-welcomewarpsigns.git</connection>
<developerConnection>scm:git:git@github.com:BentoBoxWorld/addon-welcomewarpsigns.git</developerConnection>
<url>https://github.com/BentoBoxWorld/addon-welcomewarpsigns</url>
</scm>
<scm>
<connection>scm:git:https://github.com/BentoBoxWorld/Warps.git</connection>
<developerConnection>scm:git:git@github.com:BentoBoxWorld/Warps.git</developerConnection>
<url>https://github.com/BentoBoxWorld/Warps</url>
</scm>
<ciManagement>
<system>jenkins</system>
<url>http://ci.codemc.org/job/BentoBoxWorld/job/addon-welcomewarpsigns</url>
</ciManagement>
<ciManagement>
<system>jenkins</system>
<url>http://ci.codemc.org/job/BentoBoxWorld/job/Warps</url>
</ciManagement>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/BentoBoxWorld/addon-welcomewarpsigns/issues</url>
</issueManagement>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/BentoBoxWorld/Warps/issues</url>
</issueManagement>
<distributionManagement>
<snapshotRepository>
<id>codemc-snapshots</id>
<url>https://repo.codemc.org/repository/maven-snapshots</url>
</snapshotRepository>
<repository>
<id>codemc-releases</id>
<url>https://repo.codemc.org/repository/maven-releases</url>
</repository>
</distributionManagement>
<distributionManagement>
<snapshotRepository>
<id>codemc-snapshots</id>
<url>https://repo.codemc.org/repository/maven-snapshots</url>
</snapshotRepository>
<repository>
<id>codemc-releases</id>
<url>https://repo.codemc.org/repository/maven-releases</url>
</repository>
</distributionManagement>
<properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>17</java.version>
<!-- Non-minecraft related dependencies -->
<powermock.version>2.0.2</powermock.version>
<powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions -->
<spigot.version>1.14.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>1.6.0</bentobox.version>
<level.version>1.5.0</level.version>
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
<level.version>2.7.0-SNAPSHOT</level.version>
<!-- Revision variable removes warning about dynamic version -->
<revision>${build.version}-SNAPSHOT</revision>
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>1.6.1</build.version>
</properties>
<build.version>1.15.0</build.version>
<!-- Sonar Cloud -->
<sonar.projectKey>BentoBoxWorld_Warps</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
</properties>
<!-- Profiles will allow to automatically change build version. -->
<profiles>
@ -109,48 +113,24 @@
<build.number></build.number>
</properties>
</profile>
<profile>
<id>sonar</id>
<properties>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>tastybento-github</sonar.organization>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.4.1.1168</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sonar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository>
<repository>
<id>codemc</id>
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
</repository>
<repository>
<id>codemc-public</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
</repository>
</repositories>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository>
<repository>
<id>codemc</id>
<url>https://repo.codemc.org/repository/maven-snapshots/</url>
</repository>
<repository>
<id>codemc-public</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
@ -162,7 +142,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.0.0</version>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
@ -183,15 +163,24 @@
<version>${bentobox.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>level</artifactId>
<version>${level.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>level</artifactId>
<version>${level.version}</version>
<scope>provided</scope>
</dependency>
<!-- Static analysis -->
<!-- We are using Eclipse's annotations. If you're using IDEA, update
your project settings to take these into account for in real time static
analysis -->
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.annotation</artifactId>
<version>2.2.600</version>
</dependency>
</dependencies>
<build>
<build>
<!-- By default ${revision} is ${build.version}-SNAPSHOT -->
<!-- If GIT_BRANCH variable is set to origin/master, then it will
be only ${build.version}. -->
@ -202,18 +191,18 @@
be the empty string. -->
<finalName>${project.name}-${revision}${build.number}</finalName>
<defaultGoal>clean package</defaultGoal>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources/locales</directory>
<targetPath>./locales</targetPath>
<filtering>false</filtering>
</resource>
</resources>
<defaultGoal>clean package</defaultGoal>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources/locales</directory>
<targetPath>./locales</targetPath>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -237,7 +226,39 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<version>3.0.0-M5</version>
<configuration>
<argLine>
${argLine}
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens
java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens
java.base/java.util.regex=ALL-UNNAMED
--add-opens
java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens
java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
--add-opens java.base/java.nio.file=ALL-UNNAMED
--add-opens
java.base/java.nio.charset=ALL-UNNAMED
--add-opens
java.base/java.lang.reflect=ALL-UNNAMED
--add-opens
java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/java.util.zip=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -247,15 +268,18 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
<version>3.1.1</version>
<configuration>
<show>public</show>
<source>16</source>
<show>private</show>
<failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption>
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>install</phase>
<goals>
<goal>jar</goal>
</goals>
@ -275,14 +299,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<minimizeJar>false</minimizeJar>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
@ -296,30 +312,35 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<version>0.8.10</version>
<configuration>
<append>true</append>
<excludes>
<!-- This is required to prevent Jacoco from adding synthetic fields
to a JavaBean class (causes errors in testing) -->
<!-- This is required to prevent Jacoco from adding
synthetic fields to a JavaBean class (causes errors in testing) -->
<exclude>**/*Names*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>pre-unit-test</id>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
</build>
</project>

View File

@ -1,36 +0,0 @@
package world.bentobox.warps;
import java.util.List;
import org.bukkit.Material;
/**
* Stores info on a warp sign
* @author tastybento
*
*/
public class SignCache {
private final List<String> signText;
private final Material type;
/**
* @param signText
* @param type
*/
public SignCache(List<String> signText, Material type) {
this.signText = signText;
this.type = type;
}
/**
* @return the signText
*/
public List<String> getSignText() {
return signText;
}
/**
* @return the type
*/
public Material getType() {
return type;
}
}

View File

@ -2,21 +2,31 @@ package world.bentobox.warps;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.clicklisteners.CycleClick;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level;
import world.bentobox.warps.commands.WarpCommand;
import world.bentobox.warps.commands.WarpsCommand;
import world.bentobox.warps.config.Settings;
import world.bentobox.warps.listeners.WarpSignsListener;
import world.bentobox.warps.managers.SignCacheManager;
import world.bentobox.warps.managers.WarpSignsManager;
/**
* Addin to BSkyBlock that enables welcome warp signs
* Addin to BentoBox that enables welcome warp signs
* @author tastybento
*
*/
@ -31,15 +41,20 @@ public class Warp extends Addon {
private static final String LEVEL_ADDON_NAME = "Level";
/**
* Warp panel Manager
* Permission prefix for non-game world operation
*/
private WarpPanelManager warpPanelManager;
public static final String WELCOME_WARP_SIGNS = "welcomewarpsigns";
/**
* Worlds Sign manager.
*/
private WarpSignsManager warpSignsManager;
/**
* Sign Cache Manager
*/
private SignCacheManager signCacheManager;
/**
* This variable stores in which worlds this addon is working.
*/
@ -55,6 +70,16 @@ public class Warp extends Addon {
*/
private boolean hooked;
/**
* Settings config object
*/
private Config<Settings> settingsConfig;
/**
* Create Warp Flag
*/
private Flag createWarpFlag;
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
@ -71,7 +96,11 @@ public class Warp extends Addon {
// Save default config.yml
this.saveDefaultConfig();
// Load the plugin's config
this.loadSettings();
if (this.loadSettings() && getSettings().isAllowInOtherWorlds()) {
// Load the master warp and warps command
new WarpCommand(this);
new WarpsCommand(this);
}
}
@ -83,11 +112,11 @@ public class Warp extends Addon {
{
super.onReload();
if (this.hooked) {
if (this.hooked || getSettings().isAllowInOtherWorlds()) {
this.warpSignsManager.saveWarpList();
this.loadSettings();
this.getLogger().info("WelcomeWarp addon reloaded.");
this.getLogger().info("Warps addon reloaded.");
}
}
@ -104,58 +133,81 @@ public class Warp extends Addon {
// Register commands
this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> {
if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName()))
if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())
&& gameModeAddon.getPlayerCommand().isPresent())
{
if (gameModeAddon.getPlayerCommand().isPresent())
{
this.registeredWorlds.add(gameModeAddon.getOverWorld());
this.registeredWorlds.add(gameModeAddon.getOverWorld());
new WarpCommand(this, gameModeAddon.getPlayerCommand().get());
new WarpsCommand(this, gameModeAddon.getPlayerCommand().get());
this.hooked = true;
}
new WarpCommand(this, gameModeAddon.getPlayerCommand().get());
new WarpsCommand(this, gameModeAddon.getPlayerCommand().get());
this.hooked = true;
}
});
if (hooked)
if (hooked || getSettings().isAllowInOtherWorlds())
{
// Start warp signs
warpSignsManager = new WarpSignsManager(this, this.getPlugin());
warpPanelManager = new WarpPanelManager(this);
signCacheManager = new SignCacheManager(this);
// Load the listener
getServer().getPluginManager().registerEvents(new WarpSignsListener(this), this.getPlugin());
this.registerListener(new WarpSignsListener(this));
} else {
logWarning("Addon did not hook into anything and is not running stand-alone");
this.setState(State.DISABLED);
}
this.createWarpFlag = new Flag.Builder("PLACE_WARP", Material.OAK_SIGN)
.addon(this)
.defaultRank(RanksManager.MEMBER_RANK)
.clickHandler(new CycleClick("PLACE_WARP",
RanksManager.MEMBER_RANK,
RanksManager.OWNER_RANK))
.defaultSetting(false)
.mode(Flag.Mode.EXPERT)
.build();
getPlugin().getFlagsManager().registerFlag(this, this.createWarpFlag);
}
@Override
public void onDisable(){
// Save the warps
if (warpSignsManager != null)
if (warpSignsManager != null) {
warpSignsManager.saveWarpList();
}
}
/**
* This method loads addon configuration settings in memory.
*/
private void loadSettings() {
this.settings = new Config<>(this, Settings.class).loadConfigObject();
private boolean loadSettings() {
if (settingsConfig == null) {
settingsConfig = new Config<>(this, Settings.class);
}
this.settings = settingsConfig.loadConfigObject();
if (this.settings == null) {
// Disable
this.logError("WelcomeWarp settings could not load! Addon disabled.");
this.setState(State.DISABLED);
return false;
}
// Save existing panels.
this.saveResource("panels/warps_panel.yml", false);
settingsConfig.saveConfigObject(settings);
return true;
}
/**
* Get warp panel manager
* @return
* Get sign cache manager
* @return Sign Cache Manager
*/
public WarpPanelManager getWarpPanelManager() {
return warpPanelManager;
public SignCacheManager getSignCacheManager() {
return signCacheManager;
}
public WarpSignsManager getWarpSignsManager() {
@ -182,14 +234,67 @@ public class Warp extends Addon {
return settings;
}
/**
* @return the createWarpFlag
*/
public Flag getCreateWarpFlag() {
return createWarpFlag;
}
/**
* Get the island level
* @param world - world
* @param uniqueId - player's UUID
* @return island level or null if there is no level plugin
* @return island level or null if there is no level plugin or Level is not operating in this world
*/
public Long getLevel(World world, UUID uniqueId) {
return this.getPlugin().getAddonsManager().getAddonByName(LEVEL_ADDON_NAME).map(l -> ((Level) l).getIslandLevel(world, uniqueId)).orElse(null);
// Get name of the game mode
String name = this.getPlugin().getIWM().getAddon(world).map(g -> g.getDescription().getName()).orElse("");
return this.getPlugin().getAddonsManager().getAddonByName(LEVEL_ADDON_NAME)
.map(l -> {
final Level addon = (Level) l;
//getGameModes is a list of gamemodes that Level is DISABLED in,
//so we need the opposite of the contains.
if (!name.isEmpty() && !addon.getSettings().getGameModes().contains(name)) {
return addon.getIslandLevel(world, uniqueId);
}
return null;
}).orElse(null);
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.addons.Addon#request(java.lang.String, java.util.Map)
*
* This API enables plugins to request data from the WarpSignsManager
*
*/
@Override
public Object request(String requestLabel, Map<String, Object> metaData) {
if (metaData.isEmpty()) return null;
World world = null;
UUID uuid = null;
// Parse keys
if (metaData.containsKey("world")) {
world = Bukkit.getWorld((String)metaData.get("world"));
}
if (world == null) return null;
if (metaData.containsKey("uuid")) {
try {
uuid = UUID.fromString((String)metaData.get("uuid"));
} catch (Exception e) {
logError("Requested UUID is invalid");
return null;
}
}
return switch (requestLabel) {
case "getSortedWarps" -> getWarpSignsManager().getSortedWarps(world);
case "getWarp" -> uuid == null ? null : getWarpSignsManager().getWarp(world, uuid);
case "getWarpMap" -> getWarpSignsManager().getWarpMap(world);
case "hasWarp" -> uuid == null ? null : getWarpSignsManager().hasWarp(world, uuid);
case "listWarps" -> getWarpSignsManager().listWarps(world);
default -> null;
};
}
}

View File

@ -1,131 +0,0 @@
package world.bentobox.warps;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.user.User;
public class WarpPanelManager {
private static final int PANEL_MAX_SIZE = 52;
private Warp addon;
// This is a cache of signs
private Map<World, Map<UUID, SignCache>> cachedSigns = new HashMap<>();
public WarpPanelManager(Warp addon) {
this.addon = addon;
}
private PanelItem getPanelItem(World world, UUID warpOwner) {
// Return a sign panel item
return new PanelItemBuilder()
.icon(getSignIcon(world, warpOwner))
.name(addon.getPlugin().getPlayers().getName(warpOwner))
.description(getSign(world, warpOwner))
.clickHandler((panel, clicker, click, slot) -> { {
clicker.closeInventory();
addon.getWarpSignsManager().warpPlayer(world, clicker, warpOwner);
return true;
}
}).build();
}
private Material getSignIcon(World world, UUID warpOwner) {
// Add the worlds if we haven't seen this before
cachedSigns.putIfAbsent(world, new HashMap<>());
if (cachedSigns.get(world).containsKey(warpOwner)) {
return cachedSigns.get(world).get(warpOwner).getType();
}
// Not in cache
SignCache sc = addon.getWarpSignsManager().getSignInfo(world, warpOwner);
cachedSigns.get(world).put(warpOwner, sc);
return sc.getType();
}
/**
* Gets sign text and cache it
* @param playerUUID
* @return sign text in a list
*/
private List<String> getSign(World world, UUID playerUUID) {
// Add the worlds if we haven't seen this before
cachedSigns.putIfAbsent(world, new HashMap<>());
if (cachedSigns.get(world).containsKey(playerUUID)) {
return cachedSigns.get(world).get(playerUUID).getSignText();
}
SignCache result = addon.getWarpSignsManager().getSignInfo(world, playerUUID);
cachedSigns.get(world).put(playerUUID, result);
return result.getSignText();
}
/**
* Show the warp panel for the user
* @param world - world
* @param user - user
* @param index - page to show - 0 is first
*/
public void showWarpPanel(World world, User user, int index) {
List<UUID> warps = new ArrayList<>(addon.getWarpSignsManager().getSortedWarps(world));
if (index < 0) {
index = 0;
} else if (index > (warps.size() / PANEL_MAX_SIZE)) {
index = warps.size() / PANEL_MAX_SIZE;
}
PanelBuilder panelBuilder = new PanelBuilder()
.user(user)
.name(user.getTranslation("warps.title") + " " + String.valueOf(index + 1));
int i = index * PANEL_MAX_SIZE;
for (; i < (index * PANEL_MAX_SIZE + PANEL_MAX_SIZE) && i < warps.size(); i++) {
panelBuilder.item(getPanelItem(world, warps.get(i)));
}
final int panelNum = index;
// Add signs
if (i < warps.size()) {
// Next
panelBuilder.item(new PanelItemBuilder()
.name("Next")
.icon(new ItemStack(Material.STONE))
.clickHandler((panel, clicker, click, slot) -> {
user.closeInventory();
showWarpPanel(world, user, panelNum+1);
return true;
}).build());
}
if (i > PANEL_MAX_SIZE) {
// Previous
panelBuilder.item(new PanelItemBuilder()
.name("Previous")
.icon(new ItemStack(Material.COBBLESTONE))
.clickHandler((panel, clicker, click, slot) -> {
user.closeInventory();
showWarpPanel(world, user, panelNum-1);
return true;
}).build());
}
panelBuilder.build();
}
/**
* Removes sign text from the cache
* @param key
*/
public void removeWarp(World world, UUID key) {
cachedSigns.putIfAbsent(world, new HashMap<>());
cachedSigns.get(world).remove(key);
}
}

View File

@ -1,167 +0,0 @@
package world.bentobox.warps;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.SignChangeEvent;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.warps.event.WarpRemoveEvent;
/**
* Handles warping. Players can add one sign
*
* @author tastybento
*
*/
public class WarpSignsListener implements Listener {
private BentoBox plugin;
private Warp addon;
/**
* @param addon - addon
*/
public WarpSignsListener(Warp addon) {
this.addon = addon;
this.plugin = addon.getPlugin();
}
/**
* Checks to see if a sign has been broken
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onSignBreak(BlockBreakEvent e) {
Block b = e.getBlock();
// Signs only
// FIXME: When we drop support for 1.13, switch to Tag.SIGNS
if (!e.getBlock().getType().name().contains("SIGN")) {
return;
}
if (!addon.inRegisteredWorld(b.getWorld())) {
return;
}
User user = User.getInstance(e.getPlayer());
Sign s = (Sign) b.getState();
if (s == null) {
return;
}
if (s.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
// Do a quick check to see if this sign location is in
// the list of warp signs
Map<UUID, Location> list = addon.getWarpSignsManager().getWarpMap(b.getWorld());
if (list.containsValue(s.getLocation())) {
// Welcome sign detected - check to see if it is
// this player's sign
if ((list.containsKey(user.getUniqueId()) && list.get(user.getUniqueId()).equals(s.getLocation()))
|| user.isOp() || user.hasPermission(addon.getPermPrefix(e.getBlock().getWorld()) + "mod.removesign")) {
addon.getWarpSignsManager().removeWarp(s.getLocation());
Bukkit.getPluginManager().callEvent(new WarpRemoveEvent(addon, s.getLocation(), user.getUniqueId()));
} else {
// Someone else's sign - not allowed
user.sendMessage("warps.error.no-remove");
e.setCancelled(true);
}
}
}
}
/**
* Event handler for Sign Changes
*
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onSignWarpCreate(SignChangeEvent e) {
Block b = e.getBlock();
if (!addon.inRegisteredWorld(b.getWorld())) {
return;
}
String title = e.getLine(0);
User user = User.getInstance(e.getPlayer());
// Check if someone is changing their own sign
if (title.equalsIgnoreCase(addon.getSettings().getWelcomeLine())) {
// Welcome sign detected - check permissions
if (!(user.hasPermission(addon.getPermPrefix(b.getWorld()) + "island.addwarp"))) {
user.sendMessage("warps.error.no-permission");
user.sendMessage("general.errors.no-permission", "[permission]", addon.getPermPrefix(b.getWorld()) + "island.addwarp");
return;
}
// Get level if level addon is available
Long level = addon.getLevel(Util.getWorld(b.getWorld()), user.getUniqueId());
if (level != null && level < addon.getSettings().getWarpLevelRestriction()) {
user.sendMessage("warps.error.not-enough-level");
user.sendMessage("warps.error.your-level-is",
"[level]", String.valueOf(level),
"[required]", String.valueOf(addon.getSettings().getWarpLevelRestriction()));
return;
}
// Check that the player is on their island
if (!(plugin.getIslands().userIsOnIsland(b.getWorld(), user))) {
user.sendMessage("warps.error.not-on-island");
e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
return;
}
// Check if the player already has a sign
final Location oldSignLoc = addon.getWarpSignsManager().getWarp(b.getWorld(), user.getUniqueId());
if (oldSignLoc == null) {
// First time the sign has been placed or this is a new
// sign
addSign(e, user, b);
} else {
// A sign already exists. Check if it still there and if
// so,
// deactivate it
Block oldSignBlock = oldSignLoc.getBlock();
// FIXME: When we drop support for 1.13, switch to Tag.SIGNS
if (oldSignBlock.getType().name().contains("SIGN")) {
// The block is still a sign
Sign oldSign = (Sign) oldSignBlock.getState();
if (oldSign != null) {
if (oldSign.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
oldSign.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
oldSign.update(true, false);
user.sendMessage("warps.deactivate");
addon.getWarpSignsManager().removeWarp(oldSignBlock.getWorld(), user.getUniqueId());
Bukkit.getPluginManager().callEvent(new WarpRemoveEvent(addon, oldSign.getLocation(), user.getUniqueId()));
}
}
}
// Set up the new warp sign
addSign(e, user, b);
}
}
}
private void addSign(SignChangeEvent e, User user, Block b) {
if (addon.getWarpSignsManager().addWarp(user.getUniqueId(), b.getLocation())) {
user.sendMessage("warps.success");
e.setLine(0, ChatColor.GREEN + addon.getSettings().getWelcomeLine());
for (int i = 1; i<4; i++) {
e.setLine(i, ChatColor.translateAlternateColorCodes('&', e.getLine(i)));
}
} else {
user.sendMessage("warps.error.duplicate");
e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
for (int i = 1; i<4; i++) {
e.setLine(i, ChatColor.translateAlternateColorCodes('&', e.getLine(i)));
}
}
}
}

View File

@ -0,0 +1,14 @@
package world.bentobox.warps;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.Pladdon;
public class WarpsPladdon extends Pladdon {
@Override
public Addon getAddon() {
return new Warp();
}
}

View File

@ -1,12 +1,14 @@
package world.bentobox.warps.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.World;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.DelayedTeleportCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.warps.Warp;
@ -14,20 +16,24 @@ import world.bentobox.warps.Warp;
* The /is warp <name> command
*
* @author tastybento
*
*/
public class WarpCommand extends CompositeCommand {
public class WarpCommand extends DelayedTeleportCommand {
private Warp addon;
private final Warp addon;
public WarpCommand(Warp addon, CompositeCommand bsbIslandCmd) {
super(bsbIslandCmd, "warp");
super(bsbIslandCmd, addon.getSettings().getWarpCommand());
this.addon = addon;
}
public WarpCommand(Warp addon) {
super(addon, addon.getSettings().getWarpCommand());
this.addon = addon;
}
@Override
public void setup() {
this.setPermission("island.warp");
this.setPermission(this.getParent() == null ? Warp.WELCOME_WARP_SIGNS + ".warp" : "island.warp");
this.setOnlyPlayer(true);
this.setParametersHelp("warp.help.parameters");
this.setDescription("warp.help.description");
@ -36,22 +42,33 @@ public class WarpCommand extends CompositeCommand {
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() == 1) {
World world = getWorld() == null ? user.getWorld() : getWorld();
// Warp somewhere command
Set<UUID> warpList = addon.getWarpSignsManager().listWarps(getWorld());
Set<UUID> warpList = addon.getWarpSignsManager().listWarps(world);
if (warpList.isEmpty()) {
user.sendMessage("warps.error.no-warps-yet");
user.sendMessage("warps.warpTip", "[text]", addon.getSettings().getWelcomeLine());
return true;
return false;
} else {
// Check if this is part of a name
UUID foundWarp = warpList.stream().filter(u -> getPlayers().getName(u).toLowerCase().equals(args.get(0).toLowerCase())
|| getPlayers().getName(u).toLowerCase().startsWith(args.get(0).toLowerCase())).findFirst().orElse(null);
// Attemp to find warp with exact player's name
UUID foundWarp = warpList.stream().filter(u -> getPlayers().getName(u).equalsIgnoreCase(args.get(0))).findFirst().orElse(null);
if (foundWarp == null) {
user.sendMessage("warps.error.does-not-exist");
return false;
// Atempt to find warp which starts with the given name
UUID foundAlernativeWarp = warpList.stream().filter(u -> getPlayers().getName(u).toLowerCase().startsWith(args.get(0).toLowerCase())).findFirst().orElse(null);
if (foundAlernativeWarp == null) {
user.sendMessage("warps.error.does-not-exist");
return false;
} else {
// Alternative warp found!
this.delayCommand(user, () -> addon.getWarpSignsManager().warpPlayer(world, user, foundAlernativeWarp));
return true;
}
} else {
// Warp exists!
addon.getWarpSignsManager().warpPlayer(getWorld(), user, foundWarp);
this.delayCommand(user, () -> addon.getWarpSignsManager().warpPlayer(world, user, foundWarp));
return true;
}
}
@ -62,14 +79,8 @@ public class WarpCommand extends CompositeCommand {
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
List<String> options = new ArrayList<>();
final Set<UUID> warpList = addon.getWarpSignsManager().listWarps(getWorld());
for (UUID warp : warpList) {
options.add(addon.getPlugin().getPlayers().getName(warp));
}
return Optional.of(options);
World world = getWorld() == null ? user.getWorld() : getWorld();
return Optional.of(addon.getWarpSignsManager().listWarps(world).stream().map(getPlayers()::getName).toList());
}

View File

@ -1,24 +1,31 @@
/**
*
*/
package world.bentobox.warps.commands;
import java.util.List;
import world.bentobox.warps.Warp;
import org.bukkit.World;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.warps.Warp;
import world.bentobox.warps.panels.WarpsPanel;
/**
* @author ben
* Handles the warps command
* @author tastybento
*
*/
public class WarpsCommand extends CompositeCommand {
private Warp addon;
private final Warp addon;
public WarpsCommand(Warp addon, CompositeCommand bsbIslandCmd) {
super(bsbIslandCmd, "warps");
super(bsbIslandCmd, addon.getSettings().getWarpsCommand());
this.addon = addon;
}
public WarpsCommand(Warp addon) {
super(addon.getSettings().getWarpsCommand());
this.addon = addon;
}
@ -27,7 +34,7 @@ public class WarpsCommand extends CompositeCommand {
*/
@Override
public void setup() {
this.setPermission("island.warp");
this.setPermission(this.getParent() == null ? Warp.WELCOME_WARP_SIGNS + ".warps" : "island.warps");
this.setOnlyPlayer(true);
this.setDescription("warps.help.description");
}
@ -37,12 +44,18 @@ public class WarpsCommand extends CompositeCommand {
*/
@Override
public boolean execute(User user, String label, List<String> args) {
if (addon.getWarpSignsManager().listWarps(getWorld()).isEmpty()) {
World world = getWorld() == null ? user.getWorld() : getWorld();
if (addon
.getWarpSignsManager()
.listWarps(world)
.isEmpty()) {
user.sendMessage("warps.error.no-warps-yet");
user.sendMessage("warps.warpTip", "[text]", addon.getSettings().getWelcomeLine());
} else {
addon.getWarpPanelManager().showWarpPanel(getWorld(), user,0);
return false;
}
WarpsPanel.openPanel(this.addon, world, user);
return true;
}

View File

@ -9,7 +9,7 @@ import world.bentobox.bentobox.api.configuration.ConfigObject;
import world.bentobox.bentobox.api.configuration.StoreAt;
@StoreAt(filename="config.yml", path="addons/WelcomeWarps")
@StoreAt(filename="config.yml", path="addons/Warps")
@ConfigComment("WelcomeWarps Configuration [version]")
@ConfigComment("This config file is dynamic and saved when the server is shutdown.")
@ConfigComment("You cannot edit it while the server is running because changes will")
@ -21,20 +21,20 @@ public class Settings implements ConfigObject
@ConfigComment("Warp Restriction - needed levels to be able to create a warp")
@ConfigComment("0 or negative values will disable this restriction 10 is default")
@ConfigEntry(path = "warplevelrestriction")
private int warpLevelRestriction;
private int warpLevelRestriction = 10;
@ConfigComment("")
@ConfigComment("Should warps be removed when the island protection settings")
@ConfigComment("change, and the owner of the warp does no longer have")
@ConfigComment("the correct rank")
@ConfigEntry(path = "removeExistingWarpsWhenFlagChanges")
private boolean removeExistingWarpsWhenFlagChanges = false;
@ConfigComment("")
@ConfigComment("Text that player must put on sign to make it a warp sign")
@ConfigComment("Not case sensitive!")
@ConfigEntry(path = "welcomeLine")
private String welcomeLine;
@ConfigComment("")
@ConfigComment("Icon that will be displayed in Warps list.")
@ConfigComment("It uses native Minecraft material strings, but using string 'PLAYER', it is possible to")
@ConfigComment("use player heads instead.")
@ConfigEntry(path = "icon")
private String icon = "SIGN";
private String welcomeLine = "[Welcome]";
@ConfigComment("")
@ConfigComment("This list stores GameModes in which Level addon should not work.")
@ -44,6 +44,24 @@ public class Settings implements ConfigObject
@ConfigEntry(path = "disabled-gamemodes")
private Set<String> disabledGameModes = new HashSet<>();
@ConfigComment("")
@ConfigComment("Warp panel default lore formatting.")
@ConfigComment("Example: &c will make lore red. &f is white")
@ConfigEntry(path = "lore-format")
private String loreFormat = "&f";
@ConfigComment("")
@ConfigComment("Allow use in other worlds. Players must have the welcomewarpsigns.warp permission.")
@ConfigEntry(path = "allow-in-other-worlds")
private boolean allowInOtherWorlds = false;
@ConfigComment("")
@ConfigComment("Warp and warps commands. You can change them if they clash with other addons or plugins.")
@ConfigEntry(path = "warp-command")
String warpCommand = "warp";
@ConfigEntry(path = "warps-command")
String warpsCommand = "warps";
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
@ -125,21 +143,79 @@ public class Settings implements ConfigObject
/**
* This method returns the icon object.
* @return the icon object.
* @return the loreFormat
*/
public String getIcon()
{
return icon;
public String getLoreFormat() {
return loreFormat;
}
/**
* This method sets the icon object value.
* @param icon the icon object new value.
* @param loreFormat the loreFormat to set
*/
public void setIcon(String icon)
{
this.icon = icon;
public void setLoreFormat(String loreFormat) {
this.loreFormat = loreFormat;
}
/**
* @return the allowInOtherWorlds
*/
public boolean isAllowInOtherWorlds() {
return allowInOtherWorlds;
}
/**
* @param allowInOtherWorlds the allowInOtherWorlds to set
*/
public void setAllowInOtherWorlds(boolean allowInOtherWorlds) {
this.allowInOtherWorlds = allowInOtherWorlds;
}
/**
* @return the warpCommand
*/
public String getWarpCommand() {
return warpCommand;
}
/**
* @param warpCommand the warpCommand to set
*/
public void setWarpCommand(String warpCommand) {
this.warpCommand = warpCommand;
}
/**
* @return the warpsCommand
*/
public String getWarpsCommand() {
return warpsCommand;
}
/**
* @param warpsCommand the warpsCommand to set
*/
public void setWarpsCommand(String warpsCommand) {
this.warpsCommand = warpsCommand;
}
/**
* @return the removeExistingWarpsWhenFlagChanges
*/
public boolean getRemoveExistingWarpsWhenFlagChanges() {
return removeExistingWarpsWhenFlagChanges;
}
/**
* @param removeExistingWarpsWhenFlagChanges the removeExistingWarpsWhenFlagChanges to set
*/
public void setRemoveExistingWarpsWhenFlagChanges(boolean removeExistingWarpsWhenFlagChanges) {
this.removeExistingWarpsWhenFlagChanges = removeExistingWarpsWhenFlagChanges;
}
}

View File

@ -11,39 +11,39 @@ import world.bentobox.warps.Warp;
/**
* This event is fired when a Warp is created
* A Listener to this event can use it only to get informations. e.g: broadcast something
*
*
* @author Poslovitch
*
*/
public class WarpCreateEvent extends Event{
private static final HandlerList handlers = new HandlerList();
private Location warpLoc;
private UUID creator;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc
* @param creator
*/
public WarpCreateEvent(Warp plugin, Location warpLoc, UUID creator){
this.warpLoc = warpLoc;
this.creator = creator;
}
/**
* Get the location of the created Warp
* @return created warp's location
*/
public Location getWarpLocation(){return this.warpLoc;}
/**
* Get who has created the warp
* @return the warp's creator
*/
public UUID getCreator(){return this.creator;}
@Override
private static final HandlerList handlers = new HandlerList();
private final Location warpLoc;
private final UUID creator;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc warp location
* @param creator UUID of creator
*/
public WarpCreateEvent(Warp plugin, Location warpLoc, UUID creator){
this.warpLoc = warpLoc;
this.creator = creator;
}
/**
* Get the location of the created Warp
* @return created warp's location
*/
public Location getWarpLocation(){return this.warpLoc;}
/**
* Get who has created the warp
* @return the warp's creator
*/
public UUID getCreator(){return this.creator;}
@Override
public HandlerList getHandlers() {
return handlers;
}

View File

@ -10,49 +10,49 @@ import org.bukkit.event.HandlerList;
import world.bentobox.warps.Warp;
/**
* This event is fired when a player tries to do a warp
* This event is fired when a player tries to do a warp
* A Listener to this event can use it to get informations. e.g: broadcast something
*
*
* @author tastybento
*
*/
public class WarpInitiateEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private Location warpLoc;
private final UUID player;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc - where the player is warping to
* @param player - the UUID of the player
*/
public WarpInitiateEvent(Warp plugin, Location warpLoc, UUID player){
this.warpLoc = warpLoc;
this.player = player;
}
/**
* Get the location of the Warp
* @return created warp's location
*/
public Location getWarpLoc(){return this.warpLoc;}
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private Location warpLoc;
private final UUID player;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc - where the player is warping to
* @param player - the UUID of the player
*/
public WarpInitiateEvent(Warp plugin, Location warpLoc, UUID player){
this.warpLoc = warpLoc;
this.player = player;
}
/**
* Get the location of the Warp
* @return created warp's location
*/
public Location getWarpLoc(){return this.warpLoc;}
/**
* Set a different location to where the player will go
* @param warpLoc
* @param warpLoc warp location
*/
public void setWarpLoc(Location warpLoc) {
this.warpLoc = warpLoc;
}
/**
* Get who is warping
* @return the warping player's uuid
*/
public UUID getPlayer(){return this.player;}
@Override
* Get who is warping
* @return the warping player's uuid
*/
public UUID getPlayer(){return this.player;}
@Override
public HandlerList getHandlers() {
return handlers;
}
@ -63,13 +63,12 @@ public class WarpInitiateEvent extends Event implements Cancellable {
@Override
public boolean isCancelled() {
// TODO Auto-generated method stub
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -1,75 +0,0 @@
/*******************************************************************************
* This file is part of ASkyBlock.
*
* ASkyBlock is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ASkyBlock is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ASkyBlock. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package world.bentobox.warps.event;
import java.util.List;
import java.util.UUID;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import world.bentobox.warps.Warp;
/**
* This event is fired when request is made for a sorted list of warps or when
* the API updateWarpPanel method is called.
* A listener to this event can reorder or rewrite the warp list by using setWarps.
* This new order will then be used in the warp panel.
*
* @author tastybento
*
*/
public class WarpListEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private List<UUID> warps;
/**
* @param plugin - BSkyBlock plugin objects
* @param warps
*/
public WarpListEvent(Warp plugin, List<UUID> warps) {
this.warps = warps;
}
/**
*The warp list is a collection of player UUID's and the default order is
* that players with the most recent login will be first.
* @return the warps
*/
public List<UUID> getWarps() {
return warps;
}
/**
* @param warps the warps to set
*/
public void setWarps(List<UUID> warps) {
this.warps = warps;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -5,8 +5,8 @@ import java.util.UUID;
import org.bukkit.Location;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import world.bentobox.warps.Warp;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
/**
* This event is fired when a Warp is removed (when a warp sign is broken)
@ -18,32 +18,48 @@ import world.bentobox.warps.Warp;
public class WarpRemoveEvent extends Event{
private static final HandlerList handlers = new HandlerList();
private Location warpLoc;
private UUID remover;
private final Location warpLoc;
private final UUID remover;
private final UUID owner;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc
* @param remover
* @param warpLoc - Warp location
* @param remover - UUID of remover
* @param owner - UUID of warp owner - rarely, may be null
*/
public WarpRemoveEvent(Warp plugin, Location warpLoc, UUID remover){
public WarpRemoveEvent(@NonNull Location warpLoc, UUID remover, @Nullable UUID owner){
this.warpLoc = warpLoc;
this.remover = remover;
this.owner = owner;
}
/**
* Get the location of the removed Warp
* @return removed warp's location
*/
public Location getWarpLocation(){return this.warpLoc;}
@NonNull
public Location getWarpLocation(){
return this.warpLoc;
}
/**
* Get who has removed the warp
* @return the warp's remover
*/
public UUID getRemover(){return this.remover;}
@NonNull
public UUID getRemover(){
return this.remover;
}
@Override
/**
* @return the owner
*/
@Nullable
protected UUID getOwner() {
return owner;
}
@Override
public HandlerList getHandlers() {
return handlers;
}

View File

@ -0,0 +1,297 @@
package world.bentobox.warps.listeners;
import java.util.*;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.events.addon.AddonEvent;
import world.bentobox.bentobox.api.events.flags.FlagProtectionChangeEvent;
import world.bentobox.bentobox.api.events.team.TeamKickEvent;
import world.bentobox.bentobox.api.events.team.TeamLeaveEvent;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.warps.Warp;
import world.bentobox.warps.event.WarpRemoveEvent;
/**
* Handles warping. Players can add one sign
*
* @author tastybento
*
*/
public class WarpSignsListener implements Listener {
private static final String WARPS_DEACTIVATE = "warps.deactivate";
private final BentoBox plugin;
private final Warp addon;
/**
* @param addon - addon
*/
public WarpSignsListener(Warp addon) {
this.addon = addon;
this.plugin = addon.getPlugin();
}
@EventHandler(priority = EventPriority.NORMAL)
public void onChunkLoad(ChunkLoadEvent event) {
// Delay to wait the chunk to be fully loaded
new BukkitRunnable() {
@Override
public void run() {
boolean changed = false;
Iterator<Map.Entry<UUID, Location>> iterator =
addon.getWarpSignsManager().getWarpMap(event.getWorld()).entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, Location> entry = iterator.next();
UUID uuid = entry.getKey();
Location location = entry.getValue();
if (event.getChunk().getX() == location.getBlockX() >> 4
&& event.getChunk().getZ() == location.getBlockZ() >> 4
&& !Tag.SIGNS.isTagged(location.getBlock().getType())) {
iterator.remove();
// Remove sign from warp panel cache
addon.getSignCacheManager().removeWarp(event.getWorld(), uuid);
changed = true;
}
}
if (changed) {
addon.getWarpSignsManager().saveWarpList();
}
}
}.runTask(plugin);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerLeave(TeamLeaveEvent e) {
// Remove any warp signs from this game mode
addon.getWarpSignsManager().removeWarp(e.getIsland().getWorld(), e.getPlayerUUID());
Objects.requireNonNull(User.getInstance(e.getPlayerUUID())).sendMessage(WARPS_DEACTIVATE);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerLeave(TeamKickEvent e) {
// Remove any warp signs from this game mode
addon.getWarpSignsManager().removeWarp(e.getIsland().getWorld(), e.getPlayerUUID());
Objects.requireNonNull(User.getInstance(e.getPlayerUUID())).sendMessage(WARPS_DEACTIVATE);
}
/**
* Checks to see if a sign has been broken
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onSignBreak(BlockBreakEvent e) {
Block b = e.getBlock();
boolean inWorld = addon.getPlugin().getIWM().inWorld(b.getWorld());
// Signs only
// FIXME: When we drop support for 1.13, switch to Tag.SIGNS
if (!b.getType().name().contains("SIGN")
|| (inWorld && !addon.inRegisteredWorld(b.getWorld()))
|| (!inWorld && !addon.getSettings().isAllowInOtherWorlds())
|| !isWarpSign(b)) {
return;
}
User user = User.getInstance(e.getPlayer());
UUID owner = addon.getWarpSignsManager().getWarpOwnerUUID(b.getLocation()).orElse(null);
if (isPlayersSign(e.getPlayer(), b, inWorld)) {
addon.getWarpSignsManager().removeWarp(b.getLocation());
Bukkit.getPluginManager().callEvent(new WarpRemoveEvent(b.getLocation(), user.getUniqueId(), owner));
} else {
// Someone else's sign - not allowed
user.sendMessage("warps.error.no-remove");
e.setCancelled(true);
}
}
private boolean isPlayersSign(Player player, Block b, boolean inWorld) {
// Welcome sign detected - check to see if it is this player's sign
Map<UUID, Location> list = addon.getWarpSignsManager().getWarpMap(b.getWorld());
String reqPerm = inWorld ? addon.getPermPrefix(b.getWorld()) + "mod.removesign" : Warp.WELCOME_WARP_SIGNS + ".mod.removesign";
return ((list.containsKey(player.getUniqueId()) && list.get(player.getUniqueId()).equals(b.getLocation()))
|| player.isOp() || player.hasPermission(reqPerm));
}
private boolean isWarpSign(Block b) {
Sign s = (Sign) b.getState();
return s.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())
&& addon.getWarpSignsManager().getWarpMap(b.getWorld()).containsValue(s.getLocation());
}
/**
* Event handler for Sign Changes
*
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onSignWarpCreate(SignChangeEvent e) {
Block b = e.getBlock();
boolean inWorld = addon.getPlugin().getIWM().inWorld(b.getWorld());
if ((inWorld && !addon.inRegisteredWorld(b.getWorld())) || (!inWorld && !addon.getSettings().isAllowInOtherWorlds()) ) {
return;
}
String title = e.getLine(0);
User user = Objects.requireNonNull(User.getInstance(e.getPlayer()));
// Check if someone is changing their own sign
if (title != null && title.equalsIgnoreCase(addon.getSettings().getWelcomeLine())) {
// Welcome sign detected - check permissions
if (noPerms(user, b.getWorld(), inWorld)) {
return;
}
if (inWorld && noLevelOrIsland(user, b.getWorld())) {
e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
return;
}
if(!hasCorrectIslandRank(b, user)) {
e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
user.sendMessage("warps.error.not-correct-rank");
return;
}
// Check if the player already has a sign
final Location oldSignLoc = addon.getWarpSignsManager().getWarp(b.getWorld(), user.getUniqueId());
if (oldSignLoc != null) {
// A sign already exists. Check if it still there and if
// so,
// deactivate it
Block oldSignBlock = oldSignLoc.getBlock();
// FIXME: When we drop support for 1.13, switch to Tag.SIGNS
if (oldSignBlock.getType().name().contains("SIGN")) {
// The block is still a sign
Sign oldSign = (Sign) oldSignBlock.getState();
if (oldSign.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
oldSign.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
oldSign.update(true, false);
user.sendMessage(WARPS_DEACTIVATE);
addon.getWarpSignsManager().removeWarp(oldSignBlock.getWorld(), user.getUniqueId());
@Nullable
UUID owner = addon.getWarpSignsManager().getWarpOwnerUUID(oldSignLoc).orElse(null);
Bukkit.getPluginManager().callEvent(new WarpRemoveEvent(oldSign.getLocation(), user.getUniqueId(), owner));
}
}
// Set up the new warp sign
}
addSign(e, user, b);
}
}
private boolean hasCorrectIslandRank(Block b, User user) {
final Optional<Island> islandOpt = plugin.getIslands().getIslandAt(b.getLocation());
if(islandOpt.isEmpty()) return false;
final Island island = islandOpt.get();
final int userRank = island.getRank(user);
return userRank >= island.getFlag(addon.getCreateWarpFlag());
}
@EventHandler
public void onFlagChange(FlagProtectionChangeEvent e) {
if(!e.getEditedFlag().equals(addon.getCreateWarpFlag())) return;
if(!addon.getSettings().getRemoveExistingWarpsWhenFlagChanges()) return;
final Island island = e.getIsland();
final Map<UUID, Location> islandWarps = addon
.getWarpSignsManager()
.getWarpMap(island.getWorld())
.entrySet()
.stream()
.filter(x -> island.inIslandSpace(x.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
for(Map.Entry<UUID, Location> entry : islandWarps.entrySet()) {
if(island.getRank(entry.getKey()) >= e.getSetTo()) continue;
//The user has a lower rank than the new set value.
//We need to remove the warp.
addon.getWarpSignsManager().removeWarp(island.getWorld(), entry.getKey());
if(Bukkit.getPlayer(entry.getKey()) != null) {
Objects.requireNonNull(User.getInstance(entry.getKey())).sendMessage(WARPS_DEACTIVATE);
}
}
}
private boolean noLevelOrIsland(User user, World world) {
// Get level if level addon is available
Long level = addon.getLevel(Util.getWorld(world), user.getUniqueId());
if (level != null && level < addon.getSettings().getWarpLevelRestriction()) {
user.sendMessage("warps.error.not-enough-level");
user.sendMessage("warps.error.your-level-is",
"[level]", String.valueOf(level),
"[required]", String.valueOf(addon.getSettings().getWarpLevelRestriction()));
return true;
}
// Check that the player is on their island
if (!(plugin.getIslands().userIsOnIsland(world, user))) {
user.sendMessage("warps.error.not-on-island");
return true;
}
return false;
}
/**
* Check if player has permission to execute command
* @param user - user
* @param world - world that the warp is in
* @param inWorld - true if warp is in a game world
* @return true if player does not have the required perms, false otherwise
*/
private boolean noPerms(User user, World world, boolean inWorld) {
String permReq = inWorld ? addon.getPermPrefix(world) + "island.addwarp" : Warp.WELCOME_WARP_SIGNS + ".addwarp";
if (!(user.hasPermission(permReq))) {
user.sendMessage("warps.error.no-permission");
user.sendMessage("general.errors.no-permission", "[permission]", permReq);
return true;
}
return false;
}
private void addSign(SignChangeEvent e, User user, Block b) {
if (addon.getWarpSignsManager().addWarp(user.getUniqueId(), b.getLocation())) {
user.sendMessage("warps.success");
e.setLine(0, ChatColor.GREEN + addon.getSettings().getWelcomeLine());
for (int i = 1; i<4; i++) {
String line = e.getLine(i);
if (line != null) {
e.setLine(i, ChatColor.translateAlternateColorCodes('&', line));
}
}
Map<String, Object> keyValues = new HashMap<>();
keyValues.put("eventName", "WarpCreateEvent");
keyValues.put("targetPlayer", user.getUniqueId());
keyValues.put("location", Util.getStringLocation(b.getLocation()));
new AddonEvent().builder().addon(addon).keyValues(keyValues).build();
}
// Else null player
}
}

View File

@ -0,0 +1,57 @@
package world.bentobox.warps.managers;
import java.util.List;
import org.bukkit.Material;
import com.google.gson.annotations.Expose;
/**
* Stores info on a warp sign
* @author tastybento
*
*/
public class SignCacheItem {
@Expose
private final List<String> signText;
@Expose
private final Material type;
/**
* @param signText sign text
* @param type material of sign
*/
public SignCacheItem(List<String> signText, Material type) {
this.signText = signText;
this.type = type;
}
/**
* This sign is not real
*/
public SignCacheItem() {
this.signText = null;
this.type = null;
}
/**
* @return the signText
*/
public List<String> getSignText() {
return signText;
}
/**
* @return the type
*/
public Material getType() {
return type;
}
/**
* @return the isReal
*/
public boolean isReal() {
return getType() != null;
}
}

View File

@ -0,0 +1,81 @@
package world.bentobox.warps.managers;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.database.Database;
import world.bentobox.warps.Warp;
import world.bentobox.warps.objects.SignCache;
public class SignCacheManager {
private final Map<World, Map<UUID, SignCacheItem>> cachedSigns = new HashMap<>();
private final Warp addon;
// Database handler for level data
private final Database<SignCache> handler;
public SignCacheManager(Warp addon) {
this.addon = addon;
handler = new Database<>(addon, SignCache.class);
// Load the sign caches
loadCache();
}
private void loadCache() {
cachedSigns.clear();
handler.loadObjects().forEach(w -> {
World world = Bukkit.getWorld(w.getUniqueId());
if (world != null) {
w.getSigns().values().removeIf(sci -> sci.getType().equals(Material.AIR));
cachedSigns.put(world, w.getSigns());
}
});
}
public void saveCache() {
cachedSigns.forEach((w, m) -> handler.saveObjectAsync(new SignCache(w, m)));
}
/**
* Get the sign item from cache or get it from the world if it is not in the cache
* @param world - world
* @param warpOwner - warp owner
* @return SignCacheItem
*/
@NonNull
public SignCacheItem getSignItem(World world, UUID warpOwner) {
// Add the worlds if we haven't seen this before
cachedSigns.putIfAbsent(world, new HashMap<>());
// Get from cache if available
if (cachedSigns.get(world).containsKey(warpOwner)) {
return cachedSigns.get(world).get(warpOwner);
}
// Generate and add to cache
SignCacheItem result = addon.getWarpSignsManager().getSignInfo(world, warpOwner);
if (result.isReal()) {
cachedSigns.get(world).put(warpOwner, result);
} else {
cachedSigns.get(world).remove(warpOwner);
}
return result;
}
/**
* Removes sign text from the cache
* @param world - world
* @param key - uuid of owner
* @return true if item is removed from cache
*/
public boolean removeWarp(World world, UUID key) {
if (cachedSigns.containsKey(world)) {
return cachedSigns.get(world).remove(key) != null;
}
return false;
}
}

View File

@ -1,18 +1,19 @@
package world.bentobox.warps;
package world.bentobox.warps.managers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
@ -20,20 +21,27 @@ import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.util.Util;
import world.bentobox.warps.Warp;
import world.bentobox.warps.event.WarpInitiateEvent;
import world.bentobox.warps.event.WarpListEvent;
import world.bentobox.warps.objects.WarpsData;
import world.bentobox.warps.panels.Utils;
/**
* Handles warping. Players can add one sign
@ -43,13 +51,14 @@ import world.bentobox.warps.objects.WarpsData;
*/
public class WarpSignsManager {
private static final int MAX_WARPS = 600;
private BentoBox plugin;
private static final String WARPS = "warps";
private final BentoBox plugin;
// Map of all warps stored as player, warp sign Location
private Map<World, Map<UUID, Location>> worldsWarpList;
// Database handler for level data
private Database<WarpsData> handler;
private final Database<WarpsData> handler;
private Warp addon;
private final Warp addon;
private WarpsData warpsData = new WarpsData();
/**
@ -57,7 +66,8 @@ public class WarpSignsManager {
* @param world - world
* @return map of warps
*/
public Map<UUID, Location> getWarpMap(World world) {
@NonNull
public Map<UUID, Location> getWarpMap(@Nullable World world) {
return worldsWarpList.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>());
}
@ -68,8 +78,9 @@ public class WarpSignsManager {
public WarpSignsManager(Warp addon, BentoBox plugin) {
this.addon = addon;
this.plugin = plugin;
// Set up the database handler to store and retrieve Island classes
// Note that these are saved by the BSkyBlock database
this.worldsWarpList = new HashMap<>();
// Set up the database handler
// Note that these are saved by the BentoBox database
handler = new Database<>(addon, WarpsData.class);
// Load the warps
loadWarpList();
@ -83,10 +94,15 @@ public class WarpSignsManager {
* @return true if successful, false if not
*/
public boolean addWarp(final UUID playerUUID, final Location loc) {
// Do not allow null players to set warps or warps to be in the same location
if (playerUUID == null || getWarpMap(loc.getWorld()).containsValue(loc)) {
// Do not allow null players to set warps
if (playerUUID == null || loc == null) {
return false;
}
// Check for warps placed in a location where there was a warp before
if (getWarpMap(loc.getWorld()).containsValue(loc)) {
// remove the warp at this location, then place it
this.removeWarp(loc);
}
getWarpMap(loc.getWorld()).put(playerUUID, loc);
saveWarpList();
Bukkit.getPluginManager().callEvent(new WarpInitiateEvent(addon, loc, playerUUID));
@ -101,6 +117,7 @@ public class WarpSignsManager {
* - the warp requested
* @return Location of warp or null
*/
@Nullable
public Location getWarp(World world, UUID playerUUID) {
return getWarpMap(world).get(playerUUID);
}
@ -110,22 +127,38 @@ public class WarpSignsManager {
* @param location to search
* @return Name of warp owner or empty string if there is none
*/
@NonNull
public String getWarpOwner(Location location) {
return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().equals(location))
.findFirst().map(en -> plugin.getPlayers().getName(en.getKey())).orElse("");
}
/**
* Get the optional UUID of the warp owner by location
* @param location to search
* @return Optional UUID of warp owner or empty if there is none
*/
public Optional<UUID> getWarpOwnerUUID(Location location) {
return getWarpMap(location.getWorld()).entrySet().stream().filter(en -> en.getValue().equals(location))
.findFirst().map(Map.Entry::getKey);
}
/**
* Get sorted list of warps with most recent players listed first
* @return UUID list
*/
public List<UUID> getSortedWarps(World world) {
public CompletableFuture<List<UUID>> getSortedWarps(@NonNull World world) {
CompletableFuture<List<UUID>> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> processWarpMap(r, world));
return r;
}
public List<UUID> processWarpMap(CompletableFuture<List<UUID>> r, @NonNull World world) {
// Remove any null locations - this can happen if an admin changes the name of the world and signs point to old locations
getWarpMap(world).values().removeIf(Objects::isNull);
// Bigger value of time means a more recent login
TreeMap<Long, UUID> map = new TreeMap<Long, UUID>();
getWarpMap(world).entrySet().forEach(en -> {
UUID uuid = en.getKey();
TreeMap<Long, UUID> map = new TreeMap<>();
getWarpMap(world).forEach((uuid, value) -> {
// If never played, will be zero
long lastPlayed = addon.getServer().getOfflinePlayer(uuid).getLastPlayed();
// This aims to avoid the chance that players logged off at exactly the same time
@ -139,11 +172,8 @@ public class WarpSignsManager {
if (list.size() > MAX_WARPS) {
list.subList(0, MAX_WARPS).clear();
}
// Fire event
WarpListEvent event = new WarpListEvent(addon, list);
addon.getServer().getPluginManager().callEvent(event);
// Get the result of any changes by listeners
list = event.getWarps();
// Return to main thread
Bukkit.getScheduler().runTask(plugin, () -> r.complete(list));
return list;
}
@ -153,45 +183,51 @@ public class WarpSignsManager {
*
* @return UUID set of warps
*/
public Set<UUID> listWarps(World world) {
@NonNull
public Set<UUID> listWarps(@NonNull World world) {
// Remove any null locations
getWarpMap(world).values().removeIf(Objects::isNull);
return getWarpMap(world).entrySet().stream().filter(e -> Util.sameWorld(world, e.getValue().getWorld())).map(Map.Entry::getKey).collect(Collectors.toSet());
return getWarpMap(world).entrySet().stream().filter(e -> Util.sameWorld(world, Objects.requireNonNull(e.getValue().getWorld()))).map(Map.Entry::getKey).collect(Collectors.toSet());
}
/**
* Load the warps and check if they still exist
*/
private void loadWarpList() {
addon.getLogger().info("Loading warps...");
public void loadWarpList() {
addon.log("Loading warps...");
worldsWarpList = new HashMap<>();
if (handler.objectExists("warps")) {
warpsData = handler.loadObject("warps");
if (handler.objectExists(WARPS)) {
warpsData = handler.loadObject(WARPS);
// Load into map
if (warpsData != null) {
warpsData.getWarpSigns().forEach((k,v) -> {
if (k != null && k.getWorld() != null && k.getBlock().getType().name().contains("SIGN")) {
warpsData.getWarpSigns().forEach((location,uuid) -> {
if (location != null && location.getWorld() != null) {
if (location.getWorld().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)
&& !location.getBlock().getType().name().contains("SIGN")) {
return;
}
// Add to map
getWarpMap(k.getWorld()).put(v, k);
getWarpMap(location.getWorld()).put(uuid, location);
}
});
} else {
warpsData = new WarpsData();
}
}
}
/**
* Changes the sign to red if it exists
* @param loc
* @param loc location to pop
*/
private void popSign(Location loc) {
Block b = loc.getBlock();
if (b.getType().name().contains("SIGN")) {
Sign s = (Sign) b.getState();
if (s != null) {
if (s.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
s.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
s.update(true, false);
}
if (s.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
s.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
s.update(true, false);
}
}
}
@ -199,7 +235,7 @@ public class WarpSignsManager {
/**
* Removes a warp at a location.
*
* @param loc
* @param loc location to remove
*/
public void removeWarp(Location loc) {
popSign(loc);
@ -208,13 +244,11 @@ public class WarpSignsManager {
Entry<UUID, Location> en = it.next();
if (en.getValue().equals(loc)) {
// Inform player
User user = User.getInstance(addon.getServer().getPlayer(en.getKey()));
if (user != null) {
// Inform the player
user.sendMessage("warps.sign-removed");
}
Optional.ofNullable(addon.getServer().getPlayer(en.getKey()))
.map(User::getInstance)
.ifPresent(user -> user.sendMessage("warps.sign-removed"));
// Remove sign from warp panel cache
addon.getWarpPanelManager().removeWarp(loc.getWorld(), en.getKey());
addon.getSignCacheManager().removeWarp(loc.getWorld(), en.getKey());
it.remove();
}
}
@ -224,23 +258,34 @@ public class WarpSignsManager {
/**
* Remove warp sign owned by UUID
*
* @param uuid
* @param uuid UUID of owner to remove
*/
public void removeWarp(World world, UUID uuid) {
if (getWarpMap(world).containsKey(uuid)) {
popSign(getWarpMap(world).get(uuid));
getWarpMap(world).remove(uuid);
// Remove sign from warp panel cache
addon.getWarpPanelManager().removeWarp(world, uuid);
}
// Remove sign from warp panel cache
addon.getSignCacheManager().removeWarp(world, uuid);
saveWarpList();
}
/**
* Remove the warp from the warp map
* @param world - world
* @param uuid - uuid of owner
*/
public void removeWarpFromMap(World world, UUID uuid) {
getWarpMap(world).remove(uuid);
}
/**
* Saves the warp lists to the database
*/
public void saveWarpList() {
handler.saveObject(warpsData .save(worldsWarpList));
handler.saveObjectAsync(warpsData.save(worldsWarpList));
addon.getSignCacheManager().saveCache();
}
/**
@ -250,24 +295,46 @@ public class WarpSignsManager {
* @param uuid - player's uuid
* @return Sign's content and type
*/
public SignCache getSignInfo(World world, UUID uuid) {
List<String> result = new ArrayList<>();
@NonNull
public SignCacheItem getSignInfo(@NonNull World world, @NonNull UUID uuid) {
//get the sign info
Location signLocation = getWarp(world, uuid);
if (signLocation != null && signLocation.getBlock().getType().name().contains("SIGN")) {
Sign sign = (Sign)signLocation.getBlock().getState();
result.addAll(Arrays.asList(sign.getLines()));
// Clean up - remove the [WELCOME] line
result.remove(0);
// Remove any trailing blank lines
result.removeIf(String::isEmpty);
// Get the sign type
Material type = Material.valueOf(sign.getType().name().replace("WALL_", ""));
return new SignCache(result, type);
} else {
addon.getWarpSignsManager().removeWarp(world, uuid);
if (signLocation == null || !signLocation.getBlock().getType().name().contains("SIGN")) {
return new SignCacheItem();
}
Sign sign = (Sign)signLocation.getBlock().getState();
List<String> result = new ArrayList<>(Arrays.asList(sign.getLines()));
// Clean up - remove the [WELCOME] line
result.remove(0);
// Remove any trailing blank lines
result.removeIf(String::isEmpty);
// Set the initial color per lore setting
for (int i = 0; i< result.size(); i++) {
result.set(i, ChatColor.translateAlternateColorCodes('&', addon.getSettings().getLoreFormat()) + result.get(i));
}
// Get the sign type
String prefix = plugin.getIWM().getAddon(world).map(Addon::getPermissionPrefix).orElse("");
Material icon;
if (!prefix.isEmpty())
{
icon = Material.matchMaterial(
Utils.getPermissionValue(User.getInstance(uuid), prefix + "island.warp",
Material.OAK_SIGN.name()));
}
else
{
icon = null;
}
if (icon != null && icon.name().contains("SIGN")) {
return new SignCacheItem(result, Material.valueOf(sign.getType().name().replace("WALL_", "")));
}
else
{
return new SignCacheItem(result, icon);
}
return new SignCache(Collections.emptyList(), Material.AIR);
}
/**
@ -278,78 +345,48 @@ public class WarpSignsManager {
* @param directionFacing - direction that sign is facing
* @param pvp - true if this location allowed PVP
*/
private void warpPlayer(User user, Location inFront, UUID signOwner, BlockFace directionFacing, boolean pvp) {
private void warpPlayer(@NonNull User user, @NonNull Location inFront, @NonNull UUID signOwner, @NonNull BlockFace directionFacing, boolean pvp) {
// convert blockface to angle
float yaw = blockFaceToFloat(directionFacing);
float yaw = Util.blockFaceToFloat(directionFacing);
final Location actualWarp = new Location(inFront.getWorld(), inFront.getBlockX() + 0.5D, inFront.getBlockY(),
inFront.getBlockZ() + 0.5D, yaw, 30F);
user.teleport(actualWarp);
if (pvp) {
user.sendMessage("protection.flags.PVP_OVERWORLD.active");
user.getWorld().playSound(user.getLocation(), Sound.ENTITY_ARROW_HIT, 1F, 1F);
} else {
user.getWorld().playSound(user.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
}
User warpOwner = User.getInstance(signOwner);
if (!warpOwner.equals(user)) {
warpOwner.sendMessage("warps.player-warped", "[name]", user.getName());
}
//BentoBox prevents people from teleporting to an island when
//the user is banned from the island for example.
//By checking if the teleport succeeded before sending the messages,
//we prevent issues where no one teleported, but people still
//get messages about it.
Util.teleportAsync(user.getPlayer(), actualWarp, TeleportCause.COMMAND).thenAccept(tpResult -> {
if(Boolean.FALSE.equals(tpResult)) return;
User warpOwner = Objects.requireNonNull(User.getInstance(signOwner));
// Hide invisible players
if (warpOwner.isOnline() && !warpOwner.getPlayer().canSee(user.getPlayer())) {
return;
}
if (pvp) {
user.sendMessage("protection.flags.PVP_OVERWORLD.enabled");
user.getWorld().playSound(Objects.requireNonNull(user.getLocation()), Sound.ENTITY_ARROW_HIT, 1F, 1F);
} else {
user.getWorld().playSound(Objects.requireNonNull(user.getLocation()), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
}
if (!warpOwner.equals(user)) {
final String gameMode = BentoBox
.getInstance()
.getIWM()
.getFriendlyName(actualWarp.getWorld());
warpOwner.sendMessage("warps.player-warped", "[name]", user.getName(), "[gamemode]", gameMode);
}
});
}
/**
* Converts block face direction to radial degrees. Returns 0 if block face
* is not radial.
*
* @param face
* @return degrees
*/
private float blockFaceToFloat(BlockFace face) {
switch (face) {
case EAST:
return 90F;
case EAST_NORTH_EAST:
return 67.5F;
case EAST_SOUTH_EAST:
return 0F;
case NORTH:
return 0F;
case NORTH_EAST:
return 45F;
case NORTH_NORTH_EAST:
return 22.5F;
case NORTH_NORTH_WEST:
return 337.5F;
case NORTH_WEST:
return 315F;
case SOUTH:
return 180F;
case SOUTH_EAST:
return 135F;
case SOUTH_SOUTH_EAST:
return 157.5F;
case SOUTH_SOUTH_WEST:
return 202.5F;
case SOUTH_WEST:
return 225F;
case WEST:
return 270F;
case WEST_NORTH_WEST:
return 292.5F;
case WEST_SOUTH_WEST:
return 247.5F;
default:
return 0F;
}
}
/**
* Warps a user to the warp owner by owner
* Warps a user to the warp owned by owner
*
* @param world - world to check
* @param user - user who is warping
* @param owner - owner of the warp
*/
public void warpPlayer(World world, User user, UUID owner) {
public void warpPlayer(@NonNull World world, @NonNull User user, @NonNull UUID owner) {
final Location warpSpot = getWarp(world, owner);
// Check if the warp spot is safe
if (warpSpot == null) {
@ -366,11 +403,11 @@ public class WarpSignsManager {
return;
}
Island island = addon.getPlugin().getIslands().getIsland(world, owner);
Island island = addon.getIslands().getIsland(world, owner);
boolean pvp = false;
if (island != null) {
// Check for PVP
switch (warpSpot.getWorld().getEnvironment()) {
switch (Objects.requireNonNull(warpSpot.getWorld()).getEnvironment()) {
case NETHER:
pvp = island.isAllowed(Flags.PVP_NETHER);
break;
@ -387,47 +424,51 @@ public class WarpSignsManager {
}
// Find out which direction the warp is facing
Block b = warpSpot.getBlock();
if (b.getType().name().contains("WALL_SIGN")) {
if (Tag.WALL_SIGNS.isTagged(b.getType())) {
org.bukkit.block.data.type.WallSign s = (org.bukkit.block.data.type.WallSign) b.getBlockData();
BlockFace directionFacing = s.getFacing();
Location inFront = b.getRelative(directionFacing).getLocation();
Location oneDown = b.getRelative(directionFacing).getRelative(BlockFace.DOWN).getLocation();
if ((plugin.getIslands().isSafeLocation(inFront))) {
addon.getWarpSignsManager().warpPlayer(user, inFront, owner, directionFacing, pvp);
warpPlayer(user, inFront, owner, directionFacing, pvp);
return;
} else if (plugin.getIslands().isSafeLocation(oneDown)) {
// Try one block down if this is a wall sign
addon.getWarpSignsManager().warpPlayer(user, oneDown, owner, directionFacing, pvp);
warpPlayer(user, oneDown, owner, directionFacing, pvp);
return;
}
} else if (b.getType().name().contains("SIGN")) {
} else if (Tag.ALL_HANGING_SIGNS.isTagged(b.getType())) {
Location below = b.getRelative(BlockFace.DOWN).getRelative(BlockFace.DOWN).getLocation();
if ((addon.getIslands().isSafeLocation(below))) {
warpPlayer(user, below, owner, BlockFace.DOWN, pvp);
return;
}
} else if (Tag.STANDING_SIGNS.isTagged(b.getType())) {
org.bukkit.block.data.type.Sign s = (org.bukkit.block.data.type.Sign) b.getBlockData();
BlockFace directionFacing = s.getRotation();
Location inFront = b.getRelative(directionFacing).getLocation();
if ((plugin.getIslands().isSafeLocation(inFront))) {
addon.getWarpSignsManager().warpPlayer(user, inFront, owner, directionFacing, pvp);
if ((addon.getIslands().isSafeLocation(inFront))) {
warpPlayer(user, inFront, owner, directionFacing, pvp);
return;
}
} else {
// Warp has been removed
user.sendMessage("warps.error.does-not-exist");
addon.getWarpSignsManager().removeWarp(warpSpot);
removeWarp(warpSpot);
return;
}
if (!(plugin.getIslands().isSafeLocation(warpSpot))) {
user.sendMessage("warps.error.not-safe");
return;
} else {
final Location actualWarp = new Location(warpSpot.getWorld(), warpSpot.getBlockX() + 0.5D, warpSpot.getBlockY(),
warpSpot.getBlockZ() + 0.5D);
if (pvp) {
user.sendMessage("protection.flags.PVP_OVERWORLD.active");
user.getWorld().playSound(user.getLocation(), Sound.ENTITY_ARROW_HIT, 1F, 1F);
user.sendMessage("protection.flags.PVP_OVERWORLD.enabled");
user.getWorld().playSound(Objects.requireNonNull(user.getLocation()), Sound.ENTITY_ARROW_HIT, 1F, 1F);
} else {
user.getWorld().playSound(user.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
user.getWorld().playSound(Objects.requireNonNull(user.getLocation()), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
}
user.teleport(actualWarp);
return;
Util.teleportAsync(user.getPlayer(), actualWarp, TeleportCause.COMMAND);
}
}
@ -436,8 +477,7 @@ public class WarpSignsManager {
* @param playerUUID - player's UUID
* @return true if they have warp
*/
public boolean hasWarp(World world, UUID playerUUID) {
public boolean hasWarp(@NonNull World world, @NonNull UUID playerUUID) {
return getWarpMap(world).containsKey(playerUUID);
}
}

View File

@ -0,0 +1,56 @@
package world.bentobox.warps.objects;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.World;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
import world.bentobox.warps.managers.SignCacheItem;
@Table(name = "WarpSignCache")
public class SignCache implements DataObject {
@Expose
private String uniqueId = "";
@Expose
private Map<UUID, SignCacheItem> signs = new HashMap<>();
public SignCache() {
// Required by YAML database
}
public SignCache(World w, Map<UUID, SignCacheItem> m) {
this.uniqueId = w.getName();
this.signs = m;
}
@Override
public String getUniqueId() {
return uniqueId;
}
@Override
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
/**
* @return the signs
*/
public Map<UUID, SignCacheItem> getSigns() {
return signs;
}
/**
* @param signs the signs to set
*/
public void setSigns(Map<UUID, SignCacheItem> signs) {
this.signs = signs;
}
}

View File

@ -10,15 +10,19 @@ import org.bukkit.World;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
@Table(name = "WarpsData")
public class WarpsData implements DataObject {
@Expose
private String uniqueId = "warps";
@Expose
private Map<Location, UUID> warpSigns = new HashMap<>();
public WarpsData() {}
public WarpsData() {
// Required by YAML database
}
@Override
public String getUniqueId() {
@ -31,8 +35,8 @@ public class WarpsData implements DataObject {
}
public Map<Location, UUID> getWarpSigns() {
if (warpSigns == null)
return new HashMap<>();
if (warpSigns == null)
return new HashMap<>();
return warpSigns;
}
@ -42,10 +46,11 @@ public class WarpsData implements DataObject {
/**
* Puts all the data from the map into this objects ready for saving
* @param worldsWarpList
* @param worldsWarpList 2D map of warp locations by world vs UUID
* @return this class filled with data
*/
public WarpsData save(Map<World, Map<UUID, Location>> worldsWarpList) {
getWarpSigns().clear();
worldsWarpList.values().forEach(world -> world.forEach((uuid,location) -> warpSigns.put(location, uuid)));
return this;
}

View File

@ -0,0 +1,76 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.warps.panels;
import org.bukkit.permissions.PermissionAttachmentInfo;
import java.util.List;
import java.util.stream.Collectors;
import world.bentobox.bentobox.api.user.User;
public class Utils
{
/**
* This method sends a message to the user with appended "prefix" text before message.
* @param user User who receives message.
* @param translationText Translation text of the message.
* @param parameters Parameters for the translation text.
*/
public static void sendMessage(User user, String translationText, String... parameters)
{
user.sendMessage(user.getTranslation( "warps.conversations.prefix") +
user.getTranslation( translationText, parameters));
}
/**
* This method gets string value of given permission prefix. If user does not have given permission or it have all
* (*), then return default value.
*
* @param user User who's permission should be checked.
* @param permissionPrefix Prefix that need to be found.
* @param defaultValue Default value that will be returned if permission not found.
* @return String value that follows permissionPrefix.
*/
public static String getPermissionValue(User user, String permissionPrefix, String defaultValue)
{
if (user.isPlayer())
{
if (permissionPrefix.endsWith("."))
{
permissionPrefix = permissionPrefix.substring(0, permissionPrefix.length() - 1);
}
String permPrefix = permissionPrefix + ".";
List<String> permissions = user.getEffectivePermissions().stream().
map(PermissionAttachmentInfo::getPermission).
filter(permission -> permission.startsWith(permPrefix)).
collect(Collectors.toList());
for (String permission : permissions)
{
if (permission.contains(permPrefix + "*"))
{
// * means all. So continue to search more specific.
continue;
}
String[] parts = permission.split(permPrefix);
if (parts.length > 1)
{
return parts[1];
}
}
}
return defaultValue;
}
}

View File

@ -0,0 +1,598 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.warps.panels;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
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.user.User;
import world.bentobox.warps.managers.SignCacheItem;
import world.bentobox.warps.managers.SignCacheManager;
import world.bentobox.warps.Warp;
/**
* This class shows how to set up easy panel by using BentoBox PanelBuilder API
*/
public class WarpsPanel
{
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
/**
* This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
*
* @param addon VisitAddon object
* @param world World where user will be teleported
* @param user User who opens panel
*/
private WarpsPanel(Warp addon,
World world,
User user)
{
this.addon = addon;
this.manager = this.addon.getSignCacheManager();
this.user = user;
this.world = world;
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* This method collects and validates sign warps that could be displayed in GUI.
* @param completed CompletableFeature that triggers panel opening.
*/
private void collectValidWarps(CompletableFuture<Boolean> completed)
{
this.addon.getWarpSignsManager().getSortedWarps(this.world).
thenAccept(warps ->
{
// Cache and clean the signs
Iterator<UUID> iterator = warps.iterator();
while (iterator.hasNext())
{
UUID warpOwner = iterator.next();
@NonNull
SignCacheItem sign = this.manager.getSignItem(this.world, warpOwner);
if (!sign.isReal())
{
iterator.remove();
this.addon.getWarpSignsManager().removeWarpFromMap(this.world, warpOwner);
}
}
// Assign warps to element list.
this.elementList = warps;
// Build the main body
completed.complete(true);
});
}
/**
* This is wrapper around builder to trigger main GUI building.
*/
private void initBuild()
{
CompletableFuture<Boolean> collectWarps = new CompletableFuture<>();
this.collectValidWarps(collectWarps);
collectWarps.thenAccept(done -> {
if (done)
{
this.build();
}
});
}
/**
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels.
*/
private void build()
{
// Do not open gui if there is no magic sticks.
if (this.elementList.isEmpty())
{
this.addon.logError("There are no available islands for visiting!");
Utils.sendMessage(this.user, "warps.error.no-warps-yet",
"[gamemode]", this.addon.getPlugin().getIWM().getAddon(this.world).
map(gamemode -> gamemode.getDescription().getName()).
orElse(""));
return;
}
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template.
panelBuilder.template("warps_panel", new File(this.addon.getDataFolder(), "panels"));
panelBuilder.user(this.user);
panelBuilder.world(this.user.getWorld());
// Register button builders
panelBuilder.registerTypeBuilder("WARP", this::createWarpButton);
panelBuilder.registerTypeBuilder("RANDOM", this::createRandomButton);
// Register next and previous builders
panelBuilder.registerTypeBuilder("NEXT", this::createNextButton);
panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton);
// Register unknown type builder.
panelBuilder.build();
}
// ---------------------------------------------------------------------
// Section: Buttons
// ---------------------------------------------------------------------
/**
* Create next button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
int size = this.elementList.size();
if (size <= slot.amountMap().getOrDefault("WARP", 1) ||
1.0 * size / slot.amountMap().getOrDefault("WARP", 1) <= this.pageIndex + 1)
{
// There are no next elements
return null;
}
int nextPageIndex = this.pageIndex + 2;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((Boolean) template.dataMap().getOrDefault("indexing", false))
{
clone.setAmount(nextPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.world, template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.world, template.description(),
"[number]", String.valueOf(nextPageIndex)));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
{
if ("NEXT".equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex++;
this.build();
}
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.world, action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* Create previous button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.pageIndex == 0)
{
// There are no next elements
return null;
}
int previousPageIndex = this.pageIndex;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((Boolean) template.dataMap().getOrDefault("indexing", false))
{
clone.setAmount(previousPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.world, template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.world, template.description(),
"[number]", String.valueOf(previousPageIndex)));
}
// Add ClickHandler
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
{
if ("PREVIOUS".equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex--;
this.build();
}
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.world, action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* This method creates and returns warp button.
*
* @return PanelItem that represents warp button.
*/
@Nullable
private PanelItem createWarpButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.elementList.isEmpty())
{
// Does not contain any sticks.
return null;
}
int index = this.pageIndex * slot.amountMap().getOrDefault("WARP", 1) + slot.slot();
if (index >= this.elementList.size())
{
// Out of index.
return null;
}
return this.createWarpButton(template, this.elementList.get(index), "warp");
}
/**
* This method creates and returns random warp button.
*
* @return PanelItem that represents random warp button.
*/
@Nullable
private PanelItem createRandomButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.elementList.size() < 2)
{
// Does not contain any sticks.
return null;
}
int index = random.nextInt(this.elementList.size());
return this.createWarpButton(template, this.elementList.get(index), "random");
}
// ---------------------------------------------------------------------
// Section: Other methods
// ---------------------------------------------------------------------
/**
* This method creates and returns button that switch to next page in view mode.
*
* @return PanelItem that allows to select next owner view page.
*/
private PanelItem createWarpButton(ItemTemplateRecord template, UUID ownerUUID, String type)
{
if (ownerUUID == null)
{
// return as owner has no owner. Empty button will be created.
return null;
}
SignCacheItem signCache = this.manager.getSignItem(this.world, ownerUUID);
if (!signCache.isReal())
{
this.addon.getWarpSignsManager().removeWarpFromMap(this.world, ownerUUID);
// return as signCache is not real anymore.
return null;
}
final String reference = "warps.gui.buttons." + type + ".";
String ownerName = addon.getPlugin().getPlayers().getName(ownerUUID);
// Get settings for owner.
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
if (template.icon().getType().equals(Material.PLAYER_HEAD))
{
builder.icon(ownerName);
}
else
{
builder.icon(template.icon().clone());
}
}
else
{
// Check owner for a specific icon
Material material = signCache.getType();
if (material == null)
{
// Set oak sign as icon was not specified.
material = Material.OAK_SIGN;
}
if (material == Material.PLAYER_HEAD)
{
builder.icon(ownerName);
}
else
{
builder.icon(material);
}
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.world, template.title(),
"[name]", ownerName));
}
else
{
builder.name(this.user.getTranslation(reference + "name",
"[name]", ownerName));
}
// Process Description of the button.
// Generate [sign_text] text
String descriptionText;
if (template.description() != null)
{
descriptionText = this.user.getTranslationOrNothing(template.description(),
"[name]", ownerName,
"[sign_text]", String.join("\n", signCache.getSignText())).
replaceAll("(?m)^[ \\t]*\\r?\\n", "").
replaceAll("(?<!\\\\)\\|", "\n").
replaceAll("\\\\\\|", "|");
}
else
{
descriptionText = this.user.getTranslationOrNothing(reference + "description",
"[name]", ownerName,
"[sign_text]", String.join("|", signCache.getSignText()));
// Clean up description text and split it into parts.
descriptionText = descriptionText.replaceAll("(?m)^[ \\t]*\\r?\\n", "").
replaceAll("(?<!\\\\)\\|", "\n").
replaceAll("\\\\\\|", "|");
}
builder.description(descriptionText);
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if (clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
{
if ("WARP".equalsIgnoreCase(action.actionType()))
{
this.runCommandCall(ownerName);
}
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null).
map(action -> this.user.getTranslation(this.world, action.tooltip())).
filter(text -> !text.isBlank()).
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* This method runs command call that allows player to visit clicked island.
*/
private void runCommandCall(String ownerName)
{
// Get first player command label.
String command = this.addon.getSettings().getWarpCommand().split(" ")[0];
String gamemodeCommand = this.addon.getPlugin().getIWM().getAddon(this.world).
map(gamemode -> gamemode.getPlayerCommand().map(Command::getLabel).orElse("")).
orElse("");
if (gamemodeCommand.isEmpty())
{
this.addon.log(this.user.getName() + " called: `" + command + " " + ownerName + "`");
this.user.performCommand(command + " " + ownerName);
}
else
{
this.addon.log(this.user.getName() + " called: `" + gamemodeCommand + " " + command + " " + ownerName + "`");
this.user.performCommand(gamemodeCommand + " " + command + " " + ownerName);
}
// Close inventory
this.user.closeInventory();
}
// ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open UserPanel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param addon Warps object
* @param world World where user will be teleported
* @param user User who opens panel
*/
public static void openPanel(Warp addon,
World world,
User user)
{
new WarpsPanel(addon, world, user).initBuild();
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* This variable allows to access addon object.
*/
private final Warp addon;
/**
* This variable allows to access addon manager object.
*/
private final SignCacheManager manager;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
private final User user;
/**
* This variable holds world where panel is opened. Without it panel cannot be opened.
*/
private final World world;
/**
* This variable stores filtered elements.
*/
private List<UUID> elementList;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
private int pageIndex;
/**
* Random for finding random warp.
*/
private static final Random random = new Random();
}

View File

@ -1,36 +1,20 @@
name: WelcomeWarps
name: Warps
main: world.bentobox.warps.Warp
version: ${version}${build.number}
icon: OAK_SIGN
api-version: 1.17
authors: tastybento
softdepend: AcidIsland, BSkyBlock
softdepend: AcidIsland, BSkyBlock, CaveBlock, SkyGrid, AOneBlock
permissions:
bskyblock.island.warp:
description: Player can use warp or warps commands
'[gamemode].island.warp':
description: Player can use warp command
default: true
bskyblock.island.addwarp:
'[gamemode].island.warps':
description: Player can use the warps command
default: true
'[gamemode].island.addwarp':
description: Player can create a welcome warp sign
default: true
acidisland.island.warp:
description: Player can use warp or warps commands
default: true
acidisland.island.addwarp:
description: Player can create a welcome warp sign
default: true
caveblock.island.warp:
description: Player can use warp or warps commands
default: true
caveblock.island.addwarp:
description: Player can create a welcome warp sign
default: true
skygrid.island.warp:
description: Player can use warp or warps commands
default: true
skygrid.island.addwarp:
description: Player can create a welcome warp sign
default: true

View File

@ -7,20 +7,29 @@
# Warp Restriction - needed levels to be able to create a warp
# 0 or negative values will disable this restriction 10 is default
warplevelrestriction: 10
#
# Should warps be removed when the island protection settings
# change, and the owner of the warp does no longer have
# the correct rank
removeExistingWarpsWhenFlagChanges: true
#
# Text that player must put on sign to make it a warp sign
# Not case sensitive!
welcomeLine: '[Welcome]'
#
# Icon that will be displayed in Warps list.
# It uses native Minecraft material strings, but using string 'PLAYER', it is possible to
# use player heads instead.
icon: 'SIGN'
#
# This list stores GameModes in which Level addon should not work.
# To disable addon it is necessary to write its name in new line that starts with -. Example:
# disabled-gamemodes:
# - BSkyBlock
disabled-gamemodes: []
#
uniqueId: config
#
# Warp panel default lore formatting.
# Example: &c will make lore red. &f is white
lore-format: "&f"
#
# Allow use in other worlds. Players must have the welcomewarpsigns.warp permission.
allow-in-other-worlds: false
#
# Warp and warps commands. You can change them if they clash with other addons or plugins.
warp-command: warp
warps-command: warps

View File

@ -0,0 +1,35 @@
###########################################################################################
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
# #
# Translation by: CZghost #
###########################################################################################
warp:
help:
description: "Teleportovat se na warp ceduli hráče"
parameters: <player name>
warps:
deactivate: "&c Stará warp cedule deaktivována!"
error:
does-not-exist: "&c Sakra! Tento warp již neexistuje!"
no-permission: "&c Na toto nemáš oprávnění!"
no-remove: "&c Nemůžeš odstranit tuto ceduli!"
no-warps-yet: "&c Nejsou k dispozici žádné warpy"
not-enough-level: "&c Úroveň tvého ostrova není dostatečně vysoká!"
not-on-island: "&c K tomuto musíš být na svém ostrově!"
not-safe: "&c Tento warp není bezpečný!"
your-level-is: "&c Úroveň tvého ostrova je jen [level], musí být vyšší než [required]. Spusť příkaz pro úroveň."
help:
description: "otevřít panel warpů"
next: "&6 Další stránka"
player-warped: "&2 [name] se teleportoval na tvou warp ceduli!"
previous: "&6 Předchozí stránka"
random: "&4 Náhodný warp"
sign-removed: "&c Warp cedule odstraněna!"
success: "&a Úspěch!"
title: "Warp cedule"
warpTip: "&6 Polož warp ceduli s [text] na vrchu"
warpToPlayersSign: "&6 Teleportuji tě na ceduli [player]"

View File

@ -0,0 +1,46 @@
---
warp:
help:
description: Warpt dich zu dem Warp Schild von dem Spieler
parameters: "<player name>"
warps:
deactivate: "&c Altes Warp Schild deaktiviert!"
error:
does-not-exist: "&c Oh nein! Dieser Warp existiert nicht mehr!"
no-permission: "&c Hierfür hast du keine Rechte!"
no-remove: "&c Du kannst dieses Schild nicht entfernen!"
no-warps-yet: "&c Es sind noch keine Warps verfügbar"
not-enough-level: "&c Dein Insel-Level ist nicht hoch gneug!"
not-on-island: "&c Dafür musst du auf deiner Insel sein!"
not-safe: "&c Dieser Warp ist nicht sicher!"
your-level-is: "&c Dein Insel-Level ist erst [level] und muss höher als [required]
sein. Nutze das Level Kommando."
help:
description: Öffnet das Warps Panel
player-warped: "&2 [name] hat sich zu deinem Warp Schild gewarpt!"
sign-removed: "&c Warp Schild entfernt!"
success: "&a Erfolg!"
warpTip: "&6 Platziere ein Warp Schild mit [text] in der ersten Zeile"
warpToPlayersSign: "&6 Warpe zu [player]'s Schild"
gui:
titles:
warp-title: "&0&l Warp-Schild"
buttons:
previous:
name: "&f&l Vorherige Seite"
description: "&7 Zur Seite [number] wechseln"
next:
name: "&f&l Nächste Seite"
description: "&7 Zur Seite [number] wechseln"
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Random Warp"
description: "&7 Hmm, wo werde ich erscheinen?"
tips:
click-to-previous: "&e Klicken &7, um die vorherige Seite anzuzeigen."
click-to-next: "&e Klicken &7, um die nächste Seite anzuzeigen."
click-to-warp: "&e Zum Warpen &7 klicken."
conversations:
prefix: "&l&6 [BentoBox]: &r"

View File

@ -8,26 +8,60 @@ warp:
description: "warp to the player's warp sign"
parameters: <player name>
warps:
deactivate: "&cOld warp sign deactivated!"
deactivate: "&c Old warp sign deactivated!"
error:
does-not-exist: "&cOh snap! That warp no longer exists!"
duplicate: "&CDuplicate sign placed"
no-permission: "&CYou do not have permission to do that!"
no-remove: "&CYou cannot remove that sign!"
no-warps-yet: "&CThere are no warps available yet"
not-enough-level: "&CYour island level is not high enough!"
not-on-island: "&CYou must be on your island to do that!"
not-safe: "&cThat warp is not safe!"
your-level-is: "&cYou island level is only [level] and must be higher than [required]. Run the level command."
help:
does-not-exist: "&c Oh snap! That warp no longer exists!"
no-permission: "&c You do not have permission to do that!"
no-remove: "&c You cannot remove that sign!"
no-warps-yet: "&c There are no warps available yet"
not-enough-level: "&c Your island level is not high enough!"
not-on-island: "&c You must be on your island to do that!"
not-safe: "&c That warp is not safe!"
your-level-is: "&c Your island level is only [level] and must be higher than [required]. Run the level command."
not-correct-rank: "&c You do not have the correct rank to set a warp!"
help:
description: "open the warps panel"
next: "&6Next page"
player-warped: "&2[name] warped to your warp sign!"
previous: "&6Previous page"
sign-removed: "&CWarp sign removed!"
success: "&ASuccess!"
title: "Warp Signs"
warpTip: "&6Place a warp sign with [text] on the top"
warpToPlayersSign: "&6Warping to [player]'s sign"
player-warped: "&2 [name] warped to your [gamemode] warp sign!"
sign-removed: "&c Warp sign removed!"
success: "&a Success!"
warpTip: "&6 Place a warp sign with [text] on the top"
warpToPlayersSign: "&6 Warping to [player]'s sign"
gui:
titles:
# The title of warp panel
warp-title: '&0&l Warp Signs'
buttons:
# Button that is used in multi-page GUIs which allows to return to previous page.
previous:
name: "&f&l Previous Page"
description: |-
&7 Switch to [number] page
# Button that is used in multi-page GUIs which allows to go to next page.
next:
name: "&f&l Next Page"
description: |-
&7 Switch to [number] page
# Button for a warp
warp:
name: "&f&l [name]"
description: |-
[sign_text]
# Button for a random warp
random:
name: "&f&l Random Warp"
description: |-
&7 Hmm, where will I appear?
tips:
click-to-previous: "&e Click &7 to view previous page."
click-to-next: "&e Click &7 to view next page."
click-to-warp: "&e Click &7 to warp."
conversations:
# Prefix for messages that are send from server.
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Place Warp
description: Allow placing warp sign

View File

@ -1,31 +0,0 @@
###########################################################################################################
# Este es un archivo YML. Tenga cuidado al editar. Revisa tus ediciones en un verificador de YAML como #
# el de http://yaml-online-parser.appspot.com #
###########################################################################################################
warp:
help:
description: "Teletransportarte hacia el warp del jugador"
parameters: <name>
warps:
deactivate: "&cViejo cartel a sido desactivado!"
error:
does-not-exist: "&cOh snap! Ese warp ya no existe!"
duplicate: "&CCartel duplicado puesto"
no-permission: "&CNo tienes permiso para hacer eso!"
no-remove: "&CNo puedes quitar ese cartel!"
no-warps-yet: "&CNo hay warps disponibles aun"
not-enough-level: "&CTu nivel de isla no es lo suficientemente alto!"
not-on-island: "&CDebes estar en tu isla para hacer eso!"
not-safe: "&cEse warp no es seguro!"
your-level-is: "&cTu nivel de isla es solo [level] y debe ser mayor que [required]"
help:
description: "Abre el panel de warps"
next: "&6Siguiente página"
player-warped: "&2[name] teletransportarte a tu warp!"
previous: "&6Pagina anterior"
sign-removed: "&CCartel removido!"
success: "&AExito!"
title: "Carteles de Warps"
warpTip: "&6Coloca un cartel con este texto [text] arriba"
warpToPlayersSign: "&6Teletransportandote al warp de [player]"

View File

@ -0,0 +1,53 @@
---
warp:
help:
description: Teletransportarte hacia el warp del jugador
parameters: "<player name>"
warps:
deactivate: "&c ¡Antiguo cartel de teletransportación desactivado!"
error:
does-not-exist: "&c ¡Oh, chasquido! ¡Ese teletransporte ya no existe!"
no-permission: "&c ¡No tienes permisos para hacer eso!"
no-remove: "&c ¡No puedes quitar ese cartel!"
no-warps-yet: "&c No hay teletransportes disponibles todavía"
not-enough-level: "&c ¡Tu nivel de isla no es lo suficientemente alto!"
not-on-island: "&c ¡Debes estar en tu isla para hacer eso!"
not-safe: "&c ¡Ese teletransporte no es seguro!"
your-level-is: "&c Tu nivel de isla es solo [level] y debe ser mayor que [required].
Ejecuta el comando de nivel."
not-correct-rank: "&c ¡No tienes el rango correcto para crear un teletransporte!"
help:
description: Abre el panel de warps
player-warped: "&2 [name] se ha teletransportado a tu cartel de teletransportación
de [gamemode]!"
sign-removed: "&c Cartel de teletransportación eliminado!"
success: "&a Éxito!"
warpTip: "&6 Coloca un cartel con [text] en la parte superior"
warpToPlayersSign: "&6 Teletransportandote al cartel del jugador [player]"
gui:
titles:
warp-title: "&0&l Carteles de Teletransporte"
buttons:
previous:
name: "&f&l Página Anterior"
description: "&7 Ir a la página [number]"
next:
name: "&f&l Siguiente Página"
description: "&7 Ir a la página [number]"
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Teletransporte aleatorio"
description: "&7 Hmm, ¿Dónde apareceré?"
tips:
click-to-previous: "&e Clic &7 para ver la página anterior."
click-to-next: "&e Clic &7 para ver la página siguiente."
click-to-warp: "&e Clic &7 para teletransportarse."
conversations:
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Colocar Teletransporte
description: Permitir colocar un cartel de teletransporte

View File

@ -1,31 +1,28 @@
###########################################################################################
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
###########################################################################################
warp:
help:
description: "te téléporte au Warp d'un autre joueur"
parameters: <pseudo>
warps:
---
warp:
help:
description: te téléporte au Warp d'un autre joueur
parameters: "<pseudo>"
warps:
deactivate: "&cAncien panneau de Warp désactivé !"
error:
error:
does-not-exist: "&cCe Warp n'existe plus !"
duplicate: "&cPanneau dupliqué placé."
no-permission: "&cVous n'avez pas la permission pour faire cela !"
no-remove: "&cVous ne pouvez pas supprimer ce panneau !"
no-warps-yet: "&cIl n'y a encore aucun Warp sur ce serveur."
not-enough-level: "&cVotre niveau d'île n'est pas assez élevé pour faire cela !"
not-enough-level: "&cVotre niveau d'île n'est pas assez élevé pour faire cela
!"
not-on-island: "&cVous devez être sur votre île pour faire cela !"
not-safe: "&cCe Warp n'est pas sûr!"
your-level-is: "&cVotre île est seulement niveau [level] et doit être niveau [required]."
help:
description: "Ouvre le menu des Warps"
help:
description: Ouvre le menu des Warps
next: "&6Page suivante"
player-warped: "&2[name] s'est téléporté sur votre île !"
player-warped: "& 2 [pseudo] s'est téléporté à votre panneau Warp !"
previous: "&6Page précédente"
random: "&4 Warp aléatoire "
sign-removed: "&cPanneau de Warp supprimé !"
success: "&aSuccès !"
title: "Panneau Warp"
title: Panneau Warp
warpTip: "&6Placez un panneau et écrivez [text] sur la première ligne."
warpToPlayersSign: "&6Téléportation sur l'île de [player]..."
warpToPlayersSign: "&6 Téléportation vers le panneau de [pseudo]"

View File

@ -0,0 +1,28 @@
---
warp:
help:
description: Teleportálás a játékos teleport táblájához
parameters: "<játékosnév>"
warps:
deactivate: "&cRégi teleport tábla deaktiválva!"
error:
does-not-exist: "&cAjjaj! Ez a teleport nem létezik!"
no-permission: "&cNincs jogod ehhez!"
no-remove: "&cNem törölheted ezt a táblát!"
no-warps-yet: "&cJelenleg nem érhetőek el teleportok"
not-enough-level: "&cA szigeted szintje nem elég magas!"
not-on-island: "&cEhhez szigeten kell lenned!"
not-safe: "&cEz a teleport nem biztonságos!"
your-level-is: "&cJelenleg a sziget szinted [level], és nagyobbnak kell lennie,
mint [required]."
help:
description: Teleportok panel megnyitása
next: "&6Következő oldal"
player-warped: "&2[name] teleportált a teleport tábládhoz!"
previous: "&6Előző oldal"
random: "&4Véletlenszerű Teleport"
sign-removed: "&cTeleport tábla törölve!"
success: "&aSikeres!"
title: Teleport Táblák
warpTip: "&6Helyezz le egy teleport táblát a következő szöveggel [text] a tetején"
warpToPlayersSign: "&6Teleportálás [player] táblájához"

View File

@ -0,0 +1,52 @@
---
warp:
help:
description: warp ke tanda warp pemain
parameters: "<player name>"
warps:
deactivate: "&c Tanda warp lama dinonaktifkan!"
error:
does-not-exist: "&c Oh! Warp itu tidak ada lagi!"
no-permission: "&c Kamu tidak punya izin untuk melakukan itu!"
no-remove: "&c Kamu tidak bisa menghapus tanda itu!"
no-warps-yet: "&c Belum ada warps yang tersedia"
not-enough-level: "&c Level pulaumu tidak cukup tinggi!"
not-on-island: "&c Kamu harus berada di pulaumu untuk melakukan itu!"
not-safe: "&c Warp itu tidak aman!"
your-level-is: "&c Level pulau Anda hanya [level] dan harus lebih tinggi dari
[required]. Jalankan perintah level."
not-correct-rank: "&c Kamu tidak punya rank yang benar untuk mengatur warp!"
help:
description: buka panel warps
player-warped: "&2 [name] nge-warp ke tanda warp [gamemode] kamu!"
sign-removed: "&c Tanda warp dihilangkan!"
success: "&a Sukses!"
warpTip: "&6 Tempatkan tanda warp dengan [text] di atas\n"
warpToPlayersSign: "&6 Pergi ke tanda [player]"
gui:
titles:
warp-title: "&0&l Tanda Warp"
buttons:
previous:
name: "&f&l Halaman Sebelumnya"
description: "&7 Beralih ke halaman [number]"
next:
name: "&f&l Halaman Selanjutnya"
description: "&7 Beralih ke halaman [number]"
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Warp Acak"
description: "&7 Hmm, di mana aku akan muncul?"
tips:
click-to-previous: "&e Klik &7 untuk melihat halaman sebelumnya."
click-to-next: "&e Klik &7 untuk melihat halaman selanjutnya."
click-to-warp: "&e Klik &7 untuk warp."
conversations:
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Tempatkan Warp
description: Izinkan menempatkan tanda warp

View File

@ -0,0 +1,29 @@
---
warp:
help:
description: teletrasporto al cartello Warp del giocatore
parameters: "<nome giocatore>"
warps:
deactivate: "&c Vecchio cartello Warp disattivato!"
error:
does-not-exist: "&c Quel cartello Warp non esiste più!"
no-permission: "&c Non hai i permessi per farlo!"
no-remove: "&c Non puoi rimuovere quel cartello!"
no-warps-yet: "&c Non ci sono ancora Warp disponibili"
not-enough-level: "&c Il tuo livello dell'isola non è abbastanza alto!"
not-on-island: "&c Devi essere nella tua isola per farlo!"
not-safe: "&c Il Warp non è sicuro!"
your-level-is: "&c Il tuo livello dell''isola è solo di [level] e deve essere
maggiore di [required]. Esegui il comando del calcolo dei livelli per aggiornare
il livello."
help:
description: apri il pannello dei Warp
next: "&6 Pagina Successiva"
player-warped: "&2 [name] teletrasportato al tuo cartello Warp!"
previous: "&6 Pagina Precedente"
random: "&4 Warp Random"
sign-removed: "&c Cartello Warp rimosso!"
success: "&a Successo!"
title: Cartelli Warp
warpTip: "&6 Piazza un cartello Warp con [text] scritto nella prima riga"
warpToPlayersSign: "&6 Teletrasporto al cartello Warp di [player]"

View File

@ -11,7 +11,6 @@ warps:
deactivate: "&c前のワープサインが無効になりました!"
error:
does-not-exist: "&cそのワープはもう存在しません"
duplicate: "&C重複サインを配置"
no-permission: "&C許可がありません"
no-remove: "&Cワープサインは外せません"
no-warps-yet: "&C利用可能なワープはまだありません"

View File

@ -0,0 +1,28 @@
---
warp:
help:
description: pārvietoties pie spēlētāja uzaicinājuma zīmes
parameters: "<spēlētāja vārds>"
warps:
help:
description: atver uzaicinājumu zīmju sarakstu
title: Uzaicinājuma Zīmes
deactivate: "&c Iepriekšēja uzaicinājuma zīme deaktivizēta!"
error:
does-not-exist: "&c Oh! Šī uzaicinājuma zīme vairs neeksistē!"
no-permission: "&c Tev nav atļaujas veikt šo darbību!"
no-remove: "&c Tu nevari noņemt šo zīmi!"
no-warps-yet: "&c Diemžēl neviens uzaicinājums nav pieejams."
not-enough-level: "&c Tavs salas līmenis nav pietiekošs."
not-on-island: "&c Tev ir jābūt uz savas salas, lai veiktu šo darbību!"
not-safe: "&c Šīs uzaicinājums nav drošs!"
your-level-is: "&c Tavas salas līmenis ir [level], bet ir nepieciešams vismaz
[required]. Mēģini pārrēķināt salas līmeni."
next: "&6 Nākošā lapa"
player-warped: "&2 [name] ieradās pie uzaicinājuma zīmes!"
previous: "&6 Iepriekšējā lapa"
random: "&4 Nejaušs uzaicinājums"
sign-removed: "&c Uzaicinājuma zīme noņemta!"
success: "&a Izdevās!"
warpTip: "&6 Izveido zīmi ar [text] pirmajā rindā"
warpToPlayersSign: "&6 Pārvietojas uz [player] uzaicinājuma zīmi."

View File

@ -0,0 +1,52 @@
---
warp:
help:
description: teleporteer naar een speler zijn warp bord
parameters: "<speler>"
warps:
deactivate: "&c Oude warp bord is gedeactiveerd!"
error:
does-not-exist: "&c Oh nee! Deze warp bestaat niet meer!"
no-permission: "&c Je hebt geen permissies om dit te doen!"
no-remove: "&c Je kan dit bord niet weghalen!"
no-warps-yet: "&c Er bestaan nog geen warps op dit moment"
not-enough-level: "&c Je eiland level is nog niet hoog genoeg!"
not-on-island: "&c Je moet een eiland hebben om dit te doen!"
not-safe: "&c Deze warp is niet veilig!"
your-level-is: "&c Je eiland level is [level], maar het moet op zijn minst [required]
zijn. Gebruik het level commando."
not-correct-rank: "&c Je hebt niet de correcte rank om een warp te maken!"
help:
description: open het warp paneel
player-warped: "&2 [name] teleporteerde naar jou [gamemode] warp!"
sign-removed: "&c Warp bord verwijderd!"
success: "&a Geslaagd!"
warpTip: "&6 Plaats een warp bord met [text] op de eerste regel"
warpToPlayersSign: "&6 Teleporteren naar [player]'s warp"
gui:
titles:
warp-title: "&0&l Warp Borden"
buttons:
previous:
name: "&f&l Vorige pagina"
description: "&7 Ga naar pagina [number]"
next:
name: "&f&l Volgende pagina"
description: "&7 Ga naar pagina [number]"
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Willekeurige Warp"
description: "&7 Hmm, waar ga ik heen?"
tips:
click-to-previous: "&e Klik &7 om naar de vorige pagina te gaan."
click-to-next: "&e Klik &7 om naar de volgende pagina te gaan."
click-to-warp: "&e Klik &7 om te teleporteren."
conversations:
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Plaats warp
description: Staat het toe om een warp bord te plaatsen

View File

@ -0,0 +1,46 @@
---
warp:
help:
description: teleportuje cię do tabliczki innego gracza
parameters: "<gracz>"
warps:
deactivate: "&c Stary teleport został zdezaktywowany!"
error:
does-not-exist: "&c Ten teleport nie istnieje."
no-permission: "&c Brak uprawnień!"
no-remove: "&c Nie możesz usunąć tej tabliczki!"
no-warps-yet: "&c Nie ma jeszcze stworzonych teleportów."
not-enough-level: "&c Twój poziom wyspy nie jest wystarczająco wysoki!"
not-on-island: "&c Musisz być na wyspie, by to zrobić."
not-safe: "&c Ten teleport nie jest bezpieczny!"
your-level-is: "&c Twój poziom wyspy to [level], a musi wynosić co najmniej [required].
Użyj komendy /is level."
help:
description: otwiera panel warpów
player-warped: "&2 [name] teleportował się do twojej tabliczki!"
sign-removed: "&c Usunięto tabliczkę!"
success: "&a Sukces!"
warpTip: "&6 Postaw tabliczkę z napisem [text]"
warpToPlayersSign: "&6 Teleportowanie do tabliczki gracza [player]"
gui:
titles:
warp-title: "&0&l Lista dostępnych wysp"
buttons:
previous:
name: "&f&l Poprzednia strona"
description: "&7 Przełącz na stronę [number]"
next:
name: "&f&l następna strona"
description: "&7 Przełącz na stronę [number]"
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Losowa wyspa"
description: "&7 Hmm, gdzie się pojawię?"
tips:
click-to-previous: "&e Kliknij &7, aby wyświetlić poprzednią stronę."
click-to-next: "&e Kliknij &7, aby wyświetlić następną stronę."
click-to-warp: "&e Kliknij &7, aby przenieść."
conversations:
prefix: "&l&6 [BentoBox]: &r"

View File

@ -0,0 +1,28 @@
---
warp:
help:
description: телепортироваться на знак игрока
parameters: "<название>"
warps:
deactivate: "&cСтарый знак телепортации деактивирован!"
error:
does-not-exist: "&cЭтот знак телепорта больше не существует!"
no-permission: "&cУ вас нет разрешения на это!"
no-remove: "&cВы не можете удалить этот знак!"
no-warps-yet: "&cТелепортов пока нет"
not-enough-level: "&cВаш уровень острова не достаточно!"
not-on-island: "&cВы должны быть на вашем острове, чтобы сделать это!"
not-safe: "&cМесто назначения не является безопасным!"
your-level-is: "&cУровень вашего острова всего [level] и должен быть выше [required].
Запустите команду уровня."
help:
description: открыть панель телепорта
next: "&6ледующая страница"
player-warped: "&2[name] телепортируется на ваш знак деформации!"
previous: "&6Предыдущая страница"
sign-removed: "&cЗнак телепорта удален!"
success: "&aУспех!"
title: Телепорт Знаки
warpTip: "&6Поместите знак телепортации с [text] в первой строке"
warpToPlayersSign: "&6Телепортация к знаку [player]"
random: "&4Случайный телепорт"

View File

@ -0,0 +1,26 @@
---
warp:
help:
description: Oyuncunun bölge tabelası.
parameters: "<name>"
warps:
deactivate: "&cEski ada warpı silindi!"
error:
does-not-exist: "&cÜzgünüm. Böyle bir bölge artık yok!"
no-permission: "&4Buna yetkin yok!"
no-remove: "&4Bu tabelayı kıramazsın!"
no-warps-yet: "&4Böyle bir bölge henüz yok."
not-enough-level: "&4Bunu koyman için daha fazla ada leveline ihtiyacın var."
not-on-island: "&4Adanda değilsin."
not-safe: "&4Ada bölgesi güvenli değil!"
your-level-is: "&9Ada levelin &e[level] &9ve &e[required]&9'den fazla olması gerek!"
help:
description: Bölge panelini açar.
next: "&6Sonraki Sayfa"
player-warped: "&d[name] &9ada bölgene ışınlandı!"
previous: "&6Önceki sayfa"
random: "&4Rastgele bölgeye git!"
sign-removed: "&4Ada tabelası kaldırıldı!"
title: "&dOyuncu bölgeleri &8- &5"
warpTip: "&6Ada bölgesi açmak için tabelanın en üstüne &a[text] &9yaz!"
warpToPlayersSign: "&d[player] &9bölgesine gidiliyor!"

View File

@ -0,0 +1,52 @@
---
warp:
help:
description: телепортація на знак варпа гравця
parameters: "<player name>"
warps:
deactivate: "&c Старий знак варпу деактивовано!"
error:
does-not-exist: "&c О, чорт! Того викривлення більше не існує!"
no-permission: "&c Ви не маєте на це дозволу!"
no-remove: "&c Ви не можете видалити цей знак!"
no-warps-yet: "&c Ще немає доступних варпів"
not-enough-level: "&c Рівень вашого острова недостатньо високий!"
not-on-island: "&c Для цього ви повинні бути на своєму острові!"
not-safe: "&c Ця варп небезпечна!"
your-level-is: "&c Рівень вашого острова становить лише [level] і має бути вищим
за [required]. Виконайте команду рівня."
not-correct-rank: "&c Ви не маєте належного рангу, щоб встановити варп!"
help:
description: відкрити панель варпів
player-warped: "&2 [name] телепортовано до вашого знака варпу [gamemode]!"
sign-removed: "&c Знак варпу видалено!"
success: "&a Успіх!"
warpTip: "&6 Розмістіть знак варпу з [text] угорі"
warpToPlayersSign: "&6 Телепортація на знак [player]."
gui:
titles:
warp-title: "&0&l Знаки варпів"
buttons:
previous:
name: "&f&l Попередня сторінка"
description: "&7 Перейти на сторінку [number]."
next:
name: "&f&l Наступна сторінка"
description: "&7 Перейти на сторінку [number]."
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Випадковий варп"
description: "&7 Хм, а де я з'явлюся?"
tips:
click-to-previous: "&e Натисніть &7, щоб переглянути попередню сторінку."
click-to-next: "&e Натисніть &7, щоб переглянути наступну сторінку."
click-to-warp: "&e Натисніть &7, щоб деформувати."
conversations:
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Розмістіть варп
description: Дозволити розміщення варпів

View File

@ -0,0 +1,29 @@
#
# This is a YML file. Be careful when editing. Check your edits in a YAML checker like #
# the one at http://yaml-online-parser.appspot.com #
warp:
help:
description: đi đến điểm dịch chuyển của người chơi
parameters: <tên người chơi>
warps:
deactivate: '&cBảng cũ đã vô hiệu hóa!'
error:
does-not-exist: '&cUầy! Điểm dịch chuyển đó không tồn tại!'
no-permission: '&cBạn không có quyền làm điều đó!'
no-remove: '&cBạn không thể xóa bảng đó!'
no-warps-yet: '&cChưa có điểm dịch chuyển nào'
not-enough-level: '&cCấp đảo của bạn không đủ cao!'
not-on-island: '&cBạn phải ở đảo của bạn để làm điều đó!'
not-safe: '&cĐiểm dịch chuyển không an toàn!'
your-level-is: '&cCấp đảo của bạn là [level] và phải cao hơn [required]. Hãy dùng lệnh xem cấp đảo.'
help:
description: mở bảng dịch chuyển
next: '&6Trang kế'
player-warped: '&2[name] đã vào điểm dịch chuyển của bạn!'
previous: '&6Trang trước'
random: '&4Điểm dịch chuyển ngẫu nhiên'
sign-removed: '&cĐã xóa bảng dịch chuyển!'
success: '&aThành công!'
title: Bảng Dịch Chuyển
warpTip: '&6Đặt bảng với dòng chữ [text] ở dòng đầu tiên'
warpToPlayersSign: '&6Đang dịch chuyển đến chỗ của [player]'

View File

@ -1,33 +1,27 @@
###########################################################################################
# 本文件是 YML 格式的文件. 编辑时请务必小心. #
# 请在 http://yaml-online-parser.appspot.com 等 YAML 检查器中检查您的编辑. #
###########################################################################################
warp:
help:
---
warp:
help:
description: 传送到该玩家的传送木牌处
parameters: <玩家名称>
parameters: "<player name>"
warps:
deactivate: "&c禁用的旧转移标志!"
error:
does-not-exist: "&c转移不再存在!"
duplicate: "&C木牌重复"
no-permission: "&C权限不足!"
no-remove: "&C无权移除传送木牌!"
no-warps-yet: "&C暂无可用传送木牌"
not-enough-level: "&C岛屿等级不够高!"
not-on-island: "&C操作必须在空岛上进行!"
not-safe: "&c转移不安全!"
your-level-is: "&c岛屿当前等级 [level], 需要等级 [required]"
help:
help:
description: 打开传送面板
next: "&6下一页"
player-warped: "&2[name]转移到你的标志!"
previous: "&6上一页"
sign-removed: "&C传送木牌已移除!"
success: "&A成功!"
title: 传送木牌
warpTip: "&6放置一个第一行是 [text] 的木牌以创建传送木牌"
warpToPlayersSign: "&6正传送到 [player] 的传送木牌"
deactivate: "&c 旧传送牌已停用!"
error:
does-not-exist: "&c 哦不!那个传送点已经没了!"
no-permission: "&c 你无权那样做!"
no-remove: "&c 你拿不掉那个牌子的!"
no-warps-yet: "&c 暂无可用传送点"
not-enough-level: "&c 你的岛等级不够高!"
not-on-island: "&c 你得在自己的岛屿上操作!"
not-safe: "&c 目标传送点不安全!"
your-level-is: "&c 你的岛现在 [level] 级,需要 [required] 级。 试试岛屿等级命令吧。"
next: "&6 次页"
player-warped: "&2 [name] 传送到了你的传送牌!"
previous: "&6 前页"
random: "&4 随机传送"
sign-removed: "&c 拆掉传送牌了!"
success: "&a 成了!"
warpTip: "&6 放个牌子第一行写 [text]"
warpToPlayersSign: "&6 正传送到 [player] 的牌子"

View File

@ -0,0 +1,27 @@
---
warp:
help:
description: 傳送到該玩家的傳送木牌處
parameters: "<player name>"
warps:
deactivate: "&c 舊傳送牌已停用!"
error:
does-not-exist: "&c 哦不!那個傳送點已經沒了!"
no-permission: "&c 你沒有權限這樣做!"
no-remove: "&c 你拿不掉那個牌子的!"
no-warps-yet: "&c 暫無可用傳送點"
not-enough-level: "&c 你的島嶼等級不夠高!"
not-on-island: "&c 你得在自己的島嶼上操作!"
not-safe: "&c 目標傳送點不安全!"
your-level-is: "&c 你的島嶼現在 [level] 級,需要 [required] 級。 試試島嶼等級命令吧。"
help:
description: 打開傳送面板
next: "&6 下一頁"
player-warped: "&2 [name] 傳送到了你的傳送牌!"
previous: "&6 上一頁"
random: "&4 隨機傳送"
sign-removed: "&c 拆掉傳送牌了!"
success: "&成功!"
title: 傳送告示牌
warpTip: "&6 放個牌子第一行寫 [text] "
warpToPlayersSign: "&6 正傳送到 [player] 的牌子"

View File

@ -0,0 +1,79 @@
warps_panel:
title: warps.gui.titles.warp-title
type: INVENTORY
background:
icon: BLACK_STAINED_GLASS_PANE
title: "&b&r" # Empty text
border:
icon: BLACK_STAINED_GLASS_PANE
title: "&b&r" # Empty text
force-shown: []
content:
2:
2: warp_button
3: warp_button
4: warp_button
5: warp_button
6: warp_button
7: warp_button
8: warp_button
3:
1:
icon: tipped_arrow{CustomPotionColor:11546150}
title: warps.gui.buttons.previous.name
description: warps.gui.buttons.previous.description
data:
type: PREVIOUS
indexing: true
actions:
left:
type: PREVIOUS
tooltip: warps.gui.tips.click-to-previous
2: warp_button
3: warp_button
4: warp_button
5: warp_button
6: warp_button
7: warp_button
8: warp_button
9:
icon: tipped_arrow{CustomPotionColor:8439583}
title: warps.gui.buttons.next.name
description: warps.gui.buttons.next.description
data:
type: NEXT
indexing: true
actions:
left:
type: NEXT
tooltip: warps.gui.tips.click-to-next
4:
2: warp_button
3: warp_button
4: warp_button
5: warp_button
6: warp_button
7: warp_button
8: warp_button
6:
5:
icon: DROPPER
title: warps.gui.buttons.random.name
description: warps.gui.buttons.random.description
data:
type: RANDOM
actions:
warp:
click-type: left
tooltip: warps.gui.tips.click-to-warp
reusable:
warp_button:
icon: PLAYER_HEAD
title: warps.gui.buttons.warp.name
description: warps.gui.buttons.warp.description
data:
type: WARP
actions:
warp:
click-type: left
tooltip: warps.gui.tips.click-to-warp

View File

@ -0,0 +1,9 @@
name: BentoBox-Warps
main: world.bentobox.warps.WarpsPladdon
version: ${project.version}${build.number}
api-version: "1.17"
authors: [tastybento]
contributors: ["The BentoBoxWorld Community"]
website: https://bentobox.world
description: ${project.description}

View File

@ -1,197 +0,0 @@
package world.bentobox.warps;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.warps.config.Settings;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class})
public class WarpPanelManagerTest {
@Mock
private WarpSignsManager wsm;
@Mock
private Warp addon;
@Mock
private Player player;
@Mock
private User user;
@Mock
private World world;
@Mock
private Inventory top;
private UUID uuid;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
when(addon.getWarpSignsManager()).thenReturn(wsm);
// Fill with 200 fake warps (I'm banking on them all being different, but there could be a clash)
List<UUID> list = new ArrayList<>();
for (int i = 0; i< 200; i++) {
list.add(UUID.randomUUID());
}
// One final one
uuid = UUID.randomUUID();
list.add(uuid);
when(wsm.getSortedWarps(any())).thenReturn(list);
// User and player
when(user.getPlayer()).thenReturn(player);
when(user.getTranslation(Mockito.any())).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgument(0, String.class);
}});
// BentoBox
BentoBox plugin = mock(BentoBox.class);
PlayersManager pm = mock(PlayersManager.class);
when(pm.getName(any())).thenReturn("name");
when(plugin.getPlayers()).thenReturn(pm);
when(addon.getPlugin()).thenReturn(plugin);
// Bukkit
PowerMockito.mockStatic(Bukkit.class);
ItemFactory itemF = mock(ItemFactory.class);
ItemMeta imeta = mock(ItemMeta.class);
when(itemF.getItemMeta(any())).thenReturn(imeta);
when(Bukkit.getItemFactory()).thenReturn(itemF);
// Inventory
when(top.getSize()).thenReturn(9);
when(Bukkit.createInventory(any(), Mockito.anyInt(), any())).thenReturn(top);
Settings settings = mock(Settings.class);
when(settings.getIcon()).thenReturn("SIGN");
when(addon.getSettings()).thenReturn(settings);
Location location = mock(Location.class);
Block block = mock(Block.class);
Material sign_type;
try {
sign_type = Material.valueOf("SIGN");
} catch (Exception e) {
sign_type = Material.valueOf("OAK_SIGN");
}
when(block.getType()).thenReturn(sign_type);
when(location.getBlock()).thenReturn(block);
// Sign block
when(wsm.getWarp(any(), any())).thenReturn(location);
// Sign cache
SignCache sc = mock(SignCache.class);
when(sc.getSignText()).thenReturn(Collections.singletonList("[welcome]"));
when(sc.getType()).thenReturn(sign_type);
when(wsm.getSignInfo(any(), any())).thenReturn(sc);
}
/**
* Test method for {@link WarpPanelManager#showWarpPanel(org.bukkit.World, world.bentobox.bbox.api.user.User, int)}.
*/
@Test
public void testShowWarpPanelFirst() {
WarpPanelManager wpm = new WarpPanelManager(addon);
wpm.showWarpPanel(world, user, 0);
verify(player).openInventory(Mockito.eq(top));
// Just next sign
verify(top, Mockito.times(53)).setItem(Mockito.anyInt(), Mockito.any(ItemStack.class));
}
/**
* Test method for {@link WarpPanelManager#showWarpPanel(org.bukkit.World, world.bentobox.bbox.api.user.User, int)}.
*/
@Test
public void testShowWarpPanelMiddle() {
WarpPanelManager wpm = new WarpPanelManager(addon);
wpm.showWarpPanel(world, user, 1);
verify(player).openInventory(Mockito.eq(top));
// includes previous and next signs
verify(top, Mockito.times(54)).setItem(Mockito.anyInt(), Mockito.any(ItemStack.class));
}
/**
* Test method for {@link WarpPanelManager#showWarpPanel(org.bukkit.World, world.bentobox.bbox.api.user.User, int)}.
*/
@Test
public void testShowWarpPanelLast() {
WarpPanelManager wpm = new WarpPanelManager(addon);
wpm.showWarpPanel(world, user, 3);
verify(player).openInventory(Mockito.eq(top));
// Final amount, just previous sign
verify(top, Mockito.times(46)).setItem(Mockito.anyInt(), Mockito.any(ItemStack.class));
}
/**
* Test method for {@link WarpPanelManager#showWarpPanel(org.bukkit.World, world.bentobox.bbox.api.user.User, int)}.
*/
@Test
public void testShowWarpPanelTestCache() {
WarpPanelManager wpm = new WarpPanelManager(addon);
// Do 45 initial lookups of sign text
wpm.showWarpPanel(world, user, 3);
// Get the panel again
wpm.showWarpPanel(world, user, 3);
// Should only check this 45 times because the sign text is cached
verify(wsm, Mockito.times(45)).getSignInfo(any(), any());
}
/**
* Test method for {@link WarpPanelManager#removeWarp(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testRemoveWarp() {
WarpPanelManager wpm = new WarpPanelManager(addon);
wpm.showWarpPanel(world, user, 3);
wpm.showWarpPanel(world, user, 3);
wpm.removeWarp(world, uuid);
wpm.showWarpPanel(world, user, 3);
// Removing the UUID should force a refresh and therefore 46 lookups
verify(wsm, Mockito.times(46)).getSignInfo(any(), any());
}
}

View File

@ -0,0 +1,486 @@
package world.bentobox.warps;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.Tag;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.plugin.PluginManager;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
import world.bentobox.bentobox.database.DatabaseSetup;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.LocalesManager;
import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.warps.config.Settings;
import world.bentobox.warps.event.WarpInitiateEvent;
import world.bentobox.warps.managers.SignCacheManager;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.objects.WarpsData;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, Util.class, DatabaseSetup.class})
public class WarpSignsManagerTest {
@Mock
private Warp addon;
@Mock
private BentoBox plugin;
@Mock
private World world;
@Mock
private static AbstractDatabaseHandler<Object> handler;
private WarpSignsManager wsm;
@Mock
private Logger logger;
@Mock
private WarpsData load;
private final UUID uuid = UUID.randomUUID();
@Mock
private Location location;
@Mock
private Block block;
@Mock
private PluginManager pim;
@Mock
private Server server;
@Mock
private Player player;
@Mock
private SignCacheManager wpm;
@Mock
private PlayersManager pm;
@Mock
private OfflinePlayer offlinePlayer;
@Mock
private Settings settings;
@Mock
private IslandWorldManager iwm;
@Mock
private IslandsManager im;
@Mock
private Island island;
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {
// This has to be done beforeClass otherwise the tests will interfere with each other
handler = mock(AbstractDatabaseHandler.class);
// Database
PowerMockito.mockStatic(DatabaseSetup.class);
DatabaseSetup dbSetup = mock(DatabaseSetup.class);
when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
when(dbSetup.getHandler(any())).thenReturn(handler);
}
/**
* @throws java.lang.Exception exception
*/
@Before
public void setUp() throws Exception {
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
when(addon.getPlugin()).thenReturn(plugin);
when(addon.getLogger()).thenReturn(logger);
// Player
when(player.getUniqueId()).thenReturn(uuid);
when(player.isOnline()).thenReturn(true);
when(player.canSee(any(Player.class))).thenReturn(true);
User.setPlugin(plugin);
User.getInstance(player);
// Locales
LocalesManager lm = mock(LocalesManager.class);
when(lm.getAvailablePrefixes(any())).thenReturn(Collections.emptySet());
when(lm.get(Mockito.any(), Mockito.any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(1, String.class));
when(plugin.getLocalesManager()).thenReturn(lm);
// Return the same string
PlaceholdersManager phm = mock(PlaceholdersManager.class);
when(phm.replacePlaceholders(any(), anyString())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(1, String.class));
when(plugin.getPlaceholdersManager()).thenReturn(phm);
// Server
when(addon.getServer()).thenReturn(server);
when(server.getPlayer(any(UUID.class))).thenReturn(player);
// Util
PowerMockito.mockStatic(Util.class);
when(Util.getWorld(any())).thenAnswer((Answer<World>) invocation -> invocation.getArgument(0, World.class));
when(Util.sameWorld(any(), any())).thenReturn(true);
when(Util.translateColorCodes(any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
// Location
when(location.getWorld()).thenReturn(world);
when(location.getBlock()).thenReturn(block);
when(location.getBlockX()).thenReturn(23);
when(location.getBlockY()).thenReturn(24);
when(location.getBlockZ()).thenReturn(25);
when(player.getLocation()).thenReturn(location);
when(world.getEnvironment()).thenReturn(Environment.NORMAL);
when(world.isChunkLoaded(anyInt(), anyInt())).thenReturn(true);
// Block
when(block.getType()).thenReturn(Material.ACACIA_SIGN);
when(block.getLocation()).thenReturn(location);
Sign sign = mock(Sign.class);
String[] lines = {"[Welcome]", "line2", "line3", "line4"};
when(sign.getLines()).thenReturn(lines);
when(sign.getLine(anyInt())).thenReturn("[Welcome]");
when(sign.getType()).thenReturn(Material.ACACIA_SIGN);
when(block.getState()).thenReturn(sign);
org.bukkit.block.data.type.Sign signBd = mock(org.bukkit.block.data.type.Sign.class);
when(signBd.getRotation()).thenReturn(BlockFace.EAST);
when(block.getBlockData()).thenReturn(signBd);
when(block.getRelative(any())).thenReturn(block);
// Handler
when(handler.objectExists("warps")).thenReturn(true);
Map<Location, UUID> warpMap = Collections.singletonMap(location, uuid);
when(load.getWarpSigns()).thenReturn(warpMap);
when(handler.loadObject(anyString())).thenReturn(load);
// Settings
when(addon.getSettings()).thenReturn(settings);
when(settings.getWelcomeLine()).thenReturn("[Welcome]");
when(settings.getLoreFormat()).thenReturn("&f");
// Bukkit
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
when(Bukkit.getPluginManager()).thenReturn(pim);
// Tags
when(Tag.STANDING_SIGNS.isTagged(Material.ACACIA_SIGN)).thenReturn(true);
// Players Manager
when(plugin.getPlayers()).thenReturn(pm);
when(pm.getName(uuid)).thenReturn("tastybento");
// Offline player
when(server.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer);
when(offlinePlayer.getLastPlayed()).thenReturn(System.currentTimeMillis());
// IWM
when(plugin.getIWM()).thenReturn(iwm);
when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock.");
when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock");
// Island Manager
when(addon.getIslands()).thenReturn(im);
when(im.getIsland(any(), any(UUID.class))).thenReturn(island);
when(im.isSafeLocation(any())).thenReturn(true);
// WarpPanelManager
when(addon.getSignCacheManager()).thenReturn(wpm);
wsm = new WarpSignsManager(addon, plugin);
}
/**
*/
@After
public void tearDown() {
User.clearUsers();
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
*/
@Test
public void testGetWarpMap() {
assertFalse("Map is empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
*/
@Test
public void testGetWarpMapNullWorld() {
when(location.getWorld()).thenReturn(null);
wsm = new WarpSignsManager(addon, plugin);
assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
*/
@Test
public void testGetWarpMapWrongBlockType() {
when(block.getType()).thenReturn(Material.COAL_ORE);
wsm = new WarpSignsManager(addon, plugin);
assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
*/
@Test
public void testGetWarpMapNullLocation() {
Map<Location, UUID> warpMap = Collections.singletonMap(null, uuid);
when(load.getWarpSigns()).thenReturn(warpMap);
wsm = new WarpSignsManager(addon, plugin);
assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
* @throws Exception exception
*/
@Test
public void testGetWarpMapNullDatabaseObject() throws Exception {
when(handler.loadObject(anyString())).thenReturn(null);
wsm = new WarpSignsManager(addon, plugin);
assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#getWarpMap(org.bukkit.World)}.
*/
@Test
public void testGetWarpMapNothingInDatabase() {
when(handler.objectExists("warps")).thenReturn(false);
wsm = new WarpSignsManager(addon, plugin);
assertTrue("Map is not empty", wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#WarpSignsManager(world.bentobox.warps.Warp, world.bentobox.bentobox.BentoBox)}.
*/
@Test
public void testWarpSignsManager() {
verify(addon).log("Loading warps...");
verify(load).getWarpSigns();
verify(block).getType();
}
/**
* Test method for {@link WarpSignsManager#addWarp(java.util.UUID, org.bukkit.Location)}.
*/
@Test
public void testAddWarpNullPlayer() {
assertFalse(wsm.addWarp(null, null));
}
/**
* Test method for {@link WarpSignsManager#addWarp(java.util.UUID, org.bukkit.Location)}.
*/
@Test
public void testAddWarpNullLocation() {
assertFalse(wsm.addWarp(uuid, null));
}
/**
* Test method for {@link WarpSignsManager#addWarp(java.util.UUID, org.bukkit.Location)}.
*/
@Test
public void testAddWarpReplaceOldSign() {
assertTrue(wsm.addWarp(uuid, location));
verify(player).sendMessage("warps.sign-removed");
}
/**
* Test method for {@link WarpSignsManager#addWarp(java.util.UUID, org.bukkit.Location)}.
*/
@Test
public void testAddWarpReplaceOldSignDifferentPlayer() {
assertTrue(wsm.addWarp(UUID.randomUUID(), location));
verify(player).sendMessage("warps.sign-removed");
}
/**
* Test method for {@link WarpSignsManager#addWarp(java.util.UUID, org.bukkit.Location)}.
*/
@Test
public void testAddWarp() {
Location loc = mock(Location.class);
assertTrue(wsm.addWarp(uuid, loc));
verify(pim).callEvent(any(WarpInitiateEvent.class));
}
/**
* Test method for {@link WarpSignsManager#getWarp(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetWarpWorldWorld() {
assertNull(wsm.getWarp(mock(World.class), uuid));
}
/**
* Test method for {@link WarpSignsManager#getWarp(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetWarp() {
assertEquals(location, wsm.getWarp(world, uuid));
}
/**
* Test method for {@link WarpSignsManager#getWarpOwner(org.bukkit.Location)}.
*/
@Test
public void testGetWarpOwner() {
assertEquals("tastybento", wsm.getWarpOwner(location));
}
/**
* Test method for {@link WarpSignsManager#getSortedWarps(org.bukkit.World)}.
*/
@Test
public void testGetSortedWarps() {
CompletableFuture<List<UUID>> r = new CompletableFuture<>();
assertEquals(1, wsm.processWarpMap(r, world).size());
}
/**
* Test method for {@link WarpSignsManager#listWarps(org.bukkit.World)}.
*/
@Test
public void testListWarps() {
assertEquals(1, wsm.listWarps(world).size());
assertEquals(uuid, wsm.listWarps(world).toArray()[0]);
}
/**
* Test method for {@link WarpSignsManager#removeWarp(org.bukkit.Location)}.
*/
@Test
public void testRemoveWarpLocation() {
wsm.removeWarp(location);
assertTrue(wsm.listWarps(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#removeWarp(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testRemoveWarpWorldUUID() {
wsm.removeWarp(world, uuid);
assertTrue(wsm.listWarps(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#saveWarpList()}.
* @throws Exception general exception
*/
@Test
public void testSaveWarpList() throws Exception {
wsm.saveWarpList();
verify(handler, Mockito.atLeastOnce()).saveObject(any());
}
/**
* Test method for {@link WarpSignsManager#warpPlayer(org.bukkit.World, world.bentobox.bentobox.api.user.User, java.util.UUID)}.
*/
@Test
public void testWarpPlayer() {
Player p = mock(Player.class);
when(p.getUniqueId()).thenReturn(UUID.randomUUID());
when(p.getWorld()).thenReturn(world);
when(p.getName()).thenReturn("tastybento");
when(p.getLocation()).thenReturn(location);
when(p.isOnline()).thenReturn(true);
when(p.canSee(any(Player.class))).thenReturn(true);
@Nullable
User u = User.getInstance(p);
PowerMockito.when(Util.teleportAsync(any(), any(), any())).thenReturn(CompletableFuture.completedFuture(true));
wsm.warpPlayer(world, u, uuid);
PowerMockito.verifyStatic(Util.class);
Util.teleportAsync(eq(p), any(), eq(TeleportCause.COMMAND));
verify(player).sendMessage(anyString());
}
/**
* Test method for {@link WarpSignsManager#hasWarp(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testHasWarp() {
assertTrue(wsm.hasWarp(world, uuid));
assertFalse(wsm.hasWarp(mock(World.class), uuid));
assertFalse(wsm.hasWarp(world, UUID.randomUUID()));
}
/**
* Test method for {@link WarpSignsManager#loadWarpList()}.
*/
@Test
public void testLoadWarpListNoWarpTable() {
// Run again but with no database table
when(handler.objectExists(anyString())).thenReturn(false);
wsm = new WarpSignsManager(addon, plugin);
// Save
wsm.saveWarpList();
// Default load in constructor check
verify(addon, times(2)).log("Loading warps...");
assertTrue(wsm.getWarpMap(world).isEmpty());
}
/**
* Test method for {@link WarpSignsManager#loadWarpList()}.
* @throws Exception exception
*/
@Test
public void testLoadWarpListEmptyWarpTable() throws Exception {
// Run again but with no data in table
when(handler.loadObject(anyString())).thenReturn(null);
wsm = new WarpSignsManager(addon, plugin);
// Save
wsm.saveWarpList();
// Default load in constructor check
verify(addon, times(2)).log("Loading warps...");
assertTrue(wsm.getWarpMap(world).isEmpty());
}
}

View File

@ -0,0 +1,256 @@
package world.bentobox.warps.commands;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.PluginManager;
import org.bukkit.scheduler.BukkitScheduler;
import org.eclipse.jdt.annotation.NonNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
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.CommandsManager;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.warps.Warp;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.config.Settings;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, BentoBox.class})
public class WarpCommandTest {
private static final String WELCOME_LINE = "[Welcome]";
@Mock
private CompositeCommand ic;
@Mock
private User user;
@Mock
private World world;
@Mock
private IslandWorldManager iwm;
@Mock
private Warp addon;
// Command under test
private WarpCommand wc;
@Mock
private Settings settings;
@Mock
private WarpSignsManager wsm;
@Mock
private PlayersManager pm;
@Mock
private PluginManager pim;
@Mock
private world.bentobox.bentobox.Settings s;
@Mock
private BukkitScheduler sch;
/**
*/
@Before
public void setUp() {
// Set up plugin
BentoBox plugin = mock(BentoBox.class);
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
// Command manager
CommandsManager cm = mock(CommandsManager.class);
when(plugin.getCommandsManager()).thenReturn(cm);
// Addon
when(ic.getAddon()).thenReturn(addon);
when(ic.getPermissionPrefix()).thenReturn("bskyblock.");
when(ic.getWorld()).thenReturn(world);
// IWM friendly name
when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock");
when(iwm.inWorld(any(World.class))).thenReturn(true);
when(plugin.getIWM()).thenReturn(iwm);
// Player
UUID uuid = UUID.randomUUID();
when(user.getUniqueId()).thenReturn(uuid);
when(user.getWorld()).thenReturn(world);
// settings
when(addon.getSettings()).thenReturn(settings);
when(settings.getWarpCommand()).thenReturn("warp");
when(settings.getWelcomeLine()).thenReturn(WELCOME_LINE);
// Warp Signs Manager
when(addon.getWarpSignsManager()).thenReturn(wsm);
@NonNull
Set<UUID> set = new HashSet<>();
set.add(UUID.randomUUID());
set.add(UUID.randomUUID());
set.add(UUID.randomUUID());
when(wsm.listWarps(world)).thenReturn(set);
// Players Manager
when(plugin.getPlayers()).thenReturn(pm);
when(addon.getPlayers()).thenReturn(pm);
// Repeat twice because it is asked twice
when(pm.getName(any())).thenReturn("tastybento", "tastybento", "poslovich", "poslovich", "BONNe", "BONNe", "Joe");
// Bukkit
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getPluginManager()).thenReturn(pim);
when(Bukkit.getScheduler()).thenReturn(sch);
// BentoBox settings
when(plugin.getSettings()).thenReturn(s);
when(s.getDelayTime()).thenReturn(0);
}
@After
public void tearDown() {
Mockito.framework().clearInlineMocks();
}
public void warpCommandWarpCompositeCommand() {
// Command under test
wc = new WarpCommand(addon, ic);
}
public void warpCommandWarp() {
// Command under test
wc = new WarpCommand(addon);
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#setup()}.
*/
@Test
public void testSetupWarpCompositeCommand() {
warpCommandWarpCompositeCommand();
assertEquals("bskyblock.island.warp", wc.getPermission());
assertTrue(wc.isOnlyPlayer());
assertEquals("warp.help.parameters", wc.getParameters());
assertEquals("warp.help.description", wc.getDescription());
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#setup()}.
*/
@Test
public void testSetupWarp() {
warpCommandWarp();
assertEquals(Warp.WELCOME_WARP_SIGNS + ".warp", wc.getPermission());
assertTrue(wc.isOnlyPlayer());
assertEquals("warp.help.parameters", wc.getParameters());
assertEquals("warp.help.description", wc.getDescription());
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringNoArgs() {
warpCommandWarpCompositeCommand();
wc.execute(user, "warp", Collections.emptyList());
verify(user).sendMessage("commands.help.header", TextVariables.LABEL, "BSkyBlock");
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringKnownPlayer() {
warpCommandWarpCompositeCommand();
assertTrue(wc.execute(user, "warp", Collections.singletonList("tastybento")));
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringKnownPlayerWarp() {
warpCommandWarp();
assertTrue(wc.execute(user, "warp", Collections.singletonList("tastybento")));
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringKnownPlayerMixedCase() {
warpCommandWarpCompositeCommand();
assertTrue(wc.execute(user, "warp", Collections.singletonList("tAsTyBEnTo")));
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringKnownPlayerStartOnly() {
when(pm.getName(any())).thenReturn("tastybento");
warpCommandWarpCompositeCommand();
assertTrue(wc.execute(user, "warp", Collections.singletonList("tAsTy")));
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringUnknownPlayer() {
warpCommandWarpCompositeCommand();
assertFalse(wc.execute(user, "warp", Collections.singletonList("LSPVicky")));
verify(user).sendMessage("warps.error.does-not-exist");
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringNoWarpsYet() {
when(wsm.listWarps(world)).thenReturn(Collections.emptySet());
warpCommandWarpCompositeCommand();
assertFalse(wc.execute(user, "warp", Collections.singletonList("LSPVicky")));
verify(user).sendMessage("warps.error.no-warps-yet");
verify(user).sendMessage("warps.warpTip", "[text]", WELCOME_LINE);
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testTabCompleteUserStringListOfString() {
warpCommandWarpCompositeCommand();
List<String> op = wc.tabComplete(user, "warp", Collections.singletonList("tas")).get();
assertEquals("tastybento", op.get(0));
assertEquals("tastybento", op.get(1));
assertEquals("poslovich", op.get(2));
}
}

View File

@ -0,0 +1,185 @@
package world.bentobox.warps.commands;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.eclipse.jdt.annotation.NonNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.managers.CommandsManager;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.warps.managers.SignCacheManager;
import world.bentobox.warps.Warp;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.config.Settings;
/**
* @author tastybento
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({Bukkit.class, BentoBox.class})
public class WarpsCommandTest {
private static final String WELCOME_LINE = "[Welcome]";
@Mock
private CompositeCommand ic;
@Mock
private User user;
@Mock
private World world;
@Mock
private IslandWorldManager iwm;
@Mock
private Warp addon;
// Command under test
private WarpsCommand wc;
@Mock
private Settings settings;
@Mock
private WarpSignsManager wsm;
@Mock
private PlayersManager pm;
@Mock
private SignCacheManager wpm;
/**
*/
@Before
public void setUp() {
// Set up plugin
BentoBox plugin = mock(BentoBox.class);
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
// Command manager
CommandsManager cm = mock(CommandsManager.class);
when(plugin.getCommandsManager()).thenReturn(cm);
// Addon
when(ic.getAddon()).thenReturn(addon);
when(ic.getPermissionPrefix()).thenReturn("bskyblock.");
// World
when(world.toString()).thenReturn("world");
// Player
when(user.getWorld()).thenReturn(world);
// settings
when(addon.getSettings()).thenReturn(settings);
when(settings.getWarpsCommand()).thenReturn("warps");
when(settings.getWelcomeLine()).thenReturn(WELCOME_LINE);
// Warp Signs Manager
when(addon.getWarpSignsManager()).thenReturn(wsm);
@NonNull
Set<UUID> set = new HashSet<>();
set.add(UUID.randomUUID());
set.add(UUID.randomUUID());
set.add(UUID.randomUUID());
when(wsm.listWarps(world)).thenReturn(set);
CompletableFuture<List<UUID>> warps = new CompletableFuture<>();
warps.complete(set.stream().toList());
when(wsm.getSortedWarps(world)).thenReturn(warps);
// Warp Panel Manager
when(addon.getSignCacheManager()).thenReturn(wpm);
}
public void warpCommandWarpsCompositeCommand() {
// Command under test
wc = new WarpsCommand(addon, ic);
}
public void warpCommandWarps() {
// Command under test
wc = new WarpsCommand(addon);
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#setup()}.
*/
@Test
public void testSetupWarpCompositeCommand() {
warpCommandWarpsCompositeCommand();
assertEquals("bskyblock.island.warps", wc.getPermission());
assertTrue(wc.isOnlyPlayer());
assertEquals("warps.help.description", wc.getDescription());
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#setup()}.
*/
@Test
public void testSetupWarp() {
warpCommandWarps();
assertEquals(Warp.WELCOME_WARP_SIGNS + ".warps", wc.getPermission());
assertTrue(wc.isOnlyPlayer());
assertEquals("warps.help.description", wc.getDescription());
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringNoWarpsYet() {
when(wsm.listWarps(world)).thenReturn(Collections.emptySet());
warpCommandWarpsCompositeCommand();
assertFalse(wc.execute(user, "warps", Collections.emptyList()));
verify(user).sendMessage("warps.error.no-warps-yet");
verify(user).sendMessage("warps.warpTip", "[text]", WELCOME_LINE);
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringNoWarpsYetNoAddon() {
when(wsm.listWarps(world)).thenReturn(Collections.emptySet());
warpCommandWarps();
assertFalse(wc.execute(user, "warps", Collections.emptyList()));
verify(user).sendMessage("warps.error.no-warps-yet");
verify(user).sendMessage("warps.warpTip", "[text]", WELCOME_LINE);
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfString() {
warpCommandWarpsCompositeCommand();
assertTrue(wc.execute(user, "warps", Collections.emptyList()));
}
/**
* Test method for {@link world.bentobox.warps.commands.WarpsCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}.
*/
@Test
public void testExecuteUserStringListOfStringNoAddon() {
warpCommandWarps();
assertTrue(wc.execute(user, "warps", Collections.emptyList()));
}
}

View File

@ -1,15 +1,14 @@
package world.bentobox.warps;
package world.bentobox.warps.listeners;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import java.util.HashMap;
import java.util.Map;
@ -36,19 +35,23 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.events.flags.FlagProtectionChangeEvent;
import world.bentobox.bentobox.api.flags.Flag;
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.LocalesManager;
import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.warps.Warp;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.config.Settings;
/**
@ -71,7 +74,6 @@ public class WarpSignsListenerTest {
@Mock
private WarpSignsManager wsm;
private PluginManager pm;
private UUID uuid;
private String[] lines;
@Mock
private FileConfiguration config;
@ -79,9 +81,13 @@ public class WarpSignsListenerTest {
private Settings settings;
@Mock
private IslandsManager im;
@Mock
private IslandWorldManager iwm;
@Mock
private Island island;
@Before
public void setUp() throws Exception {
public void setUp() {
// Bukkit
PowerMockito.mockStatic(Bukkit.class);
pm = mock(PluginManager.class);
@ -110,10 +116,10 @@ public class WarpSignsListenerTest {
when(block.getWorld()).thenReturn(world);
// Player
when(player.hasPermission(anyString())).thenReturn(false);
uuid = UUID.randomUUID();
UUID uuid = UUID.randomUUID();
when(player.getUniqueId()).thenReturn(uuid);
s = mock(Sign.class);
when(s.getLine(Mockito.anyInt())).thenReturn(ChatColor.GREEN + "[WELCOME]");
when(s.getLine(anyInt())).thenReturn(ChatColor.GREEN + "[WELCOME]");
when(block.getState()).thenReturn(s);
// warp signs manager
when(addon.getWarpSignsManager()).thenReturn(wsm);
@ -121,9 +127,10 @@ public class WarpSignsListenerTest {
Location location = mock(Location.class);
when(location.getBlock()).thenReturn(block);
when(s.getLocation()).thenReturn(location);
when(block.getLocation()).thenReturn(location);
list.put(uuid, location);
// Player is in world
when(wsm.getWarpMap(Mockito.eq(world))).thenReturn(list);
when(wsm.getWarpMap(world)).thenReturn(list);
//Player has a warp sign already here
when(wsm.getWarp(any(), any())).thenReturn(location);
// Unique spot
@ -133,12 +140,7 @@ public class WarpSignsListenerTest {
when(addon.getPlugin()).thenReturn(plugin);
User.setPlugin(plugin);
LocalesManager lm = mock(LocalesManager.class);
when(lm.get(any(), any())).thenAnswer(new Answer<String>(){
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgument(1, String.class);
}});
when(lm.get(any(), any())).thenReturn(null);
when(plugin.getLocalesManager()).thenReturn(lm);
// Lines
@ -148,6 +150,9 @@ public class WarpSignsListenerTest {
when(settings.getWelcomeLine()).thenReturn("[WELCOME]");
when(addon.getSettings()).thenReturn(settings);
island = mock(Island.class);
when(im.getIslandAt(any())).thenReturn(Optional.of(island));
// On island
when(plugin.getIslands()).thenReturn(im);
when(im.userIsOnIsland(any(World.class), any(User.class))).thenReturn(true);
@ -155,16 +160,20 @@ public class WarpSignsListenerTest {
// Sufficient level
when(addon.getLevel(any(), any())).thenReturn(100L);
IslandWorldManager iwm = mock(IslandWorldManager.class);
// IWM
when(plugin.getIWM()).thenReturn(iwm);
when(iwm.getAddon(any())).thenReturn(Optional.empty());
when(iwm.inWorld(any(World.class))).thenReturn(true);
Answer<String> answer = invocation -> invocation.getArgument(1, String.class);
// Util
PowerMockito.mockStatic(Util.class);
when(Util.getWorld(any())).thenReturn(world);
when(Util.stripSpaceAfterColorCodes(anyString())).thenAnswer(invocation -> invocation.getArgument(0, String.class));
when(Util.translateColorCodes(any())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
// Locales
Answer<String> answer = invocation -> invocation.getArgument(1, String.class);
when(lm.get(any(User.class), anyString())).thenAnswer(answer);
when(plugin.getLocalesManager()).thenReturn(lm);
@ -190,23 +199,13 @@ public class WarpSignsListenerTest {
}
@Test
public void testOnSignBreakWrongWorld() {
public void testOnSignNotGameWorld() {
WarpSignsListener wsl = new WarpSignsListener(addon);
BlockBreakEvent e = new BlockBreakEvent(block, player);
when(addon.inRegisteredWorld(any())).thenReturn(false);
wsl.onSignBreak(e);
assertFalse(e.isCancelled());
verify(addon).inRegisteredWorld(Mockito.eq(world));
}
@Test
public void testOnSignBreakNullState() {
WarpSignsListener wsl = new WarpSignsListener(addon);
BlockBreakEvent e = new BlockBreakEvent(block, player);
when(block.getState()).thenReturn(null);
wsl.onSignBreak(e);
assertFalse(e.isCancelled());
verify(block).getState();
verify(addon).inRegisteredWorld(world);
}
@Test
@ -216,7 +215,7 @@ public class WarpSignsListenerTest {
when(s.getLine(Mockito.anyInt())).thenReturn(ChatColor.RED + "[WELCOME]");
wsl.onSignBreak(e);
assertFalse(e.isCancelled());
verify(s).getLine(Mockito.eq(0));
verify(s).getLine(0);
verify(settings).getWelcomeLine();
}
@ -229,7 +228,7 @@ public class WarpSignsListenerTest {
when(s.getLocation()).thenReturn(mock(Location.class));
wsl.onSignBreak(e);
assertFalse(e.isCancelled());
verify(wsm).getWarpMap(Mockito.eq(world));
verify(wsm).getWarpMap(world);
verify(s).getLocation();
}
@ -283,13 +282,98 @@ public class WarpSignsListenerTest {
* Sign create
*/
@Test
public void testOnCreateWrongWorld() {
public void testOnCreateWrongWorldGameWorld() {
when(player.hasPermission(anyString())).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
when(addon.inRegisteredWorld(any())).thenReturn(false);
wsl.onSignWarpCreate(e);
verify(addon).inRegisteredWorld(Mockito.eq(world));
verify(addon).inRegisteredWorld(world);
}
@Test
public void testOnCreateNotGameWorldAllowed() {
when(settings.isAllowInOtherWorlds()).thenReturn(true);
when(iwm.inWorld(any(World.class))).thenReturn(false);
when(player.hasPermission(anyString())).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
when(addon.inRegisteredWorld(any())).thenReturn(false);
wsl.onSignWarpCreate(e);
verify(player).sendMessage("warps.success");
assertEquals(ChatColor.GREEN + "[WELCOME]", e.getLine(0));
}
@Test
public void testOnCreateWithoutCorrectRankNotAllowed() {
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
when(player.hasPermission(anyString())).thenReturn(true);
when(addon.inRegisteredWorld(any())).thenReturn(true);
when(island.getRank(player.getUniqueId())).thenReturn(0);
when(island.getFlag(any())).thenReturn(1000);
wsl.onSignWarpCreate(e);
verify(player).sendMessage("warps.error.not-correct-rank");
}
@Test
public void testOnFlagChangeWhenSettingIsOffNothingHappens() {
Flag flag = mock(Flag.class);
when(addon.getCreateWarpFlag()).thenReturn(flag);
when(settings.getRemoveExistingWarpsWhenFlagChanges()).thenReturn(false);
WarpSignsListener wsl = new WarpSignsListener(addon);
FlagProtectionChangeEvent e = new FlagProtectionChangeEvent(island, player.getUniqueId(), flag, 1000);
wsl.onFlagChange(e);
verifyNoInteractions(island);
}
@Test
public void testOnFlagChangeWhenSettingIsOnWarpGetsRemoved() {
Flag flag = mock(Flag.class);
when(addon.getCreateWarpFlag()).thenReturn(flag);
when(settings.getRemoveExistingWarpsWhenFlagChanges()).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
Map<UUID, Location> warps = Map.of(
player.getUniqueId(), block.getLocation()
);
when(wsm.getWarpMap(any())).thenReturn(warps);
when(island.inIslandSpace(any(Location.class))).thenReturn(true);
FlagProtectionChangeEvent e = new FlagProtectionChangeEvent(island, player.getUniqueId(), flag, 1000);
wsl.onFlagChange(e);
verify(addon.getWarpSignsManager()).removeWarp(any(), any());
}
@Test
public void testOnCreateNotGameWorldNotAllowed() {
when(settings.isAllowInOtherWorlds()).thenReturn(false);
when(iwm.inWorld(any(World.class))).thenReturn(false);
when(player.hasPermission(anyString())).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
when(addon.inRegisteredWorld(any())).thenReturn(false);
wsl.onSignWarpCreate(e);
verify(player, never()).sendMessage("warps.success");
}
@Test
public void testOnCreateNotGameWorldNoPerm() {
when(settings.isAllowInOtherWorlds()).thenReturn(true);
when(iwm.inWorld(any(World.class))).thenReturn(false);
when(player.hasPermission(anyString())).thenReturn(false);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
when(addon.inRegisteredWorld(any())).thenReturn(false);
wsl.onSignWarpCreate(e);
verify(player).sendMessage("warps.error.no-permission");
}
@Test
@ -345,18 +429,6 @@ public class WarpSignsListenerTest {
assertEquals(ChatColor.GREEN + "[WELCOME]", e.getLine(0));
}
@Test
public void testCreateNoSignAlreadyDuplicateSpot() {
when(wsm.addWarp(any(), any())).thenReturn(false);
when(wsm.getWarp(any(), any())).thenReturn(null);
when(player.hasPermission(anyString())).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);
wsl.onSignWarpCreate(e);
verify(player).sendMessage("warps.error.duplicate");
assertEquals(ChatColor.RED + "[WELCOME]", e.getLine(0));
}
@Test
public void testCreateNoSignDeactivateOldSign() {
when(player.hasPermission(anyString())).thenReturn(true);