1
0
mirror of https://github.com/BentoBoxWorld/Warps.git synced 2024-06-26 06:34:57 +02:00

Compare commits

...

182 Commits

Author SHA1 Message Date
tastybento
18b63f4e79
Merge pull request #135 from TreemanKing/toggle-warp
feat: toggle warp command
2024-06-21 20:18:57 -07:00
TreemanK
4bacbd7c5a feat: suggestions
- getToggleCommand is linked to toggleWarpCommand instead of hardcoded
- warpSigns is now null and not empty
2024-06-22 13:16:47 +10:00
tastybento
7f9f35253d
Merge pull request #136 from TreemanKing/fix-other-worlds
fix (partial): other worlds can be used
2024-06-21 08:16:47 -07:00
TreemanK
599a6e1d08 style: added TODO
Currently, `noLevelOrIsland and hasCorrectIslandRank` require the sign to be on an island. Currently this means the flag/level requirement are non-existent in the case of a sign being placed on a non-BSB island. This gives a possible solution if new API comes to light.
2024-06-22 00:06:21 +10:00
TreemanK
1054518831 fix: fix create warp/sign in non-island world
Currently, you can't create a warp in another world (even with permissions) as it goes into hasCorrectIslandRank. In this case, it is always false as there is no island at the block location meaning it will always go into this whilst creating a warp in other worlds.
2024-06-21 23:06:02 +10:00
TreemanK
abd526b06d feat: add toggle event 2024-06-21 20:42:31 +10:00
TreemanK
eeead7fb49 feat: toggle warp command 2024-06-21 20:19:24 +10:00
TreemanK
ef81a1c2f0 style: change method name to getWarpLocation
note `line 294` of `warp.java`, I am unsure what this does and whether it should be `getWarpLocation` or not. I will leave it for the time being.
2024-06-21 02:23:38 +10:00
TreemanK
64f8a4899a feat: make sure old data is not lost!
Now, I was thinking if there is a way to directly convert it but there isn't because of the way it was structured. This *ISN'T* the best way around things but if someone can find a better way around it, be my guest.
2024-06-21 01:49:30 +10:00
TreemanK
0a343219bc feat: changed location to playerwarp in preparation of toggle state 2024-06-20 21:57:50 +10:00
tastybento
0a12a26b06
Merge pull request #134 from BentoBoxWorld/gitlocalize-29839
French translation
2024-06-08 10:12:36 -07:00
ISOURA
b860a4dac5 Translate fr.yml via GitLocalize 2024-06-08 17:08:20 +00:00
tastybento
2da0e5e82b
Merge pull request #133 from RUYSUE/develop
updated zh-CN locale
2024-06-08 08:52:53 -07:00
RUYSUE
a39b6a3f4a
Update zh-CN.yml 2024-06-08 17:20:14 +08:00
tastybento
d70ee5d755
Merge pull request #132 from BentoBoxWorld/131_WarpCreateEvent_doesn't_fire
Call events correctly. #131
2024-06-05 14:12:29 -07:00
tastybento
efc6b795d1 Call events correctly. #131 2024-06-05 14:09:02 -07:00
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
56 changed files with 4407 additions and 1688 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

@ -5,6 +5,8 @@ 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

131
pom.xml
View File

@ -8,9 +8,9 @@
<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>
<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>
@ -25,19 +25,19 @@
</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>
<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>
<url>http://ci.codemc.org/job/BentoBoxWorld/job/Warps</url>
</ciManagement>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/BentoBoxWorld/addon-welcomewarpsigns/issues</url>
<url>https://github.com/BentoBoxWorld/Warps/issues</url>
</issueManagement>
<distributionManagement>
@ -54,19 +54,23 @@
<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.7.0</build.version>
<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. -->
@ -109,30 +113,6 @@
<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>
@ -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>
@ -189,6 +169,15 @@
<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>
@ -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,27 +312,32 @@
<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>

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

@ -7,18 +7,27 @@ 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.ToggleWarpCommand;
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
*
*/
@ -33,15 +42,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.
*/
@ -62,6 +76,11 @@ public class Warp extends Addon {
*/
private Config<Settings> settingsConfig;
/**
* Create Warp Flag
*/
private Flag createWarpFlag;
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
@ -78,7 +97,12 @@ 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);
new ToggleWarpCommand(this);
}
}
@ -90,11 +114,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.");
}
}
@ -111,42 +135,57 @@ public class Warp extends Addon {
// Register commands
this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> {
if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName()))
{
if (gameModeAddon.getPlayerCommand().isPresent())
if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())
&& gameModeAddon.getPlayerCommand().isPresent())
{
this.registeredWorlds.add(gameModeAddon.getOverWorld());
new WarpCommand(this, gameModeAddon.getPlayerCommand().get());
new WarpsCommand(this, gameModeAddon.getPlayerCommand().get());
new ToggleWarpCommand(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() {
private boolean loadSettings() {
if (settingsConfig == null) {
settingsConfig = new Config<>(this, Settings.class);
}
@ -155,16 +194,23 @@ public class Warp extends Addon {
// 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() {
@ -191,14 +237,32 @@ 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)
@ -215,8 +279,8 @@ public class Warp extends Addon {
// Parse keys
if (metaData.containsKey("world")) {
world = Bukkit.getWorld((String)metaData.get("world"));
if (world == null) return null;
}
if (world == null) return null;
if (metaData.containsKey("uuid")) {
try {
uuid = UUID.fromString((String)metaData.get("uuid"));
@ -225,20 +289,14 @@ public class Warp extends Addon {
return null;
}
}
switch(requestLabel) {
case "getSortedWarps":
return getWarpSignsManager().getSortedWarps(world);
case "getWarp":
return getWarpSignsManager().getWarp(world, uuid);
case "getWarpMap":
return getWarpSignsManager().getWarpMap(world);
case "hasWarp":
return getWarpSignsManager().hasWarp(world, uuid);
case "listWarps":
return getWarpSignsManager().listWarps(world);
default:
return null;
}
return switch (requestLabel) {
case "getSortedWarps" -> getWarpSignsManager().getSortedWarps(world);
case "getWarp" -> uuid == null ? null : getWarpSignsManager().getWarpLocation(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,135 +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) {
PanelItemBuilder pib = new PanelItemBuilder()
.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;
}
});
Material icon = getSignIcon(world, warpOwner);
if (icon.equals(Material.PLAYER_HEAD)) {
return pib.icon(addon.getPlayers().getName(warpOwner)).build();
} else {
return pib.icon(icon).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,162 +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 null player
}
}

View File

@ -1,520 +0,0 @@
package world.bentobox.warps;
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.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.permissions.PermissionAttachmentInfo;
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.event.WarpInitiateEvent;
import world.bentobox.warps.event.WarpListEvent;
import world.bentobox.warps.objects.WarpsData;
/**
* Handles warping. Players can add one sign
*
* @author tastybento
*
*/
public class WarpSignsManager {
private static final int MAX_WARPS = 600;
private 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 Warp addon;
private WarpsData warpsData = new WarpsData();
/**
* Get the warp map for this world
* @param world - world
* @return map of warps
*/
public Map<UUID, Location> getWarpMap(World world) {
return worldsWarpList.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>());
}
/**
* @param addon - addon
* @param plugin - plugin
*/
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
handler = new Database<>(addon, WarpsData.class);
// Load the warps
loadWarpList();
}
/**
* Stores warps in the warp array. If successful, fires an event
*
* @param playerUUID - the player's UUID
* @param loc - location of warp sign
* @return true if successful, false if not
*/
public boolean addWarp(final UUID playerUUID, final Location loc) {
// Do not allow null players to set warps
if (playerUUID == 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));
return true;
}
/**
* Provides the location of the warp for player or null if one is not found
*
* @param world - world to search in
* @param playerUUID - the player's UUID
* - the warp requested
* @return Location of warp or null
*/
public Location getWarp(World world, UUID playerUUID) {
return getWarpMap(world).get(playerUUID);
}
/**
* Get the name of the warp owner by location
* @param location to search
* @return Name of warp owner or empty string if there is none
*/
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 sorted list of warps with most recent players listed first
* @return UUID list
*/
public List<UUID> getSortedWarps(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();
// 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
if (!map.isEmpty() && map.containsKey(lastPlayed)) {
lastPlayed = map.firstKey() - 1;
}
map.put(lastPlayed, uuid);
});
Collection<UUID> result = map.descendingMap().values();
List<UUID> list = new ArrayList<>(result);
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 list;
}
/**
* Lists all the known warps for this world
* @param world - world
*
* @return UUID set of warps
*/
public Set<UUID> listWarps(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());
}
/**
* Load the warps and check if they still exist
*/
private void loadWarpList() {
addon.getLogger().info("Loading warps...");
worldsWarpList = new HashMap<>();
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")) {
// Add to map
getWarpMap(k.getWorld()).put(v, k);
}
});
}
}
}
/**
* Changes the sign to red if it exists
* @param loc
*/
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);
}
}
}
}
/**
* Removes a warp at a location.
*
* @param loc
*/
public void removeWarp(Location loc) {
popSign(loc);
Iterator<Entry<UUID, Location>> it = getWarpMap(loc.getWorld()).entrySet().iterator();
while (it.hasNext()) {
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");
}
// Remove sign from warp panel cache
addon.getWarpPanelManager().removeWarp(loc.getWorld(), en.getKey());
it.remove();
}
}
saveWarpList();
}
/**
* Remove warp sign owned by UUID
*
* @param uuid
*/
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);
}
saveWarpList();
}
/**
* Saves the warp lists to the database
*/
public void saveWarpList() {
handler.saveObject(warpsData .save(worldsWarpList));
}
/**
* Gets the warp sign text and material type for player's UUID in world
*
* @param world - world to look in
* @param uuid - player's uuid
* @return Sign's content and type
*/
public SignCache getSignInfo(World world, UUID uuid) {
List<String> result = new ArrayList<>();
//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
String prefix = this.plugin.getIWM().getAddon(world).map(
Addon::getPermissionPrefix).orElse("");
Material icon;
if (!prefix.isEmpty())
{
icon = Material.matchMaterial(
this.getPermissionValue(User.getInstance(uuid),
prefix + "island.warp",
this.addon.getSettings().getIcon()));
}
else
{
icon = Material.matchMaterial(this.addon.getSettings().getIcon());
}
if (icon == null || icon.name().contains("SIGN")) {
return new SignCache(result, Material.valueOf(sign.getType().name().replace("WALL_", "")));
} else {
return new SignCache(result, icon);
}
} else {
addon.getWarpSignsManager().removeWarp(world, uuid);
}
return new SignCache(Collections.emptyList(), Material.AIR);
}
/**
* Warps a player to a spot in front of a sign.
* @param user - user who is warping
* @param inFront - location in front of sign - previously checked for safety
* @param signOwner - warp sign owner
* @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) {
// convert blockface to angle
float yaw = 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());
}
}
/**
* 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
*
* @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) {
final Location warpSpot = getWarp(world, owner);
// Check if the warp spot is safe
if (warpSpot == null) {
user.sendMessage("warps.error.does-not-exist");
addon.getWarpSignsManager().removeWarp(world, owner);
return;
}
if (this.plugin.getIWM().inWorld(user.getWorld()) &&
Flags.PREVENT_TELEPORT_WHEN_FALLING.isSetForWorld(user.getWorld()) &&
user.getPlayer().getFallDistance() > 0) {
// We're sending the "hint" to the player to tell them they cannot teleport while falling.
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
return;
}
Island island = addon.getPlugin().getIslands().getIsland(world, owner);
boolean pvp = false;
if (island != null) {
// Check for PVP
switch (warpSpot.getWorld().getEnvironment()) {
case NETHER:
pvp = island.isAllowed(Flags.PVP_NETHER);
break;
case NORMAL:
pvp = island.isAllowed(Flags.PVP_OVERWORLD);
break;
case THE_END:
pvp = island.isAllowed(Flags.PVP_END);
break;
default:
break;
}
}
// Find out which direction the warp is facing
Block b = warpSpot.getBlock();
if (b.getType().name().contains("WALL_SIGN")) {
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);
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);
return;
}
} else if (b.getType().name().contains("SIGN")) {
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);
return;
}
} else {
// Warp has been removed
user.sendMessage("warps.error.does-not-exist");
addon.getWarpSignsManager().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);
} else {
user.getWorld().playSound(user.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 1F, 1F);
}
user.teleport(actualWarp);
return;
}
}
/**
* Check if a player has a warp
* @param playerUUID - player's UUID
* @return true if they have warp
*/
public boolean hasWarp(World world, UUID playerUUID) {
return getWarpMap(world).containsKey(playerUUID);
}
// ---------------------------------------------------------------------
// Section: Other methods
// ---------------------------------------------------------------------
/**
* 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.
*/
private 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,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

@ -0,0 +1,61 @@
package world.bentobox.warps.commands;
import org.bukkit.Bukkit;
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.event.WarpToggleEvent;
import world.bentobox.warps.objects.PlayerWarp;
import java.util.List;
import java.util.UUID;
public class ToggleWarpCommand extends CompositeCommand {
private final Warp addon;
public ToggleWarpCommand(Warp addon, CompositeCommand bsbIslandCmd) {
super(bsbIslandCmd, addon.getSettings().getToggleWarpCommand());
this.addon = addon;
}
public ToggleWarpCommand(Warp addon) {
super(addon.getSettings().getToggleWarpCommand());
this.addon = addon;
}
@Override
public void setup() {
this.setPermission(this.getParent() == null ? Warp.WELCOME_WARP_SIGNS + ".togglewarp" : "island.warp.toggle");
this.setOnlyPlayer(true);
this.setDescription("togglewarp.help.description");
}
@Override
public boolean execute(User user, String s, List<String> list) {
UUID userUUID = user.getUniqueId();
World userWorld = user.getWorld();
// Check if the user has a warp
boolean hasWarp = addon.getWarpSignsManager().hasWarp(userWorld, userUUID);
if (hasWarp) {
// If the user has a warp, toggle its visibility
PlayerWarp warp = addon.getWarpSignsManager().getPlayerWarp(userWorld, userUUID);
// Check extreme case if PlayerWarp is null
if (warp == null) {
user.sendMessage("togglewarp.error.generic");
return false;
}
warp.toggle();
Bukkit.getPluginManager().callEvent(new WarpToggleEvent(userUUID, warp));
String message = warp.isEnabled() ? "togglewarp.enabled" : "togglewarp.disabled";
user.sendMessage(message);
} else {
user.sendMessage("togglewarp.error.no-warp");
}
return false;
}
}

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);
// Attempt 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) {
// Attempt 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

@ -23,20 +23,19 @@ public class Settings implements ConfigObject
@ConfigEntry(path = "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 = "[Welcome]";
@ConfigComment("")
@ConfigComment("Icon that will be displayed in Warps list. SIGN counts for any kind of sign and the type of")
@ConfigComment("wood used will be reflected in the panel if the server supports it.")
@ConfigComment("It uses native Minecraft material strings, but using string 'PLAYER_HEAD', it is possible to")
@ConfigComment("use player heads instead. Beware that Mojang API rate limiting may prevent heads from loading.")
@ConfigEntry(path = "icon")
private String icon = "SIGN";
@ConfigComment("")
@ConfigComment("This list stores GameModes in which Level addon should not work.")
@ConfigComment("To disable addon it is necessary to write its name in new line that starts with -. Example:")
@ -45,6 +44,26 @@ 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";
@ConfigEntry(path = "togglewarp-command")
String toggleWarpCommand = "togglewarp";
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
@ -126,21 +145,94 @@ 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 toggleWarpCommand
*/
public String getToggleWarpCommand() {
return toggleWarpCommand;
}
/**
* @param toggleWarpCommand the toggleWarpCommand to set
*/
public void setToggleWarpCommand(String toggleWarpCommand) {
this.toggleWarpCommand = toggleWarpCommand;
}
/**
* @return the removeExistingWarpsWhenFlagChanges
*/
public boolean getRemoveExistingWarpsWhenFlagChanges() {
return removeExistingWarpsWhenFlagChanges;
}
/**
* @param removeExistingWarpsWhenFlagChanges the removeExistingWarpsWhenFlagChanges to set
*/
public void setRemoveExistingWarpsWhenFlagChanges(boolean removeExistingWarpsWhenFlagChanges) {
this.removeExistingWarpsWhenFlagChanges = removeExistingWarpsWhenFlagChanges;
}
}

View File

@ -15,16 +15,16 @@ import world.bentobox.warps.Warp;
* @author Poslovitch
*
*/
public class WarpCreateEvent extends Event{
public class WarpCreateEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private Location warpLoc;
private UUID creator;
private final Location warpLoc;
private final UUID creator;
/**
* @param plugin - BSkyBlock plugin objects
* @param warpLoc
* @param creator
* @param warpLoc warp location
* @param creator UUID of creator
*/
public WarpCreateEvent(Warp plugin, Location warpLoc, UUID creator){
this.warpLoc = warpLoc;

View File

@ -40,7 +40,7 @@ public class WarpInitiateEvent extends Event implements Cancellable {
/**
* Set a different location to where the player will go
* @param warpLoc
* @param warpLoc warp location
*/
public void setWarpLoc(Location warpLoc) {
this.warpLoc = warpLoc;
@ -63,7 +63,6 @@ public class WarpInitiateEvent extends Event implements Cancellable {
@Override
public boolean isCancelled() {
// TODO Auto-generated method stub
return 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,30 +18,46 @@ 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;
}
/**
* @return the owner
*/
@Nullable
protected UUID getOwner() {
return owner;
}
@Override
public HandlerList getHandlers() {

View File

@ -0,0 +1,72 @@
package world.bentobox.warps.event;
import org.bukkit.Location;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import world.bentobox.warps.objects.PlayerWarp;
import java.util.UUID;
/**
* This event is fired when a warp is toggled
* A Listener to this event can use it only to get information. e.g: broadcast something
*
* @since 1.16.0
* @author TreemanKing
*/
public class WarpToggleEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final UUID user;
private final PlayerWarp playerWarp;
public WarpToggleEvent(UUID user, PlayerWarp playerWarp) {
this.playerWarp = playerWarp;
this.user = user;
}
/**
* Gets the user who has toggled the warp
*
* @return the UUID of the player who toggled the warp
*/
public UUID getUser() {
return user;
}
/**
* Gets the state of the warp
*
* @return true if the warp is enabled, false otherwise
*/
public boolean isEnabled() {
return playerWarp.isEnabled();
}
/**
* Gets the PlayerWarp object
*
* @return the PlayerWarp object
*/
public PlayerWarp getPlayerWarp() {
return playerWarp;
}
/**
* Gets the location of the toggled warp
*
* @return the location of the warp
*/
public Location getLocation() {
return playerWarp.getLocation();
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,303 @@
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.objects.PlayerWarp;
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, PlayerWarp>> iterator =
addon.getWarpSignsManager().getWarpMap(event.getWorld()).entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, PlayerWarp> entry = iterator.next();
UUID uuid = entry.getKey();
Location location = entry.getValue().getLocation();
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, PlayerWarp> 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()).getLocation().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()).values().stream().anyMatch(playerWarp -> playerWarp.getLocation().equals(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;
}
// TODO: These checks are useless if the sign is placed outside a BSB world.
// This will mean level and rank requirements are nil in the case of allow-in-other-worlds: true.
// I'm not sure if there is a better way around this without adding new API checking for primary
// or last island accessed with relevant permissions.
// ignored.
if (inWorld && noLevelOrIsland(user, b.getWorld())) {
e.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
return;
}
if (inWorld && !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().getWarpLocation(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, PlayerWarp> islandWarps = addon
.getWarpSignsManager()
.getWarpMap(island.getWorld())
.entrySet()
.stream()
.filter(x -> island.inIslandSpace(x.getValue().getLocation()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
for(Map.Entry<UUID, PlayerWarp> 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

@ -0,0 +1,507 @@
package world.bentobox.warps.managers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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;
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.objects.PlayerWarp;
import world.bentobox.warps.Warp;
import world.bentobox.warps.event.WarpCreateEvent;
import world.bentobox.warps.event.WarpInitiateEvent;
import world.bentobox.warps.objects.WarpsData;
import world.bentobox.warps.panels.Utils;
/**
* Handles warping. Players can add one sign
*
* @author tastybento
*
*/
public class WarpSignsManager {
private static final int MAX_WARPS = 600;
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, PlayerWarp>> worldsWarpList;
// Database handler for level data
private final Database<WarpsData> handler;
private final Warp addon;
private WarpsData warpsData = new WarpsData();
/**
* Get the warp map for this world
* @param world - world
* @return map of warps
*/
@NonNull
public Map<UUID, PlayerWarp> getWarpMap(@Nullable World world) {
return worldsWarpList.computeIfAbsent(Util.getWorld(world), k -> new HashMap<>());
}
/**
* @param addon - addon
* @param plugin - plugin
*/
public WarpSignsManager(Warp addon, BentoBox plugin) {
this.addon = addon;
this.plugin = plugin;
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();
}
/**
* Stores warps in the warp array. If successful, fires an event
*
* @param playerUUID - the player's UUID
* @param loc - location of warp sign
* @return true if successful, false if not
*/
public boolean addWarp(final UUID playerUUID, final Location 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
for (PlayerWarp playerWarp : getWarpMap(loc.getWorld()).values()) {
if (playerWarp.getLocation().equals(loc)) {
this.removeWarp(loc);
break;
}
}
getWarpMap(loc.getWorld()).put(playerUUID, new PlayerWarp(loc, true));
saveWarpList();
Bukkit.getPluginManager().callEvent(new WarpCreateEvent(addon, loc, playerUUID));
return true;
}
/**
* Provides the location of the warp for player or null if one is not found
*
* @param world - world to search in
* @param playerUUID - the player's UUID
* - the warp requested
* @return Location of warp or null
*/
@Nullable
public Location getWarpLocation(World world, UUID playerUUID) {
PlayerWarp playerWarp = getWarpMap(world).get(playerUUID);
return playerWarp != null ? playerWarp.getLocation() : null;
}
@Nullable
public PlayerWarp getPlayerWarp(World world, UUID playerUUID) {
return getWarpMap(world).get(playerUUID);
}
/**
* Get the name of the warp owner by location
* @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().getLocation().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().getLocation().equals(location))
.findFirst().map(Map.Entry::getKey);
}
/**
* Get sorted list of warps with most recent players listed first
* @return UUID list
*/
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<>();
getWarpMap(world).forEach((uuid, value) -> {
// If the warp is not enabled, skip this iteration
if (!value.isEnabled()) {
return;
}
// 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
if (!map.isEmpty() && map.containsKey(lastPlayed)) {
lastPlayed = map.firstKey() - 1;
}
map.put(lastPlayed, uuid);
});
Collection<UUID> result = map.descendingMap().values();
List<UUID> list = new ArrayList<>(result);
if (list.size() > MAX_WARPS) {
list.subList(0, MAX_WARPS).clear();
}
// Return to main thread
Bukkit.getScheduler().runTask(plugin, () -> r.complete(list));
return list;
}
/**
* Lists all the known warps for this world
* @param world - world
*
* @return UUID set of warps
*/
@NonNull
public Set<UUID> listWarps(@NonNull World world) {
// Remove any null locations
getWarpMap(world).values().removeIf(Objects::isNull);
// Remove any warps that have not been toggled on
Map<UUID, PlayerWarp> enabledWarps = getWarpMap(world).entrySet().stream()
.filter(entry -> entry.getValue().isEnabled())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return enabledWarps.keySet();
}
/**
* Load the warps and check if they still exist
*/
public void loadWarpList() {
addon.log("Loading warps...");
worldsWarpList = new HashMap<>();
if (handler.objectExists(WARPS)) {
warpsData = handler.loadObject(WARPS);
// Load into map
if (warpsData != null) {
warpsData.getWarpSigns().forEach((pw, uuid) -> {
Location location = pw.getLocation();
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(location.getWorld()).put(uuid, new PlayerWarp(location, true));
}
});
} else {
warpsData = new WarpsData();
}
}
}
/**
* Changes the sign to red if it exists
* @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.getLine(0).equalsIgnoreCase(ChatColor.GREEN + addon.getSettings().getWelcomeLine())) {
s.setLine(0, ChatColor.RED + addon.getSettings().getWelcomeLine());
s.update(true, false);
}
}
}
/**
* Removes a warp at a location.
*
* @param loc location to remove
*/
public void removeWarp(Location loc) {
popSign(loc);
Iterator<Entry<UUID, PlayerWarp>> it = getWarpMap(loc.getWorld()).entrySet().iterator();
while (it.hasNext()) {
Entry<UUID, PlayerWarp> en = it.next();
if (en.getValue().getLocation().equals(loc)) {
// Inform player
Optional.ofNullable(addon.getServer().getPlayer(en.getKey()))
.map(User::getInstance)
.ifPresent(user -> user.sendMessage("warps.sign-removed"));
// Remove sign from warp panel cache
addon.getSignCacheManager().removeWarp(loc.getWorld(), en.getKey());
it.remove();
}
}
saveWarpList();
}
/**
* Remove warp sign owned by 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).getLocation());
getWarpMap(world).remove(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.saveObjectAsync(warpsData.save(worldsWarpList));
addon.getSignCacheManager().saveCache();
}
/**
* Gets the warp sign text and material type for player's UUID in world
*
* @param world - world to look in
* @param uuid - player's uuid
* @return Sign's content and type
*/
@NonNull
public SignCacheItem getSignInfo(@NonNull World world, @NonNull UUID uuid) {
//get the sign info
Location signLocation = getWarpLocation(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);
}
}
/**
* Warps a player to a spot in front of a sign.
* @param user - user who is warping
* @param inFront - location in front of sign - previously checked for safety
* @param signOwner - warp sign owner
* @param directionFacing - direction that sign is facing
* @param pvp - true if this location allowed PVP
*/
private void warpPlayer(@NonNull User user, @NonNull Location inFront, @NonNull UUID signOwner, @NonNull BlockFace directionFacing, boolean pvp) {
// convert blockface to angle
float yaw = Util.blockFaceToFloat(directionFacing);
final Location actualWarp = new Location(inFront.getWorld(), inFront.getBlockX() + 0.5D, inFront.getBlockY(),
inFront.getBlockZ() + 0.5D, yaw, 30F);
WarpInitiateEvent e = new WarpInitiateEvent(addon, actualWarp, user.getUniqueId());
Bukkit.getPluginManager().callEvent(e);
if (e.isCancelled()) {
return;
}
//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);
}
});
}
/**
* 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(@NonNull World world, @NonNull User user, @NonNull UUID owner) {
final Location warpSpot = getWarpLocation(world, owner);
// Check if the warp spot is safe
if (warpSpot == null) {
user.sendMessage("warps.error.does-not-exist");
addon.getWarpSignsManager().removeWarp(world, owner);
return;
}
if (this.plugin.getIWM().inWorld(user.getWorld()) &&
Flags.PREVENT_TELEPORT_WHEN_FALLING.isSetForWorld(user.getWorld()) &&
user.getPlayer().getFallDistance() > 0) {
// We're sending the "hint" to the player to tell them they cannot teleport while falling.
user.sendMessage(Flags.PREVENT_TELEPORT_WHEN_FALLING.getHintReference());
return;
}
Island island = addon.getIslands().getIsland(world, owner);
boolean pvp = false;
if (island != null) {
// Check for PVP
switch (Objects.requireNonNull(warpSpot.getWorld()).getEnvironment()) {
case NETHER:
pvp = island.isAllowed(Flags.PVP_NETHER);
break;
case NORMAL:
pvp = island.isAllowed(Flags.PVP_OVERWORLD);
break;
case THE_END:
pvp = island.isAllowed(Flags.PVP_END);
break;
default:
break;
}
}
// Find out which direction the warp is facing
Block b = warpSpot.getBlock();
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))) {
warpPlayer(user, inFront, owner, directionFacing, pvp);
return;
} else if (plugin.getIslands().isSafeLocation(oneDown)) {
// Try one block down if this is a wall sign
warpPlayer(user, oneDown, owner, directionFacing, pvp);
return;
}
} 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 ((addon.getIslands().isSafeLocation(inFront))) {
warpPlayer(user, inFront, owner, directionFacing, pvp);
return;
}
} else {
// Warp has been removed
user.sendMessage("warps.error.does-not-exist");
removeWarp(warpSpot);
return;
}
if (!(plugin.getIslands().isSafeLocation(warpSpot))) {
user.sendMessage("warps.error.not-safe");
} 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.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);
}
Util.teleportAsync(user.getPlayer(), actualWarp, TeleportCause.COMMAND);
}
}
/**
* Check if a player has a warp
* @param playerUUID - player's UUID
* @return true if they have warp
*/
public boolean hasWarp(@NonNull World world, @NonNull UUID playerUUID) {
return getWarpMap(world).containsKey(playerUUID);
}
}

View File

@ -0,0 +1,32 @@
package world.bentobox.warps.objects;
import com.google.gson.annotations.Expose;
import org.bukkit.Location;
import java.io.Serializable;
public class PlayerWarp implements Serializable {
@Expose
private final Location location;
@Expose
private boolean isEnabled;
public PlayerWarp(Location location, boolean isEnabled) {
this.location = location;
this.isEnabled = isEnabled;
}
public Location getLocation() {
return location;
}
public boolean isEnabled() {
return isEnabled;
}
public void toggle() {
isEnabled = !isEnabled;
}
}

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,23 @@ 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
@Deprecated @Expose
private Map<Location, UUID> warpSigns = new HashMap<>();
public WarpsData() {}
@Expose
private Map<PlayerWarp, UUID> newWarpSigns = new HashMap<>();
public WarpsData() {
// Required by YAML database
}
@Override
public String getUniqueId() {
@ -30,23 +38,40 @@ public class WarpsData implements DataObject {
this.uniqueId = uniqueId;
}
public Map<Location, UUID> getWarpSigns() {
if (warpSigns == null)
public Map<PlayerWarp, UUID> getWarpSigns() {
convertOldWarpSigns();
if (newWarpSigns == null)
return new HashMap<>();
return warpSigns;
}
public void setWarpSigns(Map<Location, UUID> warpSigns) {
this.warpSigns = warpSigns;
return newWarpSigns;
}
/**
* Puts all the data from the map into this objects ready for saving
* @param worldsWarpList
* Method for converting old warp signs to new warp signs
*/
public void convertOldWarpSigns() {
if (warpSigns == null) {
return;
}
for (Map.Entry<Location, UUID> entry : warpSigns.entrySet()) {
PlayerWarp playerWarp = new PlayerWarp(entry.getKey(), true);
newWarpSigns.put(playerWarp, entry.getValue());
}
warpSigns = null;
}
public void setWarpSigns(Map<PlayerWarp, UUID> warpSigns) {
this.newWarpSigns = warpSigns;
}
/**
* Puts all the data from the map into these objects ready for saving
* @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) {
worldsWarpList.values().forEach(world -> world.forEach((uuid,location) -> warpSigns.put(location, uuid)));
public WarpsData save(Map<World, Map<UUID, PlayerWarp>> worldsWarpList) {
getWarpSigns().clear();
worldsWarpList.values().forEach(world -> world.forEach((uuid,playerWarp) -> newWarpSigns.put(playerWarp, 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)).
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

@ -2,36 +2,22 @@ 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
'[gamemode].island.togglewarp':
description: Player can toggle a warp sign
default: true

View File

@ -8,18 +8,29 @@
# 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. 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.
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: []
#
# 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
togglewarp-command: togglewarp

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,25 +8,70 @@ 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!"
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."
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"
togglewarp:
help:
description: "toggle the warp sign"
enabled: "&a Your warp is now visible!"
disabled: "&c Your warp is now hidden!"
error:
no-permission: "&c You do not have permission to do that!"
generic: "&c An error occurred while toggling your warp."
no-warp: "&c You do not have a warp to toggle!"
protection:
flags:
PLACE_WARP:
name: Place Warp
description: Allow placing warp sign

View File

@ -1,30 +1,53 @@
###########################################################################################################
# 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>
description: Teletransportarte hacia el warp del jugador
parameters: "<player name>"
warps:
deactivate: "&cViejo cartel a sido desactivado!"
deactivate: "&c ¡Antiguo cartel de teletransportación desactivado!"
error:
does-not-exist: "&cOh snap! Ese warp ya no existe!"
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]"
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"
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]"
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,30 +1,53 @@
###########################################################################################
# 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>
description: te téléporte au Warp d'un autre joueur
parameters: "<pseudo>"
warps:
deactivate: "&cAncien panneau de Warp désactivé !"
deactivate: "&c Ancienne pancarte de warp désactivée !"
error:
does-not-exist: "&cCe Warp n'existe plus !"
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-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]."
does-not-exist: "&c Oh ! Cette téléportation n'existe plus !"
no-permission: "&c Vous n'avez pas le droit de faire cela !"
no-remove: "&c Vous ne pouvez pas enlever cette pancarte !"
no-warps-yet: "&c Il n'y a pas encore de téléportation disponible"
not-enough-level: "&c Le niveau de votre île n'est pas assez élevé !"
not-on-island: "&c Vous devez être sur votre île pour faire cela !"
not-safe: "&c Cette téléportation n'est pas sûre !"
your-level-is: "&c Le niveau de votre île n'est que [level] et doit être supérieur
à [required]. Exécutez la commande level."
not-correct-rank: "&c Vous n'avez pas le grade adéquat pour poser une chaîne !"
help:
description: "Ouvre le menu des Warps"
next: "&6Page suivante"
player-warped: "&2[name] s'est téléporté sur votre île !"
previous: "&6Page précédente"
sign-removed: "&cPanneau de Warp supprimé !"
success: "&aSuccès !"
title: "Panneau Warp"
warpTip: "&6Placez un panneau et écrivez [text] sur la première ligne."
warpToPlayersSign: "&6Téléportation sur l'île de [player]..."
description: Ouvre le menu des Warps
player-warped: "&2 [name] s'est rendu à votre pancarte de téléportation [gamemode]
!"
sign-removed: "&c Panneau de téléportation enlevé !"
success: "&a Succès !"
warpTip: "&6 Placer une pancarte de téléportation avec [text] sur le dessus"
warpToPlayersSign: "&6 Téléportation a la pancarte de [player]"
gui:
titles:
warp-title: "&0&l Pancarte de téléportation"
buttons:
previous:
name: "&f&l Page précédente"
description: "&7 Aller à la page [numéro]."
next:
name: "&f&l Page suivante"
description: "&7 Passer à la page [numéro]."
warp:
name: "&f&l [name]"
description: "[sign_text]"
random:
name: "&f&l Téléportation aléatoire"
description: "&7 Hmm, où vais-je apparaître ?"
tips:
click-to-previous: "&e Cliquez sur &7 pour afficher la page précédente."
click-to-next: "&e Cliquez sur &7 pour afficher la page suivante."
click-to-warp: "&e Cliquez sur &7 pour te téléporter."
conversations:
prefix: "&l&6 [BentoBox]: &r"
protection:
flags:
PLACE_WARP:
name: Place téléportation
description: Autoriser le placement d'un panneau téléportation

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

@ -4,24 +4,25 @@ warp:
description: pārvietoties pie spēlētāja uzaicinājuma zīmes
parameters: "<spēlētāja vārds>"
warps:
deactivate: "&cIepriekšējā uzaicinājumu zīme ir deaktivizēta!"
error:
does-not-exist: "&cAk vai! Uzaicinājuma zīme vairāk neeksistē!"
no-permission: "&CTev nav nepieciešamās atļaujas, lai veiktu darbību!"
no-remove: "&CTu nevari noņemt šo zīmi!"
no-warps-yet: "&CNav neviena aktīva uzaicinājuma zīme."
not-enough-level: "&CTavas salas līmenis ir pārāk zems!"
not-on-island: "&CTev ir jāatrodas uz savas salas, lai veiktu šo darbību!"
not-safe: "&cŠī uzaicinājuma zīme nav droša!"
your-level-is: "&cTavas salas līmenis ir tikai [level], bet ir nepieciešams vismaz
[required]. Vari mēģināt pārrēķināt savas salas līmeni."
help:
description: atver uzaicinājumu zīmju sarakstu
next: "&6Nākošā lapa"
player-warped: "&2[name] pārvietotojās pie tavas uzaicinājuma zīmes!"
previous: "&6Iepriekšējā lapa"
sign-removed: "&CUzaicinājuma zīme noņemta!"
success: "&AVeiksmīgi!"
title: Uzaicinājuma Zīmes
warpTip: "&6Novieto zīmi ar tekstu [text] pirmajā rindā"
warpToPlayersSign: "&6Pārvietojās uz [player] 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

@ -1,16 +1,12 @@
###########################################################################################
# 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: "Oyuncunun bölge tabelası."
parameters: <name>
description: Oyuncunun bölge tabelası.
parameters: "<name>"
warps:
deactivate: "&cEski ada warpı silindi!"
error:
does-not-exist: "&4Malasef sorun oluştu. Tekrar dene."
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."
@ -19,13 +15,12 @@ warps:
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."
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ı!"
success: "&9Başarılı!"
title: "&3&lSon&b&lCesurlar &e&lBölgeler &5"
warpTip: "&9Ada warpı açmak için tabelanın en üstüne &e[text] &9yaz!"
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,32 +1,60 @@
###########################################################################################
# 本文件是 YML 格式的文件. 编辑时请务必小心. #
# 请在 http://yaml-online-parser.appspot.com 等 YAML 检查器中检查您的编辑. #
###########################################################################################
# ##########################################################################################
# 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: 传送到该玩家的传送木牌处
parameters: <玩家名称>
description: 传送至对应玩家的坐标告示牌
parameters: <player name>
warps:
deactivate: "&c禁用的旧转移标志!"
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]"
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]. 使用/is level查看等级.'
not-correct-rank: '&c你在团队中的地位不足以让你设立传送点!'
help:
description: 打开传送面板
next: "&6下一页"
player-warped: "&2[name]转移到你的标志!"
previous: "&6上一页"
sign-removed: "&C传送木牌已移除!"
success: "&A成功!"
title: 传送木牌
warpTip: "&6放置一个第一行是 [text] 的木牌以创建传送木牌"
warpToPlayersSign: "&6正传送到 [player] 的传送木牌"
description: 打开传送点面板
player-warped: '&2 [name]传送到了你[gamemode]的坐标告示牌!'
sign-removed: '&c坐标告示牌已移除!'
success: '&a成功!'
warpTip: '&6放置一个顶部为[text]的坐标告示牌'
warpToPlayersSign: '&6正在传送至[player]的坐标告示牌'
gui:
titles:
# The title of warp panel
warp-title: '&0&l坐标告示牌'
buttons:
# Button that is used in multi-page GUIs which allows to return to previous page.
previous:
name: '&f&l上一页'
description: '&7跳转到第[number]页' # Button that is used in multi-page GUIs which allows to go to next page.
next:
name: '&f&l下一页'
description: '&7跳转到第[number]页' # Button for a warp
warp:
name: '&f&l [name]'
description: '[sign_text]' # Button for a random warp
random:
name: '&f&l随机传送'
description: '&7嗯...我会出现在哪里?'
tips:
click-to-previous: '&e点击&7 查看上一页.'
click-to-next: '&e点击&7 查看下一页.'
click-to-warp: '&e点击&7 进行传送.'
conversations:
# Prefix for messages that are send from server.
prefix: '&l&6 [BentoBox]: &r'
protection:
flags:
PLACE_WARP:
name: 放置传送点
description: 允许放置坐标告示牌

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,524 @@
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.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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.ArgumentCaptor;
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.WarpCreateEvent;
import world.bentobox.warps.event.WarpInitiateEvent;
import world.bentobox.warps.managers.SignCacheManager;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.objects.PlayerWarp;
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<PlayerWarp, UUID> warpMap = Collections.singletonMap(new PlayerWarp(location, true), 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() {
PlayerWarp playerWarp = new PlayerWarp(null, true);
Map<PlayerWarp, UUID> warpMap = Collections.singletonMap(playerWarp, 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(WarpCreateEvent.class));
}
/**
* Test method for {@link WarpSignsManager#getWarpLocation(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetWarpWorldWorld() {
assertNull(wsm.getWarpLocation(mock(World.class), uuid));
}
/**
* Test method for {@link WarpSignsManager#getWarpLocation(org.bukkit.World, java.util.UUID)}.
*/
@Test
public void testGetWarp() {
assertEquals(location, wsm.getWarpLocation(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());
verify(pim).callEvent(any(WarpInitiateEvent.class));
}
/**
* Test method for {@link WarpSignsManager#warpPlayer(org.bukkit.World, world.bentobox.bentobox.api.user.User, java.util.UUID)}.
*/
@Test
public void testWarpPlayerEventCancelled() {
// Capture the event passed to callEvent
ArgumentCaptor<WarpInitiateEvent> eventCaptor = ArgumentCaptor.forClass(WarpInitiateEvent.class);
// Simulate the event being called and cancelled
doAnswer(invocation -> {
WarpInitiateEvent event = (WarpInitiateEvent) invocation.getArgument(0);
event.setCancelled(true);
return null;
}).when(pim).callEvent(eventCaptor.capture());
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, never());
Util.teleportAsync(eq(p), any(), eq(TeleportCause.COMMAND));
verify(player, never()).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,24 @@ 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.objects.PlayerWarp;
import world.bentobox.warps.Warp;
import world.bentobox.warps.managers.WarpSignsManager;
import world.bentobox.warps.config.Settings;
/**
@ -71,7 +75,6 @@ public class WarpSignsListenerTest {
@Mock
private WarpSignsManager wsm;
private PluginManager pm;
private UUID uuid;
private String[] lines;
@Mock
private FileConfiguration config;
@ -79,9 +82,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,22 +117,23 @@ 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);
Map<UUID, Location> list = new HashMap<>();
Map<UUID, PlayerWarp> list = new HashMap<>();
Location location = mock(Location.class);
when(location.getBlock()).thenReturn(block);
when(s.getLocation()).thenReturn(location);
list.put(uuid, location);
when(block.getLocation()).thenReturn(location);
list.put(uuid, new PlayerWarp(location, true));
// 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);
when(wsm.getWarpLocation(any(), any())).thenReturn(location);
// Unique spot
when(wsm.addWarp(any(), any())).thenReturn(true);
// Bentobox
@ -133,12 +141,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 +151,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 +161,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 +200,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 +216,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 +229,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 +283,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, PlayerWarp> warps = Map.of(
player.getUniqueId(), new PlayerWarp(block.getLocation(), true)
);
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
@ -336,7 +421,7 @@ public class WarpSignsListenerTest {
@Test
public void testCreateNoSignAlreadyUniqueSpot() {
when(wsm.getWarp(any(), any())).thenReturn(null);
when(wsm.getWarpLocation(any(), any())).thenReturn(null);
when(player.hasPermission(anyString())).thenReturn(true);
WarpSignsListener wsl = new WarpSignsListener(addon);
SignChangeEvent e = new SignChangeEvent(block, player, lines);