Compare commits

...
822 Commits
Author SHA1 Message Date
JRoy 3d28d82709 Release 2.21.1 2025-05-25 20:24:18 +01:00
MD 3a5c7c44b8 Revert changes to /home and /sethome that break bed interactions (#6151)
This reverts PR #5991 (commit 3fe1495).
2025-05-25 18:05:54 +00:00
Josh Roy aebe851bff Fix double messages when deleting a non-existent home (#6150)
Fixes #6144
2025-05-25 08:12:02 +01:00
Hunh0wandJosh Roy 3fe1495fdb Fix unexpected behavior with essentials.home.bed & essentials.sethome.bed permissions (#5991)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-05-24 20:01:58 -07:00
Flask BotandMD 3b0c229e78 New Crowdin updates (#6131)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-05-18 08:42:47 +00:00
JasonandMD d137031a0a Improve config comments & ordering (#6108)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

This PR significantly overhauls the comments of the EssentialsX configuration files, ensuring consistency, clarity, and overall improved grammar (validated by Grammarly and LLMs). There are still a few things that must be completed before this is ready to be merged, but no further significant changes are expected. A couple of options have been moved entirely to make more logical sense, but this will not affect any compatibility.
2025-05-18 08:36:04 +00:00
MD a58db237a4 Update English source strings with fixes from Crowdin (#6140)
Migrate the few typo fixes from Crowdin's English US translation into the source messages.properties file (which is also English US).
2025-05-18 09:28:16 +01:00
_luftwaffleandMD fe430174a1 Fix "heer" typo in messages_en.properties (#6135)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-04-29 20:11:11 +00:00
Nassim Jahnke 3f5b12294f Use legacy text for TranslatableException#getMessage (#6134)
Fixes an issue for API consumers getting Adventure in our exception messages.
2025-04-27 19:13:35 +00:00
Flask BotandMD be3e641c62 New Crowdin updates (#6089)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-04-19 18:31:22 +00:00
Josh Roy 84552120c1 Fix /spawnmob on < 1.21.5 (#6121)
Fixes #6120
2025-04-05 07:59:29 +01:00
Josh Roy 2bb44386c7 Fix further trade sign validation mismatches (#6116)
can never win with this

fixes #6107
2025-03-31 17:43:27 +00:00
Josh Roy 06c886ca69 Fix /baltop on 1.15.2 and below (#6115)
Fixes #6093
2025-03-30 22:23:45 -04:00
Josh Roy f3a1b2b495 Fix hex colors not working in chat (#6114)
Need to explicitly enable them in our LegacyComponentSerializer.
2025-03-30 20:02:34 -04:00
MD c7ff994d18 Fire GlobalChatEvent even when local chat is disabled (#6113)
This allows EssentialsX Discord to send EssentialsX Chat messages to
Discord when `use-essentials-events: true` is set in the Discord config
while local chat is disabled.

It also allows plugins to hook and modify EssentialsX Chat message
contents, formatting and recipients when local chat is disabled.
2025-03-30 18:04:56 -04:00
Josh Roy f09541cfa6 Update to Minecraft 1.21.5 (#6109) 2025-03-29 22:49:13 +00:00
Josh Roy 0cb387fb05 Fix NPE when unlinking offline players (#6111)
Fixes #6105
2025-03-29 15:43:28 -04:00
Rasmus dabe68701d Add SignTransactionEvent for buy and sell signs (#6071) 2025-03-24 19:51:11 -04:00
William B. 50b696bc07 Adds social spy message type for in EssentialsDiscord (#5620) 2025-03-24 13:54:21 -04:00
Angelillo15 cb6187bbf2 Add option to customize nickname regex filter (#5200) 2025-03-24 16:43:53 +00:00
maxcom1 40540ebb3d Expose IUser#getLastActivityTime to API (#6101) 2025-03-24 16:29:06 +00:00
Josh Roy 46ba679c02 Prepare for 2.21.1 dev builds 2025-03-22 21:15:51 +00:00
Josh Roy 2719a189af Release 2.21.0 2025-03-22 20:52:40 +00:00
Olivia b4a97ab2a9 chore: add issue types to new issues (#6094)
Adds a new [issue
type](https://docs.github.com/en/issues/tracking-your-work-with-issues/configuring-issues/managing-issue-types-in-an-organization)
to new issues. Basically built in labels.
2025-03-19 07:43:07 +00:00
Flask BotandJosh Roy eab220183e New Crowdin updates (#6068)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-03-13 14:26:17 +00:00
Josh Roy 43d1e37b8f Fix flight ability preservation between worlds (#6079)
Fixes #6041
2025-03-09 16:13:50 +00:00
Josh Roy 80bdc5269f Fix wrong SpawnerBlockProvider on 1.21+
1.21 Adds a new org.bukkit.spawner.Spawner class as a superclass of CreatureSpawner which now contains the setMaxSpawnDelay method. Change our test to look for the method on any superclasses too.
2025-03-09 10:14:00 +00:00
Josh Roy 90d766df2b Add non-reflection provider for syncCommands
On 1.21.4+ Player#updateCommands has the same logic as CraftServer#updateCommands, so we can just use that.
2025-03-09 10:11:35 +00:00
Josh Roy 5bf158c1d4 Fix negative playtime stat breaking /baltop (#6074)
Unclear how this happens, possibly a division error?

Fixes #6043
2025-03-01 13:59:46 -05:00
Josh Roy 1531cf4146 Fix exception sending a chat with discord module (#6075)
We do not want or need to set the renderer, EssXChat or the server will do that.
2025-03-01 09:16:56 -05:00
Josh Roy b5748eb32f Update various dependencies (#6078)
Mainly Configurate, JDA, and Adventure
2025-03-01 07:21:48 -05:00
JRoy e49021fffe Fix item overflow issue with kits & trade signs 2025-02-27 22:01:43 -05:00
JRoy ba5257c8e6 Fix translation error in EssentialsSign#getItemStack 2025-02-27 22:01:43 -05:00
Flask BotandJosh Roy fe27182ba8 New Crowdin updates (#6037)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-02-24 01:58:14 +00:00
Josh Roy 5458241fcd Fix expired mails showing as unread on join (#6063) 2025-02-23 22:10:05 +00:00
Josh Roy bad79b79ac Fix /book command on 1.20+ (#6064) 2025-02-23 22:06:26 +00:00
Josh Roy 1ff24cb634 Use an identity map for Paper chat listener (#6067) 2025-02-23 17:02:31 -05:00
JRoy f5efa26300 Fix per-world flight permissions
With the implementation of #6012, there was no permission check done when we re-add flight to user's changing worlds. This broke servers with per-world flight permissions.
2025-02-23 08:42:10 +00:00
Théo DufreneandJosh Roy 263dab54d0 Fix spawn command sending message before teleportation (#6025)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-02-21 22:21:28 -05:00
Hernán Indíbil de la Cruz CalvoandJRoy e3e247dd61 Add discord module support for Paper's chat events (#5908)
Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-02-21 21:00:24 -05:00
Josh Roy 5a839c47bc Fix modifying messages with Paper chat events
Previously, our wrapper for Paper chat events did not account for other plugins modifying the message component during the event lifecycle, this will translate calls between (get/set)Message to and from legacy strings. This further add a barrier hover/click events for now but those never worked with EssXChat installed anyway and will require further refactoring at a later date in order to add support.
2025-02-20 16:41:16 -05:00
Josh Roy 3025ba5c05 Fix NPE when chat messages are canceled 2025-02-20 16:41:16 -05:00
Josh Roy 8313d522dc Add config for use of Paper chat events (#6050)
adds a config option to disable the use of paper chat events while other
plugins learn how to join the 21st century.

re #6048
re #6051
2025-02-19 21:00:46 +00:00
Josh Roy 196a2a19c4 Fix startup issue with potion providers (#6055)
The LegacyPotionMetaProvider was initializing a map with enums that
don't exist on modern versions. Since this was a static variable, this
map got initialized when the test method was called, leading to a
NoSuchFieldError.

Also improved resilience of these provider-type issues again by catching
a Throwable rather than an Exception in the ProviderFactory.

Fixes #6054
2025-02-19 15:58:00 -05:00
MD 69ed07a536 Fix the wrong potion meta provider being used on 1.8.8 (#6047) 2025-02-15 11:30:13 -05:00
Josh Roy cb00783ede Add support for Paper's chat events (#6033)
Fixes #4970
Fixes #5887
2025-02-14 15:14:44 -05:00
Josh Roy 4e6478224c Fix loot tables getting removed with essentials.keepinv (#6036) 2025-02-09 10:24:13 -05:00
Flask BotandJosh Roy fb6edc04c6 New Crowdin updates (#6024)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-02-06 14:50:21 +00:00
Josh Roy d232ce4037 Parse MiniMessage in default command usage & descriptions (#6018) 2025-02-05 08:14:09 -05:00
Josh RoyandMD 1a4d75ced7 [AntiBuild] Fix ender crystal breaking on 1.8 (#6009)
Ugly fix but it works for now...

Fixes #4418

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-02-02 14:14:21 +00:00
Josh Roy 42121aa4be Remove deprecated teleport APIs (#6017)
They have been no-op deprecations for a while now
2025-02-02 13:45:07 +00:00
Josh RoyandShane Freeder ea3ea202a7 Fix flight state between world teleports (#6012)
Thanks to @electronicboy for the general fix.

Fixes #4325 for Paper only :)

Co-authored-by: Shane Freeder <theboyetronic@gmail.com>
2025-01-28 14:26:39 -05:00
Flask Bot 62fb6a16d4 New Crowdin updates (#6019)
Fixes #6022.
2025-01-28 19:12:50 +00:00
jquery-packageandJosh Roy e2295c1e1f Add permission node for giving skulls to other players (#6008)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-01-24 10:40:55 -05:00
Flask BotandJosh Roy eb6e192d8d New Crowdin updates (#5640)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-01-24 10:35:42 -05:00
JRoy b293f2328a Fix adventure error when using color in local chat
We were feeding legacy text into miniToLegacy which logs an annoying message in console
2025-01-24 01:55:00 -05:00
JRoy 22de3d8bab Revert 4219450705
Hotfix is no longer needed as it has been fixed in adventure.
2025-01-24 01:55:00 -05:00
JRoy fe4e388c42 Update adventure versions 2025-01-24 01:55:00 -05:00
Josh Roy 803771e74a Switch to pastes.dev for /ess dump (#6011)
Requires merge of https://github.com/EssentialsX/Website/pull/106
2025-01-17 07:38:54 -05:00
Henning Storck 667b0f7523 Skip upgrading random teleport config when tpr.yml isn't initialized yet (#6003)
If there's no tpr.yml, upgrading the random teleport config will fail
and the plugin cannot be loaded.
2025-01-01 17:03:46 +00:00
Matt Baxter 8e53214b50 Fix paper maven repository URL (#6002) 2024-12-31 23:39:08 -05:00
Josh Roy 5515f9f3f6 Rework the provider system (#5087) 2024-12-31 19:29:23 -05:00
Charles DeLancey f2af9528b0 Add new "world" argument to settpr messages (#5990) 2024-12-09 19:41:53 +00:00
Justin M.andJosh Roy e1091d887d Added sell multipliers (#4770)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-12-08 20:08:23 -05:00
JRoy 424816ef51 Add 1.21.4 Items 2024-12-04 02:49:55 -05:00
JRoy baa8258936 Add 1.21.4 Tree Types 2024-12-04 02:49:55 -05:00
JRoy c37064ff74 Add 1.21.4 Mobs 2024-12-04 02:49:55 -05:00
JRoy d58db0dce9 Update to 1.21.4 2024-12-04 02:49:55 -05:00
Josh Roy bc8aa76690 Revert removal of InvalidWorldException (#5984)
Fixes API breakage for people who catch the exception.
2024-12-02 22:14:03 -05:00
Zarkrey d31ff5553e Remove extraneous code in command preprocess event handling 2024-11-30 23:56:54 +00:00
JRoy b560bbdec4 Fix actions permissions on PRs for junit reports 2024-11-27 17:12:47 -05:00
Flowsqy 2a41ea050a Add config options for specific chat type formats 2024-11-27 17:12:47 -05:00
Gibson PilconisandJosh Roy c7cc1b4f93 Fix respawn-at-anchor setting. (#5825)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 03:15:25 +00:00
Ayush DeotareandJosh Roy 57c9edcc0c Add optional requirements for balance top listing (#5394)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 03:06:08 +00:00
KyriakumandJRoy 3f0a412390 Add locale based comma support for /pay (#5962)
Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2024-11-25 02:51:52 +00:00
Charles DeLanceyandJRoy 9590d4cd75 Add translation key for /alts output (#5595)
Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2024-11-25 02:28:49 +00:00
Clement RaynaudandJosh Roy f02eeecee8 Add method to get all linked players in DiscordLinkService (#5896)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 02:20:54 +00:00
AjMacandJosh Roy 3203e97a5e Parse /pay modifiers more strictly (#5638)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 02:16:28 +00:00
Charles DeLanceyandJosh Roy bea43e8798 Fix mini message parsing in NumberUtil#displayCurrency (#5921)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 02:11:41 +00:00
int4_tandJosh Roy 1af1565059 Discord: Add start time placeholder for start message (#5686)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-25 01:45:40 +00:00
YurakaiiandJosh Roy 38e42f9f90 Add argument to /skull to give skull to other player (#5822)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-24 20:42:04 -05:00
2418a6fb78 Improvements to Random Teleport (#4271)
Co-authored-by: triagonal <10545540+triagonal@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-24 18:07:18 -05:00
Ahmed WaleedandJosh Roy 1778bf5ada Respect per player locale for command descriptions (#5972)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-11-24 22:24:03 +00:00
Adrián Oliva Del Río a87760d3ce Add wildcard support for /delhome (#5919) 2024-11-24 01:47:36 +00:00
Josh Roy 2ac37d8dd4 Fix repair sign translation error (#5967)
Fixes #5964
2024-11-10 22:41:29 -05:00
JRoy 0b49421bad Fix typo in build logic 2024-11-09 08:00:08 -05:00
JRoy aa2eca17ef Fix compile errors from Sound no longer being an enum 2024-11-09 08:00:08 -05:00
JRoy fe6b04ed84 Support boat variants on older versions
New versions will need to use the specific entity, as we cannot swap the entity type after it has been spawned
2024-11-09 08:00:08 -05:00
JRoy 2b209a4a2f 1.12.3 Item Changes 2024-11-09 08:00:08 -05:00
JRoy a4a09299e7 1.12.3 Mob Changes 2024-11-09 08:00:08 -05:00
JRoy 4a68758975 Update to 1.12.3 2024-11-09 08:00:08 -05:00
JRoy cb15b467ab Update toolchain 2024-11-09 08:00:08 -05:00
MD f7a8f86850 Update shadow config for Discord module (#5960)
trove4j's artifact name changed and so it was no longer being shaded in
the Discord module.

Fixes #5959.
2024-10-21 13:55:22 -04:00
Josh Roy cd06e737e1 Fix error message during warp sign creation (#5956)
Fixes #5954
2024-10-21 10:36:35 +00:00
Josh Roy a8452b4615 Update JDA (#5958) 2024-10-20 22:18:21 +00:00
Josh Roy 1bede4ae3f Fix confirmClear & confirmPayment messages (#5957) 2024-10-20 18:15:57 -04:00
Josh Roy c80bef9841 Fix recipe message not being translated correctly (#5952)
I truly don't understand how this slipped through the cracks.

Fixes #5943
2024-10-15 11:13:16 +00:00
Josh Roy 9c3e0eb312 Add wind charge to /fireball (#5951)
Closes #5949
2024-10-15 06:56:11 -04:00
MD 6157668a63 Bump PlaceholderAPI dependency to version 2.11.6 (#5936)
Versions older than 2.11.5 are currently missing from the PlaceholderAPI
Maven repo, so we need to bump this for EssentialsX to compile.
2024-09-15 09:11:26 +01:00
JRoy 04518dbbb3 Use a click component for discord link command 2024-09-14 16:35:45 -04:00
JRoy 3e29e04722 Fix kittycannon on old versions 2024-09-14 16:35:45 -04:00
JRoy 9dfa8cf3cb Fix similarWarpExist message not being translated correctly 2024-09-14 16:35:45 -04:00
Slimified b54c8c1774 Parse colors from broadcast world command (#5913)
Make the broadcast world command mimic the behavior of the broadcast
command which correctly parses colors from the broadcast message.

Broadcast World Command Output Before: https://i.imgur.com/aFR6Zzb.png

Broadcast World Command Output After: https://i.imgur.com/qrvb8XD.png
2024-08-25 21:25:14 +00:00
Charles DeLancey f1a5caf98e Fixes wording on CannotRemoveNegativeItems (#5901) 2024-08-11 16:32:41 -04:00
TriassicandJosh Roy 7ae01a03ac Update to Minecraft 1.21.1 (#5900)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-08-11 14:48:53 +00:00
Charles DeLanceyandJRoy b763fd3868 Fix removing negative items in /clearinventory (#5899)
Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2024-08-11 10:43:00 -04:00
Jason Penilla 7b02d22ac7 Fix incompatibility with AlternativeCommandsHandler & Paper API (#5881) 2024-08-01 19:59:37 -04:00
pop4959 8b08a8f2c8 Update placeholder colors for eco give (#5738) 2024-07-23 20:20:12 +00:00
Josh Roy 80c3150e44 Fix trade signs not updating signs (Fixes #5814)
i guess these things are transactional now whatever
2024-07-21 17:48:16 -04:00
JRoy 33e086ff05 Fix notEnoughMoney message not being translated correctly
Fixes #5759
2024-07-13 19:25:09 -04:00
JRoy 8971798a90 Fix build on new spigot versions
Villager.Profession was moved to a registry, only breaks at compile time.
2024-07-13 19:25:09 -04:00
Josh Roy fcf6e64732 Fix legacy compatibility (Fixes #5851) 2024-07-01 23:01:48 -04:00
JRoy 9e57dd6aef Fix NPE when using avatars in discord module (Fixes #5748) 2024-07-01 23:01:48 -04:00
JRoy b392f03ad1 Use enchantment key names for 1.13+
Enchantment#getName is deprecated and gives the wrong names for enchantments starting in 1.21
2024-06-29 11:40:40 +01:00
JRoy 64531aa862 Update items.json for 1.21 2024-06-29 11:40:40 +01:00
JRoy 1ea080effa Add support for 1.21 enchantments 2024-06-29 11:40:40 +01:00
JRoy 6624e6688c Add support for 1.21 potions 2024-06-29 11:40:40 +01:00
JRoy 022b0049b4 Add support for 1.21 Mobs 2024-06-29 11:40:40 +01:00
JRoy cecc00a3b6 Update to Minecraft 1.21 2024-06-29 11:40:40 +01:00
JRoy 29a122ea5e Update toolchain 2024-06-29 11:40:40 +01:00
Josh Roy 3a6fdd9bf3 Fix exception on 1.20.6 with Spigot (#5820)
How could I forget to update the md_5 magic letter

Fixes #5818
2024-06-02 19:34:47 -04:00
Pantera (Mad_Daniel) 434e7a0e70 Update adventure (#5819) 2024-06-02 11:42:27 -04:00
Josh Roy e7a4167111 Add support for data components 2024-05-27 15:53:50 -04:00
Josh Roy aec5859c2a Fix console error during /give command
There has been a method for this since 1.13.
Why have we been using this for so long?

Since item NBT is now dead, this method for checking
if an item is spawn-able is defunct and produces a console
error every time the give command is ran.
2024-05-27 15:53:50 -04:00
Josh Roy 5392169f72 Update items.json for 1.20.6 2024-05-27 15:53:50 -04:00
Josh Roy d4a3f5a9e6 Add support for 1.20.6 Mobs 2024-05-27 15:53:50 -04:00
Josh Roy bef1e57712 Reflect Enchantment API Changes 2024-05-27 15:53:50 -04:00
Josh Roy 4c5a97bf00 Reflect Potion API Changes 2024-05-27 15:53:50 -04:00
Josh Roy b9b3e4430e Reflect Banner API Changes 2024-05-27 15:53:50 -04:00
Josh Roy 84d3138b21 Fix tests
We really need to improve our tests, this will work for now
2024-05-27 15:53:50 -04:00
Jason Penilla 1c8060341f Opt-out of remapping on Paper 1.20.5+ (#5782) 2024-05-27 15:53:50 -04:00
Josh Roy c58aae78e7 Update to Minecraft 1.20.6 2024-05-27 15:53:50 -04:00
Charles DeLancey cde7184da3 Fix duplicated messages with /helpop (#5776) 2024-04-29 21:26:49 -04:00
DarkLaw 549283a1e5 Translate Gamemode placeholder in cantGamemode message (#5771) 2024-04-28 01:44:21 -07:00
Charles DeLancey 6a2527ccdb Fix sender not receiving helpop message (#5769) 2024-04-28 01:39:26 -07:00
Emilia Kond c60ed56190 Fix stack trace when buying from sign when inventory full (#5761) 2024-04-13 21:50:45 +00:00
pop4959andJosh Roy c85e179718 Make ChargeException translatable (#5736)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-04-08 01:22:31 +00:00
DarkLaw 71ca7ffbf6 Fix heal signs not healing up to the player's max health (#5752) 2024-03-31 22:53:53 +00:00
pop4959andJosh Roy 76a513a05c Don't run keyword replacer by default in translated broadcasts (#5739)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-03-25 19:11:18 +00:00
pop4959 8cfe680abc Fix empty translations being sent to Console (#5737) 2024-03-25 19:09:13 +00:00
pop4959 2cb0af0ca4 Strict parsing for legacy converted placeholders (#5740)
This fixes issues where arguments converted implicitly from MiniMessage
are prone to bleeding into the rest of the output.

The fix mostly works since `MINI_MESSAGE_NO_TAGS` is only used for
legacy conversion, while `miniMessageInstance` is used in all other
cases normally. If that were not the case, we would not want strict
parsing everywhere since we don't enforce this in translations anyway.

Fixes #5729
Fixes #5730
Fixes #5732
Fixes #5735

Fixes #5720
Closes #5728
2024-03-11 04:14:58 -04:00
Charles DeLancey 570eca2e21 Add text box to bug report template (#5715) 2024-03-10 21:31:40 -07:00
Charles DeLancey 3817a11d9d Convert color codes in tempban and tempbanip (#5726) 2024-03-05 14:58:57 -05:00
Catarina FreireandJosh Roy c19b20ea3b Fix banner patterns not working in kits (#5723)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-03-04 13:49:30 -05:00
Charles DeLancey a4ac720057 Fix format parsing in ban messages (#5722) 2024-03-04 13:45:06 -05:00
Charles DeLancey 993d7ede56 Fix shout/local chat not sending with certain formats (#5719) 2024-02-29 23:32:51 +00:00
622c8147a9 Add setting to use usernames over display names for social spy (#5613)
Co-authored-by: Bobcat00 <Bobcat00@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-02-26 15:25:30 -05:00
pop4959 6b521e78a6 Fix jail-online-time with offline players (#5701) 2024-02-26 14:32:44 -05:00
pop4959 4219450705 Hotfix legacy serializer to include capitalized formatting code chars (#5711) 2024-02-26 05:09:02 -08:00
pop4959 bd8c792fa4 Support custom biomes in random teleport excluded biome list (#5703) 2024-02-26 03:47:00 -08:00
MD 52a638f18f Add actions step to log JUnit test reports (#5684) 2024-02-26 01:51:31 +00:00
pop4959 1b1b511b61 Fix conversion issues between Legacy and MiniMessage (#5702)
Fixes #5652
2024-02-26 01:39:18 +00:00
pop4959 d4b72c8af6 Fix Discord /list command showing tags in output (#5698) 2024-02-25 00:06:52 -08:00
pop4959 185b4e266b Fix /broadcastworld sending in all but the intended world (#5699)
Fixes #5694

This issue was caused by the passed predicate being excludes rather than
includes.

Also addresses a separate issue where no help output was given when no
arguments were specified and the command was sent by a player.
2024-02-24 22:40:22 +00:00
pop4959 7a9a0e6f51 Fix /me from console (#5700)
Fixes #5689
2024-02-24 20:42:30 +00:00
pop4959andMD 9be27ad7ac Fix custom message colors config not working (#5705)
No linked issue; discussed on Discord with @Evidentsinger14

Also @JRoy, I refuse to believe you ever tested this feature, you're
trolling

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2024-02-24 20:39:40 +00:00
pop4959 09fa6463a7 Update GitHub Actions (#5704)
Bump actions steps to remove deprecation warnings on GitHub

https://github.com/EssentialsX/Essentials/assets/17698576/0d032078-0c36-4362-b680-5a9b30d30345
2024-02-24 20:28:07 +00:00
Charles DeLancey 1929d4110a Fix format parsing in /heal (Fixes #5690) 2024-02-20 16:57:57 -05:00
Josh Roy 4386713807 Fix messages not sending after /ess reload (Fixes #5663) 2024-02-19 19:37:03 -05:00
Josh Roy 139db29782 Fix format parsing in various discord commands 2024-02-19 17:01:44 -05:00
Josh Roy d307279b3b Fix format parsing in unlinked kick message (Fixes #5667) 2024-02-19 17:01:44 -05:00
Josh Roy 80e6f96c71 Fix format parsing in /msg from Discord (Fixes #5671) 2024-02-19 17:01:44 -05:00
Josh Roy 77d505bb4a Fix format parsing using in-game commands in console (Fixes #5670) 2024-02-19 17:01:44 -05:00
Josh Roy 14125d9c4c Fix format parsing in /spawner (Fixes #5680) 2024-02-19 17:01:44 -05:00
Josh Roy dfc5c49f56 Log individual JUnit test results 2024-02-19 02:07:58 -05:00
Josh Roy 221a6622f3 Fix format parsing in /time (Fixes #5661) 2024-02-19 02:07:58 -05:00
Josh Roy 82606a7ced Fix format parsing in local spy chat format (Fixes #5665) 2024-02-19 02:07:58 -05:00
Josh Roy 1dccfbfcee Fix format parsing in /suicide (Fixes #5662) 2024-02-19 02:07:58 -05:00
Josh Roy ac0ba8890f Fix format parsing in /unlimited 2024-02-19 02:07:58 -05:00
Josh Roy 0cd47639c2 Fix /kill and /suicide on latest Spigot/Paper 2024-02-19 02:07:58 -05:00
MD 63234d6b1d Update Gradle and plugins; don't enforce running tests on Java 8 (#5685)
Fixes CI failures when trying to run `testJava8`, now that Spigot API is
compiled for Java 17.
2024-02-18 16:14:56 +00:00
Josh Roy 8a57856c96 Remove CommonPlaceholders#displayName methods
I18n argument mutation already converts legacy color codes
to parsed MiniMessage. This was redundant and was creating
issues with other parts of the codebase.
2024-02-09 23:31:06 -05:00
Josh Roy 3d9d3a3254 Fix format parsing in /home (Fixes #5653) 2024-02-09 23:31:06 -05:00
Josh Roy 544f69aec8 Fix format parsing in /kit (Fixes #5651) 2024-02-09 23:31:06 -05:00
Josh Roy 596ba6e2f5 Fix format parsing in /ptime (Fixes #5655) 2024-02-09 23:31:06 -05:00
Josh Roy 1e040fa7b5 Fix format parsing in /me (Fixes #5654) 2024-02-09 23:31:06 -05:00
Josh Roy 51075adec6 Fix format parsing in /near (Fixes #5641) 2024-02-09 23:31:06 -05:00
Josh Roy 4134b5fb99 Fix format parsing for command usage strings 2024-02-09 23:31:06 -05:00
Josh Roy d4ecfd2263 Fix component appending in social spy (#5650)
fixes #5649

this time it wasn't me!
2024-02-05 18:15:13 +00:00
MDandJosh Roy 0d1462a021 Fix message display issues from Adventure refactor (#5648)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-02-04 20:31:58 -05:00
Josh Roy 16e297269d Fix several regressions from adventure PR (#5637)
* Fixes /list
* Fixes /baltop
* Fixes Social Spy
* Fixes Essentials Signs
2024-02-04 03:35:37 +00:00
Pantera (Mad_Daniel) 3b61b3ed31 Update adventure-platform-bukkit (Fix 1.20.4) (#5636) 2024-02-04 02:59:49 +00:00
Josh Roy 23093b68d7 Fix placeholders not parsing inside log messages (#5635) 2024-02-03 21:13:18 -05:00
MDandJosh Roy 746627c4b5 Use sendTl to ensure message format is parsed (#5632)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2024-02-04 01:47:20 +00:00
TiagoFar78 04c01655f6 Fix give command for tipped arrow item (#5627) 2024-02-03 17:12:35 -08:00
388d571da9 We're going on an Adventure! (#4717)
https://user-images.githubusercontent.com/10731363/147530817-f9adc58e-18a5-49ed-84c6-106e51d6948f.mp4
Add support for chat components throughout EssentialsX using the Adventure library.  
Translations have been converted to the MiniMessage format, and custom message files will be migrated on startup.  
This also introduces new options to allow players to see messages in their own language and for server owners to change the main message colours without editing message files.

Closes #2029
Closes #2391

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: pop4959 <pop4959@gmail.com>
2024-02-03 20:38:14 +00:00
Flask Bot 8234dedb22 New Crowdin updates (#5629) 2024-02-03 16:12:09 +00:00
Flask Bot 81a8731330 New Crowdin updates (#5511) 2024-02-03 15:10:46 +00:00
Charles DeLancey fbfd7e9871 Fix msgToggleCommandUsage1Description (#5614) 2024-01-13 17:16:21 -08:00
Charles DeLanceyandMD 0af4436c2f Update supported versions (#5600)
Remove 1.19.1
Update 1.19.2 -> 1.19.4

closes: #5599

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-12-16 17:35:57 +00:00
Josh Roy 84e9051f46 Update to Minecraft 1.20.4 (#5592) 2023-12-16 15:14:37 +00:00
Josh Royandoop778 bb7b334ace Use WeakReference to store alternative commands (#5572)
Co-authored-by: oop778 <oskardhavel@gmail.com>
2023-11-27 21:29:20 +00:00
Josh Roy c68b277782 Fix trade signs not updating with full inventories (#5574)
Starting with 1.19, sign block states are no longer persistent causing them to become dead after any call to Sign#update. 
To avoid this, ensure we either always get the latest block state or update the block state after any call to Sign#update.
2023-11-10 21:23:10 -05:00
Josh RoyandMD 50c4d04b02 Prevent possible NPE during legacy file conversion (#5551)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-11-10 19:27:06 -05:00
Marcel KwiatkowskiandMD 3eceec87d3 Fix incorrect online count in custom quit message (#5541)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-10-28 22:29:11 +00:00
Josh Roy fdf1875e7f Fix incorrect permission node in /potion (#5552)
Fixes #5549.
2023-10-24 21:24:06 +00:00
Josh Roy 79449ef663 Update to Minecraft 1.20.2 (#5522) 2023-09-22 18:38:57 -04:00
Flask Bot b900444ff0 New Crowdin updates (#5458) 2023-09-07 16:50:34 -04:00
YanisBftandJosh Roy a3a71afcef Add HelpopMessageSentEvent (#5490)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-28 18:41:40 -04:00
ThiagoROX 72ba87c509 Prevent /editsign on waxed signs (#5492) 2023-08-28 17:04:15 -04:00
Josh RoyandMD ee1111f6aa Prevent players from ignoring themselves (#5488)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-08-24 22:04:37 +00:00
Josh Roy 42071f4ad4 Lower non bukkit type creation warns amount 2023-08-24 18:02:00 -04:00
Josh Roy cf43e643ed Fix usermap debug message not being debug 2023-08-24 18:02:00 -04:00
Dennis GyftakisandJosh Roy bb2df0b0a4 Add whitelist indicator for /seen and /whois (#5486)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-23 15:20:09 -04:00
diademiemiandJosh Roy e3e52db10e Add discord roles blacklist and aliases for chat format (#5157)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 21:39:12 -04:00
AlexandJosh Roy ad5f17481c Add abbreviation support to /pay command (#5457)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 21:05:30 +00:00
0a4cf27342 Add ability to use texture url in /skull (#5120)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 16:10:26 -04:00
SavageAvocadoandJosh Roy dfa22969c9 Add default book title and authors (#4920)
Prevents written books from being invalid if no author/title is provided.

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 15:46:07 -04:00
JustinandJosh Roy eb76cf0fbb Added option to limit amount of lore lines (#4773)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 18:46:31 +00:00
Daniel JensenandJosh Roy 409af5d2aa Add /mail clear <player> and /mail clearall commands (#4878)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-08-08 18:03:22 +00:00
Daniel Fiala 6fb500d9fb Fix null check for I18n error logging 2023-08-07 23:40:36 +00:00
YanisBft 2b993d58cf Add custom model data support for kits (#5433) 2023-08-05 19:38:43 -04:00
Josh Roy 7eed853294 Prepare for 2.21.0 dev builds (#5467)
time for an adventure
2023-08-05 19:16:34 +01:00
Josh Roy 6d2a8ffba4 Release 2.20.1 2023-08-05 18:59:32 +01:00
Josh Roy b781c1e924 Add /ess usermap cache sub-command
Allows to view the number of known UUIDs as
well as name to UUID pairs.
2023-08-05 16:48:23 +01:00
Josh Roy 19d6db0b4c Don't cache previously known offline names
This can lead to the potential that a UUID can
be mapped to the improper name. This logic should
be handled the join logic.
2023-08-05 16:48:23 +01:00
Josh Roy 02ced188c8 Always populate last known name into cache
In theory this should never be an issue but
somehow is?
2023-08-05 16:48:23 +01:00
Josh Roy 7f3b62a180 Remove extraneous manual user cache loading
The usermap will already do this on its own,
and we should really avoid doing this outside
the usermap to avoid unforeseen behavior changes.
2023-08-05 16:48:23 +01:00
Josh Roy 908b06570f Remove extraneous offline name update code
This exact code exists in ModernUserMap#getUser(String)
2023-08-05 16:48:23 +01:00
Josh Roy d7e5c10b51 Fix remaining offline visibility checks (#5466)
Purpur added a new self-referencing method that
we don't implement with our stub class. This will
prevent Player#canSee from being called for
offline players (it would be false anyway).
2023-08-05 11:44:40 -04:00
Josh Roy b2ae28b450 Fix discord module console relay not filtering all color codes (#5454) 2023-07-26 14:22:30 -04:00
Josh Roy e558e7365a Fix trade signs not working with currency suffix (#5441) 2023-07-25 21:16:50 +00:00
Josh Roy c32a0f975e Revert duel hand behavior of /book command 2023-07-25 17:10:20 -04:00
Josh Roy ae1f1583c2 Fix enchant not working with offhand 2023-07-25 17:10:20 -04:00
Flask Bot 58eea1724f New Crowdin updates (#5424) 2023-07-23 20:55:40 +00:00
Josh Roy 52c9ca8197 Fix inaccurate online player count for Discord leave message (#5440)
fixes #5256
2023-07-23 15:10:46 +01:00
Josh Roy f26e1b2e29 Fix invalid unlimited items preventing userdata to load
Invalid material names would previously return a null value
when deserializing. This raises an exception from within
Configurate because they use an EnumSet internally during
deserialization which doesn't support null elements.
2023-07-22 22:53:26 +01:00
Josh Roy f84a311c2b Ensure user data deserialization exceptions include file name
More than just a SerializationException can happen I guess
2023-07-22 22:53:26 +01:00
MDandJosh Roy 60129594a6 Rename OfflinePlayer to OfflinePlayerStub (#5417)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-07-04 09:45:58 -04:00
Flask BotandJosh Roy 563d3fab4e New Crowdin updates (#5370)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-07-04 09:36:32 -04:00
1022f7a3ab Add KitPreExpandItemsEvent (#5407)
Co-authored-by: YanisBft <yanis.briffaut@gmail.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-07-01 17:51:58 -04:00
Josh Roy d57094c0bc Add link module to /ess dump 2023-07-01 17:45:07 -04:00
Josh Roy 863b9cffc5 Add backoff and skip for Discord console relay 2023-07-01 17:45:07 -04:00
Josh Roy 1921abd310 Shorten logger names for Discord module console relay 2023-07-01 17:45:07 -04:00
Josh Roy 803d800bdd Fix Discord module webhook closing incorrectly 2023-07-01 17:45:07 -04:00
Josh Roy 5276204cf2 Increase Discord module console relay message limit 2023-07-01 17:45:07 -04:00
Josh Roy 697128bcf2 Update Discord Module to JDA 5 2023-07-01 17:45:07 -04:00
Josh RoyandMD a1fa1e38f8 Don't do visibility checks for OfflinePlayers (#5375)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-07-01 16:41:56 -04:00
Flask Bot ed41790712 Update items.json for 1.20 2023-07-01 20:37:21 +01:00
Josh Roy 834ef4ea57 Update to Minecraft 1.20 2023-07-01 20:37:21 +01:00
Josh Roy 03ed9a5b41 Fix roles/groups not being removed instantly upon unlink 2023-06-18 21:57:27 +01:00
Josh Roy d67dd46cdc Clean up link policy code
Also error when new link policy types are added
so we remember to write out the behavior here
2023-06-18 21:57:27 +01:00
Josh Roy 694ed6e66f Use removeAccount return value for AccountLinkManager#unlinkAccount 2023-06-18 21:57:27 +01:00
Josh Roy 8a0526b1e5 Fix discord link data being rewritten when no data was changed 2023-06-18 21:57:27 +01:00
MD a116e8630d Update Gradle, build plugins and GitHub Actions (#5383) 2023-06-14 19:27:01 -04:00
Josh Roy 8b2c7d7ad1 Fix regression in #5378 (#5379) 2023-06-03 23:13:09 +01:00
Josh Roy 8d07c4bb0f Fix regression w/empty strings with KeywordReplacer (#5378) 2023-06-02 22:54:30 +00:00
Josh Roy fc3dabf8f5 Fix keywords in kit commands not working (#5377)
Don't process b64 item through keyword replacer
and don't replace spaces on command strings
2023-06-01 21:14:41 -04:00
Josh Roy cdd277da92 Fix config BigDecimal parsing returning null on error (#5373)
Also add some more exception logging for parse issues.
2023-05-31 19:46:19 -04:00
Charles DeLancey 0d322e2c6b Fix typo in Discord docs (#5371) 2023-05-30 07:46:09 -04:00
646b60a90b Add ability to get recipe of item in hand (#5346)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
Co-authored-by: pop4959 <pop4959@gmail.com>
2023-05-28 18:27:18 +00:00
Flask Bot 357cd6b246 New Crowdin updates (#5357) 2023-05-27 02:00:11 +00:00
Josh Roy a4fb02f3e6 Don't run AntiBuild checks on NPCs (#5365) 2023-05-26 21:54:46 -04:00
Josh Roy 2828901927 Fix variables in kits having underscores replaced with spaces (#5366) 2023-05-27 01:51:11 +00:00
Josh Roy 1e0f7cb984 Fix question and shout prefixes with toggle shout (#5367) 2023-05-26 21:48:10 -04:00
Josh Roy 52a4dd2c60 Fix NPE during first join for LuckPerms contexts (#5364) 2023-05-26 19:29:37 -04:00
Josh Roy 8323b47e40 Prepare for 2.20.1 dev builds (#5355) 2023-05-23 11:32:12 -04:00
Josh Roy 14cdde009c Release 2.20.0 (#5336) 2023-05-23 11:18:21 -04:00
Flask Bot 1883e6adbc New Crowdin updates (#5348) 2023-05-22 00:49:43 +00:00
Josh Roy 2f9659e7fc Fix NPE during reload when deleted warps directory (#5353) 2023-05-20 10:03:24 -07:00
Josh Roy 1a4f63915f Fix SignChangeEvent never actually being called (#5341)
Fixes #5340. Caused by a regression in #5304 which reverted the behaviour introduced in #5304.
2023-05-09 00:26:28 +01:00
Flask Bot dd3f4c2921 New Crowdin updates (#5334) 2023-05-07 04:57:44 +00:00
Josh Roy 7ebb6359e5 Fix offline player consumer returning null users (#5339) 2023-05-06 19:13:44 -04:00
Josh Roy 069ebfcd5b Fix NPE while syncing offline users for discord line module (#5338)
Only would happen for users who haven't joined
(or been loaded by different parts of the plugin)
since the last restart. This change first of all switches to a method to fetch users which will update the User base to the UUIDPlayer dummy base. Secondly, this change will not update the base of a User to a UUIDPlayer dummy unless the base is currently null (which would be the case in the condition described above).
2023-05-06 18:31:23 -04:00
pop4959 991bc61b0c Expire UserMap cache more aggressively and add cache debugging (#5331) 2023-05-05 18:44:22 -04:00
Flask Bot 8194d1f747 New Crowdin updates (#5303) 2023-05-05 06:40:58 +00:00
Josh RoyandMD ee7ea2ae39 Fire SignChangeEvent in /editsign (#5304)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-05-02 16:13:50 -04:00
Josh Roy 3d23916ad5 Add HomeModifyEvent for home create/delete/rename/set (#5216)
Implements #5213.
2023-05-02 20:48:28 +01:00
Sasha SorokinandJosh Roy e5b0c4c855 Add support for local and global chats in Discord (#4684)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-04-30 21:30:11 -04:00
Josh RoyandMD 84fd45bd09 Bump JDA 4 version (#5328)
Bumping this for 2.20 release as  JDA 5 still not out of beta

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-04-29 18:14:36 +00:00
Josh Roy 01883a6b18 Truncate command and argument descriptions (#5314)
Fixes #5315.
2023-04-29 19:00:40 +01:00
Josh Roy fd49b70a69 Fix top command not sending messages (#5302)
my three year old refactors still haunt me
2023-04-13 06:22:47 +00:00
Josh Roy 3a737480ec Remove unneeded sanitization in webhook names (#5311)
Regression from 384f63bf92
2023-04-13 07:15:48 +01:00
diademiemiandJosh Roy 384f63bf92 Allow configuring Discord webhook name (#5159)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-04-10 00:27:36 +00:00
Joel OteroandJosh Roy 77dc87bb8e Add /bottom command (#5292)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2023-04-09 21:05:53 +00:00
MD c716fc8236 Bump runServer task to Paper 1.19.4 (#5269) 2023-04-06 00:15:15 +00:00
Flask Bot c52c1fea9d New Crowdin updates (#5244) 2023-04-04 21:49:43 -07:00
Josh Roy e7c56b32a4 Fix sulk sensors detecting vanished players (#5262) 2023-04-02 22:29:29 +00:00
Josh Roy 11e6d3ca7b Fix /mail sendall not working (#5263) 2023-04-02 22:12:04 +00:00
Josh Roy 75481925f7 Update display names before formatting chat messages (#5282) 2023-04-02 22:08:38 +00:00
Josh Roy ecb1850451 Add message for unknown last location for /tpo (#5290) 2023-04-02 22:04:36 +00:00
Josh Roy 0f4af9e735 Use strict comparison for potions in trade signs (#5260) 2023-04-02 21:45:46 +00:00
Josh RoyandMD bf14b88600 Bump to 1.19.4 (#5273)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-03-17 18:54:33 +00:00
Josh Roy 43d84de2b2 Move feature requests back to issues (#5251)
* Create request-a-feature.yml

* Update config.yml
2023-02-17 12:11:21 +00:00
25ad8ac9bd Add per-command command cooldown bypass permissions (#4759)
Adds the permission `essentials.commandcooldowns.bypass.<command name>` to bypass the cooldown for a specific command.

Closes #4747.

Co-authored-by: pop4959 <pop4959@gmail.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-02-13 23:19:52 +00:00
17051eab73 Add separate events for local and global chat (#4683)
This commit adds two new events: GlobalChatEvent and LocalChatEvent,
which allow other plugin developers to know whether the message sent is
a global or local one and act accordingly. If either of those events is
cancelled, then the source event is cancelled too.

Since all chat-related events share the same structure, a new abstract
class ChatEvent is created with change made for LocalChatSpyEvent to use
it without breaking its API.

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2023-02-13 23:15:15 +00:00
Flask Bot 4dc994df3c New Crowdin updates (#5051) 2023-02-05 04:58:49 -08:00
Warrior 996285c0e3 Fix doubles not working in console for /tppos (#5231)
Closes https://github.com/EssentialsX/Essentials/issues/5230
2023-01-22 16:32:47 -05:00
Josh Roy 312d1699a8 Use metadata for tnt from /nuke (#5222)
Fixes https://github.com/EssentialsX/Essentials/issues/5219
2023-01-12 10:51:13 -05:00
Josh Roy b3238605cc Fix inventory size for 1.8.8 servers (#5212) 2023-01-05 19:13:16 +00:00
Josh Roy b7a4bea52e Revert dual hand behavior from /hat (#5205)
Fixes https://github.com/EssentialsX/Essentials/issues/5202
2022-12-30 18:30:52 -05:00
Josh Roy ca71d93963 Fix discord permission issues with link module (#5197) 2022-12-27 21:07:47 +00:00
Josh Roy 0936fe80bd Discord Link Module 2022-12-26 01:24:17 +00:00
Josh Roy 520e8f991f Add DiscordService#getInviteUrl 2022-12-26 01:24:17 +00:00
Josh Roy 3131cadf65 Add UserMailEvent 2022-12-26 01:24:17 +00:00
Josh Roy bfae6c5c69 Add IPermissionsHandler#addToGroup & IPermissionsHandler#removeFromGroup 2022-12-26 01:24:17 +00:00
Josh Roy 52c500ee6d Add InteractionRole#getAsMention + InteractionRole#canInteract 2022-12-26 01:24:17 +00:00
Josh Roy ba5a8becfc Add support for role arguments in slash commands 2022-12-26 01:24:17 +00:00
Josh Roy 2a1957229f Add InteractionMember#hasRole convince methods 2022-12-26 01:24:17 +00:00
Josh Roy e6af246170 Add IPermissionsHandler#getGroups to fetch all groups 2022-12-26 01:24:17 +00:00
Josh Roy 939e5c5a55 Add InteractionRole#isPublicRole 2022-12-26 01:24:17 +00:00
Josh Roy 80bbf8c55b Add DiscordService#getRole & DiscordService#modifyMemberRoles 2022-12-26 01:24:17 +00:00
Josh Roy 376abc4aab Add DiscordService#getMemberById 2022-12-26 01:24:17 +00:00
Josh Roy a7f4d6b16b Add InteractionMember#getAsMention 2022-12-26 01:24:17 +00:00
Josh Roy e0d6040662 Add IUser#isFreeze & IUser#setFreeze 2022-12-26 01:24:17 +00:00
Josh Roy 6ec4c97ddd Rewrite Inventory Handling (#5021)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Complete refactor of inventory handling across the plugin. This simplifies various commands, and aims to resolve issues aon various different MC versions and inconsistencies between parts of the plugin.

Fixes https://github.com/EssentialsX/Essentials/issues/3810
Fixes https://github.com/EssentialsX/Essentials/issues/4248
2022-12-24 17:58:51 +00:00
diademiemiandJosh Roy db10678095 Add first join message type for Discord (#5160)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>

Add a new message type for when a player has joined for the first time.
2022-12-24 14:59:00 +00:00
Josh Roy 582a357752 Bump to 1.19.3 (#5179) 2022-12-07 21:47:13 -05:00
Bobcat00andBobcat00 8a1e7019a7 Fix incorect play time in /whois (#5177)
Co-authored-by: Bobcat00 <Bobcat00@users.noreply.github.com>
2022-12-06 09:02:20 -05:00
Josh Roy 4414eea513 Close discord webhook clients on shutdown (#5165) 2022-11-27 19:29:36 +00:00
Josh Roy 6e931b2b3b Fix ConsoleInjector not getting reinjecting on reload (#5149) 2022-11-11 15:28:55 -08:00
Warrior 22a0d53cf2 Improve uuid cache load times (#5143) 2022-11-03 20:14:18 -04:00
20011b9996 Added permission node for Nickname Prefix bypass (#4995)
Allow a permission to determine whether the nickname prefix gets applied to a group or user. It also removes a statement that is not true in the essentials config, the nickname prefix is NOT included in the nickname max length.

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-10-25 07:44:05 +01:00
879e70fb6b Ignore NPCs in JailListener (#5130)
Co-authored-by: pop4959 <pop4959@gmail.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Citizens NPCs cannot be jailed. This change prevents unnecessary lookups of NPC users.
2022-10-20 14:19:22 -04:00
Josh Royandpop4959 f91dfed5b4 Improve PlayerTarget code (#5131)
Co-authored-by: pop4959 <pop4959@gmail.com>
2022-10-15 00:41:21 +00:00
Josh Roy 767185ec32 Auto complete /mail clear indices (#5132) 2022-10-15 00:32:47 +00:00
Noah van der AaandJosh Roy 4aa93a43db Add mutable "viewers" list to DiscordRelayEvent (#5080)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-10-14 22:26:01 +00:00
Lax 57529c4b7d Update Bukkit wiki links in config (#5094) 2022-10-14 22:15:35 +00:00
MDandJosh Roy dc7fb91939 Make usermap non-player warnings configurable (#5125)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-10-11 01:06:54 +00:00
Josh Roy e91ce0c44c Fix Essentials#getUser not caching newly created NPC users (#5129) 2022-10-11 00:36:33 +00:00
Nassim Jahnke f7cbc7b0d3 Add renamehome command (#5121) 2022-10-04 08:40:18 -04:00
MD 28d7abe642 Close any open inventories before showing shapeless recipes (#5122) 2022-10-02 18:43:13 -04:00
0ca58ce4ba Rewrite User storage and UUID cache (#4581)
Co-authored-by: triagonal <10545540+triagonal@users.noreply.github.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-09-09 15:49:43 -04:00
Josh Roy d75d3cd001 Bump to 2.20 dev
Fourth time's a charm
2022-09-09 15:49:40 -04:00
MD 1eb5d8a459 Use Netlify CLI directly to deploy Javadocs 2022-09-05 18:46:09 +01:00
Josh Roy b808511efe Update discord bot authorization tutorial (#5098) 2022-09-02 09:43:09 -04:00
MD 0897ef71f2 Bump to 2.19.8 dev (#5085) 2022-08-22 10:17:33 -04:00
Josh Roy f176893940 Release 2.19.7 2022-08-21 16:48:14 -04:00
Josh Roy af18afad7a Fix discord url link message (#5079) 2022-08-18 20:42:57 -07:00
Josh Roy bdc5f4c830 Fix baltop not working in command blocks (#5078) 2022-08-18 18:37:15 +00:00
Josh Roy 0be056b9c2 Improve error messages in Backup 2022-08-18 14:14:29 -04:00
Josh Roy 5f5d65c463 Fix null log messages causing an NPE 2022-08-18 14:14:29 -04:00
Josh Roy ca4077b204 Fix rgb color codes getting stripped in some cases (#5076) 2022-08-18 17:29:12 +00:00
Josh Roy 4ab3e20a0c Fix remaining uses of Bukkit's logger (#5075)
Fixes #5056
2022-08-18 17:35:53 +01:00
Josh Roy 576866540e Fix gson incompatibility on 1.8.8 (#5067) 2022-08-17 06:52:38 +00:00
Josh Roy dfcb9a1c52 Update Checkstyle IDEA config (#5047) 2022-08-16 08:18:12 +00:00
Josh Roy a7a9cbc034 Bump down to 2.19.7 (#5064)
Never ending cycle
2022-08-15 14:26:52 +01:00
MDandJosh Roy 2f4f555923 Fix issues with chat preview refactor (#5062)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-08-14 18:49:02 -04:00
MD 1c22edbb1b Prevent players opening their own inventories in /invsee (#5061) 2022-08-14 16:31:23 -04:00
Josh Roy b71fb520aa Prepare for 2.20.0 dev builds 2022-08-12 22:30:18 +01:00
Josh Roy 8f7b7d0a5f Release 2.19.6 2022-08-12 16:59:48 -04:00
Josh Roy f379323d7a Fix mappings in ReflOnlineModeProvider 2022-08-12 16:59:02 -04:00
MD f2dde4154c Release 2.19.5 2022-08-11 21:05:44 +01:00
Flask Bot 12c2a4f2b5 Update translations from Crowdin (#4823) 2022-08-11 20:24:59 +01:00
BoomEaro ee7c1b1cf1 Fix wrong message length check (#5041) 2022-08-08 14:53:59 -07:00
Sam 422f25af8b Fix erroneous addition to VersionUtil class check (#5037) 2022-08-07 19:41:27 +00:00
Josh RoyandMD 728c746200 Add commands info to /ess dump (#4965)
See #4961 and EssentialsX/Website#71.

Adds `commands.yml`, the known command map and `AlternativeCommandHandler` handover information to `/ess dump`.

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-08-07 11:43:59 +01:00
MD 6dfa18ca53 Implement chat message signing for 1.19.1+ (#5030)
**Known issue: this inadvertently reformats `/minecraft:tell`. There's not much we can do about this in 2.19.x.**

This commit refactors EssentialsX Chat in order to support chat previews and signed chat messages in 1.19.1+.
2022-08-06 22:27:58 +01:00
Josh Roy a394760677 Update VersionUtil and /ess version (#5036) 2022-08-06 15:48:26 -04:00
MD 5f48cdef13 Bump to 1.19.2 (#5035) 2022-08-06 12:14:07 -04:00
pop4959andMD c56a9e6741 Add spawnable boat variants (#5027)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-08-02 14:18:57 -04:00
pop4959 5b20f30350 Fix ice other message (#5028)
Fix `iceOther` being sent to the target player instead of the sender. Fixes #5002.
2022-08-02 08:40:10 +01:00
pop4959 037577162b Add swift sneak enchantment (#5026)
Closes #5024.
2022-07-31 19:03:31 +00:00
Josh Roy 908189a233 Bump Spigot to 1.19.1 2022-07-28 11:30:24 -04:00
Josh Roy c938767b2d Prepare for 1.19.1 2022-07-28 11:30:24 -04:00
Josh Roy b135fcf086 Fix leave messages being sent when vanished (#5009) 2022-07-23 18:35:14 -04:00
Warrior 09dc49aacb Fix nickname feedback message when change-displayname is off (#4980)
Make the feedback message use the nickname instead of the player's display name if change-displayname is disabled. Fixes #4979.
2022-07-11 08:02:10 +00:00
Josh Roy f4b6197a49 Fix boolean logic for sleep ignore during afk status change (#4985) 2022-07-10 22:28:08 -07:00
Josh Roy 4a53cfe7ac Prevent role snowflakes from resolving roles with ids as names (#4983)
Fixes #4981.
2022-07-10 00:24:04 +01:00
DeltricandJosh Roy 2bba577567 Fix /unlimited with changing stack sizes (#4877)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-07-05 17:01:13 +00:00
pop4959andJosh Roy 6e674c73e0 Update unsupported message (#4976)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-07-05 09:52:14 -04:00
Josh Roy a60f3c067f Fix Essentials' logger breaking on 1.8.8-1.12.2 (#4975) 2022-07-02 14:50:45 -04:00
Josh RoyandMD 6816eb4e18 Use component logger on Paper for console colors (#4941)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-06-27 14:54:10 -04:00
Tristan Hermanns 9c5536b2e6 Fix minecraft namespace not working in give command (#4960) 2022-06-25 17:40:43 -04:00
Josh Roy ff80338350 Log filenames during configuration serialization errors (#4959) 2022-06-21 22:01:25 +00:00
Josh RoyandMD 8d856dd6db Fix paths and farmland not being solid (#4915)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-06-18 19:31:54 +00:00
Josh Roy 96c6c951e8 Don't teleport to a safe location for AFK freeze when flying (#4944)
Fixes #4902.
2022-06-15 15:43:57 +00:00
Josh Roy 0931b21f6d Fix places where Discord formatting wasn't sanitized (#4945)
Fixes #4891
2022-06-15 15:35:12 +00:00
Josh Roy 9147d1036d Fix ANSI color codes not getting stripped on Paper (#4942)
Paper uses 0x7f as an intermediate character between adventure and its ANSI pattern converter, we need to strip this.
2022-06-15 16:31:42 +01:00
MD a7f602e2ad Remove commons-lang3 dependency
`commons-lang3` is scheduled for removal by Spigot.

This commit reimplements the methods we use from StringUtils in StringUtil, and replaces NumberUtils.isDigits(String) with NumberUtil.isLong(String).
2022-06-15 16:18:35 +01:00
Flask Bot 0c81980ead Update items.json for 1.19
Note: this action was performed by a human (@mdcfe) under the
Flask-Bot account.
2022-06-15 16:18:35 +01:00
MD 2d5794f68d Add mangrove signs to MaterialUtil 2022-06-15 16:18:35 +01:00
MD 6b80e0b113 Bump run-paper MC version to 1.19 2022-06-15 16:18:35 +01:00
Josh Roy 18f9af898e Add 1.19 mobs/data 2022-06-15 16:18:35 +01:00
Josh Roy d04c933abe Fix server state reflection provider mappings 2022-06-15 16:18:35 +01:00
Josh Roy b916488de2 Bump VersionUtil and Spigot 2022-06-15 16:18:35 +01:00
MDandJosh Roy c6066bd2d8 Update README and contributing guide (#4851)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>

This PR brings the README up-to-date, reduces the number of references to upstream Essentials, and fleshes out the contributing guide with much more detail.
2022-06-10 13:06:24 +00:00
Josh Roy 3af931740b Don't progress playtime for vanished users (#4918) 2022-06-04 01:43:12 +00:00
Josh Roy 1c5a7e9ffb Bump down to 2.19.5 (#4914)
Mojang will be releasing 1.19 within two weeks and 2.20.0 is not ready for that timeframe. We want to release 2.19.5 with 1.19 support before that.
2022-05-30 21:21:19 +01:00
pop4959 ea5cdc097d Fix erroneous TeleportWarmupEvent event call (#4913) 2022-05-22 17:20:39 -04:00
Deltric d891268143 Fix /nick off breaking with changecolor permissions (#4876) 2022-03-31 19:57:36 -04:00
lucko 4bd1b3c09f Reduce user lookups for LuckPerms contexts (#4869) 2022-03-27 09:07:36 -04:00
MD c4f10d9c1d Fix double charge for /tpa when target has /tpauto on (#4862)
If a player uses `/tpa` to request a teleport to someone with `/tpauto` enabled, `/tpa` manually creates a charge for the teleportation.  
This PR changes the subsequent code to throw a `NoChargeException` instead of returning, meaning only the `AsyncTeleport` charge is applied, not the standard command one.

Fixes #4854.
2022-03-20 11:19:20 +00:00
MD e12f1b5022 Bump run-paper to 1.18.2 (#4857) 2022-03-15 07:56:52 -04:00
MDandJosh Roy 31732cec95 Prepare for 2.20.0 dev builds (#4849)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2022-03-12 19:33:28 -05:00
MD f104b6ec15 Release 2.19.4 2022-03-12 23:00:24 +00:00
MD 695546420f Run update check using Bukkit scheduler (#4839)
Fixes #4825.
2022-03-12 12:02:20 +00:00
Josh Roy 997fb1800e Fix preemptive setting of owner metadata on to trade signs (#4835)
Fixes #4836.
2022-03-08 15:08:34 +00:00
MD 3984bddd9d Fix 2.19.4 dev build version (#4834) 2022-03-06 00:06:48 +00:00
MD d92ce8bdfe Bump to 2.19.4 dev (#4831) 2022-03-05 18:33:04 +00:00
MD 3348bd9129 Release 2.19.3 2022-03-04 11:41:56 +00:00
Josh Roy 1a5526867c Fix IllegalAccessException with advancement listener on 1.12.2 (#4826)
Fixes #4809.
2022-03-03 20:38:39 +00:00
MD c5253bc5e2 Update to Minecraft 1.18.2 (#4821)
This PR updates the build setup to Gradle 7.4 and updates EssentialsX to Minecraft 1.18.2.
2022-02-28 20:51:26 +00:00
Flask Bot 29a2a865c5 New Crowdin updates (#4744) 2022-02-28 20:31:02 +00:00
MD c3b994808f Use GitHub proxy fallback for update checker (#4818) 2022-02-26 17:09:54 -05:00
triagonal bc2da98703 Make OfflinePlayer#setName protected for CMI Importer (#4805) 2022-02-14 07:40:48 -05:00
Josh Roy cbb012853c Fix toggleshout causing issues with question/local chat 2022-02-13 22:44:37 -05:00
Josh Roy 4bf4bd432a Add some color to the local prefix by default 2022-02-13 22:44:37 -05:00
Josh Roy 83d0e0d9a0 Add permissions to receive certain channel channels 2022-02-13 22:44:37 -05:00
Josh Roy 2f5c24d114 Add permission for local channel chat 2022-02-13 22:44:37 -05:00
Josh Roy 88c8ccd29b Add config for default shout state and for persisting shout state 2022-02-13 22:44:37 -05:00
Josh Roy 94edbcfeb1 Add chat question config option 2022-02-13 22:44:37 -05:00
Josh RoyandMD d5822e9a41 Add config for max /tree and /bigtree range (#4728)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-02-13 16:46:18 -05:00
Josh Roy debf09437e Add UUID support to trade and protection signs (#4713) 2022-02-13 15:54:19 -05:00
Josh Roy a9e5f079a9 Fix trade sign length validation issues (#4748) 2022-02-13 15:50:43 -05:00
Josh Roy f00c2dcf92 Fix keepinv policies ignoring offhand (#4725) 2022-02-13 20:37:25 +00:00
Josh Roy 63cbf7e2da Prevent legacy material support from being initiated (#4697) 2022-02-13 15:33:51 -05:00
Josh Roy 19837f9309 Add some more dump files (#4785)
Adds worth, tpr, and spawns configs.

See also https://github.com/EssentialsX/Website/pull/68
2022-02-08 09:26:05 +00:00
Josh Roy 4b8b770c30 Allow /nick without change-displayname (#4758)
Closes #4709.
2022-02-06 18:23:17 +00:00
Josh Roy ddc258ed11 Fix teleport request queue being reversed order (#4755)
Fix #4753
2022-02-06 18:18:40 +00:00
Josh Roy d23796dade Support doubles in /tppos (#4729)
Closes #1785
2022-02-06 18:01:51 +00:00
Josh Roy 41f5cc3175 Use Paper command forwarding API (#4794)
Uses API added in PaperMC/Paper@9940bca, when available.
2022-02-06 17:56:13 +00:00
Josh Roy c6fe160b47 Fix Paper command executor on modern Paper versions (#4791) 2022-02-01 22:13:16 +00:00
sxphirus 5c46bea2ca Add translation key for /near entries (#4740) 2022-01-20 10:05:31 -05:00
Flask Bot a9585bb525 New Crowdin updates (#4668) 2022-01-09 16:27:25 +00:00
Josh Roy bb88a6a749 Fix rate limit exception in update checker (#4731) 2022-01-02 17:24:32 +00:00
Pierre Dedrie 739600eb05 Fix safelogin for 1.18 and above by using worldProvider minHeight (#4715) 2021-12-26 23:56:03 +00:00
Josh Roy 5f98d3fac5 Add mitigation for JDK-8274349 (#4711) 2021-12-24 21:40:41 -05:00
Josh Roy 84326cf13e Fix LocationUtil#getSafeDestination NSME on older versions (#4708)
Fixes a NoSuchMethodError from old guava versions in old MC versions.

Fixes #4703
2021-12-23 11:55:31 +00:00
Josh Roy 685084219e Use futureproof Paper component serializer when available (#4706)
#plainSerializer will be removed when adventure 5.0.0 releases. This PR prevents this from breaking in the future.

Closes #4705
2021-12-23 10:57:41 +00:00
Josh Roy c4e62ae22b Remove usused configuration classes (#4707)
These were not used since configurate, no plugin should be using this, and it causes compile errors on the latest spigot version.
2021-12-23 01:18:02 +00:00
MD 05ce11f657 Bump to 2.19.3 dev 2021-12-15 21:29:45 +00:00
MD bcb0fb5825 Bump to 2.19.2 release 2021-12-15 20:58:38 +00:00
MD 91f4493fe0 Run Actions builds for dev branches (#4691) 2021-12-15 20:55:37 +00:00
MD d53ff36a2a Prevent IndexOutOfBoundsException in interaction replies (#4690) 2021-12-15 20:55:37 +00:00
MD 98917d97c8 Bump down to 2.19.2-dev 2021-12-15 20:55:37 +00:00
Josh Roy 7e1d18fee4 Prepare for 2.20.0 dev builds 2021-12-14 21:26:44 +00:00
Josh Roy 0b7e7801d0 Release 2.19.1 (#4681) 2021-12-14 19:16:08 +00:00
Josh Roy 840035944a Truncate Discord interaction messages to proper length (#4686) 2021-12-14 18:46:59 +00:00
Josh RoyandMD 20a7d1c7ee Add DiscordRelayEvent to edit relayed Discord->MC messages (#4656)
Implements the event suggested in #4580.

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-12-14 16:26:57 +00:00
pop4959 253185bd0e Remove ignore teleport metadata 2021-12-12 22:23:56 +00:00
Josh Roy dae519494e Fix stack overflow when disabling commands from other addons (#4680) 2021-12-11 19:16:23 -08:00
Josh Roy dc27af78bf Update to MC 1.18.1 2021-12-11 12:13:50 +00:00
Flask Bot 41861a8551 New Crowdin updates (#4467) 2021-12-04 14:53:26 +00:00
Josh Roy 9b629e42d8 Bump JDA + discord-webhooks dependencies (#4655) 2021-12-04 14:47:40 +00:00
7794634d37 Add support for multiple queued TPA requests (#3801)
This PR adds support for players to receive multiple teleport requests, which are queued and can be managed independently of one another.

All commands should retain their current behavior but have some new additions;
* `/tpaccept`: now allows you to specify a player or `*` to accept a specific player's or all players' teleport request(s) respectively.
  - Using a wildcard will only accept all tpahere requests, as players can't teleport to multiple places simultaneously.
* `/tpdeny`: now allows you to specify a player or `*` to deny a specific player's or all players' teleport request(s) respectively.

This PR also adds a new setting for the maximum amount of pending TPA requests a user can have at once.
```yml
# The maximum amount of simultaneous tpa requests that can be pending for any given user.
# Once at this threshold, any new tpa requests will bump the oldest tpa requests out of queue.
# Defaults to 5.
tpa-max-amount: 5
```

Closes #3769
Closes #1550

Co-authored-by: Mariell Hoversholm <proximyst@proximy.st>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-12-04 14:40:06 +00:00
Josh Roy d091d6902c Fix safelogin support with 1.18 world heights (#4667)
Fixes #4665.
2021-12-03 20:31:36 +00:00
MD c45c741210 Bump supported version to 1.18 release 2021-11-30 16:33:05 +00:00
MD e35c65b976 Refactor world info provider; only clamp to logical height when already below
Fixes #4657.
2021-11-30 16:33:05 +00:00
MDandJason 0983167740 Port remaining groovy build logic to kotlin
Co-authored-by: Jason <11360596+jpenilla@users.noreply.github.com>
2021-11-30 16:33:05 +00:00
1509cf8978 Support 0>y>256 for safe/random teleports (#4641)
Co-authored-by: pop4959 <pop4959@gmail.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-11-30 16:33:05 +00:00
MD c0082ee734 Update to Gradle 7.3 and toolchain to JDK 17 2021-11-30 16:33:05 +00:00
Flask Bot db2ec1b436 Update items.json for Minecraft 1.18 2021-11-30 16:33:05 +00:00
Josh Roy 7a1e50d10f Fix reflection mappings for the ServerStateProvider in 1.18 2021-11-30 16:33:05 +00:00
Josh Roy b6ba1768a1 Mark 1.18 as a supported version 2021-11-30 16:33:05 +00:00
Josh Roy c38dd997d2 Update Spigot to 1.18 2021-11-30 16:33:05 +00:00
Josh Roy f8ca1bae5d Fix offline player error in /playtime for versions < 1.15.2 (#4649) 2021-11-25 19:50:49 +00:00
Josh Roy 3c60bcf2de Allow unsafe homes when force-disable-teleport-safety is enabled (#4650) 2021-11-25 19:47:42 +00:00
Josh Roy 3f857e3183 Fix DiscordUtil#getOrCreateWebhook using webhooks without tokens 2021-11-25 14:03:56 -05:00
Josh Roy 668c9634ca Ensure JDA is started before allowing calls to the JDA object 2021-11-25 14:03:56 -05:00
nik2143andJosh Roy 51c49b0de6 Set a metadata value for vanished players (#4646)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-11-24 09:57:55 -05:00
Lax 5723dac6ba Send discord config in dump even when plugin is disabled 2021-11-17 19:15:02 -05:00
Josh Roy 680d3230ea Add /discord command to display invite code (#4620) 2021-11-06 19:18:37 -04:00
Josh Roy 8f679de352 Add offline user support to /playtime (#4619) 2021-11-06 15:41:38 -07:00
Josh Roy 86d435eee1 Move UUID below alts in /seen (#4615) 2021-11-06 13:50:56 -07:00
Bobcat00andBobcat00 e4cf965bae Add translation for /list real name (#4614)
Co-authored-by: Bobcat00 <Bobcat00@users.noreply.github.com>
2021-11-05 22:24:21 +00:00
Andre_601 78c846a0a8 Minor typo corrections for EssentialsXDiscord (#4603) 2021-11-01 21:41:58 -04:00
pop4959andJosh Roy c466c9c370 Add metadata value to bypass last location setting on teleport (#4597)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-10-31 17:25:18 -04:00
Josh Roy 6994005a5f Fix light blocks being considering solid (#4601) 2021-10-30 22:25:03 -07:00
Josh Roy e43f06bfa6 Add /mail sendtempall (#4561)
Implements #4558
2021-10-24 16:45:01 +00:00
Josh Roy 9601687190 Add console regex filter for discord module (#4534) 2021-10-24 12:12:42 -04:00
Josh Roy 2ed94f1048 Add provider to use new Paper advancement API (#4588)
Paper added in PaperMC/Paper@40ee634
2021-10-24 14:27:07 +00:00
Josh Roy 70045c30f3 Migrate reflection in MetaItemStack to a provider (#4592) 2021-10-24 15:17:06 +01:00
Josh Roy ce88c0e1e4 Fix kit issues with potions in 1.8 (#4591)
Fixes #2867
2021-10-24 14:12:20 +00:00
Josh Roy 494e82e581 Fix ability to delete warps in unloaded worlds (#4590)
Fixes #4584.
2021-10-24 15:05:10 +01:00
MelluhandJosh Roy edd15132fd Add playtime command (#4562)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-10-23 15:29:37 -04:00
Tadhg BoyleandJosh Roy add664441e Add notify messages to jail command (#4576)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-10-23 15:18:51 -04:00
Josh Roy 58b0fb089f Improve error message when primary channel is improperly defined (#4586)
* Improve message verboseness when primary channel is improperly defined

* Clarify primary channel behavior
2021-10-20 13:09:56 +01:00
Josh Roy 90e4845627 Remove config reloads on world load/unload (#4573)
Since the Configurate rewrite, we hold locations using `LazyLocation`, which is converted to a `Location` with world lookups on demand; as such we no longer need to reload configs on each world load since invalid locations will now softly fail.
2021-10-13 18:41:48 +01:00
Josh RoyandMD 43f4d306be Add discordbroadcast command (#4541)
* Add discordbroadcast command

* Update EssentialsDiscord/src/main/resources/plugin.yml

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

* Add emoji parsing/tab complete for discordbroadcast command

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-10-11 15:54:55 -04:00
Josh Roy cb7d9baf7a Fix exception while running vanilla commands for chat command relay (#4552)
Fixes #4545
2021-10-11 19:32:41 +00:00
Josh Roy f3aea5e6ec Improve error message for updater checker rate limit (#4560)
Fixes #4556
2021-10-11 20:06:18 +01:00
Josh Roy a7a1fcd98c Fix Discord token regex censor pattern (#4563)
Discord changed their token format again
2021-10-07 09:22:41 +01:00
ef8bcc8bc4 Add kits subfolder to allow for multiple kit files (#4407)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-10-02 19:14:41 -04:00
MDandJosh Roy b3692d18b0 Update plugin.yml authors to reflect EssentialsX contributions (#4551)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-09-28 15:49:55 -04:00
Josh Roy a7b2b675d5 Add essentials.seen.uuid permission to show uuids in seen (#4531)
Adds `essentials.seen.uuid` permission requirement to show uuids in `/seen`. People were complaining this cluttered it so whatever. Also adds said permission to the `essentials.seen.extra` permission group.

Closes #4499.
2021-09-28 13:13:50 +00:00
Josh RoyandMD 7d7a8e47ac Fix verbose command usages not being disabled properly (#4530)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-09-28 13:09:04 +00:00
MDandJosh Roy f0da17b75c Asynchronously filter commands on Paper when possible (#4460)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-09-15 17:23:31 -04:00
Josh Roy d86b471402 Add action message type (#4520)
Adds a message type which will show when a player runs the /action (/me) command.

Message Type:
```yml
# Configure which Discord channels different messages will be sent to.
# You can either use the names of the channels listed above or just the id of a channel.
# If an invalid channel is used, the primary channel will be used instead.
#
# To disable a message from showing, use 'none' as the channel name.
message-types:
  ...
  # Action messages sent when a player runs the /me or /action commands.
  action: primary
```

Message Format:
```yml
# The following entries allow you to customize the formatting of messages sent by the plugin.
# Each message has a description of how it is used along with placeholders that can be used.
messages:
  ...
  # This is the message sent to Discord when a player runs the /me or /action command.
  # The following placeholders can be used here:
  # - {username}: The name of the user who ran the command
  # - {displayname}: The display name of the user who ran the command
  # - {action}: The action (message) the user used in the command.
  # ... PlaceholderAPI placeholders are also supported here too!
  action: ":person_biking: {displayname} *{action}*"
```
2021-09-15 14:52:14 +00:00
Josh Roy 767508fb87 Fix bed spawn setting when waking up villagers (#4524)
Fixes #4461.
2021-09-15 15:40:23 +01:00
LaxandJosh Roy c85d772e1b Add /ess dump all as alias for /ess dump * (#4517)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-09-08 17:48:47 -04:00
MDandJosh Roy fde6524e46 Fix error with commands ending in single colon (#4508)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-09-01 13:37:46 -04:00
b2886969f0 Update to bStats v2 (#4350)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-09-01 13:33:43 -04:00
Josh RoyandMD 19ca5186e4 Replace unneeded inputs with new /ess dump in issue template (#4479)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-09-01 13:26:54 +01:00
Josh Roy b86a34c909 Prepare for 2.19.1 dev builds (#4505) 2021-09-01 12:55:37 +01:00
MD d145fbc5cc Require forceSign property even on releases 2021-08-31 21:27:35 +01:00
Josh Roy 3ca960e31b Release 2.19.0 2021-08-31 13:04:34 -04:00
MD 8bbaf7da68 Update Gradle to 7.2; set run-paper to 1.17.1 with toolchain (#4496)
:chatting:
2021-08-31 16:53:09 +01:00
Josh Roy 14c2ab5ddd Add online mode data for /ess dump (#4497)
EssentialsX/Website#60
2021-08-31 15:34:10 +00:00
Josh Roy 6f20a789f9 Delete (& backup) invalid NPC accounts created by poor econ code (#4490)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

my magnum opus

---

* Delete (& backup) invalid NPC accounts created by poor econ code

* Add feedback for large migrations

* Improve feedback messages

- Improve the wording of console messages
- Include details about how to resume purging userdata in case it gets interrupted
- Scale the message delay based on the number of userdata files to be processed
- Always cancel the repeating task and shut down the executor when we're done

* Update Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java

* Add less ranty context to rant

At least I had caffeine before writing this
2021-08-31 16:28:44 +01:00
Josh Roy 4bee15956a Fix economy usernames being unsanitized in some places (#4484)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

This PR fixes various issues with NPC accounts:
- Fixes some NPC account names not being sanitised
- Fixes wrong keys being used when manually generating a NPC account file
- Adds some debug logging to `UserMap` name lookups
2021-08-28 17:32:45 +01:00
pop4959andJosh Roy e4c179f5ed Allow auto equip in nbt serialized kits (#4491)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-08-28 11:46:38 -04:00
Josh Roy 4811eb1558 Fix /ess dump on 1.8.8 (#4489)
Fixes #4487

pain
2021-08-27 21:50:41 +01:00
Josh Roy 69850d82c8 Fix NoClassDefFoundError on < 1.12 with Discord execute command (#4478) 2021-08-26 16:12:52 -04:00
Josh Roy df88a89c14 Add missing translation for argument pipe color (#4480) 2021-08-26 16:09:16 -04:00
Josh Roy c746a1ce17 Bump Configurate (#4475)
Some performance improvements, <3 zml
2021-08-24 11:35:53 +01:00
Flask Bot ecdaed7d11 New Crowdin updates (#4281) 2021-08-19 20:11:35 +00:00
CyberKitsuneandMD c062651821 Add option for tab complete to use displaynames (#4432)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

This feature adds a new configuration option, `change-tab-complete-name`

When `change-tab-complete-name` is turned on, `getPlayers()` in `EssentialsCommand` will use `getDisplayName()` instead of `getName()`; populating the list with display names instead of player names.

Closes #4431.
2021-08-19 20:00:06 +00:00
Josh RoyandMD 3692740762 Add /ess dump command to generate a debug dump output (#4361)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Command usage: /essentials dump [config] [discord] [kits] [log]

Either of the optional args can be used to add the given data to the dump.

Related: EssentialsX/Website#51
2021-08-19 20:35:19 +01:00
Josh Roy 1179caab88 Fix bot-command-relay option not ignoring itself (#4453) 2021-08-12 12:37:19 +01:00
Josh Roy 35b1a284f3 Fix improper MOTD delay check (#4454) 2021-08-10 10:51:33 -04:00
Josh RoyandMD 36432c65e9 Fix NPE on webhooks messages with show-webhook-messages enabled (#4452)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Fixes #4451.
2021-08-09 21:30:30 +00:00
Josh Roy 871e718f63 Add online/unique placeholders to join/leave formats for discord (#4427) 2021-08-09 16:23:49 -04:00
Josh Roy 42293596f3 Add config option to disable verbose usage strings (#4396)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

```yml
# Whether or not Essentials should show detailed command usages.
# If set to false, Essentials will collapse all usages in to one single usage message.
verbose-command-usages: true
```

This PR also fixes a regression in legacy usage strings where the `<command>` wasn't replaced.
2021-08-09 19:56:38 +00:00
Josh Roy ac8a13f039 Add name parameter for Discord avatar URL (#4369)
Allows offline mode/Geyser users to configure their own avatar URL using a service that accepts usernames.
2021-08-09 19:48:43 +00:00
Josh RoyandMD d56ecaacdc Allow world name fallback for LazyLocation (#4428)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-08-09 20:39:55 +01:00
Josh Roy 14fbfe360e Allow disabling MOTD task and cancel on player logout (#4411)
This PR allows setting `delay-motd` to a negative value in `config.yml` to disable the MOTD join task without disabling `/motd`, and fixes an issue where delayed MOTD tasks would run even after a player logged out (in case someone previously set an insanely high MOTD delay to "disable" the MOTD on join).

Fixes #4408.
2021-08-09 20:22:09 +01:00
Josh Roy 5334a3fd34 Move update check message out of MOTD task (#4410)
Update message will now show after mail, and will not be affected by insane MOTD delays.
2021-08-09 19:14:15 +00:00
Josh Roy 2fa9c6486e Fix disabled-commands not removing aliases (#4399)
Fixes #4383, an issue where only the main command name is disabled instead of the alias specified in `config.yml`.
2021-08-09 18:59:36 +00:00
Josh Roy c221e96122 Make discord chat format world name use world alias (#4419) 2021-08-09 19:57:03 +01:00
Josh Roy 26c016fea2 Add config option to allow webhooks/bots to send commands (#4442)
```yml
# Console relay settings
# The console relay sends every message shown in the console to a Discord channel.
console:
  ...
  # Set to true if bots/webhooks should be able to send commands through the command relay.
  bot-command-relay: false
```
2021-08-09 18:48:01 +00:00
Josh Roy b6a08ec712 Fix force-disable-teleport-safety enforcing world border (#4371) 2021-08-09 18:44:13 +00:00
Josh Roy 9179c6c1e2 Update EssXDiscord tutorial for new developer mode location (#4409) 2021-08-09 19:38:04 +01:00
Josh Roy 3f9aceb922 Add DiscordService#sendChatMessage
Allows plugins to send messages to the chat channel via API using the format from our config
2021-08-07 17:37:02 -07:00
Josh Roy 3da8efae49 Fix incorrect parameter name for allowGroupMentions 2021-08-07 17:37:02 -07:00
Josh Roy b84207f955 Fix regression in invalid home check (#4425) 2021-08-02 11:23:40 -04:00
1stGlitchandJosh Roy f57de40668 Fix invalid mob data in spawnmob on versions < 1.17 (#4376)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-08-02 10:33:49 -04:00
Daniil ZandJosh Roy e95df57642 Fix barrier considered as unsafe block to teleport (#4348)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-08-02 10:22:07 -04:00
Josh Roy aaddb2af1f Fix remaining Configurate nullability issues (#4370) 2021-08-02 12:32:41 +00:00
Josh Roy 082950cc18 Fix bStats becoming a transitive dependency (#4426) 2021-08-02 13:21:53 +01:00
triagonal 90828965df Fix NPE during reload with invalid discord bot tokens (#4417) 2021-07-31 10:19:20 -04:00
Josh Roy 17e026f9aa Require MessageType's start with a letter
May cause issues with YML serialization as it can get finicky with keys starting with numbers.
2021-07-28 16:24:09 -07:00
Josh Roy f1d7e50dc0 Don't warn about unregistered channel-id based MessageType's
You shouldn't be registering these, there is no point for a user to configure where these go because it would be ignored.
2021-07-28 16:24:09 -07:00
Josh Roy e2cb7b2102 Fix advanced relay breaking when multiple channels were defined 2021-07-28 16:24:09 -07:00
Josh Roy 28578a5d8a Fix some typos in the tutorial 2021-07-28 16:24:09 -07:00
Josh Roy 0d8e90b8da Move FakeServer to 1.8 provider (#4395)
Moving this removes the need to needlessly update the FakeServer class with new methods when upstream changes it.
2021-07-27 21:23:20 +01:00
Josh Roy 1556b911b2 Fix webhook messages being parsed unnecessarily (#4346)
This wasted CPU time parsing responses from Discord that we will never use, plus caused problems when parsing webhooks that our library couldn't parse correctly (MinnDevelopment/discord-webhooks#36).
2021-07-11 20:36:46 +01:00
MD eed73e3157 Cache overridden-commands and player-commands in Settings (#4345) 2021-07-11 12:25:25 -04:00
triagonal c020526b1e Strip formatting in webhook name (#4344) 2021-07-11 09:57:46 -04:00
Josh Roy cca98e817f Add show-displayname option 2021-07-10 11:08:51 -04:00
Josh Roy 46a8a7d04c Remove join/quit message null checks
If you don't want to see them, don't configure them.
2021-07-10 11:08:51 -04:00
Josh Roy d4ddc87d81 Fix less strict length check on messageQueue add rather than iteration 2021-07-10 11:08:51 -04:00
Chew b473460ffc Use native Discord timestamps for console timestamps 2021-07-10 11:08:51 -04:00
Josh Roy db027bced3 Add option to log channels to console 2021-07-10 11:08:51 -04:00
Josh Roy 12cc0b99f9 Fix some minor grammer issues in config 2021-07-10 11:08:51 -04:00
Josh Roy 15cac529c5 Improve death message validation
Now takes gamerules in to account and will not send blank or null death messages.
2021-07-10 11:08:51 -04:00
Josh Roy feed2d6a45 Fix improper bitwise logic in DiscordUtil#getRoleColorFormat 2021-07-10 11:08:51 -04:00
Josh Roy b516db669c Fix prefixes/suffixes not stripping hex format 2021-07-10 11:08:51 -04:00
Josh Roy 3bc58264ea Add advancement message type 2021-07-10 11:08:51 -04:00
Josh Roy 0a0416ae38 Fix disabling a single command causing all commands to be disabled 2021-07-10 11:08:51 -04:00
Josh Roy d244956b4b Ensure no JDA code is called during invalid shutdown 2021-07-10 11:08:51 -04:00
Josh Roy 37e3730e9d Add fake vanish join/leave + exclude vanished messages 2021-07-10 11:08:51 -04:00
triagonal 48532a0ae6 Fix NPE in UserData#getLogoutLocation (#4341) 2021-07-10 09:17:26 -04:00
Josh Roy 0b6b0f9c61 Fix NPE with custom join messages (#4334)
This fixes a bug in #4290 in which custom join messages would throw a NullPointerException if the user hasn't changed their username.
2021-07-09 16:33:44 +00:00
Josh Roy 435ac80ff9 Fix delay not working in /spawn (#4335) 2021-07-09 11:55:52 -04:00
triagonal a098055fc9 Fix NPE when teleporting offline players (#4332) 2021-07-08 09:24:40 -04:00
cba5063b33 Add config option to change discord show-avatar url (#4330)
Co-authored-by: T41US <adityakomp@gmail.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-07-07 19:55:43 +00:00
AnsandrandJosh Roy 3787b80286 Add UserTeleportSpawnEvent (#4328)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-07-07 14:52:49 -04:00
pop4959andJosh Roy 2858bd3784 Add role placeholder to Discord message format (#4329)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-07-07 11:55:25 -04:00
OliviaandJosh Roy ff58d8e443 Add config option for new username join messages (#4290)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-07-07 15:46:54 +00:00
Josh Roy 5e178943a0 Update to Minecraft 1.17.1 (#4324) 2021-07-06 18:34:40 +01:00
triagonal 33ca3b2981 Fix behavior of itemdb command on legacy versions (#4320) 2021-07-05 21:29:24 -07:00
triagonal 8c8c85d753 Fix server shutdown not setting logout location (#4318) 2021-07-05 15:47:42 -04:00
Josh Roy 6535edf4e0 Fix MailMessageSerializer not handling null UUIDs (#4314) 2021-07-03 19:32:34 -04:00
Josh Roy 7e25570344 Remove usage of java.awt.Color (#4312)
This fixes issues on headless JREs which lack java.awt classes.
2021-07-03 20:46:09 +01:00
Josh Roy 409210ccde Fix issues with 3rd-party Vault economy providers (#4303)
Turns out that when you depend on a plugin, get this, the plugin will load before you. So we need to rework how we do things here.

Fixes #4075
Fixes #4304
2021-07-03 16:54:01 +01:00
Josh Roy 555a62c582 Fix baltop something having a null name for offline players 2021-07-03 01:04:59 -04:00
Josh Roy 36a070be4c Fix Configurate conversions not saving 2021-07-03 01:04:59 -04:00
Josh Roy d903e40a53 Fix weird behavior with legacy mails (#4307) 2021-07-02 21:52:49 -07:00
Josh Roy d78729f343 Disallow empty discord filters (#4298) 2021-07-01 15:59:35 -07:00
Josh Roy a55289196b Add server start/stop messages 2021-07-01 15:56:46 -04:00
Josh Roy 3505b47d8f Fix vanilla command execution on discord 2021-07-01 15:56:46 -04:00
Josh Roy 11836e6662 Ensure MessageFormat's are clean before parsing 2021-07-01 15:56:46 -04:00
Josh Roy 4ce38bde36 Allow uppercase variables 2021-07-01 15:56:46 -04:00
Josh Roy 711f701d62 Fix typo in mail tab complete (#4296) 2021-07-01 19:22:48 +00:00
triagonal 94c509b1e2 Prevent exception when retrieving enchantments (#4297) 2021-07-01 15:18:05 -04:00
triagonalandJosh Roy 5c08a0e72c Ignore case when matching custom item aliases (#4295)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-07-01 16:30:08 +00:00
Josh Roy 9c451271e0 Rework Mail System (#3710)
* New `/mail sendtemp <time diff> <message>` command to send mail that will self-destruct after time diff.
* New `/mail clear <number>` command to clear a specific mail item.
* `/mail read` now tracks which mails you read and won't nag you about them.
* A bunch of other flexibility since we store actual data instead of strings
2021-07-01 11:23:32 -04:00
Josh Roy 55db6c2476 Fix afk list NPE (#4294) 2021-07-01 16:15:24 +01:00
0861427bf3 Discord Module (#3844)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: pop4959 <pop4959@gmail.com>
Co-authored-by: Riley Park <riley.park@meino.net>
Co-authored-by: Jason <11360596+jpenilla@users.noreply.github.com>
2021-07-01 09:43:35 -04:00
Josh Roy 9d3bf337e1 Improve itemlore and itemname tab completion (#4280)
- Improve tab completion for itemname and itemlore
- Improve air type matching
- Fix incorrect /itemlore usage messages
2021-06-28 20:19:02 +01:00
Flask BotandMD 4e4eb35240 New Crowdin updates (#4182)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-06-27 14:46:04 +01:00
Josh Roy e7e3f58c8f Fix missing translation param in ice command 2021-06-27 14:40:14 +01:00
pop4959andJosh Roy c18830ce75 Fix pickup delay with essentials.build.pickup permission (#4273)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-06-27 02:36:28 +00:00
triagonal 3f36a52685 Prevent NPEs in /powertool (#4276)
This PR fixes a long-standing bug where using the `a:` or `r:` modes in `/powertool` with no commands already set on the item would result in an NPE. To prevent this, the powertool command list is now immediately initialized if it doesn't exist upon retrieval.
2021-06-26 20:11:03 +00:00
Josh Roy 64eb39a417 Add serialization support to kits (#3248)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

This PR makes use of Paper's item serialization to serialize items into base64 allowing for all items to be properly stored without needing to manual write in special cases for every complex item. There is a config option to disable using this new serialization and use the legacy ItemDB serializer since the new serializer removes the ability to manually edit/read kits.

Defaults to not enabled

Note: The new serializer places an @ sign in front of items serialized by the new format in order to quickly determine what is serialized by the new serializer and also to retain backward compatibility with the old serializer.

Att #3114
Att #2867
Att #1694
Att #31
Att #1283
2021-06-26 20:03:27 +00:00
MD 736ecae5ff Add missing FakeServer methods 2021-06-26 20:34:04 +01:00
Flask Bot 6bd4a6a037 Update items.json from generator 2021-06-26 20:34:04 +01:00
Josh Roy 4271938c3a Add ice commmand #EasterEgg 2021-06-26 20:34:04 +01:00
Josh RoyandMD e1441c1d33 Update mobs and NMS usage to 1.17
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-06-26 20:34:04 +01:00
triagonal 9bd35a2cd2 Fix regression in /back behavior (#4264) 2021-06-23 08:44:47 -04:00
triagonal d4e194fbf7 Fix NPE when RTP center isn't set (#4251) 2021-06-19 12:23:00 -04:00
Josh Roy 003d556f3d Fix incomplete cooldowns being added to data (#4249) 2021-06-18 19:28:10 -04:00
triagonal 1da3b3f08e Implement home cleanup utility (#4244) 2021-06-18 11:24:43 -04:00
Josh Roy f08c176b68 Fix invalid home locations giving blank errors (#4240) 2021-06-17 20:28:07 +01:00
Josh Roy 780aadedf2 Fix invalid empty value for BigDecimal (#4237) 2021-06-14 22:15:25 -04:00
Josh Roy 3fe279e229 Fix NPE in UserData#getHome (#4234) 2021-06-14 07:28:42 -04:00
Josh Roy c43af5d3d9 Fix NPE for list group (#4230) 2021-06-12 14:34:58 -07:00
DebugandJosh Roy 65b80200bd Fix incorrect behavior from last PR (#4228)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-06-12 14:14:19 -04:00
DebugandJosh Roy 6e5a41a880 Add option to hide balances <=0 from baltop (#4226)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-06-12 12:31:39 -04:00
Daniil ZandJosh Roy fa87c74b56 Allow enchants on fireworks/charges/potions/banners (#3882)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-06-11 11:13:43 -04:00
antiPerson 8142da608c Fix damage from exploding beds being cancelled (#3874) 2021-06-11 15:06:12 +00:00
Debug eacc748b09 Fix /book command not supporting multi-word title & author (#4180) 2021-06-11 15:00:21 +00:00
Josh Roy def41802b8 Fix broken cooldowns causing exceptions (#4219) 2021-06-09 21:20:51 -04:00
Josh Roy 38dcdff659 Fix invalid mail key (#4218) 2021-06-09 17:03:29 +01:00
Josh Roy bd9540cea3 Fix EssentialsSpawn not defaulting on default group (#4217)
Fix regression from #4072, tested in #essx-support on MOSS
2021-06-09 14:54:47 +01:00
triagonal 0d71fe65f6 Prevent getting jail worlds before world load (#4216) 2021-06-09 08:34:14 -04:00
Josh Roy 54c70581a2 Add config option to respawn jailed players to spawn (#4132) 2021-06-08 21:04:30 -04:00
pop4959 3abddd6f84 Fix tab complete on /enchant failing on empty key (#4215) 2021-06-08 20:50:17 -04:00
Josh Roy 8e712bc827 Fix empty paths with Configurate (#4214) 2021-06-09 00:42:45 +01:00
Jason 2018149395 Clean up Bukkit API exclusion, use dep constraint for SnakeYAML (#4213) 2021-06-08 23:36:43 +01:00
Josh Roy ecea17195a Fix parsing of command cooldowns with invalid data (#4212) 2021-06-08 16:51:47 -04:00
Josh Roy a28d0823b2 Fix invalid CommandCooldowns causing NPEs (#4210)
* Fix invalid CommandCooldowns causing NPEs

* Add Map support
2021-06-08 12:40:52 -04:00
Josh Roy 6e6561a267 Minimize (#4209) 2021-06-07 23:48:18 -04:00
Josh Roy 8b1db22a53 Fix setting of locations to LazyLocations + Shade CheckerQual (#4208) 2021-06-07 23:30:37 -04:00
Josh Roy 3d09ea386b Fix remaining uses of Location over LazyLocation (#4206) 2021-06-07 22:23:13 +00:00
Josh Roy 300daea4f9 Lazy load locations in Configurate (#4203) 2021-06-07 15:52:12 -04:00
Josh Roy 7653da0e4f Migrate to Configurate (#4072)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
Co-authored-by: Riley Park <riley.park@meino.net>
Co-authored-by: zml <zml@aoeu.xyz>

Migrates all uses of SnakeYAML and Bukkit's Configuration API to Sponge's Configurate.

Configurate enables us to the do the following stuff:
* Serialize YAML off the main thread
* (in the future) Automatically update our config
* (in the future) Manipulate comments in configs
* Be epic

This commit also *finally* strips out the 3.x storage/object mapping system in favour of Configurate's object mapper.
2021-06-07 13:49:33 +01:00
JasonandJosh Roy 19b4da07b9 Clean up gradle build scripts
Adds run-paper plugin support as well as moves majority of logic to kotlin build scrips

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-06-07 08:17:39 -04:00
Josh Roy ff9f93fba4 Fix sleeping ignored vanish setting overriding permission (#4201) 2021-06-06 16:12:07 -07:00
Josh Roy fed26071ac Add option to ignore vanished players from sleeping (#4200) 2021-06-06 17:05:16 -04:00
Josh Roy a4fbfbef02 Fix offline player account creation (#4199)
This bug occurs when a player has joined before EssentialsX was installed and a player account creation is requested through Vault while they are offline.

Fixes #4195
2021-06-06 19:40:55 +01:00
Josh RoyandMD 347751a826 Fix branch name when building main branch on GH (#4193)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Fixes #4191.
2021-06-03 17:03:14 +01:00
Josh Roy fbb3a9a0a4 Fix various NPC account lookup issues (#4188) 2021-06-01 15:47:39 -04:00
Josh Roy f8f1facf43 Check legacy "NPC:" UUIDs in VaultEconomyProvider#hasAccount (#4178) 2021-05-31 15:12:06 +00:00
Flask BotandMD 0d6441d0b6 New Crowdin updates (#4145)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-05-29 23:18:25 +01:00
Josh Roy a009b2f9b5 Only use convert spawners tagged by the give command (#3963) 2021-05-28 14:29:52 -04:00
Josh RoyandMD 7852aedabd Add book page serialization (#4048)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Allows serialization of books in `/createkit`. Closes #31.
2021-05-28 18:52:18 +01:00
Josh RoyandMD b2d7d1b4cb Add prevent-type permissions for /itemname (#4133)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Adds `essentials.itemname.prevent-type.<material_name>` permissions. Closes #4044.
2021-05-28 17:35:33 +00:00
Josh Roy ec50b28f4b Add ItemStack 1.8.8+ cross-version PersistentDataContainer (#4143)
This PR itself does nothing on its own but creates the underlying backbone I need to make a less hacky solution in #3963 lmfao.

This PR creates a provider which uses NBT on 1.8.8-1.13 to mimic the exact structure of a PersistentDataContainer on 1.14+ which will allow us make any possible upgrades (which don't die from the lack of DFU on >1.13) work as expected. Additionally, this does not use reflection on modern Minecraft versions and thus will not need to be maintained/updated on MC version updates.

In the future, we will need to find a way to store data on tile entities (signs namely) so that we are able to store UUIDs on signs for future plans, but for now ItemStacks work to fix our spawner issues.
2021-05-28 11:23:44 +00:00
Josh Roy 4ed36d9bbe Add support for dev/rc/beta/alpha build types (#4177)
🍍
2021-05-27 21:16:26 +01:00
l1ttleO 887772a428 Add {PREFIX} and {SUFFIX} to custom join/quit messages (#4169) 2021-05-21 13:28:22 -04:00
MDandJosh Roy e9169b890d Set Gradle toolchain to JDK 16 (#4168)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-05-21 13:15:47 +00:00
MDandJosh Roy b716c04d96 Fix minor issue with server support minor issue fix (#4166)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-05-21 07:24:39 -04:00
l1ttleO 2d3cbc9783 Fix Java version command in pull request templates (#4167) 2021-05-21 07:15:05 -04:00
Josh Roy 23a497abf6 Update Gradle to 7.0.2; misc build improvements (#4148)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

* Bumped Gradle to 7.0.2
* Now uses Java 16 for compiling to prepare for MC 1.17
  * Release target is still set to 8 however
* Bumped Actions versions
* Bumped indra version(s)
* Switched from grgit to indra-git
* Fix PR GitHub Actions jars having invalid branch names
2021-05-19 12:34:00 +01:00
Pretty Princess Sailoriae d00b90cd61 Fix formatting issue in with comma-separated lists (#4157) 2021-05-17 10:12:14 -04:00
Jason f4fe8808a4 Remove the /itemdb alias suggestion (#4152) 2021-05-14 19:26:23 -04:00
Josh Roy 02193b0523 Fix ClassNotFoundException when vault is missing (#4151) 2021-05-14 17:33:25 +01:00
Josh Roy c9310ea429 Fix a minor issue with support status detection (#4150) 2021-05-14 16:01:50 +00:00
Josh Roy c76e4239bb Ensure NPC userdata creation for Citizen NPCs (#4149)
https://github.com/CitizensDev/Citizens2/blob/0ddfcbd0fa99678934e7b086f1a7b92e6a1bc49e/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/entity/HumanController.java#L36-L40

why????????????????????????????????????????????????????????????
2021-05-13 22:30:37 +01:00
Josh Roy 8a8d45c91e Fix vault provider account creation (#4147)
Removed an extraneous name->uuid check
  This check swallowed any warnings spit out by UserMap#trackUUID causing problems
  for plugins which didn't check the success of this method and thus leading to
  possible stack overflow exceptions or other unexpected behavior

Actually save accounts created with VaultEconomyProvider#createPlayerAccount
  Complete oversight as I was under the impression tracking the UUID would create
  the files for us.
2021-05-13 00:52:26 +00:00
Josh Roy 071f99560e Migrate to new Vault provider and economy integration (#3975)
This commit:
- Implements a new provider for VaultAPI's `Economy`
  - The legacy provider built into Vault uses player names, and has not changed since Vault was invented in 1864.
  - This properly supports UUIDs and works more predictably with EssentialsX.
- Replaces the Register method economy abstraction layer abstraction layer with a new `EconomyLayer` economy abstraction layer abstraction layer.
  - This opens the pathway for future economy abstraction layers to be supported.
  - This change also removes dubiously-licensed code from the project.

For users encountering userdata issues on this build, see this FAQ entry:
https://github.com/EssentialsX/Essentials/issues/3956#issuecomment-779254544

Fixes #4110.
Closes #3344.
Closes #2401.
2021-05-10 19:36:09 +00:00
Josh Royandtriagonal 8b23c2c4cd Add helpful command argument descriptions (#4057)
Co-authored-by: triagonal <10545540+triagonal@users.noreply.github.com>
2021-05-10 18:36:30 +00:00
Josh Taylor 0d640d1215 Fix netlify deploy message (#4141) 2021-05-05 09:36:43 -04:00
MD b4d63857ce Fix Javadoc preparation step (#4140) 2021-05-05 08:32:49 -04:00
Josh RoyandMariell Hoversholm 4976484d91 Unformat item lore and name when serializing items (#4095)
Co-authored-by: Mariell Hoversholm <proximyst@proximyst.com>
2021-05-02 16:18:35 -04:00
Josh Roy 16627d10b6 Add way for time and weather signs to display the time/weather (#4131) 2021-05-02 19:34:38 +00:00
Josh Roy 83ca7d2574 Prevent stack overflow when finding an EssX cmd as alternative (#4128) 2021-05-02 15:08:18 -04:00
Josh Roy 956afc0382 Fix kit-auto-equip not working on versions < 1.14 (#4130) 2021-05-02 19:03:43 +00:00
MDandJosh Roy 312ff110ec [CI-SKIP] Automatically publish 2.x javadocs (#4083)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-05-01 16:37:24 -04:00
NotMyFault 214d07c468 [CI-SKIP] Address template deprecations (#4125) 2021-04-25 16:45:04 +00:00
l1ttleOandJosh Roy 2c68d1b866 Fix /pay working on vanished players (#4114)
Vanished players are now treated as offline players.

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-04-18 10:08:07 -04:00
Josh Roy 5171215817 Fix various trade sign issues (#4056)
Fixes various bugs with line length validation and fixes issues with overfilled stacks.
2021-04-14 12:24:24 -04:00
Jason 195148a2a2 Fix disabled commands not marking the command map as dirty (#4108) 2021-04-12 19:08:19 -04:00
pop4959andJosh Roy edf664e19a Unregister disabled commands (#4026)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-04-07 21:37:21 -04:00
Suyeol Jeon 0668e4c48a Fix regression in permission based item spawning (#4100) 2021-04-03 11:38:04 -04:00
triagonal d75787726c Add some extra keywords (#4098) 2021-03-29 13:07:55 -04:00
triagonal 1e0d7fb0a3 Add missing respawn-at-home-bed config option (#4071)
This PR adds a missing config option that was accidentally left out in #3802 (thank you @mibby for the heads up).
2021-03-20 16:09:59 +00:00
Josh Roy 454698bf98 Fix delayed futures never being completed correctly (#4066)
This PR fixes issues related to timed teleports. All timed teleports before this PR would create their own future to send back to the AsyncTeleport api causing the futures returned in the base methods never being completed.

This PR pases the future returned in AsyncTeleport methods to the timed teleport and completes it after the timed teleport has been completed.

Fixes #4065.
2021-03-20 15:48:40 +00:00
Josh Roy bfeb0ef2ad Fix update check on 3+ digit build numbers (#4069) 2021-03-19 00:14:45 -04:00
Josh Roy 1cf2b11f1e Improve currency validation in signs (#3987)
Sanitise currency strings in more cases when handling signs. Should fix #3979.
2021-03-13 16:36:18 +00:00
Josh Roy 0ce4029483 Change createkit to use paste.gg (#4049) 2021-03-09 21:28:07 -05:00
Josh Roy 504e82e9fd Update FakeWorld (#4043) 2021-03-08 15:39:52 -08:00
Josh Roy eca6a9f247 Improve update checker startup message logging (#4039)
* Use plugin logger for update checker
* Use info log level when update checker is disabled

Closes #4038.
2021-03-06 23:52:10 +00:00
Josh Roy 94d4385d41 Fix list command exposing groups with vanished players (#4032)
Fixes #4018.
2021-03-06 19:42:59 +00:00
Josh Roy c41bb0ec42 Delay GeoIP join task to ensure display name is set (#3968)
Delays the GeoIP join messages by 2 ticks to ensure the user's display name has been set by Essentials' `DelayedJoin` task.

Fixes #2388.
2021-03-06 19:22:00 +00:00
Josh Roy 00b71cafc9 Add netherite related items to spawnmob (#4031)
Allows spawning skeletons and zombies with netherite armor and swords. Fixes #4029.
2021-03-06 19:06:31 +00:00
MD 3cd69dabe9 Fix metrics initialising before update checker (#4037)
This PR fixes an issue introduced in #3855, where bStats inits before the update checker and so fails to grab the current release branch.
2021-03-07 03:58:35 +11:00
Josh Roy 10fa3b5a31 Add update checker (#3855)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Adds an update checker to Essentials that will check for the latest version on startup, on player join (permission is `essentials.updatecheck`), or manually with `/ess version`.

On startup, the update checker will fetch build information from a resource generated at compile time and determine if the build is a dev or stable build. If it's a stable build, the update checker will only check for a new release; while a dev build will check for any new commits.

There are 6 different types of messages the update checker will return;
* Identical: The current build is the latest stable release or latest dev build. This message is only shown in the `/ess version` command.
* Behind: If the current build is stable, it's an entire stable build behind, otherwise it's one or more dev builds behind.
* Diverged: The current build was made from a branch other than `2.x` and is also one or more dev builds behind the latest commit on `2.x`.
* Diverged Latest: The current build was made from a branch other than `2.x` but is based on the latest commit from `2.x`.
* Unknown: The current build either has invalid build information or was customly built. This message is show everywhere but on player join.
* Error: There was an error while fetching the latest version information.

Update checks can be disabled using the `update-check` option in `config.yml`.
2021-03-06 16:29:42 +00:00
Josh Roy b43790e9d2 Fix failing gradle build (#4025)
Paper has recently added adventure as a first part library, and thus has shaded it in. Adventure is on maven central, so we must add it in order for Essentials to build.
2021-03-02 04:52:05 +00:00
Frank van der HeijdenandJosh Roy ee5f4b9b42 Fix some futures in AsyncTeleport never completing (#4016)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-02-27 11:19:28 -05:00
MD e5db842dd6 Document XMPP config and require server TLS by default (#4002)
Small changes related to #3962.
2021-02-21 10:46:32 -05:00
Josh Roy 657a11b1f4 Add permission to adjust delay in /spawner command (#3964) 2021-02-21 08:59:57 -05:00
Josh Roy 0ef1e23c69 Show UUIDs in /seen (#3906) 2021-02-21 13:57:19 +00:00
Josh Roy 141512f2f7 Fix afk message being sent when event cancelled (#3915) 2021-02-21 13:54:02 +00:00
Josh Roy 671983724a Fix invalid configuration section for world aliases (#4000) 2021-02-21 00:09:34 -05:00
Josh Roy 81571fc014 Add /toggleshout command (#3965)
Adds a command to toggle shout mode of a user allowing them not to have to prefix all their messages with `!` if they're shouting a lot.

Closes #3958.
2021-02-20 16:37:16 +00:00
Frank van der Heijden f806409d80 Add support for commands.yml aliases in command cooldowns (#3744)
Fixes #2058.
2021-02-20 16:30:07 +00:00
Josh RoyandMD f3c347424b Add new signs for recently added virtual commands (#3961)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

Closes #3919.
2021-02-20 15:59:30 +00:00
Josh Roy 8cbd9b7935 Add /editsign copy + paste (#3989)
Adds `copy` and `paste` subcommands to `/editsign` to allow users to copy and paste the contents of signs without having to use the tab-completion output of `/editsign set`.

The `/editsign copy` command will also perform format-permission checks to ensure the user copying the sign has the proper permission to use said formatting.

Closes #3973.
2021-02-19 15:13:49 +00:00
Josh Roy 158d273dbe Allow blank currency symbols (#3946)
Fixes a behavior previously possible before c6de771

I wrote the proper logic in c6de771 to handle blank currency symbols but forgot to remove the Settings check that was preventing blank currency symbols.

Fixes #3945.
2021-02-19 14:58:42 +00:00
Josh Roy 1258d2f0a1 Fix further incorrect messages for jail countdowns (#3941)
Fixes #3939, fixes #3940.
2021-02-16 13:56:57 +00:00
Josh Roy 36422ab22b Add Baltop API (#3702)
Co-authored-by: Mariell <proximyst@proximyst.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>

This moves storage of balances from the baltop command into the UserMap. This was needed by Glare to able to get a hold of all users balances without causing jvm hell on the usermap.

To access this API as an end user;
```java
import net.essentialsx.api.v2.services.BalanceTop;
//...
BalanceTop api = Bukkit.getServer().getServicesManager().load(BalanceTop.class);
```

Closes #3100, closes #3540
2021-02-15 15:43:10 +00:00
MD 191cea7fb3 Update issue template for new config format (#3982)
This PR updates the issue form to the new schema, which will be introduced on February 19th.

The legacy issue form will be used until the switchover date.
2021-02-15 11:51:26 +00:00
Josh Roy f8cc0e41d1 Update FakeWorld (#3985) 2021-02-12 17:36:38 -08:00
MD f29f9e1aba Relocate GeoIP dependencies (#3976)
Relocates jackson, javatar and the MaxMind database libraries to avoid conflicts with other plugins.
2021-02-10 09:27:56 -05:00
MD 34fdcf8f6f Improve logging of dangerous userdata edge cases (#3969)
This commit improves the logging of a few edge cases that can lead to loss of userdata:
- Third-party plugins forcing creation of a NPC account which could not be found on the usermap, even if the account file exists
- UserMap#trackUUID being called with a conflicting UUID but replace set to false
2021-02-08 15:50:57 +00:00
Josh Roy adef08af3e Add world aliases for Chat (#3912)
Allows world names to be overridden with a defined value from the config in EssentialsX Chat.

Closes #1793.
2021-02-05 20:31:13 +00:00
Josh Roy 1301e8fc99 Show custom item aliases in /itemdb (#3907)
Shows items from the custom_items.yml resolver in /itemdb.

Closes #3686.
2021-02-05 20:27:33 +00:00
Josh Roy eee1c0628b Add API packages (#3938)
Adds packages for future EssentialsX 2.x API and services.
2021-02-05 20:13:03 +00:00
MD 9ce839c9d8 Update issue chooser with forms and Discussions links (#3936)
Co-authored-by: triagonal <10545540+triagonal@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>

This commit:
- Introduces a bug reporting form
- Moves feature requests and support requests to Discussions
- Adds a default PR template instructing people to use the correct template
- Adds GitHub Sponsors and Open Collective to the Sponsor button
2021-02-05 19:58:26 +00:00
Josh Roy 401fc4d2eb Fix AntiBuild performing place checks for block state changes (#3914)
Bukkit sometimes decides that state changes are also a block placement. Work around this by comparing the previous block state with the new block type.

Fixes #3150.
2021-02-05 19:44:43 +00:00
Josh Roy 4e7f1377bf Infer jail name when the server only has one jail configured (#3911)
Closes #1122.
2021-02-05 19:39:20 +00:00
Josh Royanddarbyjack fccf796eeb Add /kitreset command (#3909)
Co-authored-by: darbyjack <admin@glaremasters.me>

Closes #163. Ports #2645, which had its branch messed up by merges.
2021-02-05 19:35:38 +00:00
Josh Roy 500edb7860 Add config option to disable message social spy (#3910)
Adds a config option to allow to prevent socialspy from reading private messages. Useful if all you want from social spy is to read commands defined in the list.

Closes #1095.
2021-02-05 19:02:26 +00:00
pop4959andJosh Roy cb168b0c8a Add support for material tags in enchant signs (#3694)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>

Closes #3653.
2021-02-05 18:57:32 +00:00
Simon 74131f816c Update VersionUtil Support Status Check (#3954)
Removed AirplaneLite as an unsupported fork as they removed their concerning patch.
2021-02-02 09:15:01 -05:00
luisc99andJosh Roy f1ef21eb0d Fix duplicate nickname check with custom display names (#3922)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-28 10:08:38 -05:00
DaniilandJosh Roy 79406613c1 Add /alts alias for /seen (#3931)
Adds `essentials.alts` also to allow server admins to not give mods the ips of users.

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-28 09:29:50 -05:00
Josh Roy 89c1e3b60d Fix invalid formatted jail time strings in whois/seen (#3917)
Fixes #3916, a "slight" discrepancy on jail time reporting in whois and seen commands
2021-01-23 17:41:24 -05:00
224634d8d2 Improve VersionUtil version checks (#3812)
Co-authored-by: Dylan Xaldin <Puremin0rez515@gmail.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2021-01-19 07:40:05 -05:00
Josh Roy e42d37e86d Fix various problems with firework meta (de)serialization (#3905)
Adds support for parsing fireworks charges (would previously cause exceptions due to illegal casts to FireworkMeta) and fixes createkit from producing invalid color values

Thanks to triagonal for reporting the exception caused by createkit.

Fixes #1283.
2021-01-19 07:30:09 -05:00
DaniilandJosh Roy 191c9e9302 Fix bPermissions not showing up as unsupported (#3928)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-18 21:22:04 -05:00
Daniil a482ddf0f6 Fix warp deleting message not showing (#3927)
Fixes #3926
2021-01-18 21:18:13 -05:00
Josh Roy 7245e84f00 Update to 1.16.5 (#3923) 2021-01-16 01:56:56 -05:00
DaniilandJosh Roy e287600c34 Add option to disable custom server-full message (#3899)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-11 00:10:07 -05:00
vk2gpz 817585a9a6 Properly detect enchantments with improper names (#3900) 2021-01-09 15:26:07 -05:00
triagonal eae8bc05c9 Prevent sending usage info when executing alternative commands (#3904) 2021-01-09 13:47:58 -05:00
Josh Roy 026d279556 Add option to only count online jailed time (#3705)
Adds `jail-online-time` config option to only count a player's online time to the jail cap.

This also fixes memory leak in `EssentialsTimer` which I found in the process of testing.

Closes #429.
2021-01-08 20:43:32 +00:00
Josh Roy 8177893e28 Add auto armor equip for kits (#3629)
Adds a config option to have player auto-equip armor from kits.

Also adds a performance improvement for all uses of EnumUtil#getAllMatching by switching it to an EnumSet.

Closes #347, closes #3452.
2021-01-08 20:39:32 +00:00
Josh Roy 70de83827d Allow paying offline users via /pay (#3625)
Closes #3577.
2021-01-08 20:35:00 +00:00
JoshuaandJosh Roy 5af9536db1 Add WarpModifyEvent (#3875)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-08 15:20:03 -05:00
Josh Roy cb7b2dabf5 Block removing hats with curse of binding using direct-hat (#3895)
Fixed loophole that allowed users to bypass curse of binding when using direct hat feature.

Related to #3299, which added similar checks to `/hat`.
2021-01-08 20:11:35 +00:00
LuukandJRoy 01987a0374 Fix AbstractItemDb#getMatching improper block filtering (#3691)
Co-authored-by: JRoy <joshroy126@gmail.com>
2021-01-03 12:45:32 -05:00
Josh Roy 5528b5b92c Allow users to middle click in armor invsee (#3893) 2021-01-02 15:36:12 -05:00
RyanandJosh Roy 1838354b70 Add ending user balance as a final field in trade.log (#3721)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-02 00:12:53 -05:00
1f1edd9b23 Add UserKickEvent (#3490)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>
Co-authored-by: JRoy <joshroy126@gmail.com>
2021-01-02 00:04:13 -05:00
kmecppandJRoy 2b7a4364b1 Don't lowercase item names in IItemDb#get(String, int) (#3636)
Co-authored-by: JRoy <joshroy126@gmail.com>
2021-01-01 23:16:24 -05:00
2f45d3edf3 Add API to register enchantment names/aliases (#3669)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>
Co-authored-by: JRoy <joshroy126@gmail.com>
2021-01-01 23:11:22 -05:00
Olivia a20d20574e Add TransactionEvent for successful transactions (#3649) 2021-01-01 22:45:52 -05:00
pop4959andJosh Roy d78832498e Fix warp messages sending incorrectly with teleport delays (#3696)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-01 22:06:09 -05:00
MDandJosh Roy e2c6170eba Add option to disable players respawning at beds (#3802)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-01 22:02:55 -05:00
powercas_gamerandJosh Roy 6864af762c Update plugin.yml (#3832)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2021-01-01 21:58:59 -05:00
OmegaWeaponDev a8daa2160f Separate option to cancel afk on chat from interact (#3863) 2020-12-31 19:20:08 -05:00
triagonal b84aaa1dab Fix behaviour of /list when showing real names (#3858) 2020-12-31 19:14:39 -05:00
Josh Roy c6de77130f Rework Currency Symbol Parsing (#3628)
* Made currency symbol standardized in Kit 
  * Kits should be in a standardized format.
  * Having monetary rewards in kits should not break based off of a config value oriented around currency display (suffix).
  * Additionally, the dollar sign should be the standard money symbol which works on all servers and should not be tied to the server's individual currency symbol.
  * Note that the server's individual currency symbol will still work but probably should not be used due its volatility.
* Reworked config currency symbol parsing in Settings 
  * Simplifies the number of actions needed to sanitize the input.
  * Now just defaults to `$` if the currency-symbol cannot be parsed.
* Removed symbol-suffixed parsing in NumberUtil#sanitizeCurrencyString
2020-12-30 15:37:30 -05:00
Josh Roy 02ba924f33 Fix AlternativeCommandsHandler not detecting some aliases (#3856) 2020-12-30 11:59:38 -08:00
Josh Roy 318df64e54 Fix Improper Use of Event API (#3853)
* Fix Improper Use of Event API

* Checkstyle OOOO
2020-12-21 18:35:58 +00:00
Josh Roy cb45867de8 Remove Jenkins from README (#3850)
EssX website is PogU, Jenkins is PogO
2020-12-21 15:21:36 +00:00
Jason c4ca0e51e2 Add restart note to respawn listener priority option (#3789) 2020-12-20 20:41:26 -08:00
triagonal 7bb12fd33c Fix incorrect radius check in /me command (#3848) 2020-12-20 20:08:11 -08:00
MD 001213ee2c Update bStats Metrics class to 1.8 (#3845)
Replace modified bStats Metrics 1.7 class with official 1.8 artifact. 1.8 adds random submit intervals and changes the way submissions are scheduled, and our custom modifications are no longer needed as we now use the plugin ID.
2020-12-19 16:27:07 +00:00
Josh Roy f320705216 Fix build script not working when git repo isn't cloned (#3831) 2020-12-13 20:54:54 -08:00
Josh Roy d4ed77fdc6 Update FakeServer (#3829) 2020-12-13 18:11:29 -08:00
Josh Roy ef1202923e Fix command handler not finding dynamically registered commands (#3816)
Fixes #3815.
2020-12-12 13:50:34 +00:00
e1e98f5048 Add /warpinfo command (#3820)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2020-12-11 16:38:10 +00:00
MD 5bb3cc88e5 Dispatch commands on main thread when run from XMPP (#3803)
This PR fixes an issue reported on Discord, where commands executed through XMPP are dispatched async from the XMPP listener thread.
2020-12-11 16:36:29 +00:00
Josh Roy 43eff69a2f Fix incompatibility on TacoSpigot and derivatives (#3817)
The countless support tickets from users of TacoSpigot (and forks of it)
have become so numerous it's not worth holding our ground on this. Taco fucked up
by going against upstream revision numbers, but as they're defunct, it's not
going to change now. We will not fix server implementations that do this in the
future, but hopefully they have all learned their lesson to use proper
versioning. The way md_5 intended.
2020-12-11 16:31:40 +00:00
Roman Zabaluev 450da59057 Allow console name to be translated (#3700)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>

Closes #2756. 

Notes:
- I've kept original name for internal purposes (storing bans, etc), just in case user wants to rename it to something unbearable;
- User can message console via both names (since he can try both, duh);
- Some refactoring. Just a little.
2020-12-11 16:29:48 +00:00
Josh Roy dda4d58a23 Move paperlib off snapshot (#3808) 2020-12-02 19:44:35 +00:00
Josh Roy f5d5ed9844 Fix respawn anchor explosion protection (#3778)
Fixes #3723, where respawn anchors explode at charges > 0 when glowstone isn't in the players hand, or at charges = 4 on right click regardless of item.
2020-12-02 19:43:01 +00:00
MD 2342d53dba Revert Spanish translation spam 2020-11-30 16:33:24 +00:00
MD 005d83c6fe Fix checkstyle-idea default settings 2020-11-30 15:01:14 +00:00
Flask Bot 8c4fb3fefb New Crowdin updates (#3793) 2020-11-28 11:10:49 +00:00
MD b0fa0d1ec8 Remove mavenLocal repo from default repositories 2020-11-26 12:39:52 +00:00
MD 2ce5797d5e Update metrics warnings 2020-11-26 12:26:08 +00:00
MD c2617e5a1d Fix javadoc warning suppression 2020-11-26 01:19:16 +00:00
MD 3f73e31d9e Suppress deprecation and javadoc warnings by default 2020-11-26 01:12:58 +00:00
MD 84a48adbdd Change resolution strategy to refresh snapshot dependencies more frequently 2020-11-26 01:04:11 +00:00
MD 63c29a318e Implement missing FakeWorld methods 2020-11-26 00:42:26 +00:00
MD 69748fd72e Make resource processing depend on version string 2020-11-26 00:38:27 +00:00
MD 35278c6e90 Upload GH artifacts before publish 2020-11-25 20:43:23 +00:00
MD 088fc35435 Mark gradlew as executable 2020-11-25 20:37:45 +00:00
MD 4baad6b89c Fix missing "s" which prevented GH Actions deployment 2020-11-25 20:33:33 +00:00
Josh Roy 9a23f806fe Refactor Project to Gradle (#3720)
Gradle is better than Maven, don't @ me. okay but actually it's [faster](https://www.youtube.com/watch?v=atuFSv2bLa8&feature=youtu.be&t=77), compiles and tests in parallel more efficiently, and more epic stuff).
2020-11-25 20:24:24 +00:00
powercas_gamer 82b466db0b update website links in config.yml (#3779)
Updates the website links in the config.yml to `essentialsx.net` instead of `essentialsx.cf`
2020-11-18 10:23:45 +00:00
JRoy a6edf04a60 Release 2.18.2 2020-11-16 11:32:40 +00:00
Josh Roy 3ba675a899 Allow users to modify existing homes when over homes limit (#3776)
Fixes #3761
2020-11-15 17:40:34 +00:00
pop4959 1fed7a9a3a Fix tab completion for delhome (#3775)
There was a minor issue with one line of code in delhome which was causing the user's homes not to be listed since the condition was effectively reversed. This was also causing an NPE for console senders attempting to tab complete.

Fixes #3774
2020-11-14 22:50:48 +00:00
80fb91b6d7 Fix /clear <player> <item> not clearing offhand (#3715)
Co-authored-by: Kenichycmd <kenichycmd@gmail.com>
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2020-11-09 16:43:55 +00:00
Josh Roy 5b0c2a4131 Use ray-tracing based target block for 1.13.2+ (#3763)
Uses new method of ray-tracing to target block instead of using BlockIterator which doesn't respect the player looking at a different y-level. Falls back to old BlockIterator method on server versions older than 1.13.2

Fixes #3756.
2020-11-09 16:38:02 +00:00
Josh Roy bb43e8f7b6 Add option to prevent replying from vanished players (#3703)
Closes #2066
2020-11-09 12:25:16 +00:00
Josh Roy 4d9a10147a Split backup command by space (#3762)
Splits command sent to ProcessBuilder by spaces. Fixes backup commands with arguments

Fixes #3759
2020-11-09 08:52:35 +00:00
Josh Roy ea4f5cf919 Update to 1.16.4 (#3753) 2020-11-02 13:36:48 -08:00
dependabot[bot] 295bc9f76d Bump junit from 4.12 to 4.13.1 (#3726)
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-23 00:19:54 +01:00
MD 327806d9a1 Add new PR templates and revamp issue templates (#3711)
Adds new PR templates and revamps the existing issue templates to (hopefully) make the PR/issue submission easier to understand.
2020-10-23 00:19:04 +01:00
Josh Roy 80e7558fc0 Move some tab complete messages to debug mode only (#3739) 2020-10-22 12:15:02 -07:00
md678685 9cda59511e Consolidate permissions default setup into single class 2020-10-19 21:27:05 +01:00
md678685 c12a031918 Add more specific platform support messages 2020-10-19 21:26:38 +01:00
Alexander SöderbergandMD a4f580e319 Make HelpInput use the known command map (#3675)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>

This PR makes HelpInput use knownCommands in the command map, rather than relying on plugin descriptions. This means that commands that have been registered programmatically (or impromptu) will be recognized and displayed in the help menu.

The ugly reflection is due to Spigot not exposing the command map getter. This has been tested on Spigot 1.8, 1.16.3 and Paper 1.16.3 with plugins that register commands directly to the command map.
2020-10-19 15:27:40 +01:00
md678685 583bf88207 Implement ConsoleCommandSender methods 2020-10-19 15:06:50 +01:00
montlikadani a5f8e83e03 Remove redundant checks in the tree command (#3716) 2020-10-13 00:47:12 -07:00
Alexander Söderberg e2f17f11d3 Tab-complete plugin names in /help (#3713)
Plugin names are accepted as input to `/essentials:help`, but they are not completed. This makes it easier to lookup plugin commands.
2020-10-07 11:54:30 -07:00
md678685 4f8c040d06 Add link to Register license notice 2020-10-05 17:34:55 +01:00
Josh Roy af1d068022 Add more inventory commands (#3704)
Add more commands to access virtual crafting inventories (on platforms with the required APIs).

Closes #3195 and #108.
2020-10-05 14:45:33 +01:00
Andre_601andMD 1bd8d1c85c Update old EssentialsX URLs in issue templates (#3678)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>

This updates the old essentialsx.github.io domains to the new essentialsx.net one.
2020-10-05 14:20:17 +01:00
pop4959 d7fc859d14 Use username instead of nickname in console /sell messages (#3693)
Closes #3662

This PR changes the default placeholder `{0}` to username. Display name can still be used with `{5}`.

Before change:
messages.properties
`itemSoldConsole=\u00a7e{0} \u00a7asold\u00a7e {1}\u00a7a for \u00a7e{2} \u00a7a({3} items at {4} each).`
console
`[Essentials] ~test sold dirt for $64 (64 items at $1 each).`

After change:
messages.properties
`itemSoldConsole=\u00a7e{0} \u00a7asold\u00a7e {1}\u00a7a for \u00a7e{2} \u00a7a({3} items at {4} each).`
console
`[Essentials] pop4959 sold dirt for $64 (64 items at $1 each).`

messages.properties
`itemSoldConsole=\u00a7e{5} \u00a7asold\u00a7e {1}\u00a7a for \u00a7e{2} \u00a7a({3} items at {4} each).`
console
`[Essentials] ~test sold dirt for $64 (64 items at $1 each).`
2020-10-05 09:50:56 +01:00
Ryan ac953cd114 Remove sign color codes from trade.log (#3699)
Started looking into #3116 but noticed this issue and wanted to fix it first.

Also added a comment as to why the username is truncated as this took me a long time to figure out and seemed arbitrary. Probably could truncate the username later for trade signs only as other signs don't require any username to be present on the sign. However, for now I simply opted to document.
2020-10-05 09:47:12 +01:00
md678685 6e6dd041d5 Add Javadocs to most API classes 2020-10-04 19:59:42 +01:00
md678685 9b3c8a9df2 Reformat and fix Checkstyle violations 2020-10-04 19:59:42 +01:00
md678685 234a481b0d Add Checkstyle rules and suppressions
This commit adds Checkstyle rules for EssentialsX, based on those from the adventure library (https://github.com/KyoriPowered/adventure/tree/master/.checkstyle).

It also makes Checkstyle passes mandatory for a GH build to pass.
2020-10-04 19:59:42 +01:00
pop4959 89b3c9233e Bump Paperlib version (#3695) 2020-10-03 12:29:43 -07:00
Josh Roy 5d61a43fe1 Add balanceTopLine i18n translation string (#3626) 2020-09-25 19:16:12 -07:00
Olivia 29d5ccf751 Fix /lightning ignoring essentials.lightning.others (#3671) 2020-09-13 17:23:50 -07:00
l1ttleO fde0665593 Update Readme to list 1.16.3 as a supported version (#3666) 2020-09-12 10:58:54 -07:00
l1ttleO 29541b0995 Bump supported server version to 1.16.3 (#3664) 2020-09-12 10:37:18 -07:00
Josh Roy 05f31c5ddf Fix time command throwing exceptions when used in console (#3622)
Fixes #3621
2020-09-09 16:00:13 +01:00
JRoy f89840545d Revert clearinventory command to pre-command-refactor
This command is too big to rewrite without a behavior change
2020-09-09 15:52:59 +01:00
JRoy 18534f8576 Fix inverted messages in Commandpaytoggle 2020-09-09 15:52:38 +01:00
JRoy 1382a05dc8 Fix IEssentials#getOnlineUsers ABI breakage 2020-09-09 15:52:37 +01:00
JRoy 1671d4839c Clean up some tab completes that I missed 2020-09-09 15:52:29 +01:00
Josh Roy 4d2fbd04c5 Fix /sell and /worth messages displaying {1} instead of the item(s) value (#3656) 2020-09-08 12:36:05 -07:00
md678685 dc6b9b89cd Handle funky bStats classloading correctly
Fixes bStats for plugins that do funky classloading things, like AntiVPN.
2020-08-29 20:37:20 +01:00
Josh Roy 76f36aa895 [CI-SKIP] Some issue template updates (#3623)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>

* Forces use of issue templates
* Auto-labels issue templates
* Links to discord in the issue-create matrix as a better form of troubleshooting
2020-08-29 17:21:22 +01:00
Josh Roy fa835e9c64 Fix tablist name getting reset after AFK status change (#3624)
Fixes #3606.
2020-08-29 16:03:49 +01:00
md678685 c8dc874a56 Release 2.18.1 2020-08-23 17:18:31 +01:00
Josh Roy b3a6307052 Fix miscellaneous command permission bugs (#3616)
Fixes #3612, in which `/list`'s tab complete previously revealed the presence of vanished players in certain groups, and fixes #3613 in which `/time set` did not properly check whether the player had permission to change the time.
2020-08-22 20:07:21 +01:00
pop4959 3ca0181b79 Update block damaging checks in teleportation safety (#3537)
This PR updates and improves LocationUtil#isBlockDamaging. Notably, the lava check is fixed (it was not working on older versions of Minecraft), many newer materials that were missing damage checks have been added, and the code is a bit cleaner. I've tested this code on MC 1.8 and 1.16 and everything should work correctly.
2020-08-22 18:37:15 +01:00
pop4959 98557dcecf Add world border checking to teleportation safety (#3536)
Fixes #3499. Please see that issue for discussion about the bug.

Fix demonstration with `/tppos`: https://imgur.com/a/Mo4okQa
2020-08-22 17:51:23 +01:00
MD 3351092c79 Add "amplifier" potion meta attribute (#3614)
Adds an `amplifier:<value>` potion meta attribute to MetaItemStack that applies a raw amplifier value, instead of translating inputs between 1 and 3 to match their vanilla names like `power:<value>` does. This matches the Mojang `/effect` command, which doesn't translate any potion effect amplifiers, and allows for creation of level IV potions (using `amplifier:3` instead of a `power` value) through EssentialsX without breaking existing usages of `power:<value>`.

More context for this commit can be found at https://github.com/EssentialsX/Essentials/pull/3592#issuecomment-678656107.

Closes #3592 and fixes #3589.
2020-08-22 17:40:40 +01:00
pop4959 68cd509d41 Fix /bigtree command on versions 1.12 and prior (#3569)
Fixes #3549.

Replaces the `Block#isPassable` check with `Material#isSolid`, which is compatible with all versions through 1.8 to 1.16.
2020-08-22 17:01:50 +01:00
xtomyserrax 5fa65b42ae Fix creeper player damage being cancelled incorrectly (#3604)
Previously, disabling creeper explosion block damage with the `protect.prevent.creeper-explosion` setting would also prevent players getting hurt. This fixes this, meaning servers can disable creeper block damage while still allowing players to take damage from creeper explosions.
2020-08-22 16:55:32 +01:00
Andre_601 bdae259a1d Update links in readme (#3596)
This PR updates several links:

- https://essentialsx.cf -> https://essentialsx.net
- Wiki links (Either github.io links or GitHub Wiki links) now point to their corresponding pages on the actual wiki (https://essentialsx.net/wiki/Home.html)
2020-08-22 16:49:50 +01:00
Jason fea86ea70e Add config note regarding displayname (#3535) 2020-08-22 14:15:24 +01:00
Josh Roy 1133770aeb Fix changing players' gamemodes from the console (#3590)
Fixes #3587 which was introduced by #3337.
2020-08-21 16:37:54 +01:00
MD 2c462106bc Fix blacklisted blocks being pulled by pistons (#3610)
Closes #3609.
2020-08-21 16:31:34 +01:00
triagonal bd1e624c91 clarify piston blacklist config comment 2020-08-21 23:00:56 +10:00
triagonal 6875fa4c7e fix blacklisted blocks being pulled by sticky piston 2020-08-21 23:00:18 +10:00
pop4959 def5f2155d Fix compilation issues on latest Spigot/Paper (#3607) 2020-08-20 19:24:42 +01:00
triagonal 73d2457fea Fix regression with teleportation commands where command cost gets charged twice (#3601) 2020-08-16 14:40:58 -07:00
MD bc52ca856e Update to 1.16.2 (#3586) 2020-08-11 23:12:41 +01:00
Flask Bot 18ccea320a Update items.json from generator
Note: this action was performed by a human (@md678685) under the
Flask-Bot account.
2020-08-11 23:06:07 +01:00
Josh Roy f6cb9ff470 Improve command codestyle (#3337)
Co-authored-by: MD <1917406+md678685@users.noreply.github.com>

Fixes #3579 (async `/skull` command)
Fixes #3336 (improve codestyle of commands)
Partially addresses #3339 (`/spawn` and `/setspawn` are now hidden from tabcomplete)
Closes #3087 (`/paytoggle` is now a loop command)
2020-08-11 19:09:22 +01:00
pop4959 14c6c2a055 Fix spawner delay feature (#3239)
Closes #1332

The spawner delay feature has been broken in Essentials for as long as anyone can remember. The reasons for this are mentioned in the issue above.

This PR fixes this by changing the command to utilize new API for setting the minimum and maximum spawn delay on spawners. This API was added in 1.12.2, so all supported versions before that (1.8.8 thru 1.12.1) require NMS to function properly. I'm aware that Essentials avoids NMS for maintainability reasons, however that should not be of much concern here since all versions 1.12.2 and later are going to be using the Bukkit API. Hence, no NMS updates will be necessary.

Also let me know if you want the NMS code refactored somewhere else. I saw the net.ess3.nms packages, but I wasn't sure where this would fit into the organisation of that.

Tested on:
1.8.8, 1.9.4, 1.10.2, 1.11.2 (NMS)
1.12.2, 1.15.2 (Bukkit API)
2020-08-05 20:47:42 +01:00
pop4959 68d0aa9688 Improve GeoIP errors/warnings (#3568)
Aims to provide a better and less confusing error for users of GeoIP who have not configured properly (which is more common now after the license key requirement). Seems like a lot of people miss the initial error on startup, and so this may help a bit.

Closes #3561.
2020-08-04 16:22:14 +01:00
MD 1ef4e15a4d Fix regression in /tpaccept (#3567)
d9bf099c3d introduced a regression where `/tpaccept` no longer unconditionally cancels the teleportation request when it is accepted. This restores the previous request cancellation behaviour.

Fixes #3563.
2020-08-04 16:20:24 +01:00
latiku d35b4b9128 Don't send chat messages if they only contain formatting codes (#3505)
Closes #3442.

When a player with the appropriate permission node to use color formatting codes sends a message with nothing but valid formatting code, a message with an empty line is sent. This PR solves this issue by checking if the final formatted message is just a color code, and if so, cancels the event.

Below are some attached screenshots with debug messages containing the event's raw message, the event message after it is formatted, and in the case of the "before" screenshot, the message sent in-game. 

Before: https://user-images.githubusercontent.com/47498808/87505823-7a8c5080-c62f-11ea-99b4-29fc6eaff042.png

After: https://user-images.githubusercontent.com/47498808/87505857-8c6df380-c62f-11ea-95f8-dd59dadaf27c.png
2020-08-04 15:38:53 +01:00
Zefferis 6995be7dbd Updated website in plugin.yml files (#3528)
Replaced "http://tiny.cc/EssentialsCommands" with "https://essentialsx.net"
2020-08-04 15:35:34 +01:00
uf0h f9de6763d3 Use playerNeverOnServer message where appropriate (#3489)
### Use `playerNeverOnServer` message where it should be used:

### https://github.com/EssentialsX/Essentials/pull/3489/commits/309e1c470d4bcfe2a2186f9720ce33f26441b802 
`playerNeverOnServer=\u00a74Player\u00a7c {0} \u00a74was never on this server.` is currently used in the **Commandmail** class at [Line 61](https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/commands/Commandmail.java#L61), [Line 116](https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/commands/Commandmail.java#L116) and [Line 129](https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/commands/Commandmail.java#L129) however is **not called** as PlayerNotFoundException is thrown by `#getPlayer` breaking current execution (below).
```
Commandmail#run() throws Exception {
...
    User u = getPlayer(server, args[1], true, true); // throws PlayerNotFoundException
        if (u == null) {
            throw new Exception(tl("playerNeverOnServer", args[1]));
    } 
...
}
```

Before changes:
![bm](https://user-images.githubusercontent.com/24858857/87236993-6b679180-c3e8-11ea-83a7-002194f5c467.png)
After changes:
![mailafter](https://user-images.githubusercontent.com/24858857/87237060-3576dd00-c3e9-11ea-8020-d5a80a958ca0.png)

--------------------------

**Commandseen** currently throws the default PlayerNotFoundException `playerNotFound` message for players that have not logged on to the server where it would be more appropriate to use the `playerNeverOnServer` message.

```
Commandseen#run throws Exception {
...
    AsyncRunnable#run() {
        User userFromBukkit = ess.getUserMap().getUserFromBukkit(args[0]);  <-- ***
        try {
            if (userFromBukkit != null) {                             <--- ***
                showUserSeen(userFromBukkit);
            } else {
                showUserSeen(getPlayer(server, sender, args, 0)); <--- ***
            }
        } catch (Exception e) {
            ess.showError(sender, e, commandLabel);
        }
    }
    private void showUserSeen(User user) throws Exception {
        if (user == null) {                                    <--- ***
            throw new PlayerNotFoundException();
        }
        showSeenMessage(server, sender, user, showBan, showIp, showLocation);
    }
...
}
```

**`<-- ***`:**
`usersFromBukkit` null check is performed before `#showUserSeen` so there is no need for another null check.
`EssentialsCommand#getPlayer` throws **NotEnoughArguementsException** or **PlayerNotFoundException** after [arg checks](https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/commands/EssentialsCommand.java#L88) and [player checks](https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/commands/EssentialsCommand.java#L103). https://github.com/EssentialsX/Essentials/pull/3489#issuecomment-657138524

Before changes:
![sb](https://user-images.githubusercontent.com/24858857/87237038-e9c43380-c3e8-11ea-8294-8f91e8b6f25d.png)
After changes:
![seenafter](https://user-images.githubusercontent.com/24858857/87237067-53444200-c3e9-11ea-92c5-1784b4dcd739.png)

----------------

### 725128e
Catch more specfic exception `PlayerNotFoundException`.

Before changes:
![banbefore](https://user-images.githubusercontent.com/24858857/87237021-c1d4d000-c3e8-11ea-99e4-eb97b5a5ba6d.png)
After changes:
![afterunban](https://user-images.githubusercontent.com/24858857/87237081-8d154880-c3e9-11ea-9d35-a25b8c105969.png)
2020-08-04 15:30:05 +01:00
pop4959 dfce971565 Fix stale tpaccept 2020-08-04 02:37:37 -07:00
Olivia 97e3f32d7f Fix some minor errors in config/docs (#3566) 2020-08-04 01:13:09 -07:00
Cameron Seid c86e1a5973 Update backup comments in config.yml to be less ambiguous (#3553) 2020-07-30 21:58:19 -07:00
md678685 ab545dc44c Fix rare ConcurrentModificationException in quit handler
Copies the list of viewers before iterating over it to fix a rare CME that is *sometimes* thrown. It's not clear *why* this broke - Spigot seems to have changed behaviour in late 1.15 or 1.16?
2020-07-30 14:23:22 +01:00
Josh Roy 11cd57e8fb Add sign text tab completion in editsign command (#3497) 2020-07-28 13:12:07 -07:00
pop4959 b7c18d0785 Add missing getRecipe(NamespacedKey) method to FakeServer (#3530) 2020-07-22 12:02:00 -07:00
Josh Roy 9aec89f381 Properly handle jail respawn in togglejail (#3522) 2020-07-21 16:22:20 -07:00
Josh Roy e34984513d Add config option to respawn at respawn anchors (#3498) 2020-07-17 14:13:30 -07:00
Tim S cedd42f411 Fix how random teleport picks a location (#3480) 2020-07-17 13:56:48 -07:00
untuned c9b75aabb2 include new sign types in definitions (#3487) 2020-07-11 15:21:33 -07:00
pop4959 41ae12ed3d Disable random tp pre-cache by default (#3496) 2020-07-11 12:35:19 -07:00
JakeTS dba35f043d Typo, should be "match" not "maths". (#3488) 2020-07-10 21:10:12 -07:00
918 changed files with 114405 additions and 53372 deletions

No files matched your search

+183
View File
@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!-- based on https://github.com/KyoriPowered/adventure/blob/master/.checkstyle/checkstyle.xml -->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, properties, xml"/>
<property name="severity" value="error"/>
<!-- https://checkstyle.org/config_filefilters.html#BeforeExecutionExclusionFileFilter -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#FileTabCharacter -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<!-- https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${configDirectory}/suppressions.xml"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter -->
<module name="SuppressWithPlainTextCommentFilter"/>
<module name="TreeWalker">
<!-- https://checkstyle.org/config_misc.html#ArrayTypeStyle -->
<module name="ArrayTypeStyle"/>
<!-- https://checkstyle.org/config_imports.html#AvoidStarImport -->
<module name="AvoidStarImport"/>
<!-- https://checkstyle.org/config_misc.html#AvoidEscapedUnicodeCharacters -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowByTailComment" value="true"/>
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
<property name="severity" value="info"/>
</module>
<!-- https://checkstyle.org/config_misc.html#CommentsIndentation -->
<module name="CommentsIndentation"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad -->
<module name="EmptyForInitializerPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad -->
<module name="EmptyForIteratorPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyLineSeparator -->
<module name="EmptyLineSeparator">
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
<property name="tokens"
value="CLASS_DEF, CTOR_DEF, ENUM_DEF, IMPORT, INSTANCE_INIT, INTERFACE_DEF, METHOD_DEF, STATIC_IMPORT, STATIC_INIT, VARIABLE_DEF"/> <!-- remove PACKAGE_DEF, temporarily remove COMPACT_CTOR_DEF, RECORD_DEF -->
</module>
<!-- https://checkstyle.org/config_coding.html#FallThrough -->
<module name="FallThrough">
<property name="checkLastCaseGroup" value="true"/>
</module>
<!-- https://checkstyle.org/config_design.html#FinalClass -->
<module name="FinalClass"/>
<!-- https://checkstyle.org/config_coding.html#FinalLocalVariable -->
<module name="FinalLocalVariable"/>
<!-- https://checkstyle.org/config_whitespace.html#GenericWhitespace -->
<module name="GenericWhitespace"/>
<!-- https://checkstyle.org/config_design.html#HideUtilityClassConstructor -->
<module name="HideUtilityClassConstructor"/>
<!-- https://checkstyle.org/config_imports.html#IllegalImport -->
<module name="IllegalImport">
<property name="illegalPkgs" value="sun, jdk, com.sun"/>
</module>
<!-- https://checkstyle.org/config_coding.html#IllegalTokenText -->
<module name="IllegalTokenText">
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
<property name="tokens" value="CHAR_LITERAL, STRING_LITERAL"/>
</module>
<!-- https://checkstyle.org/config_misc.html#Indentation -->
<module name="Indentation"/>
<!-- https://checkstyle.org/config_blocks.html#LeftCurly -->
<module name="LeftCurly"/>
<!-- https://checkstyle.org/config_naming.html#MethodName -->
<module name="MethodName">
<property name="format" value="^[_]?[a-z][a-zA-Z0-9]*$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#MethodParamPad -->
<module name="MethodParamPad"/>
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocMethod -->
<module name="MissingJavadocMethod">
<property name="allowMissingPropertyJavadoc" value="true"/>
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- remove CTOR_DEF, COMPACT_CTOR_DEF -->
</module>
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocType -->
<module name="MissingJavadocType"/>
<!-- https://checkstyle.org/config_coding.html#NoFinalizer -->
<module name="NoFinalizer"/>
<!-- https://checkstyle.org/config_whitespace.html#NoLineWrap -->
<module name="NoLineWrap"/>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceAfter -->
<module name="NoWhitespaceAfter">
<property name="allowLineBreaks" value="false"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceBefore -->
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
</module>
<!-- https://checkstyle.org/config_coding.html#OneStatementPerLine -->
<module name="OneStatementPerLine"/>
<!-- https://checkstyle.org/config_misc.html#OuterTypeFilename -->
<module name="OuterTypeFilename"/>
<!-- https://checkstyle.org/config_imports.html#RedundantImport -->
<module name="RedundantImport"/>
<!-- https://checkstyle.org/config_modifier.html#RedundantModifier -->
<module name="RedundantModifier">
<property name="tokens"
value="ANNOTATION_FIELD_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INTERFACE_DEF, VARIABLE_DEF"/> <!-- remove METHOD_DEF and RESOURCE -->
</module>
<!-- https://checkstyle.org/config_coding.html#RequireThis -->
<module name="RequireThis"/>
<!-- https://checkstyle.org/config_blocks.html#RightCurly -->
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INSTANCE_INIT, LITERAL_FOR, LITERAL_WHILE, METHOD_DEF, STATIC_INIT"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="option" value="same"/>
<property name="tokens"
value="LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF, LITERAL_TRY"/> <!-- add LITERAL_DO -->
</module>
<!-- https://checkstyle.org/config_whitespace.html#SingleSpaceSeparator -->
<module name="SingleSpaceSeparator">
<property name="validateComments" value="true"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#TypecastParenPad -->
<module name="TypecastParenPad"/>
<!-- https://checkstyle.org/config_coding.html#UnnecessaryParentheses -->
<module name="UnnecessaryParentheses"/>
<!-- https://checkstyle.org/config_imports.html#UnusedImports -->
<module name="UnusedImports"/>
</module>
</module>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "http://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<suppress files=".*[\\/]test[\\/]java[\\/].*" checks="RequireExplicitVisibilityModifier"/>
<!-- TODO: don't suppress I-prefixed API interfaces under com.earth2me.essentials -->
<suppress files="(com[\\/]earth2me[\\/]essentials[\\/]|net[\\/]ess3[\\/]|net[\\/]essentialsx[\\/])(?:[^\\/]+$|(?!api)[^\\/]+[\\/])" checks="MissingJavadoc(Method|Type)"/>
</suppressions>
+2 -2
View File
@@ -1,10 +1,10 @@
# Currently used
patreon: EssentialsX
ko_fi: essentialsx
github: EssentialsX
open_collective: EssentialsX
# Currently not used
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#open_collective: # Replace with a single Open Collective username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
+17
View File
@@ -0,0 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: View common issues
url: https://github.com/EssentialsX/Essentials/issues/3956
about: Check the list of known common issues to see if it solves your problem.
- name: Check past help requests
url: https://github.com/EssentialsX/Essentials/discussions/categories/q-a
about: Visit the Q&A forum to search for previous help requests.
- name: Check past feature suggestions
url: https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+label%3A%22type%3A+enhancement%22
about: Visit the Feature Suggestions forum to view and discuss existing feature suggestions.
- name: Create help request
url: https://github.com/EssentialsX/Essentials/discussions/new?category=q-a
about: Create a support ticket to get help from developers.
- name: Get help from the community on Discord
url: https://discord.gg/casfFyh
about: Join the MOSS Discord for community-powered EssentialsX support!
-45
View File
@@ -1,45 +0,0 @@
---
name: Help!
about: Encountered a problem with EssentialsX? Not sure how to fix it?
---
<!-- EssentialsX help guide
Don't write inside the arrows as they will be hidden when you post your issue.
Want faster support? Come join our Discord server to get help sooner:
https://discord.gg/F7gexAQ
If you're happy to wait (or you were sent here from Discord), read on:
1. Fill out the template.
This will help us understand what problem you've encountered and help us
find a solution.
2. Check the Common Issues page.
Read through the wiki page to see if you've encountered a regular issue:
https://essentialsx.github.io/#/Common-Issues
3. Delete this line and all above lines before posting your issue! -->
### Information
Full output of `/ess version`:
<!-- Replace this with the command output -->
**Server log:** <!-- Upload `logs/latest.log` to https://gist.github.com/ -->
**EssentialsX config** <!-- Upload `plugins/Essentials/config.yml` to https://gist.github.com/ -->
### Help request
**Problem**
<!-- What problem did you encounter? -->
**What I have tried**
<!-- What have you tried so far? -->
**Screenshots**
<!-- If relevant, include any screenshots here. -->
-58
View File
@@ -1,58 +0,0 @@
---
name: Report a bug
about: Report an EssentialsX bug. Only use this if you're 100% sure there's something wrong with EssentialsX - otherwise, try "Help!".
---
<!-- EssentialsX bug reporting guide
Don't put anything inside this block, as it won't be included in the issue.
If you are reporting a bug, please follow the following steps:
1. Fill out the template in full.
Run the commands in the console. Don't just put "latest" as a version,
or we will ignore it.
2. When linking files, do not attach them to the post!
Copy and paste any logs into https://gist.github.com/, then paste a
link to them in the relevant parts of the template. Avoid using
Hastebin or Pastebin, as this makes providing support more difficult.
**DO NOT drag files into this box!**
3. If you are reporting a performance issue, please include a link to a
Timings and/or profiler report.
4. If you are reporting a bug with commands or something else in-game,
please include screenshots to help us diagnose the problem.
5. Make sure not to write between the arrows, as anything there will be
hidden.
6. Delete this line and all above lines before posting your issue! -->
### Information
**Full output of `/ess version`**:
```
<!-- Run the command in your console, and copy and paste the whole output here -->
```
**Server log**: <!-- Upload `logs/latest.log` to https://gist.github.com/, replace this with link -->
**EssentialsX config**: <!-- Upload `plugins/Essentials/config.yml` to https://gist.github.com/, replace this with link -->
### Details
**Description**
<!-- Replace this with a brief summary of the bug. -->
**Steps to reproduce**
<!-- Replace this with exactly what you did to cause the bug. -->
**Expected behavior**
<!-- Replace this with what you expected to happen. -->
**Screenshots**
<!-- Replace this with screenshots, if necessary. -->
+83
View File
@@ -0,0 +1,83 @@
name: Report bug
description: Report a bug in EssentialsX.
labels: 'bug: unconfirmed'
type: Bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report an EssentialsX bug! Fill out the form below to provide us with info to help fix the bug.
Only use this if you're 100% sure you've found a bug and can reproduce it. If you're looking for general help with EssentialsX, try the Q&A forum or MOSS Discord server.
- type: dropdown
attributes:
label: Type of bug
description: What type of bug is this? Choose all that apply.
multiple: true
options:
- Performance issue or memory leak
- Data loss
- Exploit
- Compatibility issue
- Error in console
- Other unexpected behaviour
validations:
required: true
- type: input
attributes:
label: "`/ess dump all` output"
description: Run `/ess dump all` in the console, then copy and paste the link from the output of the command into this box. (If this command doesn't work, you should [update EssentialsX](https://essentialsx.net/downloads.html) and try again.)
validations:
required: true
- type: input
attributes:
label: Error log (if applicable)
description: If you are reporting a console error, upload any relevant log excerpts to either https://paste.gg or https://gist.github.com, save and the paste the link in this box. If you included those files in the same paste as your startup log, paste the same link here.
placeholder: "Example: https://paste.gg/p/anonymous/109dd6a10a734a3aa430d5a351ea5210"
- type: textarea
attributes:
label: Bug description
description: Describe roughly what the bug is here.
placeholder: |
Example: "When running /nuke after putting everyone into adventure mode, there aren't any explosions..."
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Provide an example of how to trigger the bug.
placeholder: |
Example:
1. Have at least 3 people online
2. Run `/gma *` to put everyone into adventure mode
3. Run `/nuke`
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: Explain what you should expect to happen.
placeholder: |
Example: "Everything should explode!"
validations:
required: true
- type: textarea
attributes:
label: Actual behaviour
description: Explain what actually happens.
placeholder: |
Example: "Everything doesn't explode :("
validations:
required: true
- type: textarea
attributes:
label: Additional Information
description: |
In this box, you can attach any relevant screenshots, files and links to Timings/spark profiler reports.
You can also include a link to a heapdump if necessary, but please make sure you don't include any private player data in the heapdump.
If you suspect this issue is related to a prior issue/PR/commit, please mention it here.
validations:
required: false
@@ -1,45 +0,0 @@
---
name: Request a feature
about: Suggest a feature you want to see in EssentialsX!
---
<!-- EssentialsX feature request guide
Fill out the template. Don't write inside the arrows as they will be hidden
when you post your issue.
Not sure if your feature fits in EssentialsX? Feel free to ask on our Discord
server: https://discord.gg/F7gexAQ
If you have a feature suggestion for EssentialsX, read the following tips:
1. Fill out the template.
This will help us understand what you're requesting and why you want us
to add it.
2. Keep it simple.
Make sure it's easy to understand what you're requesting. A good way is
to keep it to one request per GitHub issue, as we can then easily track
feature requests.
3. Check whether it has already been asked or added.
You can search the issue tracker to see if your feature has already been
requested at https://github.com/EssentialsX/Essentials/issues. You can
also check the wiki at https://essentialsx.github.io/ to see if the
feature was recently added.
4. Ask yourself: "Does this belong in EssentialsX?"
There are lots of features that we reject because most servers won't
need or use them. If your feature is very specific or already exists in
another plugin, it might not be a good fit for EssentialsX.
5. Delete this line and all above lines before posting your issue! -->
### Feature request
**Feature description**
<!-- What feature are you suggesting? -->
**How the feature is useful**
<!-- How is the feature useful to players, server owners and/or developers? -->
@@ -0,0 +1,41 @@
name: Request a feature
description: Suggest a feature you want to see in EssentialsX!
labels: 'type: enhancement'
type: Feature
body:
- type: markdown
attributes:
value: |
If you have a feature suggestion for EssentialsX, read the following tips:
1. Fill out the template.
This will help us understand what you're requesting and why you want us
to add it.
2. Keep it simple.
Make sure it's easy to understand what you're requesting. A good way is
to keep it to one request per GitHub issue, as we can then easily track
feature requests.
3. Check whether it has already been asked or added.
You can search the issue tracker to see if your feature has already been
requested at https://github.com/EssentialsX/Essentials/issues. You can
also check the changelogs to see if the feature was recently added:
https://github.com/EssentialsX/Essentials/releases
4. Ask yourself: "Does this belong in EssentialsX?"
There are lots of features that we reject because most servers won't
need or use them. If your feature is very specific or already exists in
another plugin, it might not be a good fit for EssentialsX.
- type: textarea
attributes:
label: Feature description
description: Explain what you should expect to happen.
placeholder: |
What feature are you suggesting?
validations:
required: true
- type: textarea
attributes:
label: How the feature is useful
description: Explain what actually happens.
placeholder: |
How is the feature useful to players, server owners and/or developers?
validations:
required: true
+94
View File
@@ -0,0 +1,94 @@
<!--
EssentialsX bug fix submission guide
====================================
NOTE: Failure to fill out this template properly may result in your PR being
delayed or ignored without warning.
NOTE: Don't type between any arrows in the template, as this text will be
hidden. This includes this header block and any other explanation text
blocks.
Want to discuss your PR before submitting it? Join the EssentialsX Development
server: https://discord.gg/CUN7qVb
EssentialsX is GPL
------------------
By contributing to EssentialsX, you agree to license your code under the
GNU General Public License version 3, which can be found at the link below:
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
Instructions
------------
If you are submitting a bug fix, please follow the following steps:
1. Fill out the template in full.
This includes providing screenshots and a link to the original bug
report. If there isn't an existing bug report, we recommend opening a new
detailed bug report BEFORE opening your PR to fix it, else your PR may be
delayed or rejected without warning.
You can open a new bug report by following this link:
https://github.com/EssentialsX/Essentials/issues/new/choose
2. When linking logs or config files, do not attach them to the post!
Copy and paste any logs into https://gist.github.com/, then paste a
link to them in the relevant parts of the template. Do not use Hastebin
or Pastebin, as this can cause issues with future reviews.
DO NOT drag logs directly into this text box, as we cannot read these!
3. If you are fixing a performance issue, please include a link to a
Timings and/or profiler report, both before and after your PR.
4. If you are fixing a visual bug, such as in commands, please include
screenshots so that we can more easily review the proposed fix.
(You can drag screenshots into the bottom of the editor.)
-->
### Information
<!--
Replace #nnnn with the number of the original issue. If this PR fixes
multiple issues, you should repeat the phrase "fixes #nnnn" for each issue.
-->
This PR fixes #nnnn.
### Details
**Proposed fix:**
<!-- Type a description of your proposed fix below this line. -->
**Environments tested:**
<!-- Type the OS you have used below. -->
OS:
<!-- Type the JDK version (from java -version) you have used below. -->
Java version:
<!--
Put an "x" inside the boxes for the server software you have tested this
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
environments, add a new line with relevant details.
-->
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [ ] CraftBukkit 1.8.8
**Demonstration:**
<!--
Below this block, include screenshots/log snippets from before and after as
necessary. If you have created or used a test case plugin, please link to a
download of the plugin, source code and exact version used where possible.
-->
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: MOSS Discord Server
url: https://discord.gg/casfFyh
about: Need help with using EssentialsX? Join the MOSS Discord server for help.
- name: EssentialsX Development Discord Server
url: https://discord.gg/CUN7qVb
about: Got questions or want to discuss changes before opening your PR? Join the EssentialsX Development Discord server. (Need help? Join MOSS instead.)
@@ -0,0 +1,93 @@
<!--
EssentialsX feature submission guide
====================================
NOTE: Failure to fill out this template properly may result in your PR being
delayed or ignored without warning.
NOTE: Don't type between any arrows in the template, as this text will be
hidden. This includes this header block and any other explanation text
blocks.
Want to discuss your PR before submitting it? Join the EssentialsX Development
server: https://discord.gg/CUN7qVb
EssentialsX is GPL
------------------
By contributing to EssentialsX, you agree to license your code under the
GNU General Public License version 3, which can be found at the link below:
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
Instructions
------------
If you are submitting a new feature, please follow the following steps:
1. Fill out the template in full.
This includes providing screenshots and a link to the original feature
request. If there isn't an existing feature request, we strongly
recommend opening a new feature request BEFORE opening your PR to
implement it, as this allows us to review whether we're likely to accept
your feature in advance, and also allows us to discuss possible
implementations for the feature. If there is no associated feature
request, your PR may be delayed or rejected without warning.
You can open a new feature request by following this link:
https://github.com/EssentialsX/Essentials/issues/new/choose
2. If you are fixing a performance issue, please use the "Bug fix" PR template
instead. The "bug fix" template is better suited to performance issues.
3. Include a demonstration.
If you are adding commands, please provide screenshots and/or a video
demonstration of the feature. Similarly, if you are adding a new API,
please include a link to example code that takes advantage of your
proposed API. This will aid us in reviewing PRs and speed up the process
significantly.
-->
### Information
<!--
Replace #nnnn with the number of the original issue. If this PR implements
features from multiple issues, you should repeat the phrase "closes #nnnn"
for each issue.
-->
This PR closes #nnnn.
### Details
**Proposed feature:**
<!-- Type a description of your proposed feature below this line. -->
**Environments tested:**
<!-- Type the OS you have used below. -->
OS:
<!-- Type the JDK version (from java -version) you have used below. -->
Java version:
<!--
Put an "x" inside the boxes for the server software you have tested this
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
environments, add a new line with relevant details.
-->
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [ ] CraftBukkit 1.8.8
**Demonstration:**
<!--
Below this block, include screenshots/log snippets from before and after as
necessary. If you have created or used a test case plugin, please link to a
download of the plugin, source code and exact version used where possible.
-->
+10
View File
@@ -0,0 +1,10 @@
You need to select a pull request template!
If you're adding a new feature, copy and paste the following parameter at the end of the URL:
?template=new-feature.md
If you're fixing a bug, copy and paste the following parameter at the end of the URL:
?template=bug-fix.md
For more information about contributing to EssentialsX, see CONTRIBUTING.md:
https://github.com/EssentialsX/Essentials/blob/2.x/CONTRIBUTING.md
+97
View File
@@ -0,0 +1,97 @@
name: Build EssentialsX
on:
push:
branches:
- 2.x
- dev/*
release:
types:
- published
jobs:
build:
name: Build and upload
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew build --stacktrace
- name: Publish JUnit report
uses: mikepenz/action-junit-report@v4
if: success() || failure() # Run even if the previous step fails
with:
report_paths: '**/build/test-results/test*/TEST-*.xml'
- name: Archive plugin jars on GitHub
uses: actions/upload-artifact@v4
with:
name: EssentialsX plugin jars
path: jars/
- name: Deploy to Maven repo
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
env:
ORG_GRADLE_PROJECT_essxUsername: ${{ secrets.ESSENTIALSX_DEPLOY_USERNAME }}
ORG_GRADLE_PROJECT_essxPassword: ${{ secrets.ESSENTIALSX_DEPLOY_PASSWORD }}
run: |
./gradlew publish
- name: Prepare Javadocs
run: |
mv Essentials/build/docs/javadoc/ javadocs/
cp -r EssentialsAntiBuild/build/docs/javadoc/ javadocs/EssentialsAntiBuild/
cp -r EssentialsChat/build/docs/javadoc/ javadocs/EssentialsChat/
cp -r EssentialsDiscord/build/docs/javadoc/ javadocs/EssentialsDiscord/
cp -r EssentialsDiscordLink/build/docs/javadoc/ javadocs/EssentialsDiscordLink/
cp -r EssentialsGeoIP/build/docs/javadoc/ javadocs/EssentialsGeoIP/
cp -r EssentialsProtect/build/docs/javadoc/ javadocs/EssentialsProtect/
cp -r EssentialsSpawn/build/docs/javadoc/ javadocs/EssentialsSpawn/
cp -r EssentialsXMPP/build/docs/javadoc/ javadocs/EssentialsXMPP/
- name: Archive Javadocs
uses: actions/upload-artifact@v4
with:
name: javadocs
path: javadocs/
publish-jd:
name: Publish Javadocs
needs: build
if: ${{ github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
runs-on: ubuntu-latest
steps:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 16
- name: Download Javadocs
uses: actions/download-artifact@v4
with:
name: javadocs
path: javadocs/
- name: Deploy Javadocs
run: npx netlify-cli deploy --dir=javadocs/ --prod --message="GitHubActionsDeploy"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_JD_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_JD_2X_SITE_ID }}
+52
View File
@@ -0,0 +1,52 @@
name: Build Pull Request
on:
push:
branches:
- mc/* # MC version updates
- pr/* # PR rebases
pull_request:
branches:
- 2.x
- mc/*
- dev/*
permissions:
checks: write
jobs:
build:
name: Build and upload
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew build --stacktrace
- name: Publish JUnit report
uses: mikepenz/action-junit-report@v4
if: success() || failure() # Run even if the previous step fails
with:
report_paths: '**/build/test-results/test*/TEST-*.xml'
- name: Archive plugin jars on GitHub
uses: actions/upload-artifact@v4
with:
name: EssentialsX plugin jars
path: jars/
-38
View File
@@ -1,38 +0,0 @@
name: Build EssentialsX
on:
push:
branches:
- 2.x
- mc/*
pull_request:
branches:
- 2.x
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v1
- name: Restore Maven cache
uses: actions/cache@v1
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn package --file pom.xml
- name: Copy artifacts
run: mkdir -p ./out/ && cp -t ./out/ **/target/Essentials*.jar
- uses: actions/upload-artifact@master
with:
name: Plugin jars
path: out/
+15 -4
View File
@@ -1,19 +1,30 @@
# IDE files
# Other IDE files
*.classpath
*.project
*.settings/
.idea/
.idea/*
*.iml
.DS_Store
# Checkstyle-IDEA config
!.idea/checkstyle-idea.xml
# Essentials files
/Essentials/userdata
/Essentials/config.yml
/Essentials/kits.yml
/Essentials/userdata/testplayer1.yml
/Essentials/usermap.csv
/Essentials/usermap.bin
/Essentials/uuids.bin
# Build files
.gradle/
/jars/
out/
build/
target/
jars/
out/
*.class
# Run directory
/run/
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA" serialisationVersion="2">
<checkstyleVersion>8.36.2</checkstyleVersion>
<scanScope>JavaOnly</scanScope>
<copyLibs>true</copyLibs>
<scanBeforeCheckin>true</scanBeforeCheckin>
<option name="thirdPartyClasspath" />
<option name="activeLocationIds">
<option value="321e5753-4201-4d2c-bf31-6232011122fd" />
</option>
<option name="locations">
<list>
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="321e5753-4201-4d2c-bf31-6232011122fd" type="PROJECT_RELATIVE" scope="All" description="EssentialsX">
$PROJECT_DIR$/.checkstyle/checkstyle.xml
<option name="properties">
<map>
<entry key="configDirectory" value=".checkstyle/" />
</map>
</option>
</ConfigurationLocation>
</list>
</option>
</component>
</project>
+102
View File
@@ -0,0 +1,102 @@
# Contributing to EssentialsX
*By contributing to EssentialsX, you agree to license your changes under the [GNU General Public License version 3](https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE).*
## Submitting code changes to EssentialsX
If you're interested in submitting new code to EssentialsX, we accept changes via GitHub pull requests.
### Adding features
In general, we will only consider PRs for features if they have already been discussed with the team through
GitHub issues and discussions. Check the list of
["open to PR" issues](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22status%3A+open+to+PR%22)
and [feature request discussions](https://github.com/EssentialsX/Essentials/discussions/categories/ideas-and-feature-suggestions)
before you start working on your changes. If you don't see your desired feature listed,
[open a feature request](https://github.com/EssentialsX/Essentials/issues/new/choose) and wait for a response, otherwise
you may end up wasting your time on a feature that we aren't in a position to accept.
#### Keep it focused
Please try to keep feature PRs focused around one feature. Your PR should ideally contain
one feature, or a few closely-linked features. If you submit several unrelated features
in one PR, the PR will not be accepted.
### Fixing bugs
If you're opening a PR to fix a bug, please ensure a bug report has been filed - search the
[issue tracker](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22bug%3A+confirmed%22%2C%22bug%3A+unconfirmed%22%2C%22bug%3A+upstream%22)
for an existing report, and if you can't find a bug report,
[create one](https://github.com/EssentialsX/Essentials/issues/new/choose) before you submit your PR.
#### Unconfirmed bugs
You may find a bug report with the label `bug: unconfirmed`. This means the EssentialsX team hasn't had the time
to review the bug report yet or hasn't been able to confirm
whether the reported issue is actually a bug in EssentialsX. You can help us by following the steps in the report and
posting whether you are able to replicate this issue, ideally on the latest versions of EssentialsX and Paper. This will
help us confirm issues and prioritise PRs accordingly.
#### Upstream bugs
You may also find bug reports with the `bug: upstream` label. This label means the issue is caused by a bug in *another
project*, not EssentialsX. This includes bugs in CraftBukkit/Spigot and bugs in other plugins. It may be appropriate to
mitigate an issue in EssentialsX, but please check with us in the [EssentialsX Development Discord server](https://discord.gg/CUN7qVb) before you submit a PR.
### Making your changes
You'll need the following to make your changes to EssentialsX:
* A GitHub account
* [`git`](https://git-scm.com/downloads): the Git command-line tool, used to track and save your changes
* [`gh`](https://cli.github.com/): the GitHub command-line tool, used to submit your changes to EssentialsX
* Your IDE of choice - we recommend [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download)
If you're already familiar with Git, you can skip this next section.
You'll need to clone the EssentialsX repository
(`gh repo clone EssentialsX/Essentials`), then create a new branch for your work
(`cd Essentials` then `git switch -c my-cool-pr`).
You can then make changes using your IDE of choice. Follow the instructions in `README.md` to build and test your
changes.
Once you've finished, you should commit your changes (`git commit -am "My cool commit name!"`).
### Submitting your PR
*Even if you're already familiar with GitHub, you will need to follow these steps in order to submit a PR to
EssentialsX. This ensures we have the information we need to test and review your changes.*
Next, you'll need to fork EssentialsX on GitHub and push your changes to a branch on that fork. We strongly recommend
using the `gh` command-line tool for this:
* In your terminal, run `gh pr create`.
* Select either the bug fix or feature template, depending on what your PR is for.
* If you get asked `Where should we push the '...' branch?`, select `Create a fork of EssentialsX/Essentials`.
* For the title, choose a title that describes what your PR does. For example, `Add more ducks to /spawnmob`.
* For the body, press E. You will then need to fill in the PR template in your text editor.
- Follow the instructions in the template and fill out the sections as required.
- This step is important! Without it, we won't know why you've made your changes or how to test them.
* Save the file in your text editor and close it, then return to the terminal.
* Select `Submit` to open your PR.
If you follow these steps correctly, GitHub will create a fork (if necessary) and then open a PR with your changes. You
can now sit back while we review your changes. If we need to ask questions or request further changes to your code,
we will review your PR and post a comment on GitHub. You can respond to our reviews and comments on the GitHub website,
and you can push new changes using `git commit` and `git push`.
## Submitting community translations to EssentialsX
EssentialsX relies on community translations for its messages in other languages. You can help translate EssentialsX on
[Crowdin](https://translate.essentialsx.net/). You'll need a Crowdin account to translate and improve messages.
If your language isn't listed or doesn't have an active proofreader, please let us know on Discord (see below).
## Discussing EssentialsX contributions
Want to discuss something before opening a PR or translating messages? Join the
[EssentialsX Development Discord server](https://discord.gg/CUN7qVb). Note that this server is **not for end-users** -
if you need support with EssentialsX, you should join [MOSS](https://discord.gg/casfFyh) instead.
+87
View File
@@ -0,0 +1,87 @@
plugins {
id("essentials.shadow-module")
}
dependencies {
compileOnly('com.github.milkbowl:VaultAPI:1.7') {
exclude group: "org.bukkit", module: "bukkit"
}
compileOnly 'net.luckperms:api:5.3'
api 'io.papermc:paperlib:1.0.6'
implementation 'org.spongepowered:configurate-yaml:4.2.0'
implementation 'org.checkerframework:checker-qual:3.49.0'
implementation 'nu.studer:java-ordered-properties:1.0.4'
implementation 'net.kyori:adventure-api:4.19.0'
implementation 'net.kyori:adventure-text-minimessage:4.19.0'
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
// Providers
api project(':providers:BaseProviders')
api project(path: ':providers:PaperProvider', configuration: 'shadow')
api(project(':providers:NMSReflectionProvider')) {
exclude group: "org.bukkit", module: "bukkit"
}
api(project(':providers:1_8Provider')) {
exclude group: "org.spigotmc", module: "spigot"
}
api(project(':providers:1_12Provider')) {
exclude group: "org.bukkit", module: "bukkit"
}
}
test {
testLogging.showStandardStreams = true
}
shadowJar {
dependencies {
include (dependency('io.papermc:paperlib'))
include (dependency('org.bstats:bstats-bukkit'))
include (dependency('org.bstats:bstats-base'))
include (dependency('org.spongepowered:configurate-yaml'))
include (dependency('org.spongepowered:configurate-core'))
include (dependency('org.yaml:snakeyaml'))
include (dependency('io.leangen.geantyref:geantyref'))
include (dependency('org.checkerframework:checker-qual'))
include (dependency('nu.studer:java-ordered-properties'))
include (dependency('net.kyori:adventure-api'))
include (dependency('net.kyori:adventure-key'))
include (dependency('net.kyori:examination-api'))
include (dependency('net.kyori:examination-string'))
include (dependency('net.kyori:option'))
include (dependency('net.kyori:adventure-platform-bukkit'))
include (dependency('net.kyori:adventure-platform-api'))
include (dependency('net.kyori:adventure-platform-facet'))
include (dependency('net.kyori:adventure-nbt'))
include (dependency('net.kyori:adventure-text-serializer-bungeecord'))
include (dependency('net.kyori:adventure-text-serializer-gson'))
include (dependency('net.kyori:adventure-text-serializer-gson-legacy-impl'))
include (dependency('net.kyori:adventure-text-serializer-json'))
include (dependency('net.kyori:adventure-text-serializer-json-legacy-impl'))
include (dependency('net.kyori:adventure-text-serializer-legacy'))
include (dependency('net.kyori:adventure-text-minimessage'))
include (project(':providers:BaseProviders'))
include (project(path: ':providers:PaperProvider', configuration: 'shadow'))
include (project(':providers:NMSReflectionProvider'))
include (project(':providers:1_8Provider'))
include (project(':providers:1_12Provider'))
}
relocate 'io.papermc.lib', 'com.earth2me.essentials.paperlib'
relocate 'org.bstats', 'com.earth2me.essentials.libs.bstats'
relocate 'org.spongepowered.configurate', 'com.earth2me.essentials.libs.configurate'
relocate 'org.yaml.snakeyaml', 'com.earth2me.essentials.libs.snakeyaml'
relocate 'io.leangen.geantyref', 'com.earth2me.essentials.libs.geantyref'
relocate 'org.checkerframework', 'com.earth2me.essentials.libs.checkerframework'
relocate 'net.kyori', 'com.earth2me.essentials.libs.kyori'
relocate 'net.essentialsx.temp.adventure', 'net.kyori.adventure'
minimize {
include(dependency('org.checkerframework:checker-qual'))
include(dependency('net.kyori:adventure-api'))
include(dependency('net.kyori:adventure-platform-bukkit'))
include(dependency('net.kyori:adventure-text-minimessage'))
}
}
-118
View File
@@ -1,118 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.ess3</groupId>
<artifactId>EssentialsXParent</artifactId>
<version>2.18.0</version>
</parent>
<artifactId>EssentialsX</artifactId>
<build>
<finalName>EssentialsX-${full.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<showDeprecation>false</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>com.earth2me.essentials.paperlib</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.github.milkbowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>BaseProviders</artifactId>
<version>2.18.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>NMSReflectionProvider</artifactId>
<version>2.18.0</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>PaperProvider</artifactId>
<version>2.18.0</version>
<exclusions>
<exclusion>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>1_8Provider</artifactId>
<version>2.18.0</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -1,104 +0,0 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.PluginCommandYamlParser;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AlternativeCommandsHandler {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private final transient Map<String, List<PluginCommand>> altcommands = new HashMap<>();
private final transient Map<String, String> disabledList = new HashMap<>();
private final transient IEssentials ess;
public AlternativeCommandsHandler(final IEssentials ess) {
this.ess = ess;
for (Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
if (plugin.isEnabled()) {
addPlugin(plugin);
}
}
}
public final void addPlugin(final Plugin plugin) {
if (plugin.getDescription().getMain().contains("com.earth2me.essentials")) {
return;
}
final List<Command> commands = PluginCommandYamlParser.parse(plugin);
final String pluginName = plugin.getDescription().getName().toLowerCase(Locale.ENGLISH);
for (Command command : commands) {
final PluginCommand pc = (PluginCommand) command;
final List<String> labels = new ArrayList<>(pc.getAliases());
labels.add(pc.getName());
PluginCommand reg = ess.getServer().getPluginCommand(pluginName + ":" + pc.getName().toLowerCase(Locale.ENGLISH));
if (reg == null) {
reg = ess.getServer().getPluginCommand(pc.getName().toLowerCase(Locale.ENGLISH));
}
if (reg == null || !reg.getPlugin().equals(plugin)) {
continue;
}
for (String label : labels) {
List<PluginCommand> plugincommands = altcommands.computeIfAbsent(label.toLowerCase(Locale.ENGLISH), k -> new ArrayList<>());
boolean found = false;
for (PluginCommand pc2 : plugincommands) {
if (pc2.getPlugin().equals(plugin)) {
found = true;
break;
}
}
if (!found) {
plugincommands.add(reg);
}
}
}
}
public void removePlugin(final Plugin plugin) {
final Iterator<Map.Entry<String, List<PluginCommand>>> iterator = altcommands.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<String, List<PluginCommand>> entry = iterator.next();
entry.getValue().removeIf(pc -> pc.getPlugin() == null || pc.getPlugin().equals(plugin));
if (entry.getValue().isEmpty()) {
iterator.remove();
}
}
}
public PluginCommand getAlternative(final String label) {
final List<PluginCommand> commands = altcommands.get(label);
if (commands == null || commands.isEmpty()) {
return null;
}
if (commands.size() == 1) {
return commands.get(0);
}
// return the first command that is not an alias
for (PluginCommand command : commands) {
if (command.getName().equalsIgnoreCase(label)) {
return command;
}
}
// return the first alias
return commands.get(0);
}
public void executed(final String label, final PluginCommand pc) {
final String altString = pc.getPlugin().getName() + ":" + pc.getLabel();
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, "Essentials: Alternative command " + label + " found, using " + altString);
}
disabledList.put(label, altString);
}
public Map<String, String> disabledCommands() {
return disabledList;
}
}
@@ -1,12 +0,0 @@
package com.earth2me.essentials;
public class ChargeException extends Exception {
public ChargeException(final String message) {
super(message);
}
public ChargeException(final String message, final Throwable throwable) {
super(message, throwable);
}
}
@@ -1,39 +0,0 @@
package com.earth2me.essentials;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CommandSource {
protected CommandSender sender;
public CommandSource(final CommandSender base) {
this.sender = base;
}
public final CommandSender getSender() {
return sender;
}
public final Player getPlayer() {
if (sender instanceof Player) {
return (Player) sender;
}
return null;
}
public final boolean isPlayer() {
return (sender instanceof Player);
}
public final CommandSender setSender(final CommandSender base) {
return this.sender = base;
}
public void sendMessage(String message) {
if (!message.isEmpty()) {
sender.sendMessage(message);
}
}
}
@@ -1,77 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.messaging.IMessageRecipient;
import com.earth2me.essentials.messaging.SimpleMessageRecipient;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
public final class Console implements IMessageRecipient {
public static final String NAME = "Console";
private static Console instance; // Set in essentials
private final IEssentials ess;
private final IMessageRecipient messageRecipient;
public static Console getInstance() {
return instance;
}
static void setInstance(IEssentials ess) { // Called in Essentials#onEnable()
instance = new Console(ess);
}
/**
* @deprecated Use {@link Console#getCommandSender()}
*/
@Deprecated
public static CommandSender getCommandSender(Server server) throws Exception {
return server.getConsoleSender();
}
private Console(IEssentials ess) {
this.ess = ess;
this.messageRecipient = new SimpleMessageRecipient(ess, this);
}
public CommandSender getCommandSender() {
return ess.getServer().getConsoleSender();
}
@Override public String getName() {
return Console.NAME;
}
@Override public String getDisplayName() {
return Console.NAME;
}
@Override public void sendMessage(String message) {
getCommandSender().sendMessage(message);
}
@Override public boolean isReachable() {
return true;
}
/* ================================
* >> DELEGATE METHODS
* ================================ */
@Override public MessageResponse sendMessage(IMessageRecipient recipient, String message) {
return this.messageRecipient.sendMessage(recipient, message);
}
@Override public MessageResponse onReceiveMessage(IMessageRecipient sender, String message) {
return this.messageRecipient.onReceiveMessage(sender, message);
}
@Override public IMessageRecipient getReplyRecipient() {
return this.messageRecipient.getReplyRecipient();
}
@Override public void setReplyRecipient(IMessageRecipient recipient) {
this.messageRecipient.setReplyRecipient(recipient);
}
}
@@ -1,298 +0,0 @@
package com.earth2me.essentials;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Enchantments {
private static final Map<String, Enchantment> ENCHANTMENTS = new HashMap<>();
private static final Map<String, Enchantment> ALIASENCHANTMENTS = new HashMap<>();
private static boolean isFlat;
static {
ENCHANTMENTS.put("alldamage", Enchantment.DAMAGE_ALL);
ALIASENCHANTMENTS.put("alldmg", Enchantment.DAMAGE_ALL);
ENCHANTMENTS.put("sharpness", Enchantment.DAMAGE_ALL);
ALIASENCHANTMENTS.put("sharp", Enchantment.DAMAGE_ALL);
ALIASENCHANTMENTS.put("dal", Enchantment.DAMAGE_ALL);
ENCHANTMENTS.put("ardmg", Enchantment.DAMAGE_ARTHROPODS);
ENCHANTMENTS.put("baneofarthropods", Enchantment.DAMAGE_ARTHROPODS);
ALIASENCHANTMENTS.put("baneofarthropod", Enchantment.DAMAGE_ARTHROPODS);
ALIASENCHANTMENTS.put("arthropod", Enchantment.DAMAGE_ARTHROPODS);
ALIASENCHANTMENTS.put("dar", Enchantment.DAMAGE_ARTHROPODS);
ENCHANTMENTS.put("undeaddamage", Enchantment.DAMAGE_UNDEAD);
ENCHANTMENTS.put("smite", Enchantment.DAMAGE_UNDEAD);
ALIASENCHANTMENTS.put("du", Enchantment.DAMAGE_UNDEAD);
ENCHANTMENTS.put("digspeed", Enchantment.DIG_SPEED);
ENCHANTMENTS.put("efficiency", Enchantment.DIG_SPEED);
ALIASENCHANTMENTS.put("minespeed", Enchantment.DIG_SPEED);
ALIASENCHANTMENTS.put("cutspeed", Enchantment.DIG_SPEED);
ALIASENCHANTMENTS.put("ds", Enchantment.DIG_SPEED);
ALIASENCHANTMENTS.put("eff", Enchantment.DIG_SPEED);
ENCHANTMENTS.put("durability", Enchantment.DURABILITY);
ALIASENCHANTMENTS.put("dura", Enchantment.DURABILITY);
ENCHANTMENTS.put("unbreaking", Enchantment.DURABILITY);
ALIASENCHANTMENTS.put("d", Enchantment.DURABILITY);
ENCHANTMENTS.put("thorns", Enchantment.THORNS);
ENCHANTMENTS.put("highcrit", Enchantment.THORNS);
ALIASENCHANTMENTS.put("thorn", Enchantment.THORNS);
ALIASENCHANTMENTS.put("highercrit", Enchantment.THORNS);
ALIASENCHANTMENTS.put("t", Enchantment.THORNS);
ENCHANTMENTS.put("fireaspect", Enchantment.FIRE_ASPECT);
ENCHANTMENTS.put("fire", Enchantment.FIRE_ASPECT);
ALIASENCHANTMENTS.put("meleefire", Enchantment.FIRE_ASPECT);
ALIASENCHANTMENTS.put("meleeflame", Enchantment.FIRE_ASPECT);
ALIASENCHANTMENTS.put("fa", Enchantment.FIRE_ASPECT);
ENCHANTMENTS.put("knockback", Enchantment.KNOCKBACK);
ALIASENCHANTMENTS.put("kback", Enchantment.KNOCKBACK);
ALIASENCHANTMENTS.put("kb", Enchantment.KNOCKBACK);
ALIASENCHANTMENTS.put("k", Enchantment.KNOCKBACK);
ALIASENCHANTMENTS.put("blockslootbonus", Enchantment.LOOT_BONUS_BLOCKS);
ENCHANTMENTS.put("fortune", Enchantment.LOOT_BONUS_BLOCKS);
ALIASENCHANTMENTS.put("fort", Enchantment.LOOT_BONUS_BLOCKS);
ALIASENCHANTMENTS.put("lbb", Enchantment.LOOT_BONUS_BLOCKS);
ALIASENCHANTMENTS.put("mobslootbonus", Enchantment.LOOT_BONUS_MOBS);
ENCHANTMENTS.put("mobloot", Enchantment.LOOT_BONUS_MOBS);
ENCHANTMENTS.put("looting", Enchantment.LOOT_BONUS_MOBS);
ALIASENCHANTMENTS.put("lbm", Enchantment.LOOT_BONUS_MOBS);
ALIASENCHANTMENTS.put("oxygen", Enchantment.OXYGEN);
ENCHANTMENTS.put("respiration", Enchantment.OXYGEN);
ALIASENCHANTMENTS.put("breathing", Enchantment.OXYGEN);
ENCHANTMENTS.put("breath", Enchantment.OXYGEN);
ALIASENCHANTMENTS.put("o", Enchantment.OXYGEN);
ENCHANTMENTS.put("protection", Enchantment.PROTECTION_ENVIRONMENTAL);
ALIASENCHANTMENTS.put("prot", Enchantment.PROTECTION_ENVIRONMENTAL);
ENCHANTMENTS.put("protect", Enchantment.PROTECTION_ENVIRONMENTAL);
ALIASENCHANTMENTS.put("p", Enchantment.PROTECTION_ENVIRONMENTAL);
ALIASENCHANTMENTS.put("explosionsprotection", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("explosionprotection", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("expprot", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("blastprotection", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("bprotection", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("bprotect", Enchantment.PROTECTION_EXPLOSIONS);
ENCHANTMENTS.put("blastprotect", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("pe", Enchantment.PROTECTION_EXPLOSIONS);
ALIASENCHANTMENTS.put("fallprotection", Enchantment.PROTECTION_FALL);
ENCHANTMENTS.put("fallprot", Enchantment.PROTECTION_FALL);
ENCHANTMENTS.put("featherfall", Enchantment.PROTECTION_FALL);
ALIASENCHANTMENTS.put("featherfalling", Enchantment.PROTECTION_FALL);
ALIASENCHANTMENTS.put("pfa", Enchantment.PROTECTION_FALL);
ALIASENCHANTMENTS.put("fireprotection", Enchantment.PROTECTION_FIRE);
ALIASENCHANTMENTS.put("flameprotection", Enchantment.PROTECTION_FIRE);
ENCHANTMENTS.put("fireprotect", Enchantment.PROTECTION_FIRE);
ALIASENCHANTMENTS.put("flameprotect", Enchantment.PROTECTION_FIRE);
ENCHANTMENTS.put("fireprot", Enchantment.PROTECTION_FIRE);
ALIASENCHANTMENTS.put("flameprot", Enchantment.PROTECTION_FIRE);
ALIASENCHANTMENTS.put("pf", Enchantment.PROTECTION_FIRE);
ENCHANTMENTS.put("projectileprotection", Enchantment.PROTECTION_PROJECTILE);
ENCHANTMENTS.put("projprot", Enchantment.PROTECTION_PROJECTILE);
ALIASENCHANTMENTS.put("pp", Enchantment.PROTECTION_PROJECTILE);
ENCHANTMENTS.put("silktouch", Enchantment.SILK_TOUCH);
ALIASENCHANTMENTS.put("softtouch", Enchantment.SILK_TOUCH);
ALIASENCHANTMENTS.put("st", Enchantment.SILK_TOUCH);
ENCHANTMENTS.put("waterworker", Enchantment.WATER_WORKER);
ENCHANTMENTS.put("aquaaffinity", Enchantment.WATER_WORKER);
ALIASENCHANTMENTS.put("watermine", Enchantment.WATER_WORKER);
ALIASENCHANTMENTS.put("ww", Enchantment.WATER_WORKER);
ALIASENCHANTMENTS.put("firearrow", Enchantment.ARROW_FIRE);
ENCHANTMENTS.put("flame", Enchantment.ARROW_FIRE);
ENCHANTMENTS.put("flamearrow", Enchantment.ARROW_FIRE);
ALIASENCHANTMENTS.put("af", Enchantment.ARROW_FIRE);
ENCHANTMENTS.put("arrowdamage", Enchantment.ARROW_DAMAGE);
ENCHANTMENTS.put("power", Enchantment.ARROW_DAMAGE);
ALIASENCHANTMENTS.put("arrowpower", Enchantment.ARROW_DAMAGE);
ALIASENCHANTMENTS.put("ad", Enchantment.ARROW_DAMAGE);
ENCHANTMENTS.put("arrowknockback", Enchantment.ARROW_KNOCKBACK);
ALIASENCHANTMENTS.put("arrowkb", Enchantment.ARROW_KNOCKBACK);
ENCHANTMENTS.put("punch", Enchantment.ARROW_KNOCKBACK);
ALIASENCHANTMENTS.put("arrowpunch", Enchantment.ARROW_KNOCKBACK);
ALIASENCHANTMENTS.put("ak", Enchantment.ARROW_KNOCKBACK);
ALIASENCHANTMENTS.put("infinitearrows", Enchantment.ARROW_INFINITE);
ENCHANTMENTS.put("infarrows", Enchantment.ARROW_INFINITE);
ENCHANTMENTS.put("infinity", Enchantment.ARROW_INFINITE);
ALIASENCHANTMENTS.put("infinite", Enchantment.ARROW_INFINITE);
ALIASENCHANTMENTS.put("unlimited", Enchantment.ARROW_INFINITE);
ALIASENCHANTMENTS.put("unlimitedarrows", Enchantment.ARROW_INFINITE);
ALIASENCHANTMENTS.put("ai", Enchantment.ARROW_INFINITE);
ENCHANTMENTS.put("luck", Enchantment.LUCK);
ALIASENCHANTMENTS.put("luckofsea", Enchantment.LUCK);
ALIASENCHANTMENTS.put("luckofseas", Enchantment.LUCK);
ALIASENCHANTMENTS.put("rodluck", Enchantment.LUCK);
ENCHANTMENTS.put("lure", Enchantment.LURE);
ALIASENCHANTMENTS.put("rodlure", Enchantment.LURE);
// 1.8
try {
Enchantment depthStrider = Enchantment.getByName("DEPTH_STRIDER");
if (depthStrider != null) {
ENCHANTMENTS.put("depthstrider", depthStrider);
ALIASENCHANTMENTS.put("depth", depthStrider);
ALIASENCHANTMENTS.put("strider", depthStrider);
}
} catch (IllegalArgumentException ignored) {}
// 1.9
try {
Enchantment frostWalker = Enchantment.getByName("FROST_WALKER");
if (frostWalker != null) {
ENCHANTMENTS.put("frostwalker", frostWalker);
ALIASENCHANTMENTS.put("frost", frostWalker);
ALIASENCHANTMENTS.put("walker", frostWalker);
}
Enchantment mending = Enchantment.getByName("MENDING");
if (mending != null) {
ENCHANTMENTS.put("mending", mending);
}
} catch (IllegalArgumentException ignored) {}
// 1.11
try {
Enchantment bindingCurse = Enchantment.getByName("BINDING_CURSE");
if (bindingCurse != null) {
ENCHANTMENTS.put("bindingcurse", bindingCurse);
ALIASENCHANTMENTS.put("bindcurse", bindingCurse);
ALIASENCHANTMENTS.put("binding", bindingCurse);
ALIASENCHANTMENTS.put("bind", bindingCurse);
}
Enchantment vanishingCurse = Enchantment.getByName("VANISHING_CURSE");
if (vanishingCurse != null) {
ENCHANTMENTS.put("vanishingcurse", vanishingCurse);
ALIASENCHANTMENTS.put("vanishcurse", vanishingCurse);
ALIASENCHANTMENTS.put("vanishing", vanishingCurse);
ALIASENCHANTMENTS.put("vanish", vanishingCurse);
}
Enchantment sweeping = Enchantment.getByName("SWEEPING_EDGE");
if (sweeping != null) {
ENCHANTMENTS.put("sweepingedge", sweeping);
ALIASENCHANTMENTS.put("sweepedge", sweeping);
ALIASENCHANTMENTS.put("sweeping", sweeping);
}
} catch (IllegalArgumentException ignored) {}
try { // 1.13
Enchantment loyalty = Enchantment.getByName("LOYALTY");
if (loyalty != null) {
ENCHANTMENTS.put("loyalty", loyalty);
ALIASENCHANTMENTS.put("loyal", loyalty);
ALIASENCHANTMENTS.put("return", loyalty);
}
Enchantment impaling = Enchantment.getByName("IMPALING");
if (impaling != null) {
ENCHANTMENTS.put("impaling", impaling);
ALIASENCHANTMENTS.put("impale", impaling);
ALIASENCHANTMENTS.put("oceandamage", impaling);
ALIASENCHANTMENTS.put("oceandmg", impaling);
}
Enchantment riptide = Enchantment.getByName("RIPTIDE");
if (riptide != null) {
ENCHANTMENTS.put("riptide", riptide);
ALIASENCHANTMENTS.put("rip", riptide);
ALIASENCHANTMENTS.put("tide", riptide);
ALIASENCHANTMENTS.put("launch", riptide);
}
Enchantment channelling = Enchantment.getByName("CHANNELING");
if (channelling != null) {
ENCHANTMENTS.put("channelling", channelling);
ALIASENCHANTMENTS.put("chanelling", channelling);
ALIASENCHANTMENTS.put("channeling", channelling);
ALIASENCHANTMENTS.put("chaneling", channelling);
ALIASENCHANTMENTS.put("channel", channelling);
}
} catch (IllegalArgumentException ignored) {}
try { // 1.14
Enchantment multishot = Enchantment.getByName("MULTISHOT");
if (multishot != null) {
ENCHANTMENTS.put("multishot", multishot);
ALIASENCHANTMENTS.put("tripleshot", multishot);
}
Enchantment quickCharge = Enchantment.getByName("QUICK_CHARGE");
if (quickCharge != null) {
ENCHANTMENTS.put("quickcharge", quickCharge);
ALIASENCHANTMENTS.put("quickdraw", quickCharge);
ALIASENCHANTMENTS.put("fastcharge", quickCharge);
ALIASENCHANTMENTS.put("fastdraw", quickCharge);
}
Enchantment piercing = Enchantment.getByName("PIERCING");
if (piercing != null) {
ENCHANTMENTS.put("piercing", piercing);
}
} catch (IllegalArgumentException ignored) {}
try { // 1.16
Enchantment soulspeed = Enchantment.getByName("SOUL_SPEED");
if (soulspeed != null) {
ENCHANTMENTS.put("soulspeed", soulspeed);
ALIASENCHANTMENTS.put("soilspeed", soulspeed);
ALIASENCHANTMENTS.put("sandspeed", soulspeed);
}
} catch (IllegalArgumentException ignored) {}
try {
Class<?> namespacedKeyClass = Class.forName("org.bukkit.NamespacedKey");
Class<?> enchantmentClass = Class.forName("org.bukkit.enchantments.Enchantment");
enchantmentClass.getDeclaredMethod("getByKey", namespacedKeyClass);
isFlat = true;
} catch (ClassNotFoundException | NoSuchMethodException e) {
isFlat = false;
}
}
public static Enchantment getByName(String name) {
Enchantment enchantment = null;
if (isFlat) { // 1.13+ only
enchantment = Enchantment.getByKey(NamespacedKey.minecraft(name.toLowerCase()));
}
if (enchantment == null) {
enchantment = Enchantment.getByName(name.toUpperCase());
}
if (enchantment == null) {
enchantment = ENCHANTMENTS.get(name.toLowerCase(Locale.ENGLISH));
}
if (enchantment == null) {
enchantment = ALIASENCHANTMENTS.get(name.toLowerCase(Locale.ENGLISH));
}
return enchantment;
}
public static Set<Entry<String, Enchantment>> entrySet() {
return ENCHANTMENTS.entrySet();
}
public static Set<String> keySet() {
return ENCHANTMENTS.keySet();
}
}
File diff suppressed because it is too large. Load diff
@@ -1,662 +0,0 @@
package com.earth2me.essentials;
import com.google.common.io.Files;
import net.ess3.api.InvalidWorldException;
import org.bukkit.OfflinePlayer;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.io.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsConf extends YamlConfiguration {
protected static final Logger LOGGER = Logger.getLogger("Essentials");
protected final File configFile;
protected String templateName = null;
protected static final Charset UTF8 = StandardCharsets.UTF_8;
private Class<?> resourceClass = EssentialsConf.class;
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
private final AtomicInteger pendingDiskWrites = new AtomicInteger(0);
private final AtomicBoolean transaction = new AtomicBoolean(false);
public EssentialsConf(final File configFile) {
super();
this.configFile = configFile.getAbsoluteFile();
}
private final byte[] bytebuffer = new byte[1024];
public synchronized void load() {
if (pendingDiskWrites.get() != 0) {
LOGGER.log(Level.INFO, "File {0} not read, because it''s not yet written to disk.", configFile);
return;
}
if (!configFile.getParentFile().exists()) {
if (!configFile.getParentFile().mkdirs()) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
}
}
// This will delete files where the first character is 0. In most cases they are broken.
if (configFile.exists() && configFile.length() != 0) {
try {
final InputStream input = new FileInputStream(configFile);
try {
if (input.read() == 0) {
input.close();
configFile.delete();
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
} finally {
try {
input.close();
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
} catch (FileNotFoundException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
if (!configFile.exists()) {
if (legacyFileExists()) {
convertLegacyFile();
} else if (altFileExists()) {
convertAltFile();
} else if (templateName != null) {
LOGGER.log(Level.INFO, tl("creatingConfigFromTemplate", configFile.toString()));
createFromTemplate();
} else {
return;
}
}
try {
try (FileInputStream inputStream = new FileInputStream(configFile)) {
long startSize = configFile.length();
if (startSize > Integer.MAX_VALUE) {
throw new InvalidConfigurationException("File too big");
}
ByteBuffer buffer = ByteBuffer.allocate((int) startSize);
int length;
while ((length = inputStream.read(bytebuffer)) != -1) {
if (length > buffer.remaining()) {
ByteBuffer resize = ByteBuffer.allocate(buffer.capacity() + length - buffer.remaining());
int resizePosition = buffer.position();
// Fix builds compiled against Java 9+ breaking on Java 8
((Buffer) buffer).rewind();
resize.put(buffer);
resize.position(resizePosition);
buffer = resize;
}
buffer.put(bytebuffer, 0, length);
}
((Buffer) buffer).rewind();
final CharBuffer data = CharBuffer.allocate(buffer.capacity());
CharsetDecoder decoder = UTF8.newDecoder();
CoderResult result = decoder.decode(buffer, data, true);
if (result.isError()) {
((Buffer) buffer).rewind();
((Buffer) data).clear();
LOGGER.log(Level.INFO, "File " + configFile.getAbsolutePath() + " is not utf-8 encoded, trying " + Charset.defaultCharset().displayName());
decoder = Charset.defaultCharset().newDecoder();
result = decoder.decode(buffer, data, true);
if (result.isError()) {
throw new InvalidConfigurationException("Invalid Characters in file " + configFile.getAbsolutePath());
} else {
decoder.flush(data);
}
} else {
decoder.flush(data);
}
final int end = data.position();
((Buffer) data).rewind();
super.loadFromString(data.subSequence(0, end).toString());
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
} catch (InvalidConfigurationException ex) {
File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
configFile.renameTo(broken);
LOGGER.log(Level.SEVERE, "The file " + configFile.toString() + " is broken, it has been renamed to " + broken.toString(), ex.getCause());
}
}
public boolean legacyFileExists() {
return false;
}
public void convertLegacyFile() {
LOGGER.log(Level.SEVERE, "Unable to import legacy config file.");
}
public boolean altFileExists() {
return false;
}
public void convertAltFile() {
LOGGER.log(Level.SEVERE, "Unable to import alt config file.");
}
private void createFromTemplate() {
InputStream istr = null;
OutputStream ostr = null;
try {
istr = resourceClass.getResourceAsStream(templateName);
if (istr == null) {
LOGGER.log(Level.SEVERE, tl("couldNotFindTemplate", templateName));
return;
}
ostr = new FileOutputStream(configFile);
byte[] buffer = new byte[1024];
int length = 0;
length = istr.read(buffer);
while (length > 0) {
ostr.write(buffer, 0, length);
length = istr.read(buffer);
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToWriteConfig", configFile.toString()), ex);
} finally {
try {
if (istr != null) {
istr.close();
}
} catch (IOException ex) {
Logger.getLogger(EssentialsConf.class.getName()).log(Level.SEVERE, null, ex);
}
try {
if (ostr != null) {
ostr.close();
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToCloseConfig", configFile.toString()), ex);
}
}
}
public void setTemplateName(final String templateName) {
this.templateName = templateName;
}
public File getFile() {
return configFile;
}
public void setTemplateName(final String templateName, final Class<?> resClass) {
this.templateName = templateName;
this.resourceClass = resClass;
}
public void startTransaction() {
transaction.set(true);
}
public void stopTransaction() {
transaction.set(false);
save();
}
public void save() {
try {
save(configFile);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
}
}
public void saveWithError() throws IOException {
save(configFile);
}
@Override
public synchronized void save(final File file) throws IOException {
if (!transaction.get()) {
delayedSave(file);
}
}
//This may be aborted if there are stagnant requests sitting in queue.
//This needs fixed to discard outstanding save requests.
public synchronized void forceSave() {
try {
Future<?> future = delayedSave(configFile);
if (future != null) {
future.get();
}
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
}
}
public synchronized void cleanup() {
forceSave();
}
private Future<?> delayedSave(final File file) {
if (file == null) {
throw new IllegalArgumentException("File cannot be null");
}
final String data = saveToString();
if (data.length() == 0) {
return null;
}
pendingDiskWrites.incrementAndGet();
return EXECUTOR_SERVICE.submit(new WriteRunner(configFile, data, pendingDiskWrites));
}
private static class WriteRunner implements Runnable {
private final File configFile;
private final String data;
private final AtomicInteger pendingDiskWrites;
private WriteRunner(final File configFile, final String data, final AtomicInteger pendingDiskWrites) {
this.configFile = configFile;
this.data = data;
this.pendingDiskWrites = pendingDiskWrites;
}
@Override
public void run() {
//long startTime = System.nanoTime();
synchronized (configFile) {
if (pendingDiskWrites.get() > 1) {
// Writes can be skipped, because they are stored in a queue (in the executor).
// Only the last is actually written.
pendingDiskWrites.decrementAndGet();
//LOGGER.log(Level.INFO, configFile + " skipped writing in " + (System.nanoTime() - startTime) + " nsec.");
return;
}
try {
Files.createParentDirs(configFile);
if (!configFile.exists()) {
try {
LOGGER.log(Level.INFO, tl("creatingEmptyConfig", configFile.toString()));
if (!configFile.createNewFile()) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
return;
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()), ex);
return;
}
}
try (FileOutputStream fos = new FileOutputStream(configFile)) {
try (OutputStreamWriter writer = new OutputStreamWriter(fos, UTF8)) {
writer.write(data);
}
}
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
} finally {
//LOGGER.log(Level.INFO, configFile + " written to disk in " + (System.nanoTime() - startTime) + " nsec.");
pendingDiskWrites.decrementAndGet();
}
}
}
}
public boolean hasProperty(final String path) {
return isSet(path);
}
public Location getLocation(final String path, final Server server) throws InvalidWorldException {
final String worldString = (path == null ? "" : path + ".") + "world";
final String worldName = getString(worldString);
if (worldName == null || worldName.isEmpty()) {
return null;
}
final World world = server.getWorld(worldName);
if (world == null) {
throw new InvalidWorldException(worldName);
}
return new Location(world, getDouble((path == null ? "" : path + ".") + "x", 0), getDouble((path == null ? "" : path + ".") + "y", 0), getDouble((path == null ? "" : path + ".") + "z", 0), (float) getDouble((path == null ? "" : path + ".") + "yaw", 0), (float) getDouble((path == null ? "" : path + ".") + "pitch", 0));
}
public void setProperty(final String path, final Location loc) {
set((path == null ? "" : path + ".") + "world", loc.getWorld().getName());
set((path == null ? "" : path + ".") + "x", loc.getX());
set((path == null ? "" : path + ".") + "y", loc.getY());
set((path == null ? "" : path + ".") + "z", loc.getZ());
set((path == null ? "" : path + ".") + "yaw", loc.getYaw());
set((path == null ? "" : path + ".") + "pitch", loc.getPitch());
}
@Override
public ItemStack getItemStack(final String path) {
final ItemStack stack = new ItemStack(Material.valueOf(getString(path + ".type", "AIR")), getInt(path + ".amount", 1), (short) getInt(path + ".damage", 0));
final ConfigurationSection enchants = getConfigurationSection(path + ".enchant");
if (enchants != null) {
for (String enchant : enchants.getKeys(false)) {
final Enchantment enchantment = Enchantment.getByName(enchant.toUpperCase(Locale.ENGLISH));
if (enchantment == null) {
continue;
}
final int level = getInt(path + ".enchant." + enchant, enchantment.getStartLevel());
stack.addUnsafeEnchantment(enchantment, level);
}
}
return stack;
/*
* ,
* (byte)getInt(path + ".data", 0)
*/
}
public void setProperty(final String path, final ItemStack stack) {
final Map<String, Object> map = new HashMap<>();
map.put("type", stack.getType().toString());
map.put("amount", stack.getAmount());
map.put("damage", stack.getDurability());
Map<Enchantment, Integer> enchantments = stack.getEnchantments();
if (!enchantments.isEmpty()) {
Map<String, Integer> enchant = new HashMap<>();
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
enchant.put(entry.getKey().getName().toLowerCase(Locale.ENGLISH), entry.getValue());
}
map.put("enchant", enchant);
}
// getData().getData() is broken
//map.put("data", stack.getDurability());
set(path, map);
}
public void setProperty(String path, List object) {
set(path, new ArrayList(object));
}
public void setProperty(String path, Map object) {
set(path, new LinkedHashMap(object));
}
public Object getProperty(String path) {
return get(path);
}
public void setProperty(final String path, final BigDecimal bigDecimal) {
set(path, bigDecimal.toString());
}
public void setProperty(String path, Object object) {
set(path, object);
}
public void removeProperty(String path) {
set(path, null);
}
@Override
public synchronized Object get(String path) {
return super.get(path);
}
@Override
public synchronized Object get(String path, Object def) {
return super.get(path, def);
}
public synchronized BigDecimal getBigDecimal(final String path, final BigDecimal def) {
final String input = super.getString(path);
return toBigDecimal(input, def);
}
public static BigDecimal toBigDecimal(final String input, final BigDecimal def) {
if (input == null || input.isEmpty()) {
return def;
} else {
try {
return new BigDecimal(input, MathContext.DECIMAL128);
} catch (NumberFormatException | ArithmeticException e) {
return def;
}
}
}
@Override
public synchronized boolean getBoolean(String path) {
return super.getBoolean(path);
}
@Override
public synchronized boolean getBoolean(String path, boolean def) {
return super.getBoolean(path, def);
}
@Override
public synchronized List<Boolean> getBooleanList(String path) {
return super.getBooleanList(path);
}
@Override
public synchronized List<Byte> getByteList(String path) {
return super.getByteList(path);
}
@Override
public synchronized List<Character> getCharacterList(String path) {
return super.getCharacterList(path);
}
@Override
public synchronized ConfigurationSection getConfigurationSection(String path) {
return super.getConfigurationSection(path);
}
@Override
public synchronized double getDouble(String path) {
return super.getDouble(path);
}
@Override
public synchronized double getDouble(final String path, final double def) {
return super.getDouble(path, def);
}
@Override
public synchronized List<Double> getDoubleList(String path) {
return super.getDoubleList(path);
}
@Override
public synchronized List<Float> getFloatList(String path) {
return super.getFloatList(path);
}
@Override
public synchronized int getInt(String path) {
return super.getInt(path);
}
@Override
public synchronized int getInt(String path, int def) {
return super.getInt(path, def);
}
@Override
public synchronized List<Integer> getIntegerList(String path) {
return super.getIntegerList(path);
}
@Override
public synchronized ItemStack getItemStack(String path, ItemStack def) {
return super.getItemStack(path, def);
}
@Override
public synchronized Set<String> getKeys(boolean deep) {
return super.getKeys(deep);
}
@Override
public synchronized List<?> getList(String path) {
return super.getList(path);
}
@Override
public synchronized List<?> getList(String path, List<?> def) {
return super.getList(path, def);
}
@Override
public synchronized long getLong(String path) {
return super.getLong(path);
}
@Override
public synchronized long getLong(final String path, final long def) {
return super.getLong(path, def);
}
@Override
public synchronized List<Long> getLongList(String path) {
return super.getLongList(path);
}
public synchronized Map<String, Object> getMap() {
return map;
}
@Override
public synchronized List<Map<?, ?>> getMapList(String path) {
return super.getMapList(path);
}
@Override
public synchronized OfflinePlayer getOfflinePlayer(String path) {
return super.getOfflinePlayer(path);
}
@Override
public synchronized OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def) {
return super.getOfflinePlayer(path, def);
}
@Override
public synchronized List<Short> getShortList(String path) {
return super.getShortList(path);
}
@Override
public synchronized String getString(String path) {
return super.getString(path);
}
@Override
public synchronized String getString(String path, String def) {
return super.getString(path, def);
}
@Override
public synchronized List<String> getStringList(String path) {
return super.getStringList(path);
}
@Override
public synchronized Map<String, Object> getValues(boolean deep) {
return super.getValues(deep);
}
@Override
public synchronized Vector getVector(String path) {
return super.getVector(path);
}
@Override
public synchronized Vector getVector(String path, Vector def) {
return super.getVector(path, def);
}
@Override
public synchronized boolean isBoolean(String path) {
return super.isBoolean(path);
}
@Override
public synchronized boolean isConfigurationSection(String path) {
return super.isConfigurationSection(path);
}
@Override
public synchronized boolean isDouble(String path) {
return super.isDouble(path);
}
@Override
public synchronized boolean isInt(String path) {
return super.isInt(path);
}
@Override
public synchronized boolean isItemStack(String path) {
return super.isItemStack(path);
}
@Override
public synchronized boolean isList(String path) {
return super.isList(path);
}
@Override
public synchronized boolean isLong(String path) {
return super.isLong(path);
}
@Override
public synchronized boolean isOfflinePlayer(String path) {
return super.isOfflinePlayer(path);
}
@Override
public synchronized boolean isSet(String path) {
return super.isSet(path);
}
@Override
public synchronized boolean isString(String path) {
return super.isString(path);
}
@Override
public synchronized boolean isVector(String path) {
return super.isVector(path);
}
@Override
public synchronized void set(String path, Object value) {
super.set(path, value);
}
}
@@ -1,768 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.craftbukkit.BanLookup;
import com.earth2me.essentials.craftbukkit.FakeWorld;
import com.earth2me.essentials.settings.Spawns;
import com.earth2me.essentials.storage.YamlStorageWriter;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import net.ess3.api.IEssentials;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import java.io.*;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsUpgrade {
private final static Logger LOGGER = Logger.getLogger("Essentials");
private final transient IEssentials ess;
private final transient EssentialsConf doneFile;
EssentialsUpgrade(final IEssentials essentials) {
ess = essentials;
if (!ess.getDataFolder().exists()) {
ess.getDataFolder().mkdirs();
}
doneFile = new EssentialsConf(new File(ess.getDataFolder(), "upgrades-done.yml"));
doneFile.load();
}
public void convertIgnoreList() {
Pattern pattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
if (doneFile.getBoolean("updateUsersIgnoreListUUID", false)) {
return;
}
LOGGER.info("Attempting to migrate ignore list to UUIDs");
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
try {
config.load();
if (config.hasProperty("ignore")) {
List<String> migratedIgnores = new ArrayList<>();
for (String name : Collections.synchronizedList(config.getStringList("ignore"))) {
if (name == null) {
continue;
}
if (pattern.matcher(name.trim()).matches()) {
LOGGER.info("Detected already migrated ignore list!");
return;
}
User user = ess.getOfflineUser(name);
if (user != null && user.getBase() != null) {
migratedIgnores.add(user.getBase().getUniqueId().toString());
}
}
config.removeProperty("ignore");
config.setProperty("ignore", migratedIgnores);
config.forceSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
throw ex;
}
}
doneFile.setProperty("updateUsersIgnoreListUUID", true);
doneFile.save();
LOGGER.info("Done converting ignore list.");
}
public void convertKits() {
Kits kits = ess.getKits();
EssentialsConf config = kits.getConfig();
if (doneFile.getBoolean("kitsyml", false)) {
return;
}
LOGGER.info("Attempting to convert old kits in config.yml to new kits.yml");
ConfigurationSection section = ess.getSettings().getKitSection();
if (section == null) {
LOGGER.info("No kits found to migrate.");
return;
}
Map<String, Object> legacyKits = ess.getSettings().getKitSection().getValues(true);
for (Map.Entry<String, Object> entry : legacyKits.entrySet()) {
LOGGER.info("Converting " + entry.getKey());
config.set("kits." + entry.getKey(), entry.getValue());
}
config.save();
doneFile.setProperty("kitsyml", true);
doneFile.save();
LOGGER.info("Done converting kits.");
}
private void moveMotdRulesToFile(String name) {
if (doneFile.getBoolean("move" + name + "ToFile", false)) {
return;
}
try {
final File file = new File(ess.getDataFolder(), name + ".txt");
if (file.exists()) {
return;
}
final File configFile = new File(ess.getDataFolder(), "config.yml");
if (!configFile.exists()) {
return;
}
final EssentialsConf conf = new EssentialsConf(configFile);
conf.load();
List<String> lines = conf.getStringList(name);
if (lines != null && !lines.isEmpty()) {
if (!file.createNewFile()) {
throw new IOException("Failed to create file " + file);
}
PrintWriter writer = new PrintWriter(file);
for (String line : lines) {
writer.println(line);
}
writer.close();
}
doneFile.setProperty("move" + name + "ToFile", true);
doneFile.save();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, tl("upgradingFilesError"), e);
}
}
private void removeLinesFromConfig(File file, String regex, String info) throws Exception {
boolean needUpdate = false;
final BufferedReader bReader = new BufferedReader(new FileReader(file));
final File tempFile = File.createTempFile("essentialsupgrade", ".tmp.yml", ess.getDataFolder());
final BufferedWriter bWriter = new BufferedWriter(new FileWriter(tempFile));
do {
final String line = bReader.readLine();
if (line == null) {
break;
}
if (line.matches(regex)) {
if (!needUpdate && info != null) {
bWriter.write(info, 0, info.length());
bWriter.newLine();
}
needUpdate = true;
} else {
if (line.endsWith("\r\n")) {
bWriter.write(line, 0, line.length() - 2);
} else if (line.endsWith("\r") || line.endsWith("\n")) {
bWriter.write(line, 0, line.length() - 1);
} else {
bWriter.write(line, 0, line.length());
}
bWriter.newLine();
}
} while (true);
bReader.close();
bWriter.close();
if (needUpdate) {
if (!file.renameTo(new File(file.getParentFile(), file.getName().concat("." + System.currentTimeMillis() + ".upgradebackup")))) {
throw new Exception(tl("configFileMoveError"));
}
if (!tempFile.renameTo(file)) {
throw new Exception(tl("configFileRenameError"));
}
} else {
tempFile.delete();
}
}
private void updateUsersPowerToolsFormat() {
if (doneFile.getBoolean("updateUsersPowerToolsFormat", false)) {
return;
}
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
try {
config.load();
if (config.hasProperty("powertools")) {
final Map<String, Object> powertools = config.getConfigurationSection("powertools").getValues(false);
if (powertools == null) {
continue;
}
for (Map.Entry<String, Object> entry : powertools.entrySet()) {
if (entry.getValue() instanceof String) {
List<String> temp = new ArrayList<>();
temp.add((String) entry.getValue());
powertools.put(entry.getKey(), temp);
}
}
config.forceSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
throw ex;
}
}
doneFile.setProperty("updateUsersPowerToolsFormat", true);
doneFile.save();
}
private void updateUsersHomesFormat() {
if (doneFile.getBoolean("updateUsersHomesFormat", false)) {
return;
}
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
try {
config.load();
if (config.hasProperty("home") && config.hasProperty("home.default")) {
final String defworld = (String) config.getProperty("home.default");
final Location defloc = getFakeLocation(config, "home.worlds." + defworld);
if (defloc != null) {
config.setProperty("homes.home", defloc);
}
Set<String> worlds = config.getConfigurationSection("home.worlds").getKeys(false);
Location loc;
String worldName;
if (worlds == null) {
continue;
}
for (String world : worlds) {
if (defworld.equalsIgnoreCase(world)) {
continue;
}
loc = getFakeLocation(config, "home.worlds." + world);
if (loc == null) {
continue;
}
worldName = loc.getWorld().getName().toLowerCase(Locale.ENGLISH);
if (worldName != null && !worldName.isEmpty()) {
config.setProperty("homes." + worldName, loc);
}
}
config.removeProperty("home");
config.forceSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
throw ex;
}
}
doneFile.setProperty("updateUsersHomesFormat", true);
doneFile.save();
}
private void sanitizeAllUserFilenames() {
if (doneFile.getBoolean("sanitizeAllUserFilenames", false)) {
return;
}
final File usersFolder = new File(ess.getDataFolder(), "userdata");
if (!usersFolder.exists()) {
return;
}
final File[] listOfFiles = usersFolder.listFiles();
for (File listOfFile : listOfFiles) {
final String filename = listOfFile.getName();
if (!listOfFile.isFile() || !filename.endsWith(".yml")) {
continue;
}
final String sanitizedFilename = StringUtil.sanitizeFileName(filename.substring(0, filename.length() - 4)) + ".yml";
if (sanitizedFilename.equals(filename)) {
continue;
}
final File tmpFile = new File(listOfFile.getParentFile(), sanitizedFilename + ".tmp");
final File newFile = new File(listOfFile.getParentFile(), sanitizedFilename);
if (!listOfFile.renameTo(tmpFile)) {
LOGGER.log(Level.WARNING, tl("userdataMoveError", filename, sanitizedFilename));
continue;
}
if (newFile.exists()) {
LOGGER.log(Level.WARNING, tl("duplicatedUserdata", filename, sanitizedFilename));
continue;
}
if (!tmpFile.renameTo(newFile)) {
LOGGER.log(Level.WARNING, tl("userdataMoveBackError", sanitizedFilename, sanitizedFilename));
}
}
doneFile.setProperty("sanitizeAllUserFilenames", true);
doneFile.save();
}
private World getFakeWorld(final String name) {
final File bukkitDirectory = ess.getDataFolder().getParentFile().getParentFile();
final File worldDirectory = new File(bukkitDirectory, name);
if (worldDirectory.exists() && worldDirectory.isDirectory()) {
return new FakeWorld(worldDirectory.getName(), World.Environment.NORMAL);
}
return null;
}
public Location getFakeLocation(EssentialsConf config, String path) {
String worldName = config.getString((path != null ? path + "." : "") + "world");
if (worldName == null || worldName.isEmpty()) {
return null;
}
World world = getFakeWorld(worldName);
if (world == null) {
return null;
}
return new Location(world, config.getDouble((path != null ? path + "." : "") + "x", 0), config.getDouble((path != null ? path + "." : "") + "y", 0), config.getDouble((path != null ? path + "." : "") + "z", 0), (float) config.getDouble((path != null ? path + "." : "") + "yaw", 0), (float) config.getDouble((path != null ? path + "." : "") + "pitch", 0));
}
private void deleteOldItemsCsv() {
if (doneFile.getBoolean("deleteOldItemsCsv", false)) {
return;
}
final File file = new File(ess.getDataFolder(), "items.csv");
if (file.exists()) {
try {
final Set<BigInteger> oldconfigs = new HashSet<>();
oldconfigs.add(new BigInteger("66ec40b09ac167079f558d1099e39f10", 16)); // sep 1
oldconfigs.add(new BigInteger("34284de1ead43b0bee2aae85e75c041d", 16)); // crlf
oldconfigs.add(new BigInteger("c33bc9b8ee003861611bbc2f48eb6f4f", 16)); // jul 24
oldconfigs.add(new BigInteger("6ff17925430735129fc2a02f830c1daa", 16)); // crlf
MessageDigest digest = ManagedFile.getDigest();
final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
final byte[] buffer = new byte[1024];
try (DigestInputStream dis = new DigestInputStream(bis, digest)) {
while (dis.read(buffer) != -1) {
}
}
BigInteger hash = new BigInteger(1, digest.digest());
if (oldconfigs.contains(hash) && !file.delete()) {
throw new IOException("Could not delete file " + file.toString());
}
doneFile.setProperty("deleteOldItemsCsv", true);
doneFile.save();
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
private void updateSpawnsToNewSpawnsConfig() {
if (doneFile.getBoolean("updateSpawnsToNewSpawnsConfig", false)) {
return;
}
final File configFile = new File(ess.getDataFolder(), "spawn.yml");
if (configFile.exists()) {
final EssentialsConf config = new EssentialsConf(configFile);
try {
config.load();
if (!config.hasProperty("spawns")) {
final Spawns spawns = new Spawns();
Set<String> keys = config.getKeys(false);
for (String group : keys) {
Location loc = getFakeLocation(config, group);
spawns.getSpawns().put(group.toLowerCase(Locale.ENGLISH), loc);
}
if (!configFile.renameTo(new File(ess.getDataFolder(), "spawn.yml.old"))) {
throw new Exception(tl("fileRenameError", "spawn.yml"));
}
try (PrintWriter writer = new PrintWriter(configFile)) {
new YamlStorageWriter(writer).save(spawns);
}
}
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
doneFile.setProperty("updateSpawnsToNewSpawnsConfig", true);
doneFile.save();
}
private void updateJailsToNewJailsConfig() {
if (doneFile.getBoolean("updateJailsToNewJailsConfig", false)) {
return;
}
final File configFile = new File(ess.getDataFolder(), "jail.yml");
if (configFile.exists()) {
final EssentialsConf config = new EssentialsConf(configFile);
try {
config.load();
if (!config.hasProperty("jails")) {
final com.earth2me.essentials.settings.Jails jails = new com.earth2me.essentials.settings.Jails();
Set<String> keys = config.getKeys(false);
for (String jailName : keys) {
Location loc = getFakeLocation(config, jailName);
jails.getJails().put(jailName.toLowerCase(Locale.ENGLISH), loc);
}
if (!configFile.renameTo(new File(ess.getDataFolder(), "jail.yml.old"))) {
throw new Exception(tl("fileRenameError", "jail.yml"));
}
try (PrintWriter writer = new PrintWriter(configFile)) {
new YamlStorageWriter(writer).save(jails);
}
}
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
doneFile.setProperty("updateJailsToNewJailsConfig", true);
doneFile.save();
}
private void warnMetrics() {
if (doneFile.getBoolean("warnMetrics", false)) {
return;
}
doneFile.setProperty("warnMetrics", true);
doneFile.save();
}
private void uuidFileChange() {
if (doneFile.getBoolean("uuidFileChange", false)) {
return;
}
Boolean ignoreUFCache = doneFile.getBoolean("ignore-userfiles-cache", false);
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
return;
}
int countFiles = 0;
int countReqFiles = 0;
for (String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
countFiles++;
final String name = string.substring(0, string.length() - 4);
UUID uuid = null;
try {
uuid = UUID.fromString(name);
} catch (IllegalArgumentException ex) {
countReqFiles++;
}
if (countFiles > 100) {
break;
}
}
if (countReqFiles < 1) {
return;
}
ess.getLogger().info("#### Starting Essentials UUID userdata conversion in a few seconds. ####");
ess.getLogger().info("We recommend you take a backup of your server before upgrading from the old username system.");
try {
Thread.sleep(15000);
} catch (InterruptedException ex) {
// NOOP
}
uuidFileConvert(ess, ignoreUFCache);
doneFile.setProperty("uuidFileChange", true);
doneFile.save();
}
public static void uuidFileConvert(IEssentials ess, Boolean ignoreUFCache) {
ess.getLogger().info("Starting Essentials UUID userdata conversion");
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
return;
}
int countFiles = 0;
int countFails = 0;
int countEssCache = 0;
int countBukkit = 0;
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
for (String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
final int showProgress = countFiles % 250;
if (showProgress == 0) {
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
}
countFiles++;
String name = string.substring(0, string.length() - 4);
EssentialsUserConf config;
UUID uuid = null;
try {
uuid = UUID.fromString(name);
} catch (IllegalArgumentException ex) {
File file = new File(userdir, string);
EssentialsConf conf = new EssentialsConf(file);
conf.load();
conf.setProperty("lastAccountName", name);
conf.save();
String uuidConf = ignoreUFCache ? "force-uuid" : "uuid";
String uuidString = conf.getString(uuidConf, null);
for (int i = 0; i < 4; i++) {
try {
uuid = UUID.fromString(uuidString);
countEssCache++;
break;
} catch (Exception ex2) {
if (conf.getBoolean("npc", false)) {
uuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
break;
}
org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
uuid = player.getUniqueId();
}
if (uuid != null) {
countBukkit++;
break;
}
}
if (uuid != null) {
conf.forceSave();
config = new EssentialsUserConf(name, uuid, new File(userdir, uuid + ".yml"));
config.convertLegacyFile();
ess.getUserMap().trackUUID(uuid, name, false);
continue;
}
countFails++;
}
}
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
ess.getLogger().info("To rerun the conversion type /essentials uuidconvert");
}
public void banFormatChange() {
if (doneFile.getBoolean("banFormatChange", false)) {
return;
}
ess.getLogger().info("Starting Essentials ban format conversion");
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
return;
}
int countFiles = 0;
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
for (String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
final int showProgress = countFiles % 250;
if (showProgress == 0) {
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
}
countFiles++;
final File pFile = new File(userdir, string);
final EssentialsConf conf = new EssentialsConf(pFile);
conf.load();
String banReason;
long banTimeout;
try {
banReason = conf.getConfigurationSection("ban").getString("reason");
} catch (NullPointerException n) {
banReason = null;
}
final String playerName = conf.getString("lastAccountName");
if (playerName != null && playerName.length() > 1 && banReason != null && banReason.length() > 1) {
try {
if (conf.getConfigurationSection("ban").contains("timeout")) {
banTimeout = Long.parseLong(conf.getConfigurationSection("ban").getString("timeout"));
} else {
banTimeout = 0L;
}
} catch (NumberFormatException n) {
banTimeout = 0L;
}
if (BanLookup.isBanned(ess, playerName)) {
updateBan(playerName, banReason, banTimeout);
}
}
conf.removeProperty("ban");
conf.save();
}
doneFile.setProperty("banFormatChange", true);
doneFile.save();
ess.getLogger().info("Ban format update complete.");
}
private void updateBan(String playerName, String banReason, Long banTimeout) {
if (banTimeout == 0) {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, null, Console.NAME);
} else {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, new Date(banTimeout), Console.NAME);
}
}
private static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
private static final Pattern PATTERN_CONFIG_NAME = Pattern.compile(PATTERN_CONFIG_NAME_REGEX);
private void repairUserMap() {
if (doneFile.getBoolean("userMapRepaired", false)) {
return;
}
ess.getLogger().info("Starting usermap repair");
File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.isDirectory()) {
ess.getLogger().warning("Missing userdata folder, aborting");
return;
}
File[] files = userdataFolder.listFiles(YML_FILTER);
final DecimalFormat format = new DecimalFormat("#0.00");
final Map<String, UUID> names = Maps.newHashMap();
for (int index = 0; index < files.length; index++) {
final File file = files[index];
try {
UUID uuid = null;
final String filename = file.getName();
final String configData = new String(java.nio.file.Files.readAllBytes(file.toPath()), Charsets.UTF_8);
if (filename.length() > 36) {
try {
// ".yml" ending has 4 chars...
uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
} catch (IllegalArgumentException ignored) {
}
}
final Matcher uuidMatcher = PATTERN_CONFIG_UUID.matcher(configData);
if (uuidMatcher.find()) {
try {
uuid = UUID.fromString(uuidMatcher.group(1));
} catch (IllegalArgumentException ignored) {
}
}
if (uuid == null) {
// Don't import
continue;
}
final Matcher nameMatcher = PATTERN_CONFIG_NAME.matcher(configData);
if (nameMatcher.find()) {
final String username = nameMatcher.group(1);
if (username != null && username.length() > 0) {
names.put(StringUtil.safeString(username), uuid);
}
}
if (index % 1000 == 0) {
ess.getLogger().info("Reading: " + format.format((100d * (double) index) / files.length)
+ "%");
}
} catch (final IOException e) {
ess.getLogger().log(Level.SEVERE, "Error while reading file: ", e);
return;
}
}
ess.getUserMap().getNames().putAll(names);
ess.getUserMap().reloadConfig();
doneFile.setProperty("userMapRepaired", true);
doneFile.save();
ess.getLogger().info("Completed usermap repair.");
}
public void beforeSettings() {
if (!ess.getDataFolder().exists()) {
ess.getDataFolder().mkdirs();
}
moveMotdRulesToFile("motd");
moveMotdRulesToFile("rules");
}
public void afterSettings() {
sanitizeAllUserFilenames();
updateUsersPowerToolsFormat();
updateUsersHomesFormat();
deleteOldItemsCsv();
updateSpawnsToNewSpawnsConfig();
updateJailsToNewJailsConfig();
uuidFileChange();
banFormatChange();
warnMetrics();
repairUserMap();
convertIgnoreList();
}
}
@@ -1,63 +0,0 @@
package com.earth2me.essentials;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.UUID;
import java.util.logging.Level;
public class EssentialsUserConf extends EssentialsConf {
public final String username;
public final UUID uuid;
public EssentialsUserConf(final String username, final UUID uuid, final File configFile) {
super(configFile);
this.username = username;
this.uuid = uuid;
}
@Override
public boolean legacyFileExists() {
final File file = new File(configFile.getParentFile(), username + ".yml");
return file.exists();
}
@Override
public void convertLegacyFile() {
final File file = new File(configFile.getParentFile(), username + ".yml");
try {
Files.move(file, new File(configFile.getParentFile(), uuid + ".yml"));
} catch (IOException ex) {
Bukkit.getLogger().log(Level.WARNING, "Failed to migrate user: " + username, ex);
}
setProperty("lastAccountName", username);
}
private File getAltFile() {
final UUID fn = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username.toLowerCase(Locale.ENGLISH)).getBytes(Charsets.UTF_8));
return new File(configFile.getParentFile(), fn.toString() + ".yml");
}
@Override
public boolean altFileExists() {
if (username.equals(username.toLowerCase())) {
return false;
}
return getAltFile().exists();
}
@Override
public void convertAltFile() {
try {
Files.move(getAltFile(), new File(configFile.getParentFile(), uuid + ".yml"));
} catch (IOException ex) {
Bukkit.getLogger().log(Level.WARNING, "Failed to migrate user: " + username, ex);
}
}
}
@@ -1,198 +0,0 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class I18n implements net.ess3.api.II18n {
private static I18n instance;
private static final String MESSAGES = "messages";
private final transient Locale defaultLocale = Locale.getDefault();
private transient Locale currentLocale = defaultLocale;
private transient ResourceBundle customBundle;
private transient ResourceBundle localeBundle;
private final transient ResourceBundle defaultBundle;
private transient Map<String, MessageFormat> messageFormatCache = new HashMap<>();
private final transient IEssentials ess;
private static final Pattern NODOUBLEMARK = Pattern.compile("''");
private static final ResourceBundle NULL_BUNDLE = new ResourceBundle() {
public Enumeration<String> getKeys() {
return null;
}
protected Object handleGetObject(String key) {
return null;
}
};
public I18n(final IEssentials ess) {
this.ess = ess;
defaultBundle = ResourceBundle.getBundle(MESSAGES, Locale.ENGLISH, new UTF8PropertiesControl());
localeBundle = defaultBundle;
customBundle = NULL_BUNDLE;
}
public void onEnable() {
instance = this;
}
public void onDisable() {
instance = null;
}
@Override
public Locale getCurrentLocale() {
return currentLocale;
}
private String translate(final String string) {
try {
try {
return customBundle.getString(string);
} catch (MissingResourceException ex) {
return localeBundle.getString(string);
}
} catch (MissingResourceException ex) {
Logger.getLogger("Essentials").log(Level.WARNING, String.format("Missing translation key \"%s\" in translation file %s", ex.getKey(), localeBundle.getLocale().toString()), ex);
return defaultBundle.getString(string);
}
}
public static String tl(final String string, final Object... objects) {
if (instance == null) {
return "";
}
if (objects.length == 0) {
return NODOUBLEMARK.matcher(instance.translate(string)).replaceAll("'");
} else {
return instance.format(string, objects);
}
}
public String format(final String string, final Object... objects) {
String format = translate(string);
MessageFormat messageFormat = messageFormatCache.get(format);
if (messageFormat == null) {
try {
messageFormat = new MessageFormat(format);
} catch (IllegalArgumentException e) {
ess.getLogger().log(Level.SEVERE, "Invalid Translation key for '" + string + "': " + e.getMessage());
format = format.replaceAll("\\{(\\D*?)\\}", "\\[$1\\]");
messageFormat = new MessageFormat(format);
}
messageFormatCache.put(format, messageFormat);
}
return messageFormat.format(objects).replace(' ', ' '); // replace nbsp with a space
}
public void updateLocale(final String loc) {
if (loc != null && !loc.isEmpty()) {
final String[] parts = loc.split("[_\\.]");
if (parts.length == 1) {
currentLocale = new Locale(parts[0]);
}
if (parts.length == 2) {
currentLocale = new Locale(parts[0], parts[1]);
}
if (parts.length == 3) {
currentLocale = new Locale(parts[0], parts[1], parts[2]);
}
}
ResourceBundle.clearCache();
messageFormatCache = new HashMap<>();
Logger.getLogger("Essentials").log(Level.INFO, String.format("Using locale %s", currentLocale.toString()));
try {
localeBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
localeBundle = NULL_BUNDLE;
}
try {
customBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new FileResClassLoader(I18n.class.getClassLoader(), ess), new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
customBundle = NULL_BUNDLE;
}
}
public static String capitalCase(final String input) {
return input == null || input.length() == 0 ? input : input.toUpperCase(Locale.ENGLISH).charAt(0) + input.toLowerCase(Locale.ENGLISH).substring(1);
}
/**
* Attempts to load properties files from the plugin directory before falling back to the jar.
*/
private static class FileResClassLoader extends ClassLoader {
private final transient File dataFolder;
FileResClassLoader(final ClassLoader classLoader, final IEssentials ess) {
super(classLoader);
this.dataFolder = ess.getDataFolder();
}
@Override
public URL getResource(final String string) {
final File file = new File(dataFolder, string);
if (file.exists()) {
try {
return file.toURI().toURL();
} catch (MalformedURLException ignored) {}
}
return null;
}
@Override
public InputStream getResourceAsStream(final String string) {
final File file = new File(dataFolder, string);
if (file.exists()) {
try {
return new FileInputStream(file);
} catch (FileNotFoundException ignored) {}
}
return null;
}
}
/**
* Reads .properties files as UTF-8 instead of ISO-8859-1, which is the default on Java 8/below.
* Java 9 fixes this by defaulting to UTF-8 for .properties files.
*/
private static class UTF8PropertiesControl extends ResourceBundle.Control {
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IOException {
String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
ResourceBundle bundle = null;
InputStream stream = null;
if (reload) {
URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
try {
// use UTF-8 here, this is the important bit
bundle = new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
} finally {
stream.close();
}
}
return bundle;
}
}
}
@@ -1,205 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.api.IAsyncTeleport;
import com.earth2me.essentials.commands.IEssentialsCommand;
import net.ess3.api.ITeleport;
import net.ess3.api.MaxMoneyException;
import net.ess3.api.events.AfkStatusChangeEvent;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public interface IUser {
boolean isAuthorized(String node);
boolean isAuthorized(IEssentialsCommand cmd);
boolean isAuthorized(IEssentialsCommand cmd, String permissionPrefix);
boolean isPermissionSet(String node);
void healCooldown() throws Exception;
void giveMoney(BigDecimal value) throws MaxMoneyException;
void giveMoney(final BigDecimal value, final CommandSource initiator) throws MaxMoneyException;
void payUser(final User reciever, final BigDecimal value) throws Exception;
void takeMoney(BigDecimal value);
void takeMoney(final BigDecimal value, final CommandSource initiator);
boolean canAfford(BigDecimal value);
Boolean canSpawnItem(final Material material);
void setLastLocation();
void setLogoutLocation();
void requestTeleport(final User player, final boolean here);
/**
* Returns whether this user has an outstanding teleport request to deal with.
*
* @return whether there is a teleport request
*/
boolean hasOutstandingTeleportRequest();
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.api.IAsyncTeleport IAsyncTeleport} with {@link IUser#getAsyncTeleport()}
*/
@Deprecated
ITeleport getTeleport();
IAsyncTeleport getAsyncTeleport();
BigDecimal getMoney();
void setMoney(final BigDecimal value) throws MaxMoneyException;
@Deprecated
void setAfk(final boolean set);
void setAfk(final boolean set, final AfkStatusChangeEvent.Cause cause);
/**
* 'Hidden' Represents when a player is hidden from others. This status includes when the player is hidden via other
* supported plugins. Use isVanished() if you want to check if a user is vanished by Essentials.
*
* @return If the user is hidden or not
*
* @see IUser#isVanished()
*/
boolean isHidden();
void setHidden(boolean vanish);
boolean isGodModeEnabled();
String getGroup();
boolean inGroup(final String group);
boolean canBuild();
long getTeleportRequestTime();
void enableInvulnerabilityAfterTeleport();
void resetInvulnerabilityAfterTeleport();
boolean hasInvulnerabilityAfterTeleport();
/**
* 'Vanished' Represents when a player is hidden from others by Essentials. This status does NOT include when the
* player is hidden via other plugins. Use isHidden() if you want to check if a user is vanished by any supported
* plugin.
*
* @return If the user is vanished or not
*
* @see IUser#isHidden()
*/
boolean isVanished();
void setVanished(boolean vanish);
boolean isIgnoreExempt();
void sendMessage(String message);
/*
* UserData
*/
Location getHome(String name) throws Exception;
Location getHome(Location loc) throws Exception;
List<String> getHomes();
void setHome(String name, Location loc);
void delHome(String name) throws Exception;
boolean hasHome();
Location getLastLocation();
Location getLogoutLocation();
long getLastTeleportTimestamp();
void setLastTeleportTimestamp(long time);
String getJail();
void setJail(String jail);
List<String> getMails();
void addMail(String mail);
boolean isAfk();
void setIgnoreMsg(boolean ignoreMsg);
boolean isIgnoreMsg();
void setConfigProperty(String node, Object object);
Set<String> getConfigKeys();
Map<String, Object> getConfigMap();
Map<String, Object> getConfigMap(String node);
Map<Pattern, Long> getCommandCooldowns();
Date getCommandCooldownExpiry(String label);
void addCommandCooldown(Pattern pattern, Date expiresAt, boolean save);
boolean clearCommandCooldown(Pattern pattern);
/*
* PlayerExtension
*/
Player getBase();
CommandSource getSource();
String getName();
String getAfkMessage();
void setAfkMessage(final String message);
long getAfkSince();
boolean isAcceptingPay();
void setAcceptingPay(boolean acceptingPay);
boolean isPromptingPayConfirm();
void setPromptingPayConfirm(boolean prompt);
boolean isPromptingClearConfirm();
void setPromptingClearConfirm(boolean prompt);
boolean isLastMessageReplyRecipient();
void setLastMessageReplyRecipient(boolean enabled);
Map<User, BigDecimal> getConfirmingPayments();
}
@@ -1,298 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.storage.AsyncStorageObjectHolder;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
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.BlockDamageEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.*;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.plugin.PluginManager;
import java.io.File;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.settings.Jails> implements net.ess3.api.IJails {
private static final transient Logger LOGGER = Bukkit.getLogger();
private static transient boolean enabled = false;
public Jails(final IEssentials ess) {
super(ess, com.earth2me.essentials.settings.Jails.class);
reloadConfig();
}
private void registerListeners() {
enabled = true;
final PluginManager pluginManager = ess.getServer().getPluginManager();
final JailListener blockListener = new JailListener();
pluginManager.registerEvents(blockListener, ess);
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, "Registering Jail listener");
}
}
@Override
public File getStorageFile() {
return new File(ess.getDataFolder(), "jail.yml");
}
@Override
public void finishRead() {
checkRegister();
}
@Override
public void finishWrite() {
checkRegister();
}
public void resetListener() {
enabled = false;
checkRegister();
}
private void checkRegister() {
if (!enabled && getCount() > 0) {
registerListeners();
}
}
@Override
public Location getJail(final String jailName) throws Exception {
acquireReadLock();
try {
if (getData().getJails() == null || jailName == null || !getData().getJails().containsKey(jailName.toLowerCase(Locale.ENGLISH))) {
throw new Exception(tl("jailNotExist"));
}
Location loc = getData().getJails().get(jailName.toLowerCase(Locale.ENGLISH));
if (loc == null || loc.getWorld() == null) {
throw new Exception(tl("jailNotExist"));
}
return loc;
} finally {
unlock();
}
}
@Override
public Collection<String> getList() throws Exception {
acquireReadLock();
try {
if (getData().getJails() == null) {
return Collections.emptyList();
}
return new ArrayList<>(getData().getJails().keySet());
} finally {
unlock();
}
}
@Override
public void removeJail(final String jail) throws Exception {
acquireWriteLock();
try {
if (getData().getJails() == null) {
return;
}
getData().getJails().remove(jail.toLowerCase(Locale.ENGLISH));
} finally {
unlock();
}
}
/**
* @deprecated This method does not use asynchronous teleportation. Use {@link Jails#sendToJail(IUser, String, CompletableFuture)}
*/
@Override
@Deprecated
public void sendToJail(final IUser user, final String jail) throws Exception {
acquireReadLock();
try {
if (user.getBase().isOnline()) {
Location loc = getJail(jail);
user.getTeleport().now(loc, false, TeleportCause.COMMAND);
}
user.setJail(jail);
} finally {
unlock();
}
}
@Override
public void sendToJail(IUser user, String jail, CompletableFuture<Boolean> future) throws Exception {
acquireReadLock();
try {
if (user.getBase().isOnline()) {
Location loc = getJail(jail);
user.getAsyncTeleport().now(loc, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
user.setJail(jail);
});
return;
}
user.setJail(jail);
} finally {
unlock();
}
}
@Override
public void setJail(final String jailName, final Location loc) throws Exception {
acquireWriteLock();
try {
if (getData().getJails() == null) {
getData().setJails(new HashMap<>());
}
getData().getJails().put(jailName.toLowerCase(Locale.ENGLISH), loc);
} finally {
unlock();
}
}
@Override
public int getCount() {
try {
return getList().size();
} catch (Exception ex) {
return 0;
}
}
private class JailListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailBlockBreak(final BlockBreakEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed() && !user.isAuthorized("essentials.jail.allow-break")) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailBlockPlace(final BlockPlaceEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed() && !user.isAuthorized("essentials.jail.allow-place")) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailBlockDamage(final BlockDamageEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed() && !user.isAuthorized("essentials.jail.allow-block-damage")) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailEntityDamageByEntity(final EntityDamageByEntityEvent event) {
if (event.getCause() != DamageCause.ENTITY_ATTACK || event.getEntity().getType() != EntityType.PLAYER) {
return;
}
final Entity damager = event.getDamager();
if (damager.getType() == EntityType.PLAYER) {
final User user = ess.getUser((Player) damager);
if (user != null && user.isJailed()) {
event.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailPlayerInteract(final PlayerInteractEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed() && !user.isAuthorized("essentials.jail.allow-interact")) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailPlayerGameModeChange(PlayerGameModeChangeEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed()) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onJailPlayerRespawn(final PlayerRespawnEvent event) {
final User user = ess.getUser(event.getPlayer());
if (!user.isJailed() || user.getJail() == null || user.getJail().isEmpty()) {
return;
}
try {
event.setRespawnLocation(getJail(user.getJail()));
} catch (Exception ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()));
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onJailPlayerTeleport(final PlayerTeleportEvent event) {
final User user = ess.getUser(event.getPlayer());
if (!user.isJailed() || user.getJail() == null || user.getJail().isEmpty()) {
return;
}
try {
event.setTo(getJail(user.getJail()));
} catch (Exception ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()));
}
}
user.sendMessage(tl("jailMessage"));
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onJailPlayerJoin(final PlayerJoinEvent event) {
final User user = ess.getUser(event.getPlayer());
final long currentTime = System.currentTimeMillis();
user.checkJailTimeout(currentTime);
if (!user.isJailed() || user.getJail() == null || user.getJail().isEmpty()) {
return;
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(ex -> {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()));
}
return false;
});
future.thenAccept(success -> user.sendMessage(tl("jailMessage")));
try {
sendToJail(user, user.getJail(), future);
} catch (Exception ex) {
future.completeExceptionally(ex);
}
}
}
}
@@ -1,134 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import java.io.File;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static com.earth2me.essentials.I18n.capitalCase;
import static com.earth2me.essentials.I18n.tl;
public class Kits implements IConf {
private final EssentialsConf config;
private ConfigurationSection kits;
public Kits(final IEssentials essentials) {
config = new EssentialsConf(new File(essentials.getDataFolder(), "kits.yml"));
config.setTemplateName("/kits.yml");
reloadConfig();
}
@Override
public void reloadConfig() {
config.load();
kits = _getKits();
}
private ConfigurationSection _getKits() {
if (config.isConfigurationSection("kits")) {
final ConfigurationSection section = config.getConfigurationSection("kits");
final ConfigurationSection newSection = new MemoryConfiguration();
for (String kitItem : section.getKeys(false)) {
if (section.isConfigurationSection(kitItem)) {
newSection.set(kitItem.toLowerCase(Locale.ENGLISH), section.getConfigurationSection(kitItem));
}
}
return newSection;
}
return null;
}
public EssentialsConf getConfig() {
return config;
}
public ConfigurationSection getKits() {
return kits;
}
public Map<String, Object> getKit(String name) {
name = name.replace('.', '_').replace('/', '_');
if (getKits() != null) {
final ConfigurationSection kits = getKits();
// For some reason, YAML doesn't sees keys as always lowercase even if they aren't defined like that.
// Workaround is to toLowercase when getting from the config, but showing normally elsewhere.
// ODDLY ENOUGH when you get the configuration section for ALL kits, it will return the proper
// case of each kit. But when you check for each kit's configuration section, it won't return the kit
// you just found if you don't toLowercase it.
if (kits.isConfigurationSection(name.toLowerCase())) {
return kits.getConfigurationSection(name.toLowerCase()).getValues(true);
}
}
return null;
}
// Tries to find an existing kit name that matches the given name, ignoring case. Returns null if no match.
public String matchKit(String name) {
if (config.isConfigurationSection("kits")) {
final ConfigurationSection section = config.getConfigurationSection("kits");
if (section != null) {
for (String kitName : section.getKeys(false)) {
if (kitName.equalsIgnoreCase(name)) {
return kitName;
}
}
}
}
return null;
}
public void addKit(String name, List<String> lines, long delay) {
// Will overwrite but w/e
config.set("kits." + name + ".delay", delay);
config.set("kits." + name + ".items", lines);
kits = _getKits();
config.save();
}
public void removeKit(String name) {
config.set("kits." + name, null);
kits = _getKits();
config.save();
}
public String listKits(final net.ess3.api.IEssentials ess, final User user) throws Exception {
try {
final ConfigurationSection kits = config.getConfigurationSection("kits");
final StringBuilder list = new StringBuilder();
for (String kitItem : kits.getKeys(false)) {
if (user == null) {
list.append(" ").append(capitalCase(kitItem));
} else if (user.isAuthorized("essentials.kits." + kitItem.toLowerCase(Locale.ENGLISH))) {
String cost = "";
String name = capitalCase(kitItem);
BigDecimal costPrice = new Trade("kit-" + kitItem.toLowerCase(Locale.ENGLISH), ess).getCommandCost(user);
if (costPrice.signum() > 0) {
cost = tl("kitCost", NumberUtil.displayCurrency(costPrice, ess));
}
Kit kit = new Kit(kitItem, ess);
double nextUse = kit.getNextUse(user);
if (nextUse == -1 && ess.getSettings().isSkippingUsedOneTimeKitsFromKitList()) {
continue;
} else if (nextUse != 0) {
name = tl("kitDelay", name);
}
list.append(" ").append(name).append(cost);
}
}
return list.toString().trim();
} catch (Exception ex) {
throw new Exception(tl("kitError"), ex);
}
}
}
@@ -1,598 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.textreader.BookInput;
import com.earth2me.essentials.textreader.BookPager;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.base.Joiner;
import net.ess3.api.IEssentials;
import net.ess3.nms.refl.ReflUtil;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.Material;
import org.bukkit.block.Banner;
import org.bukkit.block.banner.PatternType;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class MetaItemStack {
private static final Map<String, DyeColor> colorMap = new HashMap<>();
private static final Map<String, FireworkEffect.Type> fireworkShape = new HashMap<>();
static {
for (DyeColor color : DyeColor.values()) {
colorMap.put(color.name(), color);
}
for (FireworkEffect.Type type : FireworkEffect.Type.values()) {
fireworkShape.put(type.name(), type);
}
}
private final transient Pattern splitPattern = Pattern.compile("[:+',;.]");
private ItemStack stack;
private FireworkEffect.Builder builder = FireworkEffect.builder();
private PotionEffectType pEffectType;
private PotionEffect pEffect;
private boolean validFirework = false;
private boolean validPotionEffect = false;
private boolean validPotionDuration = false;
private boolean validPotionPower = false;
private boolean isSplashPotion = false;
private boolean completePotion = false;
private int power = 1;
private int duration = 120;
public MetaItemStack(final ItemStack stack) {
this.stack = stack.clone();
}
public ItemStack getItemStack() {
return stack;
}
public boolean isValidFirework() {
return validFirework;
}
public boolean isValidPotion() {
return validPotionEffect && validPotionDuration && validPotionPower;
}
public FireworkEffect.Builder getFireworkBuilder() {
return builder;
}
public PotionEffect getPotionEffect() {
return pEffect;
}
public boolean completePotion() {
return completePotion;
}
private void resetPotionMeta() {
pEffect = null;
pEffectType = null;
validPotionEffect = false;
validPotionDuration = false;
validPotionPower = false;
isSplashPotion = false;
completePotion = true;
}
public boolean canSpawn(final IEssentials ess) {
try {
ess.getServer().getUnsafe().modifyItemStack(stack.clone(), "{}");
return true;
} catch (NoSuchMethodError nsme) {
return true;
} catch (Throwable npe) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Itemstack is invalid", npe);
}
return false;
}
}
public void parseStringMeta(final CommandSource sender, final boolean allowUnsafe, String[] string, int fromArg, final IEssentials ess) throws Exception {
if (string[fromArg].startsWith("{") && hasMetaPermission(sender, "vanilla", false, true, ess)) {
try {
stack = ess.getServer().getUnsafe().modifyItemStack(stack, Joiner.on(' ').join(Arrays.asList(string).subList(fromArg, string.length)));
} catch (NullPointerException npe) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Itemstack is invalid", npe);
}
} catch (NoSuchMethodError nsme) {
throw new Exception(tl("noMetaJson"), nsme);
} catch (Throwable throwable) {
throw new Exception(throwable.getMessage(), throwable);
}
} else {
for (int i = fromArg; i < string.length; i++) {
addStringMeta(sender, allowUnsafe, string[i], ess);
}
if (validFirework) {
if (!hasMetaPermission(sender, "firework", true, true, ess)) {
throw new Exception(tl("noMetaFirework"));
}
FireworkEffect effect = builder.build();
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
fmeta.addEffect(effect);
if (fmeta.getEffects().size() > 1 && !hasMetaPermission(sender, "firework-multiple", true, true, ess)) {
throw new Exception(tl("multipleCharges"));
}
stack.setItemMeta(fmeta);
}
}
}
public void addStringMeta(final CommandSource sender, final boolean allowUnsafe, final String string, final IEssentials ess) throws Exception {
final String[] split = splitPattern.split(string, 2);
if (split.length < 1) {
return;
}
Material WRITTEN_BOOK = EnumUtil.getMaterial("WRITTEN_BOOK");
if (split.length > 1 && split[0].equalsIgnoreCase("name") && hasMetaPermission(sender, "name", false, true, ess)) {
final String displayName = FormatUtil.replaceFormat(split[1].replace('_', ' '));
final ItemMeta meta = stack.getItemMeta();
meta.setDisplayName(displayName);
stack.setItemMeta(meta);
} else if (split.length > 1 && (split[0].equalsIgnoreCase("lore") || split[0].equalsIgnoreCase("desc")) && hasMetaPermission(sender, "lore", false, true, ess)) {
final List<String> lore = new ArrayList<>();
for (String line : split[1].split("(?<!\\\\)\\|")) {
lore.add(FormatUtil.replaceFormat(line.replace('_', ' ').replace("\\|", "|")));
}
final ItemMeta meta = stack.getItemMeta();
meta.setLore(lore);
stack.setItemMeta(meta);
} else if (split[0].equalsIgnoreCase("unbreakable") && hasMetaPermission(sender, "unbreakable", false, true, ess)) {
boolean value = split.length <= 1 || Boolean.parseBoolean(split[1]);
setUnbreakable(stack, value);
} else if (split.length > 1 && (split[0].equalsIgnoreCase("player") || split[0].equalsIgnoreCase("owner")) && hasMetaPermission(sender, "head", false, true, ess)) {
if (MaterialUtil.isPlayerHead(stack.getType(), stack.getDurability())) {
final String owner = split[1];
setSkullOwner(ess, stack, owner);
} else {
throw new Exception(tl("onlyPlayerSkulls"));
}
} else if (split.length > 1 && split[0].equalsIgnoreCase("book") && stack.getType() == WRITTEN_BOOK && (hasMetaPermission(sender, "book", true, true, ess) || hasMetaPermission(sender, "chapter-" + split[1].toLowerCase(Locale.ENGLISH), true, true, ess))) {
final BookMeta meta = (BookMeta) stack.getItemMeta();
final IText input = new BookInput("book", true, ess);
final BookPager pager = new BookPager(input);
List<String> pages = pager.getPages(split[1]);
meta.setPages(pages);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("author") && stack.getType() == WRITTEN_BOOK && hasMetaPermission(sender, "author", false, true, ess)) {
final String author = FormatUtil.replaceFormat(split[1]);
final BookMeta meta = (BookMeta) stack.getItemMeta();
meta.setAuthor(author);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("title") && stack.getType() == WRITTEN_BOOK && hasMetaPermission(sender, "title", false, true, ess)) {
final String title = FormatUtil.replaceFormat(split[1].replace('_', ' '));
final BookMeta meta = (BookMeta) stack.getItemMeta();
meta.setTitle(title);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("power") && (MaterialUtil.isFirework(stack.getType()))&& hasMetaPermission(sender, "firework-power", false, true, ess)) {
final int power = NumberUtil.isInt(split[1]) ? Integer.parseInt(split[1]) : 0;
final FireworkMeta meta = (FireworkMeta) stack.getItemMeta();
meta.setPower(power > 3 ? 4 : power);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("itemflags") && hasMetaPermission(sender, "itemflags", false, true, ess)) {
addItemFlags(string);
} else if (MaterialUtil.isFirework(stack.getType())) {
//WARNING - Meta for fireworks will be ignored after this point.
addFireworkMeta(sender, false, string, ess);
} else if (MaterialUtil.isPotion(stack.getType())) {
//WARNING - Meta for potions will be ignored after this point.
addPotionMeta(sender, false, string, ess);
} else if (MaterialUtil.isBanner(stack.getType())) {
if (stack.getType().toString().equals("SHIELD") && Enchantments.getByName(split[0]) != null) {
parseEnchantmentStrings(sender, allowUnsafe, split, ess);
} else {
//WARNING - Meta for banners will be ignored after this point.
addBannerMeta(sender, false, string, ess);
}
} else if (split.length > 1 && (split[0].equalsIgnoreCase("color") || split[0].equalsIgnoreCase("colour")) && MaterialUtil.isLeatherArmor(stack.getType())) {
final String[] color = split[1].split("(\\||,)");
if (color.length == 1 && (NumberUtil.isInt(color[0]) || color[0].startsWith("#"))) {
// Either integer or hexadecimal
final LeatherArmorMeta meta = (LeatherArmorMeta) stack.getItemMeta();
String input = color[0];
if (input.startsWith("#")) { // Hex
meta.setColor(Color.fromRGB(
Integer.valueOf(input.substring(1, 3), 16),
Integer.valueOf(input.substring(3, 5), 16),
Integer.valueOf(input.substring(5, 7), 16)));
} else { // Int
meta.setColor(Color.fromRGB(Integer.parseInt(input)));
}
stack.setItemMeta(meta);
} else if (color.length == 3) { // r,g,b
final int red = NumberUtil.isInt(color[0]) ? Integer.parseInt(color[0]) : 0;
final int green = NumberUtil.isInt(color[1]) ? Integer.parseInt(color[1]) : 0;
final int blue = NumberUtil.isInt(color[2]) ? Integer.parseInt(color[2]) : 0;
final LeatherArmorMeta meta = (LeatherArmorMeta) stack.getItemMeta();
meta.setColor(Color.fromRGB(red, green, blue));
stack.setItemMeta(meta);
} else {
throw new Exception(tl("leatherSyntax"));
}
} else {
parseEnchantmentStrings(sender, allowUnsafe, split, ess);
}
}
public void addItemFlags(final String string) throws Exception {
String[] separate = splitPattern.split(string, 2);
if (separate.length != 2) {
throw new Exception(tl("invalidItemFlagMeta", string));
}
String[] split = separate[1].split(",");
ItemMeta meta = stack.getItemMeta();
for (String s : split) {
for (ItemFlag flag : ItemFlag.values()) {
if (s.equalsIgnoreCase(flag.name())) {
meta.addItemFlags(flag);
}
}
}
if (meta.getItemFlags().isEmpty()) {
throw new Exception(tl("invalidItemFlagMeta", string));
}
stack.setItemMeta(meta);
}
public void addFireworkMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception {
if (MaterialUtil.isFirework(stack.getType())) {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
return;
}
if (split[0].equalsIgnoreCase("color") || split[0].equalsIgnoreCase("colour") || (allowShortName && split[0].equalsIgnoreCase("c"))) {
if (validFirework) {
if (!hasMetaPermission(sender, "firework", true, true, ess)) {
throw new Exception(tl("noMetaFirework"));
}
FireworkEffect effect = builder.build();
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
fmeta.addEffect(effect);
if (fmeta.getEffects().size() > 1 && !hasMetaPermission(sender, "firework-multiple", true, true, ess)) {
throw new Exception(tl("multipleCharges"));
}
stack.setItemMeta(fmeta);
builder = FireworkEffect.builder();
}
List<Color> primaryColors = new ArrayList<>();
String[] colors = split[1].split(",");
for (String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
validFirework = true;
primaryColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
builder.withColor(primaryColors);
} else if (split[0].equalsIgnoreCase("shape") || split[0].equalsIgnoreCase("type") || (allowShortName && (split[0].equalsIgnoreCase("s") || split[0].equalsIgnoreCase("t")))) {
FireworkEffect.Type finalEffect = null;
split[1] = (split[1].equalsIgnoreCase("large") ? "BALL_LARGE" : split[1]);
if (fireworkShape.containsKey(split[1].toUpperCase())) {
finalEffect = fireworkShape.get(split[1].toUpperCase());
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
if (finalEffect != null) {
builder.with(finalEffect);
}
} else if (split[0].equalsIgnoreCase("fade") || (allowShortName && split[0].equalsIgnoreCase("f"))) {
List<Color> fadeColors = new ArrayList<>();
String[] colors = split[1].split(",");
for (String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
fadeColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
if (!fadeColors.isEmpty()) {
builder.withFade(fadeColors);
}
} else if (split[0].equalsIgnoreCase("effect") || (allowShortName && split[0].equalsIgnoreCase("e"))) {
String[] effects = split[1].split(",");
for (String effect : effects) {
if (effect.equalsIgnoreCase("twinkle")) {
builder.flicker(true);
} else if (effect.equalsIgnoreCase("trail")) {
builder.trail(true);
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
}
}
}
public void addPotionMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception {
if (MaterialUtil.isPotion(stack.getType())) {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
return;
}
if (split[0].equalsIgnoreCase("effect") || (allowShortName && split[0].equalsIgnoreCase("e"))) {
pEffectType = Potions.getByName(split[1]);
if (pEffectType != null && pEffectType.getName() != null) {
if (hasMetaPermission(sender, "potions." + pEffectType.getName().toLowerCase(Locale.ENGLISH), true, false, ess)) {
validPotionEffect = true;
} else {
throw new Exception(tl("noPotionEffectPerm", pEffectType.getName().toLowerCase(Locale.ENGLISH)));
}
} else {
throw new Exception(tl("invalidPotionMeta", split[1]));
}
} else if (split[0].equalsIgnoreCase("power") || (allowShortName && split[0].equalsIgnoreCase("p"))) {
if (NumberUtil.isInt(split[1])) {
validPotionPower = true;
power = Integer.parseInt(split[1]);
if (power > 0 && power < 4) {
power -= 1;
}
} else {
throw new Exception(tl("invalidPotionMeta", split[1]));
}
} else if (split[0].equalsIgnoreCase("duration") || (allowShortName && split[0].equalsIgnoreCase("d"))) {
if (NumberUtil.isInt(split[1])) {
validPotionDuration = true;
duration = Integer.parseInt(split[1]) * 20; //Duration is in ticks by default, converted to seconds
} else {
throw new Exception(tl("invalidPotionMeta", split[1]));
}
} else if (split[0].equalsIgnoreCase("splash") || (allowShortName && split[0].equalsIgnoreCase("s"))) {
isSplashPotion = Boolean.parseBoolean(split[1]);
}
if (isValidPotion()) {
PotionMeta pmeta = (PotionMeta) stack.getItemMeta();
pEffect = pEffectType.createEffect(duration, power);
if (pmeta.getCustomEffects().size() > 1 && !hasMetaPermission(sender, "potions.multiple", true, false, ess)) {
throw new Exception(tl("multiplePotionEffects"));
}
pmeta.addCustomEffect(pEffect, true);
stack.setItemMeta(pmeta);
if (ReflUtil.getNmsVersionObject().isHigherThanOrEqualTo(ReflUtil.V1_9_R1)) {
if (isSplashPotion && stack.getType() != Material.SPLASH_POTION) {
stack.setType(Material.SPLASH_POTION);
} else if (!isSplashPotion && stack.getType() != Material.POTION) {
stack.setType(Material.POTION);
}
} else {
Potion potion = Potion.fromItemStack(stack);
potion.setSplash(isSplashPotion);
potion.apply(stack);
}
resetPotionMeta();
}
}
}
private void parseEnchantmentStrings(final CommandSource sender, final boolean allowUnsafe, final String[] split, final IEssentials ess) throws Exception {
final Enchantment enchantment = Enchantments.getByName(split[0]);
if (enchantment == null || !hasMetaPermission(sender, "enchantments." + enchantment.getName().toLowerCase(Locale.ENGLISH), false, false, ess)) {
return;
}
int level = -1;
if (split.length > 1) {
try {
level = Integer.parseInt(split[1]);
} catch (NumberFormatException ex) {
level = -1;
}
}
if (level < 0 || (!allowUnsafe && level > enchantment.getMaxLevel())) {
level = enchantment.getMaxLevel();
}
addEnchantment(sender, allowUnsafe, enchantment, level);
}
public void addEnchantment(final CommandSource sender, final boolean allowUnsafe, final Enchantment enchantment, final int level) throws Exception {
if (enchantment == null) {
throw new Exception(tl("enchantmentNotFound"));
}
try {
if (stack.getType().equals(Material.ENCHANTED_BOOK)) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) stack.getItemMeta();
if (level == 0) {
meta.removeStoredEnchant(enchantment);
} else {
meta.addStoredEnchant(enchantment, level, allowUnsafe);
}
stack.setItemMeta(meta);
} else // all other material types besides ENCHANTED_BOOK
{
if (level == 0) {
stack.removeEnchantment(enchantment);
} else {
if (allowUnsafe) {
stack.addUnsafeEnchantment(enchantment, level);
} else {
stack.addEnchantment(enchantment, level);
}
}
}
} catch (Exception ex) {
throw new Exception("Enchantment " + enchantment.getName() + ": " + ex.getMessage(), ex);
}
}
public Enchantment getEnchantment(final User user, final String name) throws Exception {
final Enchantment enchantment = Enchantments.getByName(name);
if (enchantment == null) {
return null;
}
final String enchantmentName = enchantment.getName().toLowerCase(Locale.ENGLISH);
if (!hasMetaPermission(user, "enchantments." + enchantmentName, true, false)) {
throw new Exception(tl("enchantmentPerm", enchantmentName));
}
return enchantment;
}
public void addBannerMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception {
if (MaterialUtil.isBanner(stack.getType()) && !stack.getType().toString().equals("SHIELD") && string != null) {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
throw new Exception(tl("invalidBanner", split[1]));
}
PatternType patternType = null;
try {
patternType = PatternType.valueOf(split[0]);
} catch (Exception ignored ) {}
final BannerMeta meta = (BannerMeta) stack.getItemMeta();
if (split[0].equalsIgnoreCase("basecolor")) {
Color color = Color.fromRGB(Integer.parseInt(split[1]));
meta.setBaseColor(DyeColor.getByColor(color));
} else if (patternType != null) {
PatternType type = PatternType.valueOf(split[0]);
DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
meta.addPattern(pattern);
}
stack.setItemMeta(meta);
} else if (stack.getType().toString().equals("SHIELD") && string != null) {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
throw new Exception(tl("invalidBanner", split[1]));
}
PatternType patternType = null;
try {
patternType = PatternType.valueOf(split[0]);
} catch (Exception ignored ) {}
// Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795
BlockStateMeta meta = (BlockStateMeta) stack.getItemMeta();
Banner banner = (Banner) meta.getBlockState();
if (split[0].equalsIgnoreCase("basecolor")) {
Color color = Color.fromRGB(Integer.parseInt(split[1]));
banner.setBaseColor(DyeColor.getByColor(color));
} else if (patternType != null) {
PatternType type = PatternType.valueOf(split[0]);
DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
banner.addPattern(pattern);
}
banner.update();
meta.setBlockState(banner);
stack.setItemMeta(meta);
}
}
private boolean hasMetaPermission(final CommandSource sender, final String metaPerm, final boolean graceful, final boolean includeBase, final IEssentials ess) throws Exception {
final User user = sender != null && sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null;
return hasMetaPermission(user, metaPerm, graceful, includeBase);
}
private boolean hasMetaPermission(final User user, final String metaPerm, final boolean graceful, final boolean includeBase) throws Exception {
final String permBase = includeBase ? "essentials.itemspawn.meta-" : "essentials.";
if (user == null || user.isAuthorized(permBase + metaPerm)) {
return true;
}
if (graceful) {
return false;
} else {
throw new Exception(tl("noMetaPerm", metaPerm));
}
}
private static int bukkitUnbreakableSupport = -1;
private static Method spigotMethod;
private static Method setUnbreakableMethod;
private void setUnbreakable(ItemStack is, boolean unbreakable) {
ItemMeta meta = is.getItemMeta();
try {
if (bukkitUnbreakableSupport == -1) {
try {
ItemMeta.class.getDeclaredMethod("setUnbreakable", boolean.class);
bukkitUnbreakableSupport = 1;
} catch (NoSuchMethodException | SecurityException ex) {
bukkitUnbreakableSupport = 0;
}
}
if (bukkitUnbreakableSupport == 1) {
meta.setUnbreakable(unbreakable);
} else {
if (spigotMethod == null) {
spigotMethod = meta.getClass().getDeclaredMethod("spigot");
spigotMethod.setAccessible(true);
}
Object itemStackSpigot = spigotMethod.invoke(meta);
if (setUnbreakableMethod == null) {
setUnbreakableMethod = itemStackSpigot.getClass().getDeclaredMethod("setUnbreakable", Boolean.TYPE);
setUnbreakableMethod.setAccessible(true);
}
setUnbreakableMethod.invoke(itemStackSpigot, unbreakable);
}
is.setItemMeta(meta);
} catch (Throwable t) {
t.printStackTrace();
}
}
private static boolean useNewSkullMethod = true;
private static void setSkullOwner(final IEssentials ess, final ItemStack stack, final String owner) {
if (!(stack.getItemMeta() instanceof SkullMeta)) return;
SkullMeta meta = (SkullMeta) stack.getItemMeta();
if (useNewSkullMethod) {
try {
meta.setOwningPlayer(ess.getServer().getOfflinePlayer(owner));
stack.setItemMeta(meta);
return;
} catch (NoSuchMethodError e) {
useNewSkullMethod = false;
}
}
meta.setOwner(owner);
stack.setItemMeta(meta);
}
}
@@ -1,215 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.nms.refl.ReflUtil;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Fox;
import org.bukkit.entity.Llama;
import org.bukkit.entity.MushroomCow;
import org.bukkit.entity.Ocelot;
import org.bukkit.entity.Panda;
import org.bukkit.entity.Parrot;
import org.bukkit.entity.TropicalFish;
import org.bukkit.entity.Villager;
import java.lang.reflect.Method;
import static com.earth2me.essentials.utils.EnumUtil.getEntityType;
public class MobCompat {
// Constants for mob interfaces added in later versions
public static final Class RAIDER = ReflUtil.getClassCached("org.bukkit.entity.Raider");
// Constants for mobs added in later versions
public static final EntityType LLAMA = getEntityType("LLAMA");
public static final EntityType PARROT = getEntityType("PARROT");
public static final EntityType TROPICAL_FISH = getEntityType("TROPICAL_FISH");
public static final EntityType PANDA = getEntityType("PANDA");
public static final EntityType TRADER_LLAMA = getEntityType("TRADER_LLAMA");
public static final EntityType SHULKER = getEntityType("SHULKER");
public static final EntityType STRAY = getEntityType("STRAY");
public static final EntityType FOX = getEntityType("FOX");
public static final EntityType PHANTOM = getEntityType("PHANTOM");
// Constants for mobs that have changed since earlier versions
public static final EntityType CAT = getEntityType("CAT", "OCELOT");
public static final EntityType ZOMBIFIED_PIGLIN = getEntityType("ZOMBIFIED_PIGLIN", "PIG_ZOMBIE");
public enum CatType {
// These are (loosely) Mojang names for the cats
SIAMESE("SIAMESE", "SIAMESE_CAT"),
WHITE("WHITE", "SIAMESE_CAT"),
RED("RED", "RED_CAT"),
TABBY("TABBY", "RED_CAT"),
TUXEDO("BLACK", "BLACK_CAT"),
BRITISH_SHORTHAIR("BRITISH_SHORTHAIR", "SIAMESE_CAT"),
CALICO("CALICO", "RED_CAT"),
PERSIAN("PERSIAN", "RED_CAT"),
RAGDOLL("RAGDOLL", "SIAMESE_CAT"),
JELLIE("JELLIE", "SIAMESE_CAT"),
BLACK("ALL_BLACK", "BLACK_CAT"),
;
private final String catTypeName;
private final String ocelotTypeName;
CatType(final String catTypeName, final String ocelotTypeName) {
this.catTypeName = catTypeName;
this.ocelotTypeName = ocelotTypeName;
}
}
public enum VillagerProfession {
// These are 1.14+ villager professions mapped to their respective pre-V&P profession and career
NONE("FARMER", "FARMER", "NONE"),
ARMORER("BLACKSMITH", "ARMORER"),
BUTCHER("FARMER", "BUTCHER"),
CARTOGRAPHER("LIBRARIAN", "CARTOGRAPHER"),
CLERIC("PRIEST", "CLERIC"),
FARMER("FARMER", "FARMER"),
FISHERMAN("FARMER", "FISHERMAN"),
FLETCHER("FARMER", "FLETCHER"),
LEATHERWORKER("BUTCHER", "LEATHERWORKER"),
LIBRARIAN("LIBRARIAN", "LIBRARIAN"),
MASON(null, null, "MASON"),
NITWIT("NITWIT", "NITWIT"),
SHEPHERD("FARMER", "SHEPHERD"),
TOOLSMITH("BLACKSMITH", "TOOL_SMITH", "TOOLSMITH"),
WEAPONSMITH("BLACKSMITH", "WEAPON_SMITH", "WEAPONSMITH"),
;
private final String oldProfession;
private final String oldCareer;
private final String newProfession;
VillagerProfession(final String oldProfession, final String career) {
this.oldProfession = oldProfession;
this.oldCareer = career;
this.newProfession = career;
}
VillagerProfession(final String oldProfession, final String oldCareer, final String newProfession) {
this.oldProfession = oldProfession;
this.oldCareer = oldCareer;
this.newProfession = newProfession;
}
private Villager.Profession asEnum() {
return EnumUtil.valueOf(Villager.Profession.class, newProfession, oldProfession);
}
}
// Older cats are Ocelots, whereas 1.14+ cats are Cats
public static void setCatType(final Entity entity, final CatType type) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
((Ocelot) entity).setCatType(Ocelot.Type.valueOf(type.ocelotTypeName));
} else {
Class cat = ReflUtil.getClassCached("org.bukkit.entity.Cat");
Class catType = ReflUtil.getClassCached("org.bukkit.entity.Cat$Type");
Method setCatType = ReflUtil.getMethodCached(cat, "setCatType", catType);
try {
setCatType.invoke(entity, EnumUtil.valueOf(catType, type.catTypeName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Older villagers have professions and careers, 1.14+ villagers only have professions
public static void setVillagerProfession(final Entity entity, final VillagerProfession profession) {
if (!(entity instanceof Villager)) {
return;
}
Villager villager = (Villager) entity;
villager.setProfession(profession.asEnum());
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
Class villagerCareer = ReflUtil.getClassCached("org.bukkit.entity.Villager$Career");
Method setCareer = ReflUtil.getMethodCached(Villager.class, "setCareer", villagerCareer);
try {
setCareer.invoke(entity, EnumUtil.valueOf(villagerCareer, profession.oldCareer));
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Only 1.14+ villagers have biome variants
public static void setVillagerType(final Entity entity, final String type) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
return;
}
if (entity instanceof Villager) {
((Villager) entity).setVillagerType(Villager.Type.valueOf(type));
}
}
// Llamas only exist in 1.11+
public static void setLlamaColor(final Entity entity, final String color) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_11_R01)) {
return;
}
if (entity instanceof Llama) {
((Llama) entity).setColor(Llama.Color.valueOf(color));
}
}
// Parrots only exist in 1.12+
public static void setParrotVariant(final Entity entity, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_12_0_R01)) {
return;
}
if (entity instanceof Parrot) {
((Parrot) entity).setVariant(Parrot.Variant.valueOf(variant));
}
}
// Tropical fish only exist in 1.13+
public static void setTropicalFishPattern(final Entity entity, final String pattern) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_12_0_R01)) {
return;
}
if (entity instanceof TropicalFish) {
((TropicalFish) entity).setPattern(TropicalFish.Pattern.valueOf(pattern));
}
}
// Mushroom cow variant API only exists in 1.14+
public static void setMooshroomVariant(final Entity entity, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
return;
}
if (entity instanceof MushroomCow) {
((MushroomCow) entity).setVariant(MushroomCow.Variant.valueOf(variant));
}
}
// Pandas only exists in 1.14+
public static void setPandaGene(final Entity entity, final String gene, final boolean mainGene) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
return;
}
if (entity instanceof Panda) {
Panda panda = (Panda) entity;
Panda.Gene pandaGene = Panda.Gene.valueOf(gene);
if (mainGene) {
panda.setMainGene(pandaGene);
} else {
panda.setHiddenGene(pandaGene);
}
}
}
// Foxes only exist in 1.14+
public static void setFoxType(final Entity entity, final String type) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
return;
}
if (entity instanceof Fox) {
((Fox) entity).setFoxType(Fox.Type.valueOf(type));
}
}
}
@@ -1,119 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class PlayerList {
// Cosmetic list formatting
public static String listUsers(final IEssentials ess, final List<User> users, final String seperator) {
final StringBuilder groupString = new StringBuilder();
Collections.sort(users);
boolean needComma = false;
for (User user : users) {
if (needComma) {
groupString.append(seperator);
}
needComma = true;
if (user.isAfk()) {
groupString.append(tl("listAfkTag"));
}
if (user.isHidden()) {
groupString.append(tl("listHiddenTag"));
}
user.setDisplayNick();
groupString.append(user.getDisplayName());
if (ess.getSettings().realNamesOnList() && !ChatColor.stripColor(user.getDisplayName()).equals(user.getName())) {
groupString.append(" (").append(user.getName()).append(")");
}
groupString.append("\u00a7f");
}
return groupString.toString();
}
// Produce a user summary: There are 5 out of maximum 10 players online.
public static String listSummary(final IEssentials ess, final User user, final boolean showHidden) {
Server server = ess.getServer();
int playerHidden = 0;
int hiddenCount = 0;
for (User onlinePlayer : ess.getOnlineUsers()) {
if (onlinePlayer.isHidden() || (user != null && !user.getBase().canSee(onlinePlayer.getBase()))) {
playerHidden++;
if (showHidden || user != null && user.getBase().canSee(onlinePlayer.getBase())) {
hiddenCount++;
}
}
}
String online;
if (hiddenCount > 0) {
online = tl("listAmountHidden", ess.getOnlinePlayers().size() - playerHidden, hiddenCount, server.getMaxPlayers());
} else {
online = tl("listAmount", ess.getOnlinePlayers().size() - playerHidden, server.getMaxPlayers());
}
return online;
}
// Build the basic player list, divided by groups.
public static Map<String, List<User>> getPlayerLists(final IEssentials ess, final User sender, final boolean showHidden) {
final Map<String, List<User>> playerList = new HashMap<>();
for (User onlineUser : ess.getOnlineUsers()) {
if ((sender == null && !showHidden && onlineUser.isHidden()) || (sender != null && !showHidden && !sender.getBase().canSee(onlineUser.getBase()))) {
continue;
}
final String group = FormatUtil.stripFormat(FormatUtil.stripEssentialsFormat(onlineUser.getGroup().toLowerCase()));
List<User> list = playerList.computeIfAbsent(group, k -> new ArrayList<>());
list.add(onlineUser);
}
return playerList;
}
// Handle the merging of groups
public static List<User> getMergedList(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) {
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
final List<User> users = new ArrayList<>();
for (String configGroup : configGroups) {
if (configGroup.equalsIgnoreCase(groupName)) {
String[] groupValues = ess.getSettings().getListGroupConfig().get(configGroup).toString().trim().split(" ");
for (String groupValue : groupValues) {
groupValue = groupValue.toLowerCase(Locale.ENGLISH);
if (groupValue.isEmpty()) {
continue;
}
List<User> u = playerList.get(groupValue.trim());
if (u == null || u.isEmpty()) {
continue;
}
playerList.remove(groupValue);
users.addAll(u);
}
}
}
return users;
}
// Output a playerlist of just a single group, /list <groupname>
public static String listGroupUsers(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) throws Exception {
final List<User> users = getMergedList(ess, playerList, groupName);
final List<User> groupUsers = playerList.get(groupName);
if (groupUsers != null && !groupUsers.isEmpty()) {
users.addAll(groupUsers);
}
if (users.isEmpty()) {
throw new Exception(tl("groupDoesNotExist"));
}
String displayGroupName = Character.toTitleCase(groupName.charAt(0)) +
groupName.substring(1);
return outputFormat(displayGroupName, listUsers(ess, users, ", "));
}
// Build the output string
public static String outputFormat(final String group, final String message) {
return tl("listGroupTag", FormatUtil.replaceFormat(group)) +
message;
}
}
@@ -1,191 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.LocationUtil;
import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.InvalidWorldException;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Biome;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
public class RandomTeleport implements IConf {
private final IEssentials essentials;
private final EssentialsConf config;
private final ConcurrentLinkedQueue<Location> cachedLocations = new ConcurrentLinkedQueue<>();
private static final Random RANDOM = new Random();
private static final int HIGHEST_BLOCK_Y_OFFSET = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_15_R01) ? 1 : 0;
public RandomTeleport(final IEssentials essentials) {
this.essentials = essentials;
File file = new File(essentials.getDataFolder(), "tpr.yml");
config = new EssentialsConf(file);
config.setTemplateName("/tpr.yml");
config.options().copyHeader(true);
reloadConfig();
}
@Override
public void reloadConfig() {
config.load();
cachedLocations.clear();
}
public Location getCenter() {
try {
Location center = config.getLocation("center", essentials.getServer());
if (center != null) {
return center;
}
} catch (InvalidWorldException ignored) {
}
Location center = essentials.getServer().getWorlds().get(0).getWorldBorder().getCenter();
center.setY(center.getWorld().getHighestBlockYAt(center) + 1);
setCenter(center);
return center;
}
public void setCenter(Location center) {
config.setProperty("center", center);
config.save();
}
public double getMinRange() {
return config.getDouble("min-range", 0d);
}
public void setMinRange(double minRange) {
config.setProperty("min-range", minRange);
config.save();
}
public double getMaxRange() {
return config.getDouble("max-range", getCenter().getWorld().getWorldBorder().getSize() / 2);
}
public void setMaxRange(double maxRange) {
config.setProperty("max-range", maxRange);
config.save();
}
public Set<Biome> getExcludedBiomes() {
List<String> biomeNames = config.getStringList("excluded-biomes");
Set<Biome> excludedBiomes = new HashSet<>();
for (String biomeName : biomeNames) {
try {
excludedBiomes.add(Biome.valueOf(biomeName.toUpperCase()));
} catch (IllegalArgumentException ignored) {
}
}
return excludedBiomes;
}
public int getFindAttempts() {
return config.getInt("find-attempts", 10);
}
public int getCacheThreshold() {
return config.getInt("cache-threshold", 10);
}
public boolean getPreCache() {
return config.getBoolean("pre-cache", true);
}
public Queue<Location> getCachedLocations() {
return cachedLocations;
}
// Get a random location; cached if possible. Otherwise on demand.
public CompletableFuture<Location> getRandomLocation(Location center, double minRange, double maxRange) {
int findAttempts = this.getFindAttempts();
Queue<Location> cachedLocations = this.getCachedLocations();
// Try to build up the cache if it is below the threshold
if (cachedLocations.size() < this.getCacheThreshold()) {
cacheRandomLocations(center, minRange, maxRange);
}
CompletableFuture<Location> future = new CompletableFuture<>();
// Return a random location immediately if one is available, otherwise try to find one now
if (cachedLocations.isEmpty()) {
attemptRandomLocation(findAttempts, center, minRange, maxRange).thenAccept(future::complete);
} else {
future.complete(cachedLocations.poll());
}
return future;
}
// Prompts caching random valid locations, up to a maximum number of attempts
public void cacheRandomLocations(Location center, double minRange, double maxRange) {
essentials.getServer().getScheduler().scheduleSyncDelayedTask(essentials, () -> {
for (int i = 0; i < this.getFindAttempts(); ++i) {
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
if (isValidRandomLocation(location)) {
this.getCachedLocations().add(location);
}
});
}
});
}
// Recursively attempt to find a random location. After a maximum number of attempts, the center is returned.
private CompletableFuture<Location> attemptRandomLocation(int attempts, Location center, double minRange, double maxRange) {
CompletableFuture<Location> future = new CompletableFuture<>();
if (attempts > 0) {
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
if (isValidRandomLocation(location)) {
future.complete(location);
} else {
attemptRandomLocation(attempts - 1, center, minRange, maxRange).thenAccept(future::complete);
}
});
} else {
future.complete(center);
}
return future;
}
// Calculates a random location asynchronously.
private CompletableFuture<Location> calculateRandomLocation(Location center, double minRange, double maxRange) {
CompletableFuture<Location> future = new CompletableFuture<>();
final int dx = RANDOM.nextBoolean() ? 1 : -1, dz = RANDOM.nextBoolean() ? 1 : -1;
Location location = new Location(
center.getWorld(),
center.getX() + dx * (minRange + RANDOM.nextDouble() * (maxRange - minRange)),
center.getWorld().getMaxHeight(),
center.getZ() + dz * (minRange + RANDOM.nextDouble() * (maxRange - minRange)),
360 * RANDOM.nextFloat() - 180,
0
);
PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
if (World.Environment.NETHER.equals(center.getWorld().getEnvironment())) {
location.setY(getNetherYAt(location));
} else {
location.setY(center.getWorld().getHighestBlockYAt(location) + HIGHEST_BLOCK_Y_OFFSET);
}
future.complete(location);
});
return future;
}
// Returns an appropriate elevation for a given location in the nether, or -1 if none is found
private double getNetherYAt(Location location) {
for (int y = 32; y < location.getWorld().getMaxHeight() / 2; ++y) {
if (!LocationUtil.isBlockUnsafe(location.getWorld(), location.getBlockX(), y, location.getBlockZ())) {
return y;
}
}
return -1;
}
private boolean isValidRandomLocation(Location location) {
return location.getBlockY() > 0 && !this.getExcludedBiomes().contains(location.getBlock().getBiome());
}
}
@@ -1,52 +0,0 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;
public class TNTExplodeListener implements Listener, Runnable {
private final transient IEssentials ess;
private transient boolean enabled = false;
private transient int timer = -1;
public TNTExplodeListener(final IEssentials ess) {
super();
this.ess = ess;
}
public void enable() {
if (!enabled) {
enabled = true;
timer = ess.scheduleSyncDelayedTask(this, 200);
return;
}
if (timer != -1) {
ess.getScheduler().cancelTask(timer);
timer = ess.scheduleSyncDelayedTask(this, 200);
}
}
@EventHandler(priority = EventPriority.LOW)
public void onEntityExplode(final EntityExplodeEvent event) {
if (!enabled) {
return;
}
if (event.getEntity() instanceof LivingEntity) {
return;
}
if (event.blockList().size() < 1) {
return;
}
event.setCancelled(true);
event.getLocation().getWorld().createExplosion(event.getLocation(), 0F);
}
@Override
public void run() {
enabled = false;
}
}
@@ -1,404 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.LocationUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.IEssentials;
import net.ess3.api.ITeleport;
import net.ess3.api.IUser;
import net.ess3.api.events.UserWarpEvent;
import net.ess3.api.events.teleport.PreTeleportEvent;
import net.ess3.api.events.teleport.TeleportWarmupEvent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.GregorianCalendar;
import static com.earth2me.essentials.I18n.tl;
/**
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.AsyncTeleport AsyncTeleport}
*/
@Deprecated
public class Teleport implements ITeleport {
private final IUser teleportOwner;
private final IEssentials ess;
private TimedTeleport timedTeleport;
private TeleportType tpType;
@Deprecated
public Teleport(IUser user, IEssentials ess) {
this.teleportOwner = user;
this.ess = ess;
tpType = TeleportType.NORMAL;
}
public enum TeleportType {
TPA,
BACK,
NORMAL
}
@Deprecated
public void cooldown(boolean check) throws Exception {
final Calendar time = new GregorianCalendar();
if (teleportOwner.getLastTeleportTimestamp() > 0) {
// Take the current time, and remove the delay from it.
final double cooldown = ess.getSettings().getTeleportCooldown();
final Calendar earliestTime = new GregorianCalendar();
earliestTime.add(Calendar.SECOND, -(int) cooldown);
earliestTime.add(Calendar.MILLISECOND, -(int) ((cooldown * 1000.0) % 1000.0));
// This value contains the most recent time a teleportPlayer could have been used that would allow another use.
final long earliestLong = earliestTime.getTimeInMillis();
// When was the last teleportPlayer used?
final long lastTime = teleportOwner.getLastTeleportTimestamp();
if (lastTime > time.getTimeInMillis()) {
// This is to make sure time didn't get messed up on last teleportPlayer use.
// If this happens, let's give the user the benifit of the doubt.
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
return;
} else if (lastTime > earliestLong
&& cooldownApplies()) {
time.setTimeInMillis(lastTime);
time.add(Calendar.SECOND, (int) cooldown);
time.add(Calendar.MILLISECOND, (int) ((cooldown * 1000.0) % 1000.0));
throw new Exception(tl("timeBeforeTeleport", DateUtil.formatDateDiff(time.getTimeInMillis())));
}
}
// if justCheck is set, don't update lastTeleport; we're just checking
if (!check) {
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
}
}
@Deprecated
private boolean cooldownApplies() {
boolean applies = true;
String globalBypassPerm = "essentials.teleport.cooldown.bypass";
switch (tpType) {
case NORMAL:
applies = !teleportOwner.isAuthorized(globalBypassPerm);
break;
case BACK:
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.back"));
break;
case TPA:
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.tpa"));
break;
}
return applies;
}
@Deprecated
private void warnUser(final IUser user, final double delay) {
Calendar c = new GregorianCalendar();
c.add(Calendar.SECOND, (int) delay);
c.add(Calendar.MILLISECOND, (int) ((delay * 1000.0) % 1000.0));
user.sendMessage(tl("dontMoveMessage", DateUtil.formatDateDiff(c.getTimeInMillis())));
}
//The now function is used when you want to skip tp delay when teleporting someone to a location or player.
@Override
@Deprecated
public void now(Location loc, boolean cooldown, TeleportCause cause) throws Exception {
if (cooldown) {
cooldown(false);
}
final ITarget target = new LocationTarget(loc);
now(teleportOwner, target, cause);
}
@Override
@Deprecated
public void now(Player entity, boolean cooldown, TeleportCause cause) throws Exception {
if (cooldown) {
cooldown(false);
}
final ITarget target = new PlayerTarget(entity);
now(teleportOwner, target, cause);
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
@Deprecated
protected void now(IUser teleportee, ITarget target, TeleportCause cause) throws Exception {
cancel(false);
Location loc = target.getLocation();
PreTeleportEvent event = new PreTeleportEvent(teleportee, cause, target);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
if (teleportee.isAuthorized("essentials.back.onteleport")) {
teleportee.setLastLocation();
}
if (!teleportee.getBase().isEmpty()) {
if (!ess.getSettings().isTeleportPassengerDismount()) {
throw new Exception(tl("passengerTeleportFail"));
}
teleportee.getBase().eject();
}
if (LocationUtil.isBlockUnsafeForUser(teleportee, loc.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (ess.getSettings().isTeleportSafetyEnabled()) {
if (ess.getSettings().isForceDisableTeleportSafety()) {
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
} else {
PaperLib.teleportAsync(teleportee.getBase(), LocationUtil.getSafeDestination(ess, teleportee, loc), cause);
}
} else {
throw new Exception(tl("unsafeTeleportDestination", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
} else {
if (ess.getSettings().isForceDisableTeleportSafety()) {
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
} else {
if (ess.getSettings().isTeleportToCenterLocation()) {
loc = LocationUtil.getRoundedDestination(loc);
}
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
}
}
}
//The teleportPlayer function is used when you want to normally teleportPlayer someone to a location or player.
//This method is nolonger used internally and will be removed.
@Deprecated
@Override
public void teleport(Location loc, Trade chargeFor) throws Exception {
teleport(loc, chargeFor, TeleportCause.PLUGIN);
}
@Override
@Deprecated
public void teleport(Location loc, Trade chargeFor, TeleportCause cause) throws Exception {
teleport(teleportOwner, new LocationTarget(loc), chargeFor, cause);
}
//This is used when teleporting to a player
@Override
@Deprecated
public void teleport(Player entity, Trade chargeFor, TeleportCause cause) throws Exception {
ITarget target = new PlayerTarget(entity);
teleportOwner.sendMessage(tl("teleportToPlayer", entity.getDisplayName()));
teleport(teleportOwner, target, chargeFor, cause);
}
//This is used when teleporting to stored location
@Override
@Deprecated
public void teleportPlayer(IUser teleportee, Location loc, Trade chargeFor, TeleportCause cause) throws Exception {
teleport(teleportee, new LocationTarget(loc), chargeFor, cause);
}
//This is used on /tphere
@Override
@Deprecated
public void teleportPlayer(IUser teleportee, Player entity, Trade chargeFor, TeleportCause cause) throws Exception {
ITarget target = new PlayerTarget(entity);
teleport(teleportee, target, chargeFor, cause);
teleportee.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
}
@Deprecated
private void teleport(IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
delay = event.getDelay();
Trade cashCharge = chargeFor;
if (chargeFor != null) {
chargeFor.isAffordableFor(teleportOwner);
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
if (!chargeFor.getCommandCost(teleportOwner).equals(BigDecimal.ZERO)) {
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
cashCharge = new Trade(chargeFor.getCommandCost(teleportOwner), ess);
}
}
cooldown(true);
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass") || teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
cooldown(false);
now(teleportee, target, cause);
if (cashCharge != null) {
cashCharge.charge(teleportOwner);
}
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
}
@Deprecated
private void teleportOther(IUser teleporter, IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleporter, teleportee, cause, target, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
delay = event.getDelay();
Trade cashCharge = chargeFor;
if (teleporter != null && chargeFor != null) {
chargeFor.isAffordableFor(teleporter);
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
if (!chargeFor.getCommandCost(teleporter).equals(BigDecimal.ZERO)) {
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
cashCharge = new Trade(chargeFor.getCommandCost(teleporter), ess);
}
}
cooldown(true);
if (delay <= 0 || teleporter == null
|| teleporter.isAuthorized("essentials.teleport.timer.bypass")
|| teleportOwner.isAuthorized("essentials.teleport.timer.bypass")
|| teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
cooldown(false);
now(teleportee, target, cause);
if (teleporter != null && cashCharge != null) {
cashCharge.charge(teleporter);
}
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
}
//The respawn function is a wrapper used to handle tp fallback, on /jail and /home
@Override
@Deprecated
public void respawn(final Trade chargeFor, TeleportCause cause) throws Exception {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleportOwner, cause, null, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
delay = event.getDelay();
if (chargeFor != null) {
chargeFor.isAffordableFor(teleportOwner);
}
cooldown(true);
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass")) {
cooldown(false);
respawnNow(teleportOwner, cause);
if (chargeFor != null) {
chargeFor.charge(teleportOwner);
}
return;
}
cancel(false);
warnUser(teleportOwner, delay);
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true);
}
@Deprecated
void respawnNow(IUser teleportee, TeleportCause cause) throws Exception {
final Player player = teleportee.getBase();
Location bed = player.getBedSpawnLocation();
if (bed != null) {
now(teleportee, new LocationTarget(bed), cause);
} else {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Could not find bed spawn, forcing respawn event.");
}
final PlayerRespawnEvent pre = new PlayerRespawnEvent(player, player.getWorld().getSpawnLocation(), false);
ess.getServer().getPluginManager().callEvent(pre);
now(teleportee, new LocationTarget(pre.getRespawnLocation()), cause);
}
}
//The warp function is a wrapper used to teleportPlayer a player to a /warp
@Override
@Deprecated
public void warp(IUser teleportee, String warp, Trade chargeFor, TeleportCause cause) throws Exception {
UserWarpEvent event = new UserWarpEvent(teleportee, warp, chargeFor);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
warp = event.getWarp();
Location loc = ess.getWarps().getWarp(warp);
teleportee.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
if (!teleportee.equals(teleportOwner)) {
teleportOwner.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
teleport(teleportee, new LocationTarget(loc), chargeFor, cause);
}
//The back function is a wrapper used to teleportPlayer a player /back to their previous location.
@Override
@Deprecated
public void back(Trade chargeFor) throws Exception {
back(teleportOwner, chargeFor);
}
//This function is a wrapper over the other back function for cases where another player performs back for them
@Override
@Deprecated
public void back(IUser teleporter, Trade chargeFor) throws Exception {
tpType = TeleportType.BACK;
final Location loc = teleportOwner.getLastLocation();
teleportOwner.sendMessage(tl("backUsageMsg", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
teleportOther(teleporter, teleportOwner, new LocationTarget(loc), chargeFor, TeleportCause.COMMAND);
}
//This function is used to throw a user back after a jail sentence
@Override
@Deprecated
public void back() throws Exception {
now(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND);
}
@Deprecated
public void setTpType(TeleportType tpType) {
this.tpType = tpType;
}
//If we need to cancelTimer a pending teleportPlayer call this method
@Deprecated
private void cancel(boolean notifyUser) {
if (timedTeleport != null) {
timedTeleport.cancelTimer(notifyUser);
timedTeleport = null;
}
}
@Deprecated
private void initTimer(long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
timedTeleport = new TimedTeleport(teleportOwner, ess, this, delay, teleportUser, target, chargeFor, cause, respawn);
}
}
@@ -1,139 +0,0 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import org.bukkit.Location;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
@Deprecated
public class TimedTeleport implements Runnable {
private static final double MOVE_CONSTANT = 0.3;
private final IUser teleportOwner;
private final IEssentials ess;
private final Teleport teleport;
private final UUID timer_teleportee;
private int timer_task;
private final long timer_started; // time this task was initiated
private final long timer_delay; // how long to delay the teleportPlayer
private double timer_health;
// note that I initially stored a clone of the location for reference, but...
// when comparing locations, I got incorrect mismatches (rounding errors, looked like)
// so, the X/Y/Z values are stored instead and rounded off
private final long timer_initX;
private final long timer_initY;
private final long timer_initZ;
private final ITarget timer_teleportTarget;
private final boolean timer_respawn;
private final boolean timer_canMove;
private final Trade timer_chargeFor;
private final TeleportCause timer_cause;
TimedTeleport(IUser user, IEssentials ess, Teleport teleport, long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
this.teleportOwner = user;
this.ess = ess;
this.teleport = teleport;
this.timer_started = System.currentTimeMillis();
this.timer_delay = delay;
this.timer_health = teleportUser.getBase().getHealth();
this.timer_initX = Math.round(teleportUser.getBase().getLocation().getX() * MOVE_CONSTANT);
this.timer_initY = Math.round(teleportUser.getBase().getLocation().getY() * MOVE_CONSTANT);
this.timer_initZ = Math.round(teleportUser.getBase().getLocation().getZ() * MOVE_CONSTANT);
this.timer_teleportee = teleportUser.getBase().getUniqueId();
this.timer_teleportTarget = target;
this.timer_chargeFor = chargeFor;
this.timer_cause = cause;
this.timer_respawn = respawn;
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
}
@Override
public void run() {
if (teleportOwner == null || !teleportOwner.getBase().isOnline() || teleportOwner.getBase().getLocation() == null) {
cancelTimer(false);
return;
}
final IUser teleportUser = ess.getUser(this.timer_teleportee);
if (teleportUser == null || !teleportUser.getBase().isOnline()) {
cancelTimer(false);
return;
}
final Location currLocation = teleportUser.getBase().getLocation();
if (currLocation == null) {
cancelTimer(false);
return;
}
if (!timer_canMove && (Math.round(currLocation.getX() * MOVE_CONSTANT) != timer_initX || Math.round(currLocation.getY() * MOVE_CONSTANT) != timer_initY || Math.round(currLocation.getZ() * MOVE_CONSTANT) != timer_initZ || teleportUser.getBase().getHealth() < timer_health)) {
// user moved, cancelTimer teleportPlayer
cancelTimer(true);
return;
}
class DelayedTeleportTask implements Runnable {
@Override
public void run() {
timer_health = teleportUser.getBase().getHealth(); // in case user healed, then later gets injured
final long now = System.currentTimeMillis();
if (now > timer_started + timer_delay) {
try {
teleport.cooldown(false);
} catch (Exception ex) {
teleportOwner.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
if (teleportOwner != teleportUser) {
teleportUser.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
}
}
try {
cancelTimer(false);
teleportUser.sendMessage(tl("teleportationCommencing"));
if (timer_chargeFor != null) {
timer_chargeFor.isAffordableFor(teleportOwner);
}
if (timer_respawn) {
teleport.respawnNow(teleportUser, timer_cause);
} else {
teleport.now(teleportUser, timer_teleportTarget, timer_cause);
}
if (timer_chargeFor != null) {
timer_chargeFor.charge(teleportOwner);
}
} catch (Exception ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
}
}
}
}
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
}
//If we need to cancelTimer a pending teleportPlayer call this method
void cancelTimer(boolean notifyUser) {
if (timer_task == -1) {
return;
}
try {
ess.getServer().getScheduler().cancelTask(timer_task);
if (notifyUser) {
teleportOwner.sendMessage(tl("pendingTeleportCancelled"));
if (timer_teleportee != null && !timer_teleportee.equals(teleportOwner.getBase().getUniqueId())) {
ess.getUser(timer_teleportee).sendMessage(tl("pendingTeleportCancelled"));
}
}
} finally {
timer_task = -1;
}
}
}
@@ -1,151 +0,0 @@
package com.earth2me.essentials;
import com.google.common.io.Files;
import org.bukkit.Bukkit;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.regex.Pattern;
public class UUIDMap {
private final transient net.ess3.api.IEssentials ess;
private final File userList;
private final transient Pattern splitPattern = Pattern.compile(",");
private static boolean pendingWrite;
private static final ScheduledExecutorService writeScheduler = Executors.newScheduledThreadPool(1);
private final Runnable writeTaskRunnable;
private static boolean loading = false;
public UUIDMap(final net.ess3.api.IEssentials ess) {
this.ess = ess;
userList = new File(ess.getDataFolder(), "usermap.csv");
pendingWrite = false;
writeTaskRunnable = () -> {
if (pendingWrite) {
try {
new WriteRunner(ess.getDataFolder(), userList, ess.getUserMap().getNames()).run();
} catch (Throwable t) { // bad code to prevent task from being suppressed
t.printStackTrace();
}
}
};
writeScheduler.scheduleWithFixedDelay(writeTaskRunnable, 5, 5, TimeUnit.SECONDS);
}
public void loadAllUsers(final ConcurrentSkipListMap<String, UUID> names, final ConcurrentSkipListMap<UUID, ArrayList<String>> history) {
try {
if (!userList.exists()) {
userList.createNewFile();
}
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Reading usermap from disk");
}
if (loading) {
return;
}
names.clear();
history.clear();
loading = true;
try (BufferedReader reader = new BufferedReader(new FileReader(userList))) {
while (true) {
final String line = reader.readLine();
if (line == null) {
break;
} else {
final String[] values = splitPattern.split(line);
if (values.length == 2) {
final String name = values[0];
final UUID uuid = UUID.fromString(values[1]);
names.put(name, uuid);
if (!history.containsKey(uuid)) {
final ArrayList<String> list = new ArrayList<>();
list.add(name);
history.put(uuid, list);
} else {
final ArrayList<String> list = history.get(uuid);
if (!list.contains(name)) {
list.add(name);
}
}
}
}
}
}
loading = false;
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
public void writeUUIDMap() {
pendingWrite = true;
}
public void forceWriteUUIDMap() {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Forcing usermap write to disk");
}
pendingWrite = true;
writeTaskRunnable.run();
}
public void shutdown() {
writeScheduler.submit(writeTaskRunnable);
writeScheduler.shutdown();
}
private static class WriteRunner implements Runnable {
private final File location;
private final File endFile;
private final Map<String, UUID> names;
private WriteRunner(final File location, final File endFile, final Map<String, UUID> names) {
this.location = location;
this.endFile = endFile;
this.names = new HashMap<>(names);
}
@Override
public void run() {
pendingWrite = false;
if (loading || names.isEmpty()) {
return;
}
File configFile = null;
try {
configFile = File.createTempFile("usermap", ".tmp.csv", location);
final BufferedWriter bWriter = new BufferedWriter(new FileWriter(configFile));
for (Map.Entry<String, UUID> entry : names.entrySet()) {
bWriter.write(entry.getKey() + "," + entry.getValue().toString());
bWriter.newLine();
}
bWriter.close();
Files.move(configFile, endFile);
} catch (IOException ex) {
try {
if (configFile != null && configFile.exists()) {
Files.move(configFile, new File(endFile.getParentFile(), "usermap.bak.csv"));
}
} catch (Exception ex2) {
Bukkit.getLogger().log(Level.SEVERE, ex2.getMessage(), ex2);
}
Bukkit.getLogger().log(Level.WARNING, ex.getMessage(), ex);
}
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,313 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.base.Charsets;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.UncheckedExecutionException;
import net.ess3.api.IEssentials;
import org.bukkit.entity.Player;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
public class UserMap extends CacheLoader<String, User> implements IConf {
private final transient IEssentials ess;
private final transient ConcurrentSkipListSet<UUID> keys = new ConcurrentSkipListSet<>();
private final transient ConcurrentSkipListMap<String, UUID> names = new ConcurrentSkipListMap<>();
private final transient ConcurrentSkipListMap<UUID, ArrayList<String>> history = new ConcurrentSkipListMap<>();
private final UUIDMap uuidMap;
private final transient Cache<String, User> users;
private static boolean legacy = false;
public UserMap(final IEssentials ess) {
super();
this.ess = ess;
uuidMap = new UUIDMap(ess);
//RemovalListener<UUID, User> remListener = new UserMapRemovalListener();
//users = CacheBuilder.newBuilder().maximumSize(ess.getSettings().getMaxUserCacheCount()).softValues().removalListener(remListener).build(this);
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
int maxCount = ess.getSettings().getMaxUserCacheCount();
try {
cacheBuilder.maximumSize(maxCount);
} catch (NoSuchMethodError nsme) {
legacy = true;
legacyMaximumSize(cacheBuilder, maxCount);
}
cacheBuilder.softValues();
if (!legacy) {
users = cacheBuilder.build(this);
} else {
users = legacyBuild(cacheBuilder);
}
}
private void loadAllUsersAsync(final IEssentials ess) {
ess.runTaskAsynchronously(() -> {
synchronized (users) {
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
return;
}
keys.clear();
users.invalidateAll();
for (String string : userdir.list()) {
if (!string.endsWith(".yml")) {
continue;
}
final String name = string.substring(0, string.length() - 4);
try {
keys.add(UUID.fromString(name));
} catch (IllegalArgumentException ex) {
//Ignore these users till they rejoin.
}
}
uuidMap.loadAllUsers(names, history);
}
});
}
public boolean userExists(final UUID uuid) {
return keys.contains(uuid);
}
public User getUser(final String name) {
try {
final String sanitizedName = StringUtil.safeString(name);
if (names.containsKey(sanitizedName)) {
final UUID uuid = names.get(sanitizedName);
return getUser(uuid);
}
final File userFile = getUserFileFromString(sanitizedName);
if (userFile.exists()) {
ess.getLogger().info("Importing user " + name + " to usermap.");
User user = new User(new OfflinePlayer(sanitizedName, ess.getServer()), ess);
trackUUID(user.getBase().getUniqueId(), user.getName(), true);
return user;
}
return null;
} catch (UncheckedExecutionException ex) {
return null;
}
}
public User getUser(final UUID uuid) {
try {
if (!legacy) {
return ((LoadingCache<String, User>) users).get(uuid.toString());
} else {
return legacyCacheGet(uuid);
}
} catch (ExecutionException | UncheckedExecutionException ex) {
return null;
}
}
public void trackUUID(final UUID uuid, final String name, boolean replace) {
if (uuid != null) {
keys.add(uuid);
if (name != null && name.length() > 0) {
final String keyName = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name;
if (!names.containsKey(keyName)) {
names.put(keyName, uuid);
uuidMap.writeUUIDMap();
} else if (!names.get(keyName).equals(uuid)) {
if (replace) {
ess.getLogger().info("Found new UUID for " + name + ". Replacing " + names.get(keyName).toString() + " with " + uuid.toString());
names.put(keyName, uuid);
uuidMap.writeUUIDMap();
} else {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Found old UUID for " + name + " (" + uuid.toString() + "). Not adding to usermap.");
}
}
}
}
}
}
@Override
public User load(final String stringUUID) throws Exception {
UUID uuid = UUID.fromString(stringUUID);
Player player = ess.getServer().getPlayer(uuid);
if (player != null) {
final User user = new User(player, ess);
trackUUID(uuid, user.getName(), true);
return user;
}
final File userFile = getUserFileFromID(uuid);
if (userFile.exists()) {
player = new OfflinePlayer(uuid, ess.getServer());
final User user = new User(player, ess);
((OfflinePlayer) player).setName(user.getLastAccountName());
trackUUID(uuid, user.getName(), false);
return user;
}
throw new Exception("User not found!");
}
@Override
public void reloadConfig() {
getUUIDMap().forceWriteUUIDMap();
loadAllUsersAsync(ess);
}
public void invalidateAll() {
users.invalidateAll();
}
public void removeUser(final String name) {
if (names == null) {
ess.getLogger().warning("Name collection is null, cannot remove user.");
return;
}
UUID uuid = names.get(name);
if (uuid != null) {
keys.remove(uuid);
users.invalidate(uuid.toString());
}
names.remove(name);
names.remove(StringUtil.safeString(name));
}
public Set<UUID> getAllUniqueUsers() {
return Collections.unmodifiableSet(keys);
}
public int getUniqueUsers() {
return keys.size();
}
protected ConcurrentSkipListMap<String, UUID> getNames() {
return names;
}
protected ConcurrentSkipListMap<UUID, ArrayList<String>> getHistory() {
return history;
}
public List<String> getUserHistory(final UUID uuid) {
return history.get(uuid);
}
public UUIDMap getUUIDMap() {
return uuidMap;
}
private File getUserFileFromID(final UUID uuid) {
final File userFolder = new File(ess.getDataFolder(), "userdata");
return new File(userFolder, uuid.toString() + ".yml");
}
public File getUserFileFromString(final String name) {
final File userFolder = new File(ess.getDataFolder(), "userdata");
return new File(userFolder, StringUtil.sanitizeFileName(name) + ".yml");
}
// class UserMapRemovalListener implements RemovalListener
// {
// @Override
// public void onRemoval(final RemovalNotification notification)
// {
// Object value = notification.getValue();
// if (value != null)
// {
// ((User)value).cleanup();
// }
// }
// }
private final Pattern validUserPattern = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
@SuppressWarnings("deprecation")
public User getUserFromBukkit(String name) {
name = StringUtil.safeString(name);
if (ess.getSettings().isDebug()) {
ess.getLogger().warning("Using potentially blocking Bukkit UUID lookup for: " + name);
}
// Don't attempt to look up entirely invalid usernames
if (name == null || !validUserPattern.matcher(name).matches()) {
return null;
}
org.bukkit.OfflinePlayer offlinePlayer = ess.getServer().getOfflinePlayer(name);
if (offlinePlayer == null) {
return null;
}
UUID uuid;
try {
uuid = offlinePlayer.getUniqueId();
} catch (UnsupportedOperationException | NullPointerException e) {
return null;
}
// This is how Bukkit generates fake UUIDs
if (UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)).equals(uuid)) {
return null;
} else {
names.put(name, uuid);
return getUser(uuid);
}
}
private static Method getLegacy;
private User legacyCacheGet(UUID uuid) {
if (getLegacy == null) {
Class<?> usersClass = users.getClass();
for (Method m : usersClass.getDeclaredMethods()) {
if (m.getName().equals("get")) {
getLegacy = m;
getLegacy.setAccessible(true);
break;
}
}
}
try {
return (User) getLegacy.invoke(users, uuid.toString());
} catch (IllegalAccessException | InvocationTargetException e) {
return null;
}
}
private void legacyMaximumSize(CacheBuilder builder, int maxCount) {
try {
Method maxSizeLegacy = builder.getClass().getDeclaredMethod("maximumSize", Integer.TYPE);
maxSizeLegacy.setAccessible(true);
maxSizeLegacy.invoke(builder, maxCount);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
private Cache<String, User> legacyBuild(CacheBuilder builder) {
Method build = null;
for (Method method : builder.getClass().getDeclaredMethods()) {
if (method.getName().equals("build")) {
build = method;
break;
}
}
Cache<String, User> legacyUsers;
try {
assert build != null;
build.setAccessible(true);
legacyUsers = (Cache<String, User>) build.invoke(builder, this);
} catch (IllegalAccessException | InvocationTargetException e) {
legacyUsers = null;
}
return legacyUsers;
}
}
@@ -1,167 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.commands.WarpNotFoundException;
import com.earth2me.essentials.utils.StringUtil;
import net.ess3.api.InvalidNameException;
import net.ess3.api.InvalidWorldException;
import org.bukkit.Location;
import org.bukkit.Server;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class Warps implements IConf, net.ess3.api.IWarps {
private static final Logger logger = Logger.getLogger("Essentials");
private final Map<StringIgnoreCase, EssentialsConf> warpPoints = new HashMap<>();
private final File warpsFolder;
private final Server server;
public Warps(Server server, File dataFolder) {
this.server = server;
warpsFolder = new File(dataFolder, "warps");
if (!warpsFolder.exists()) {
warpsFolder.mkdirs();
}
reloadConfig();
}
@Override
public boolean isEmpty() {
return warpPoints.isEmpty();
}
@Override
public Collection<String> getList() {
final List<String> keys = new ArrayList<>();
for (StringIgnoreCase stringIgnoreCase : warpPoints.keySet()) {
keys.add(stringIgnoreCase.getString());
}
keys.sort(String.CASE_INSENSITIVE_ORDER);
return keys;
}
@Override
public Location getWarp(String warp) throws WarpNotFoundException, InvalidWorldException {
EssentialsConf conf = warpPoints.get(new StringIgnoreCase(warp));
if (conf == null) {
throw new WarpNotFoundException();
}
return conf.getLocation(null, server);
}
@Override
public void setWarp(String name, Location loc) throws Exception {
setWarp(null, name, loc);
}
@Override
public void setWarp(IUser user, String name, Location loc) throws Exception {
String filename = StringUtil.sanitizeFileName(name);
EssentialsConf conf = warpPoints.get(new StringIgnoreCase(name));
if (conf == null) {
File confFile = new File(warpsFolder, filename + ".yml");
if (confFile.exists()) {
throw new Exception(tl("similarWarpExist"));
}
conf = new EssentialsConf(confFile);
warpPoints.put(new StringIgnoreCase(name), conf);
}
conf.setProperty(null, loc);
conf.setProperty("name", name);
if (user != null) conf.setProperty("lastowner", user.getBase().getUniqueId().toString());
try {
conf.saveWithError();
} catch (IOException ex) {
throw new IOException(tl("invalidWarpName"));
}
}
@Override
public UUID getLastOwner(String warp) throws WarpNotFoundException {
EssentialsConf conf = warpPoints.get(new StringIgnoreCase(warp));
if (conf == null) {
throw new WarpNotFoundException();
}
UUID uuid = null;
try {
uuid = UUID.fromString(conf.getString("lastowner"));
}
catch (Exception ignored) {}
return uuid;
}
@Override
public void removeWarp(String name) throws Exception {
EssentialsConf conf = warpPoints.get(new StringIgnoreCase(name));
if (conf == null) {
throw new Exception(tl("warpNotExist"));
}
if (!conf.getFile().delete()) {
throw new Exception(tl("warpDeleteError"));
}
warpPoints.remove(new StringIgnoreCase(name));
}
@Override
public final void reloadConfig() {
warpPoints.clear();
File[] listOfFiles = warpsFolder.listFiles();
if (listOfFiles.length >= 1) {
for (File listOfFile : listOfFiles) {
String filename = listOfFile.getName();
if (listOfFile.isFile() && filename.endsWith(".yml")) {
try {
EssentialsConf conf = new EssentialsConf(listOfFile);
conf.load();
String name = conf.getString("name");
if (name != null) {
warpPoints.put(new StringIgnoreCase(name), conf);
}
} catch (Exception ex) {
logger.log(Level.WARNING, tl("loadWarpError", filename), ex);
}
}
}
}
}
//This is here for future 3.x api support. Not implemented here becasue storage is handled differently
@Override
public File getWarpFile(String name) throws InvalidNameException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getCount() {
return getList().size();
}
private static class StringIgnoreCase {
private final String string;
public StringIgnoreCase(String string) {
this.string = string;
}
@Override
public int hashCode() {
return getString().toLowerCase(Locale.ENGLISH).hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof StringIgnoreCase) {
return getString().equalsIgnoreCase(((StringIgnoreCase) o).getString());
}
return false;
}
public String getString() {
return string;
}
}
}
@@ -1,123 +0,0 @@
package com.earth2me.essentials.api;
import com.earth2me.essentials.Trade;
import net.ess3.api.IUser;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.concurrent.CompletableFuture;
public interface IAsyncTeleport {
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param loc - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Location loc, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param entity - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Player entity, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause.
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param otherUser - Which user will be teleported
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param otherUser - Which user will be teleported
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle tp fallback on /jail and /home
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void respawn(final Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /warp teleports
*
* @param otherUser - Which user will be teleported
* @param warp - The name of the warp the user will be teleported too.
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void warp(IUser otherUser, String warp, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports that
* are executed by a different player with this
* instance of teleport as a target.
*
* @param teleporter - The user performing the /back command.
* This value may be {@code null} to indicate console.
* @param chargeFor - What the {@code teleporter} will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(IUser teleporter, Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle throwing user home after a jail sentence
*
* @param future - Future which is completed with the success status of the execution
*/
void back(CompletableFuture<Boolean> future);
}
@@ -1,13 +0,0 @@
package com.earth2me.essentials.api;
import java.util.Locale;
public interface II18n {
/**
* Gets the current locale setting
*
* @return the current locale, if not set it will return the default locale
*/
Locale getCurrentLocale();
}
@@ -1,6 +0,0 @@
package com.earth2me.essentials.api;
public interface IReload {
void onReload();
}
@@ -1,17 +0,0 @@
package com.earth2me.essentials.api;
import static com.earth2me.essentials.I18n.tl;
public class InvalidWorldException extends Exception {
private final String world;
public InvalidWorldException(final String world) {
super(tl("invalidWorld"));
this.world = world;
}
public String getWorld() {
return this.world;
}
}
@@ -1,16 +0,0 @@
package com.earth2me.essentials.api;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
public class UserDoesNotExistException extends Exception {
public UserDoesNotExistException(String name) {
super(tl("userDoesNotExist", name));
}
public UserDoesNotExistException(UUID uuid) {
super(tl("uuidDoesNotExist", uuid.toString()));
}
}
@@ -1,111 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DateUtil;
import net.ess3.api.events.AfkStatusChangeEvent;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandafk extends EssentialsCommand {
public Commandafk() {
super("afk");
}
@Override
public void run(Server server, User user, String commandLabel, String[] args) throws Exception {
if (args.length > 0 && user.isAuthorized("essentials.afk.others")) {
User afkUser = user; // if no player found, but message specified, set command executor to target user
String message;
try {
afkUser = getPlayer(server, user, args, 0);
message = args.length > 1 ? getFinalArg(args, 1) : null;
} catch (PlayerNotFoundException e) {
message = getFinalArg(args, 0);
}
toggleAfk(user, afkUser, message);
} else {
String message = args.length > 0 ? getFinalArg(args, 0) : null;
toggleAfk(user, user, message);
}
}
@Override
public void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception {
if (args.length > 0) {
User afkUser = getPlayer(server, args, 0, true, false);
String message = args.length > 1 ? getFinalArg(args, 1) : null;
toggleAfk(null, afkUser, message);
} else {
throw new NotEnoughArgumentsException();
}
}
private void toggleAfk(User sender, User user, String message) throws Exception {
if (message != null && sender != null) {
if (sender.isMuted()) {
String dateDiff = sender.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(sender.getMuteTimeout()) : null;
if (dateDiff == null) {
throw new Exception(sender.hasMuteReason() ? tl("voiceSilencedReason", sender.getMuteReason()) : tl("voiceSilenced"));
}
throw new Exception(sender.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, sender.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
}
if (!sender.isAuthorized("essentials.afk.message")) {
throw new Exception(tl("noPermToAFKMessage"));
}
}
user.setDisplayNick();
String msg = "";
String selfmsg = "";
if (!user.toggleAfk(AfkStatusChangeEvent.Cause.COMMAND)) {
if (!user.isHidden()) {
msg = tl("userIsNotAway", user.getDisplayName());
selfmsg = tl("userIsNotAwaySelf", user.getDisplayName());
}
user.updateActivity(false, AfkStatusChangeEvent.Cause.COMMAND);
} else {
if (!user.isHidden()) {
if (message != null) {
msg = tl("userIsAwayWithMessage", user.getDisplayName(), message);
selfmsg = tl("userIsAwaySelfWithMessage", user.getDisplayName(), message);
} else {
msg = tl("userIsAway", user.getDisplayName());
selfmsg = tl("userIsAwaySelf", user.getDisplayName());
}
}
user.setAfkMessage(message);
}
if (!msg.isEmpty() && ess.getSettings().broadcastAfkMessage()) {
// exclude user from receiving general AFK announcement in favor of personal message
ess.broadcastMessage(user, msg, u -> u == user);
}
if (!selfmsg.isEmpty()) {
user.sendMessage(selfmsg);
}
user.setDisplayNick(); // Set this again after toggling
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1 && user.isAuthorized("essentials.afk.others")) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,61 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandbalance extends EssentialsCommand {
public Commandbalance() {
super("balance");
}
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
User target = getPlayer(server, args, 0, false, true);
sender.sendMessage(tl("balanceOther", target.isHidden() ? target.getName() : target.getDisplayName(), NumberUtil.displayCurrency(target.getMoney(), ess)));
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length == 1 && user.isAuthorized("essentials.balance.others")) {
final User target = getPlayer(server, args, 0, true, true);
final BigDecimal bal = target.getMoney();
user.sendMessage(tl("balanceOther", target.isHidden() ? target.getName() : target.getDisplayName(), NumberUtil.displayCurrency(bal, ess)));
} else if (args.length < 2) {
final BigDecimal bal = user.getMoney();
user.sendMessage(tl("balance", NumberUtil.displayCurrency(bal, ess)));
} else {
throw new NotEnoughArgumentsException();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1 && user.isAuthorized("essentials.balance.others")) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,174 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static com.earth2me.essentials.I18n.tl;
public class Commandbalancetop extends EssentialsCommand {
public Commandbalancetop() {
super("balancetop");
}
private static final int CACHETIME = 2 * 60 * 1000;
public static final int MINUSERS = 50;
private static final SimpleTextInput cache = new SimpleTextInput();
private static long cacheage = 0;
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
int page = 0;
boolean force = false;
if (args.length > 0) {
try {
page = Integer.parseInt(args[0]);
} catch (NumberFormatException ex) {
if (args[0].equalsIgnoreCase("force") && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force"))) {
force = true;
}
}
}
if (!force && lock.readLock().tryLock()) {
try {
if (cacheage > System.currentTimeMillis() - CACHETIME) {
outputCache(sender, commandLabel, page);
return;
}
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
}
} finally {
lock.readLock().unlock();
}
ess.runTaskAsynchronously(new Viewer(sender, commandLabel, page, force));
} else {
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
}
ess.runTaskAsynchronously(new Viewer(sender, commandLabel, page, force));
}
}
private static void outputCache(final CommandSource sender, String command, int page) {
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(cacheage);
final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
sender.sendMessage(tl("balanceTop", format.format(cal.getTime())));
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
}
private class Calculator implements Runnable {
private final transient Viewer viewer;
private final boolean force;
public Calculator(final Viewer viewer, final boolean force) {
this.viewer = viewer;
this.force = force;
}
@Override
public void run() {
lock.writeLock().lock();
try {
if (force || cacheage <= System.currentTimeMillis() - CACHETIME) {
cache.getLines().clear();
final Map<String, BigDecimal> balances = new HashMap<>();
BigDecimal totalMoney = BigDecimal.ZERO;
if (ess.getSettings().isEcoDisabled()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Internal economy functions disabled, aborting baltop.");
}
} else {
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user != null) {
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
// Don't list NPCs in output
continue;
}
if (!user.isAuthorized("essentials.balancetop.exclude")) {
final BigDecimal userMoney = user.getMoney();
user.updateMoneyCache(userMoney);
totalMoney = totalMoney.add(userMoney);
final String name = user.isHidden() ? user.getName() : user.getDisplayName();
balances.put(name, userMoney);
}
}
}
}
final List<Map.Entry<String, BigDecimal>> sortedEntries = new ArrayList<>(balances.entrySet());
sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
cache.getLines().add(tl("serverTotal", NumberUtil.displayCurrency(totalMoney, ess)));
int pos = 1;
for (Map.Entry<String, BigDecimal> entry : sortedEntries) {
cache.getLines().add(pos + ". " + entry.getKey() + ", " + NumberUtil.displayCurrency(entry.getValue(), ess));
pos++;
}
cacheage = System.currentTimeMillis();
}
} finally {
lock.writeLock().unlock();
}
ess.runTaskAsynchronously(viewer);
}
}
private class Viewer implements Runnable {
private final transient CommandSource sender;
private final transient int page;
private final transient boolean force;
private final transient String commandLabel;
public Viewer(final CommandSource sender, final String commandLabel, final int page, final boolean force) {
this.sender = sender;
this.page = page;
this.force = force;
this.commandLabel = commandLabel;
}
@Override
public void run() {
lock.readLock().lock();
try {
if (!force && cacheage > System.currentTimeMillis() - CACHETIME) {
outputCache(sender, commandLabel, page);
return;
}
} finally {
lock.readLock().unlock();
}
ess.runTaskAsynchronously(new Calculator(new Viewer(sender, commandLabel, page, false), force));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList("1");
if (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force")) {
options.add("force");
}
return options;
} else {
return Collections.emptyList();
}
}
}
@@ -1,98 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.InventoryWorkaround;
import com.earth2me.essentials.utils.EnumUtil;
import com.google.common.collect.Lists;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandbook extends EssentialsCommand {
private static final Material WRITABLE_BOOK = EnumUtil.getMaterial("WRITABLE_BOOK", "BOOK_AND_QUILL");
public Commandbook() {
super("book");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final ItemStack item = user.getItemInHand();
final String player = user.getName();
if (item.getType() == Material.WRITTEN_BOOK) {
BookMeta bmeta = (BookMeta) item.getItemMeta();
if (args.length > 1 && args[0].equalsIgnoreCase("author")) {
if (user.isAuthorized("essentials.book.author") && (isAuthor(bmeta, player) || user.isAuthorized("essentials.book.others"))) {
bmeta.setAuthor(args[1]);
item.setItemMeta(bmeta);
user.sendMessage(tl("bookAuthorSet", getFinalArg(args, 1)));
} else {
throw new Exception(tl("denyChangeAuthor"));
}
} else if (args.length > 1 && args[0].equalsIgnoreCase("title")) {
if (user.isAuthorized("essentials.book.title") && (isAuthor(bmeta, player) || user.isAuthorized("essentials.book.others"))) {
bmeta.setTitle(args[1]);
item.setItemMeta(bmeta);
user.sendMessage(tl("bookTitleSet", getFinalArg(args, 1)));
} else {
throw new Exception(tl("denyChangeTitle"));
}
} else {
if (isAuthor(bmeta, player) || user.isAuthorized("essentials.book.others")) {
ItemStack newItem = new ItemStack(WRITABLE_BOOK, item.getAmount());
newItem.setItemMeta(bmeta);
InventoryWorkaround.setItemInMainHand(user.getBase(), newItem);
user.sendMessage(tl("editBookContents"));
} else {
throw new Exception(tl("denyBookEdit"));
}
}
} else if (item.getType() == WRITABLE_BOOK) {
BookMeta bmeta = (BookMeta) item.getItemMeta();
if (!user.isAuthorized("essentials.book.author")) {
bmeta.setAuthor(player);
}
ItemStack newItem = new ItemStack(Material.WRITTEN_BOOK, item.getAmount());
newItem.setItemMeta(bmeta);
InventoryWorkaround.setItemInMainHand(user.getBase(), newItem);
user.sendMessage(tl("bookLocked"));
} else {
throw new Exception(tl("holdBook"));
}
}
private boolean isAuthor(BookMeta bmeta, String player) {
String author = bmeta.getAuthor();
return author != null && author.equalsIgnoreCase(player);
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
// Right now, we aren't testing what's held in the player's hand - we could, but it's not necessarily worth it
if (args.length == 1) {
List<String> options = Lists.newArrayList("sign", "unsign"); // sign and unsign aren't real, but work
if (user.isAuthorized("essentials.book.author")) {
options.add("author");
}
if (user.isAuthorized("essentials.book.title")) {
options.add("title");
}
return options;
} else if (args.length == 2 && args[0].equalsIgnoreCase("author") && user.isAuthorized("essentials.book.author")) {
List<String> options = getPlayers(server, user);
options.add("Herobrine"); // #EasterEgg
return options;
} else {
return Collections.emptyList();
}
}
}
@@ -1,33 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandbroadcast extends EssentialsCommand {
public Commandbroadcast() {
super("broadcast");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
sendBroadcast(user.getDisplayName(), args);
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sendBroadcast(sender.getSender().getName(), args);
}
private void sendBroadcast(final String name, final String[] args) throws NotEnoughArgumentsException {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
ess.broadcastMessage(tl("broadcast", FormatUtil.replaceFormat(getFinalArg(args, 0)).replace("\\n", "\n"), name));
}
}
@@ -1,110 +0,0 @@
package com.earth2me.essentials.commands;
import static com.earth2me.essentials.I18n.tl;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.utils.FormatUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Commandbroadcastworld extends EssentialsCommand {
public Commandbroadcastworld() {
super("broadcastworld");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
World world = user.getWorld();
String message = getFinalArg(args, 0);
if (args.length < 1) {
throw new NotEnoughArgumentsException();
} else if (args.length > 1 && ess.getSettings().isAllowWorldInBroadcastworld()) {
World argWorld = ess.getWorld(args[0]);
if (argWorld != null) {
world = argWorld;
message = getFinalArg(args, 1);
}
}
if (world == null) {
world = user.getWorld();
message = getFinalArg(args, 0);
}
sendBroadcast(world.getName(), user.getDisplayName(), message);
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new NotEnoughArgumentsException("world");
}
sendBroadcast(args[0], sender.getSender().getName(), getFinalArg(args, 1));
}
private void sendBroadcast(final String worldName, final String name, final String message) throws Exception {
World world = ess.getWorld(worldName);
if (world == null) {
throw new Exception(tl("invalidWorld"));
}
if (message.isEmpty()) {
throw new NotEnoughArgumentsException();
}
sendToWorld(world, tl("broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), name));
}
private void sendToWorld(World world, String message) {
IText broadcast = new SimpleTextInput(message);
final Collection<Player> players = ess.getOnlinePlayers();
for (Player player : players) {
if (player.getWorld().equals(world)) {
final User user = ess.getUser(player);
broadcast = new KeywordReplacer(broadcast, new CommandSource(player), ess, false);
for (String messageText : broadcast.getLines()) {
user.sendMessage(messageText);
}
}
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1 && ess.getSettings().isAllowWorldInBroadcastworld()) {
List<String> worlds = Lists.newArrayList();
for (World world : server.getWorlds()) {
worlds.add(world.getName());
}
return worlds;
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
List<String> worlds = Lists.newArrayList();
for (World world : server.getWorlds()) {
worlds.add(world.getName());
}
return worlds;
} else {
return Collections.emptyList();
}
}
}
@@ -1,128 +0,0 @@
package com.earth2me.essentials.commands;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DateUtil;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.bukkit.inventory.ItemStack;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static com.earth2me.essentials.I18n.tl;
public class Commandcreatekit extends EssentialsCommand {
private static final String PASTE_URL = "https://hastebin.com/";
private static final String PASTE_UPLOAD_URL = PASTE_URL + "documents";
private static final Gson GSON = new Gson();
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final DumperOptions yamlOptions = new DumperOptions();
private final Representer yamlRepresenter = new YamlRepresenter();
private final YamlConstructor yamlConstructor = new YamlConstructor();
public Commandcreatekit() {
super("createkit");
yamlOptions.setDefaultFlowStyle(FlowStyle.BLOCK);
yamlRepresenter.setDefaultFlowStyle(FlowStyle.BLOCK);
}
// /createkit <name> <delay>
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length != 2) {
throw new NotEnoughArgumentsException();
}
// Command handler will auto fail if this fails.
long delay = Long.parseLong(args[1]);
String kitname = args[0];
ItemStack[] items = user.getBase().getInventory().getContents();
List<String> list = new ArrayList<>();
for (ItemStack is : items) {
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
String serialized = ess.getItemDb().serialize(is);
list.add(serialized);
}
}
// Some users might want to directly write to config knowing the consequences. *shrug*
if (!ess.getSettings().isPastebinCreateKit()) {
ess.getKits().addKit(kitname, list, delay);
user.sendMessage(tl("createdKit", kitname, list.size(), delay));
} else {
ConfigurationSection config = new MemoryConfiguration();
config.set("kits." + kitname + ".delay", delay);
config.set("kits." + kitname + ".items", list);
final Yaml yaml = new Yaml(yamlConstructor, yamlRepresenter, yamlOptions);
String fileContents = "# Copy the kit code below into the kits section in your config.yml file\n";
fileContents += yaml.dump(config.getValues(false));
uploadPaste(user.getSource(), kitname, delay, fileContents);
}
}
private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final String contents) {
executorService.submit(() -> {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(PASTE_UPLOAD_URL).openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("User-Agent", "EssentialsX plugin");
try (OutputStream os = connection.getOutputStream()) {
os.write(contents.getBytes(Charsets.UTF_8));
}
// Error
if (connection.getResponseCode() >= 400) {
sender.sendMessage(tl("createKitFailed", kitName));
String message = CharStreams.toString(new InputStreamReader(connection.getErrorStream(), Charsets.UTF_8));
ess.getLogger().severe("Error creating kit: " + message);
return;
}
// Read URL
JsonObject object = GSON.fromJson(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8), JsonObject.class);
String pasteUrl = PASTE_URL + object.get("key").getAsString();
connection.disconnect();
String separator = tl("createKitSeparator");
String delayFormat = "0";
if (delay > 0) {
delayFormat = DateUtil.formatDateDiff(System.currentTimeMillis() + (delay * 1000));
}
sender.sendMessage(separator);
sender.sendMessage(tl("createKitSuccess", kitName, delayFormat, pasteUrl));
sender.sendMessage(separator);
if (ess.getSettings().isDebug()) {
ess.getLogger().info(sender.getSender().getName() + " created a kit: " + pasteUrl);
}
} catch (Exception e) {
sender.sendMessage(tl("createKitFailed", kitName));
e.printStackTrace();
}
});
}
}
@@ -1,81 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commanddelhome extends EssentialsCommand {
public Commanddelhome() {
super("delhome");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
User user = ess.getUser(sender.getPlayer());
String name;
String[] expandedArg;
//Allowing both formats /sethome khobbits house | /sethome khobbits:house
final String[] nameParts = args[0].split(":");
if (nameParts[0].length() != args[0].length()) {
expandedArg = nameParts;
} else {
expandedArg = args;
}
if (expandedArg.length > 1 && (user == null || user.isAuthorized("essentials.delhome.others"))) {
user = getPlayer(server, expandedArg, 0, true, true);
name = expandedArg[1];
} else if (user == null) {
throw new NotEnoughArgumentsException();
} else {
name = expandedArg[0];
}
if (name.equalsIgnoreCase("bed")) {
throw new Exception(tl("invalidHomeName"));
}
user.delHome(name.toLowerCase(Locale.ENGLISH));
sender.sendMessage(tl("deleteHome", name));
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
User user = ess.getUser(sender.getPlayer());
boolean canDelOthers = user == null || user.isAuthorized("essentials.delhome.others");
if (args.length == 1) {
List<String> homes = user == null ? new ArrayList<>() : user.getHomes();
if (canDelOthers) {
int sepIndex = args[0].indexOf(':');
if (sepIndex < 0) {
getPlayers(server, sender).forEach(player -> homes.add(player + ":"));
} else {
String namePart = args[0].substring(0, sepIndex);
User otherUser;
try {
otherUser = getPlayer(server, new String[]{namePart}, 0, true, true);
} catch (Exception ex) {
return homes;
}
otherUser.getHomes().forEach(home -> homes.add(namePart + ":" + home));
}
}
return homes;
} else {
return Collections.emptyList();
}
}
}
@@ -1,36 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import org.bukkit.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commanddelwarp extends EssentialsCommand {
public Commanddelwarp() {
super("delwarp");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
ess.getWarps().removeWarp(args[0]);
sender.sendMessage(tl("deleteWarp", args[0]));
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return new ArrayList<>(ess.getWarps().getList());
} else {
return Collections.emptyList();
}
}
}
@@ -1,131 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.ChargeException;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import net.ess3.api.MaxMoneyException;
import net.ess3.api.events.UserBalanceUpdateEvent;
import org.bukkit.Server;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commandeco extends EssentialsLoopCommand {
Commandeco.EcoCommands cmd;
BigDecimal amount;
boolean isPercent;
public Commandeco() {
super("eco");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new NotEnoughArgumentsException();
}
BigDecimal startingBalance = ess.getSettings().getStartingBalance();
try {
cmd = Commandeco.EcoCommands.valueOf(args[0].toUpperCase(Locale.ENGLISH));
isPercent = cmd != EcoCommands.RESET && args[2].endsWith("%");
amount = (cmd == Commandeco.EcoCommands.RESET) ? startingBalance : new BigDecimal(args[2].replaceAll("[^0-9\\.]", ""));
} catch (Exception ex) {
throw new NotEnoughArgumentsException(ex);
}
loopOfflinePlayers(server, sender, false, true, args[1], args);
if (cmd == Commandeco.EcoCommands.RESET || cmd == Commandeco.EcoCommands.SET) {
if (args[1].contentEquals("**")) {
server.broadcastMessage(tl("resetBalAll", NumberUtil.displayCurrency(amount, ess)));
} else if (args[1].contentEquals("*")) {
server.broadcastMessage(tl("resetBal", NumberUtil.displayCurrency(amount, ess)));
}
}
}
@Override
protected void updatePlayer(final Server server, final CommandSource sender, final User player, final String[] args) throws NotEnoughArgumentsException, ChargeException, MaxMoneyException {
if (isPercent && cmd != EcoCommands.RESET) {
amount = player.getMoney().multiply(amount).scaleByPowerOfTen(-2);
}
switch (cmd) {
case GIVE:
player.giveMoney(amount, sender, UserBalanceUpdateEvent.Cause.COMMAND_ECO);
break;
case TAKE:
take(amount, player, sender);
break;
case RESET:
case SET:
set(amount, player, sender);
break;
}
}
private void take(BigDecimal amount, final User player, final CommandSource sender) throws ChargeException {
BigDecimal money = player.getMoney();
BigDecimal minBalance = ess.getSettings().getMinMoney();
if (money.subtract(amount).compareTo(minBalance) >= 0) {
player.takeMoney(amount, sender, UserBalanceUpdateEvent.Cause.COMMAND_ECO);
} else if (sender == null) {
try {
player.setMoney(minBalance, UserBalanceUpdateEvent.Cause.COMMAND_ECO);
} catch (MaxMoneyException ex) {
// Take shouldn't be able to throw a max money exception
}
player.sendMessage(tl("takenFromAccount", NumberUtil.displayCurrency(player.getMoney(), ess)));
} else {
throw new ChargeException(tl("insufficientFunds"));
}
}
private void set(BigDecimal amount, final User player, final CommandSource sender) throws MaxMoneyException {
BigDecimal minBalance = ess.getSettings().getMinMoney();
BigDecimal maxBalance = ess.getSettings().getMaxMoney();
boolean underMinimum = (amount.compareTo(minBalance) < 0);
boolean aboveMax = (amount.compareTo(maxBalance) > 0);
player.setMoney(underMinimum ? minBalance : aboveMax ? maxBalance : amount, UserBalanceUpdateEvent.Cause.COMMAND_ECO);
player.sendMessage(tl("setBal", NumberUtil.displayCurrency(player.getMoney(), ess)));
if (sender != null) {
sender.sendMessage(tl("setBalOthers", player.getDisplayName(), NumberUtil.displayCurrency(player.getMoney(), ess)));
}
}
private enum EcoCommands {
GIVE, TAKE, SET, RESET
}
@Override
protected List<String> getTabCompleteOptions(Server server, final CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList();
for (EcoCommands command : EcoCommands.values()) {
options.add(command.name().toLowerCase(Locale.ENGLISH));
}
return options;
} else if (args.length == 2) {
return getPlayers(server, sender);
} else if (args.length == 3 && !args[0].equalsIgnoreCase(EcoCommands.RESET.name())) {
if (args[0].equalsIgnoreCase(EcoCommands.SET.name())) {
return Lists.newArrayList("0", ess.getSettings().getStartingBalance().toString());
} else {
return Lists.newArrayList("1", "10", "100", "1000");
}
} else {
return Collections.emptyList();
}
}
}
@@ -1,74 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandeditsign extends EssentialsCommand {
public Commandeditsign() {
super("editsign");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
if (args.length == 0 || (args.length > 1 && !NumberUtil.isInt(args[1]))) {
throw new NotEnoughArgumentsException();
}
Block target = user.getBase().getTargetBlock(null, 5); //5 is a good number
if (!(target.getState() instanceof Sign)) {
throw new Exception(tl("editsignCommandTarget"));
}
Sign sign = (Sign) target.getState();
try {
if (args[0].equalsIgnoreCase("set") && args.length > 2) {
int line = Integer.parseInt(args[1]) - 1;
String text = FormatUtil.formatString(user, "essentials.editsign", getFinalArg(args, 2)).trim();
if (ChatColor.stripColor(text).length() > 15 && !user.isAuthorized("essentials.editsign.unlimited")) {
throw new Exception(tl("editsignCommandLimit"));
}
sign.setLine(line, text);
sign.update();
user.sendMessage(tl("editsignCommandSetSuccess", line + 1, text));
} else if (args[0].equalsIgnoreCase("clear")) {
if (args.length == 1) {
for (int i = 0; i < 4; i++) { // A whole one line of line savings!
sign.setLine(i, "");
}
sign.update();
user.sendMessage(tl("editsignCommandClear"));
} else {
int line = Integer.parseInt(args[1]) - 1;
sign.setLine(line, "");
sign.update();
user.sendMessage(tl("editsignCommandClearLine", line + 1));
}
} else {
throw new NotEnoughArgumentsException();
}
} catch (IndexOutOfBoundsException e) {
throw new Exception(tl("editsignCommandNoLine"));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return Lists.newArrayList("set", "clear");
} else if (args.length == 2) {
return Lists.newArrayList("1", "2", "3", "4");
} else {
return Collections.emptyList();
}
}
}
@@ -1,468 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.EssentialsUpgrade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.UserMap;
import com.earth2me.essentials.utils.*;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import java.util.function.Supplier;
import static com.earth2me.essentials.I18n.tl;
// This command has 4 undocumented behaviours #EasterEgg
public class Commandessentials extends EssentialsCommand {
private static final Sound NOTE_HARP = EnumUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO");
private static final Sound MOO_SOUND = EnumUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK");
private static final String NYAN_TUNE = "1D#,1E,2F#,,2A#,1E,1D#,1E,2F#,2B,2D#,2E,2D#,2A#,2B,,2F#,,1D#,1E,2F#,2B,2C#,2A#,2B,2C#,2E,2D#,2E,2C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1B,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1B,,";
private static final String[] CONSOLE_MOO = new String[]{" (__)", " (oo)", " /------\\/", " / | ||", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
private static final String[] PLAYER_MOO = new String[]{" (__)", " (oo)", " /------\\/", " / | | |", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
public Commandessentials() {
super("essentials");
}
private transient TuneRunnable currentTune = null;
private static final List<String> versionPlugins = Arrays.asList(
"Vault", // API
"Reserve", // API
"PlaceholderAPI", // API
"CMI", // potential for issues
"Towny", // past issues; admins should ensure latest
"ChestShop", // past issues; admins should ensure latest
"Citizens", // fires player events
"LuckPerms", // permissions (recommended)
"UltraPermissions",
"PermissionsEx", // permissions (unsupported)
"GroupManager", // permissions (unsupported)
"bPermissions" // permissions (unsupported)
);
private static final List<String> officialPlugins = Arrays.asList(
"EssentialsAntiBuild",
"EssentialsChat",
"EssentialsGeoIP",
"EssentialsProtect",
"EssentialsSpawn",
"EssentialsXMPP"
);
private static final List<String> warnPlugins = Arrays.asList(
"PermissionsEx",
"GroupManager",
"bPremissions"
);
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length == 0) {
showUsage(sender);
}
switch(args[0]) {
// Info commands
case "debug":
case "verbose":
runDebug(server, sender, commandLabel, args);
break;
case "ver":
case "version":
runVersion(server, sender, commandLabel, args);
break;
case "cmd":
case "commands":
runCommands(server, sender, commandLabel, args);
break;
// Data commands
case "reload":
runReload(server, sender, commandLabel, args);
break;
case "reset":
runReset(server, sender, commandLabel, args);
break;
case "cleanup":
runCleanup(server, sender, commandLabel, args);
break;
case "uuidconvert":
runUUIDConvert(server, sender, commandLabel, args);
break;
case "uuidtest":
runUUIDTest(server, sender, commandLabel, args);
break;
// "#EasterEgg"
case "nya":
case "nyan":
runNya(server, sender, commandLabel, args);
break;
case "moo":
runMoo(server, sender, commandLabel, args);
break;
default:
showUsage(sender);
}
}
// Displays the command's usage.
private void showUsage(final CommandSource sender) throws Exception {
throw new NotEnoughArgumentsException("/<command> <reload/debug/commands>");
}
// Lists commands that are being handed over to other plugins.
private void runCommands(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
final StringBuilder disabledCommands = new StringBuilder();
for (Map.Entry<String, String> entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) {
if (disabledCommands.length() > 0) {
disabledCommands.append("\n");
}
disabledCommands.append(entry.getKey()).append(" => ").append(entry.getValue());
}
if (disabledCommands.length() > 0) {
sender.sendMessage(tl("blockList"));
sender.sendMessage(disabledCommands.toString());
} else {
sender.sendMessage(tl("blockListEmpty"));
}
}
// Resets the given player's user data.
private void runReset(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new Exception("/<command> reset <player>");
}
final User user = getPlayer(server, args, 1, true, true);
user.reset();
sender.sendMessage("Reset Essentials userdata for player: " + user.getDisplayName());
}
// Toggles debug mode.
private void runDebug(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
ess.getSettings().setDebug(!ess.getSettings().isDebug());
sender.sendMessage("Essentials " + ess.getDescription().getVersion() + " debug mode " + (ess.getSettings().isDebug() ? "enabled" : "disabled"));
}
// Reloads all reloadable configs.
private void runReload(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
ess.reload();
sender.sendMessage(tl("essentialsReload", ess.getDescription().getVersion()));
}
// Pop tarts.
private void runNya(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (currentTune != null) {
currentTune.cancel();
}
currentTune = new TuneRunnable(NYAN_TUNE, NOTE_HARP, ess::getOnlinePlayers);
currentTune.runTaskTimer(ess, 20, 2);
}
// Cow farts.
private void runMoo(final Server server, final CommandSource sender, final String command, final String[] args) {
if (args.length == 2 && args[1].equals("moo")) {
for (String s : CONSOLE_MOO) {
logger.info(s);
}
for (Player player : ess.getOnlinePlayers()) {
player.sendMessage(PLAYER_MOO);
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
}
} else {
if (sender.isPlayer()) {
sender.getSender().sendMessage(PLAYER_MOO);
final Player player = sender.getPlayer();
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
} else {
sender.getSender().sendMessage(CONSOLE_MOO);
}
}
}
// Cleans up inactive users.
private void runCleanup(final Server server, final CommandSource sender, final String command, final String[] args) throws Exception {
if (args.length < 2 || !NumberUtil.isInt(args[1])) {
sender.sendMessage("This sub-command will delete users who haven't logged in in the last <days> days.");
sender.sendMessage("Optional parameters define the minimum amount required to prevent deletion.");
sender.sendMessage("Unless you define larger default values, this command will ignore people who have more than 0 money/homes.");
throw new Exception("/<command> cleanup <days> [money] [homes]");
}
sender.sendMessage(tl("cleaning"));
final long daysArg = Long.parseLong(args[1]);
final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9\\.]", "")) : 0;
final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0;
final UserMap userMap = ess.getUserMap();
ess.runTaskAsynchronously(() -> {
long currTime = System.currentTimeMillis();
for (UUID u : userMap.getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user == null) {
continue;
}
long lastLog = user.getLastLogout();
if (lastLog == 0) {
lastLog = user.getLastLogin();
}
if (lastLog == 0) {
user.setLastLogin(currTime);
}
if (user.isNPC()) {
continue;
}
long timeDiff = currTime - lastLog;
long milliDays = daysArg * 24L * 60L * 60L * 1000L;
int homeCount = user.getHomes().size();
double moneyCount = user.getMoney().doubleValue();
if ((lastLog == 0) || (timeDiff < milliDays) || (homeCount > homesArg) || (moneyCount > moneyArg)) {
continue;
}
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Deleting user: " + user.getName() + " Money: " + moneyCount + " Homes: " + homeCount + " Last seen: " + DateUtil.formatDateDiff(lastLog));
}
user.reset();
}
sender.sendMessage(tl("cleaned"));
});
}
// Forces a rerun of userdata UUID conversion.
private void runUUIDConvert(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sender.sendMessage("Starting Essentials UUID userdata conversion; this may lag the server.");
Boolean ignoreUFCache = (args.length > 2 && args[1].toLowerCase(Locale.ENGLISH).contains("ignore"));
EssentialsUpgrade.uuidFileConvert(ess, ignoreUFCache);
sender.sendMessage("UUID conversion complete. Check your server log for more information.");
}
// Looks up various UUIDs for a user.
private void runUUIDTest(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new Exception("/<command> uuidtest <name>");
}
String name = args[1];
sender.sendMessage("Looking up UUID for " + name);
UUID onlineUUID = null;
for (Player player : ess.getOnlinePlayers()) {
if (player.getName().equalsIgnoreCase(name)) {
onlineUUID = player.getUniqueId();
break;
}
}
UUID essUUID = ess.getUserMap().getUser(name).getConfigUUID();
org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
UUID bukkituuid = player.getUniqueId();
sender.sendMessage("Bukkit Lookup: " + bukkituuid.toString());
if (onlineUUID != null && onlineUUID != bukkituuid) {
sender.sendMessage("Online player: " + onlineUUID.toString());
}
if (essUUID != null && essUUID != bukkituuid) {
sender.sendMessage("Essentials config: " + essUUID.toString());
}
UUID npcuuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
sender.sendMessage("NPC UUID: " + npcuuid.toString());
UUID offlineuuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
sender.sendMessage("Offline Mode UUID: " + offlineuuid.toString());
}
// Displays versions of EssentialsX and related plugins.
private void runVersion(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.version")) return;
boolean isMismatched = false;
boolean isVaultInstalled = false;
boolean isUnsupported = false;
final boolean isServerSupported = VersionUtil.isServerSupported();
final PluginManager pm = server.getPluginManager();
final String essVer = pm.getPlugin("Essentials").getDescription().getVersion();
sender.sendMessage(tl(isServerSupported ? "versionOutputFine" : "versionOutputWarn", "Server", server.getBukkitVersion() + " " + server.getVersion()));
sender.sendMessage(tl("versionOutputFine", "EssentialsX", essVer));
for (Plugin plugin : pm.getPlugins()) {
final PluginDescriptionFile desc = plugin.getDescription();
String name = desc.getName();
String version = desc.getVersion();
if (name.startsWith("Essentials") && !name.equalsIgnoreCase("Essentials")) {
if (officialPlugins.contains(name)) {
name = name.replace("Essentials", "EssentialsX");
if (!version.equalsIgnoreCase(essVer)) {
isMismatched = true;
sender.sendMessage(tl("versionOutputWarn", name, version));
} else {
sender.sendMessage(tl("versionOutputFine", name, version));
}
} else {
sender.sendMessage(tl("versionOutputUnsupported", name, version));
isUnsupported = true;
}
}
if (versionPlugins.contains(name)) {
if (warnPlugins.contains(name)) {
sender.sendMessage(tl("versionOutputUnsupported", name, version));
isUnsupported = true;
} else {
sender.sendMessage(tl("versionOutputFine", name, version));
}
}
if (name.equals("Vault")) isVaultInstalled = true;
}
if (isMismatched) {
sender.sendMessage(tl("versionMismatchAll"));
}
if (!isVaultInstalled) {
sender.sendMessage(tl("versionOutputVaultMissing"));
}
if (isUnsupported) {
sender.sendMessage(tl("versionOutputUnsupportedPlugins"));
}
if (!VersionUtil.isServerSupported()) {
sender.sendMessage(tl("serverUnsupported"));
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList();
options.add("debug");
options.add("commands");
options.add("version");
options.add("reload");
options.add("reset");
options.add("cleanup");
//options.add("uuidconvert");
//options.add("uuidtest");
//options.add("nya");
//options.add("moo");
return options;
}
switch (args[0]) {
case "moo":
if (args.length == 2) {
return Lists.newArrayList("moo");
}
break;
case "reset":
case "uuidtest":
if (args.length == 2) {
return getPlayers(server, sender);
}
break;
case "cleanup":
if (args.length == 2) {
return COMMON_DURATIONS;
} else if (args.length == 3 || args.length == 4) {
return Lists.newArrayList("-1", "0");
}
break;
case "uuidconvert":
if (args.length == 2) {
return Lists.newArrayList("ignoreUFCache");
}
break;
}
return Collections.emptyList();
}
private static class TuneRunnable extends BukkitRunnable {
private static final Map<String, Float> noteMap = ImmutableMap.<String, Float>builder()
.put("1F#", 0.5f)
.put("1G", 0.53f)
.put("1G#", 0.56f)
.put("1A", 0.6f)
.put("1A#", 0.63f)
.put("1B", 0.67f)
.put("1C", 0.7f)
.put("1C#", 0.76f)
.put("1D", 0.8f)
.put("1D#", 0.84f)
.put("1E", 0.9f)
.put("1F", 0.94f)
.put("2F#", 1.0f)
.put("2G", 1.06f)
.put("2G#", 1.12f)
.put("2A", 1.18f)
.put("2A#", 1.26f)
.put("2B", 1.34f)
.put("2C", 1.42f)
.put("2C#", 1.5f)
.put("2D", 1.6f)
.put("2D#", 1.68f)
.put("2E", 1.78f)
.put("2F", 1.88f)
.build();
private final String[] tune;
private final Sound sound;
private final Supplier<Collection<Player>> players;
private int i = 0;
TuneRunnable(final String tuneStr, final Sound sound, final Supplier<Collection<Player>> players) {
this.tune = tuneStr.split(",");
this.sound = sound;
this.players = players;
}
@Override
public void run() {
final String note = tune[i];
i++;
if (i >= tune.length) {
cancel();
}
if (note == null || note.isEmpty()) {
return;
}
for (Player onlinePlayer : players.get()) {
onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note));
}
}
}
}
@@ -1,226 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.SetExpFix;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commandexp extends EssentialsCommand {
public Commandexp() {
super("exp");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length == 0) {
showExp(user.getSource(), user);
} else if (args.length > 1 && args[0].equalsIgnoreCase("set") && user.isAuthorized("essentials.exp.set")) {
if (args.length == 3 && user.isAuthorized("essentials.exp.set.others")) {
expMatch(server, user.getSource(), args[1], args[2], false);
} else {
setExp(user.getSource(), user, args[1], false);
}
} else if (args.length > 1 && args[0].equalsIgnoreCase("give") && user.isAuthorized("essentials.exp.give")) {
if (args.length == 3 && user.isAuthorized("essentials.exp.give.others")) {
expMatch(server, user.getSource(), args[1], args[2], true);
} else {
setExp(user.getSource(), user, args[1], true);
}
} else if (args.length > 1 && args[0].equalsIgnoreCase("take") && user.isAuthorized("essentials.exp.take")) {
if (args.length == 3 && user.isAuthorized("essentials.exp.take.others")) {
expMatch(server, user.getSource(), args[1], "-" + args[2], true);
} else {
setExp(user.getSource(), user, "-" + args[1], true);
}
} else if (args.length < 3 && args[0].equalsIgnoreCase("reset") && user.isAuthorized("essentials.exp.reset")) {
if (args.length == 2 && user.isAuthorized("essentials.exp.reset.others")) {
expMatch(server, user.getSource(), args[1], "0", false);
} else {
setExp(user.getSource(), user, "0", false);
}
} else if (args[0].equalsIgnoreCase("show")) {
if (args.length >= 2 && user.isAuthorized("essentials.exp.others")) {
String match = args[1].trim();
showMatch(server, user.getSource(), match);
} else {
showExp(user.getSource(), user);
}
} else {
if (args.length >= 1 && NumberUtil.isInt(args[0].toLowerCase(Locale.ENGLISH).replace("l", "")) && user.isAuthorized("essentials.exp.give")) {
if (args.length >= 2 && user.isAuthorized("essentials.exp.give.others")) {
expMatch(server, user.getSource(), args[1], args[0], true);
} else {
setExp(user.getSource(), user, args[0], true);
}
} else if (args.length >= 1 && user.isAuthorized("essentials.exp.others")) {
String match = args[0].trim();
showMatch(server, user.getSource(), match);
} else {
showExp(user.getSource(), user);
}
}
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
} else if (args.length > 2 && args[0].equalsIgnoreCase("set")) {
expMatch(server, sender, args[1], args[2], false);
} else if (args.length > 2 && args[0].equalsIgnoreCase("give")) {
expMatch(server, sender, args[1], args[2], true);
} else if (args.length > 2 && args[0].equalsIgnoreCase("take")) {
expMatch(server, sender, args[1], "-" + args[2], true);
} else if (args.length > 2 && args[0].equalsIgnoreCase("reset")) {
expMatch(server, sender, args[1], "0", false);
} else {
String match = args[0].trim();
if (args.length >= 2 && NumberUtil.isInt(args[0].toLowerCase(Locale.ENGLISH).replace("l", ""))) {
match = args[1].trim();
expMatch(server, sender, match, args[0], true);
} else if (args.length == 1) {
match = args[0].trim();
}
showMatch(server, sender, match);
}
}
private void showMatch(final Server server, final CommandSource sender, final String match) throws PlayerNotFoundException {
boolean skipHidden = sender.isPlayer() && !ess.getUser(sender.getPlayer()).canInteractVanished();
boolean foundUser = false;
final List<Player> matchedPlayers = server.matchPlayer(match);
for (Player matchPlayer : matchedPlayers) {
final User player = ess.getUser(matchPlayer);
if (skipHidden && player.isHidden(sender.getPlayer()) && !sender.getPlayer().canSee(matchPlayer)) {
continue;
}
foundUser = true;
showExp(sender, player);
}
if (!foundUser) {
throw new PlayerNotFoundException();
}
}
private void expMatch(final Server server, final CommandSource sender, final String match, String amount, final boolean give) throws NotEnoughArgumentsException, PlayerNotFoundException {
boolean skipHidden = sender.isPlayer() && !ess.getUser(sender.getPlayer()).canInteractVanished();
boolean foundUser = false;
final List<Player> matchedPlayers = server.matchPlayer(match);
for (Player matchPlayer : matchedPlayers) {
final User player = ess.getUser(matchPlayer);
if (skipHidden && player.isHidden(sender.getPlayer()) && !sender.getPlayer().canSee(matchPlayer)) {
continue;
}
foundUser = true;
setExp(sender, player, amount, give);
}
if (!foundUser) {
throw new PlayerNotFoundException();
}
}
private void showExp(final CommandSource sender, final User target) {
sender.sendMessage(tl("exp", target.getDisplayName(), SetExpFix.getTotalExperience(target.getBase()), target.getBase().getLevel(), SetExpFix.getExpUntilNextLevel(target.getBase())));
}
//TODO: Limit who can give negative exp?
private void setExp(final CommandSource sender, final User target, String strAmount, final boolean give) throws NotEnoughArgumentsException {
long amount;
strAmount = strAmount.toLowerCase(Locale.ENGLISH);
if (strAmount.contains("l")) {
strAmount = strAmount.replaceAll("l", "");
int neededLevel = Integer.parseInt(strAmount);
if (give) {
neededLevel += target.getBase().getLevel();
}
amount = SetExpFix.getExpToLevel(neededLevel);
SetExpFix.setTotalExperience(target.getBase(), 0);
} else {
amount = Long.parseLong(strAmount);
if (amount > Integer.MAX_VALUE || amount < Integer.MIN_VALUE) {
throw new NotEnoughArgumentsException();
}
}
if (give) {
amount += SetExpFix.getTotalExperience(target.getBase());
}
if (amount > Integer.MAX_VALUE) {
amount = Integer.MAX_VALUE;
}
if (amount < 0L) {
amount = 0L;
}
SetExpFix.setTotalExperience(target.getBase(), (int) amount);
sender.sendMessage(tl("expSet", target.getDisplayName(), amount));
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList("show");
if (user.isAuthorized("essentials.exp.set")) {
options.add("set");
}
if (user.isAuthorized("essentials.exp.give")) {
options.add("give");
}
if (user.isAuthorized("essentials.exp.take")) {
options.add("take");
}
if (user.isAuthorized("essentials.exp.reset")) {
options.add("reset");
}
return options;
} else if (args.length == 2) {
if ((args[0].equalsIgnoreCase("set") && user.isAuthorized("essentials.exp.set")) || (args[0].equalsIgnoreCase("give") && user.isAuthorized("essentials.exp.give")) || (args[0].equalsIgnoreCase("take") && user.isAuthorized("essentials.exp.take"))) {
String levellessArg = args[1].toLowerCase(Locale.ENGLISH).replaceAll("l", "");
if (NumberUtil.isInt(levellessArg)) {
return Lists.newArrayList(levellessArg + "l");
}
}
if (user.isAuthorized("essentials.exp.others")) {
return getPlayers(server, user);
}
} else if (args.length == 3 && !(args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("reset"))) {
String levellessArg = args[2].toLowerCase(Locale.ENGLISH).replaceAll("l", "");
if (NumberUtil.isInt(levellessArg)) {
return Lists.newArrayList(levellessArg + "l");
}
}
return Collections.emptyList();
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
// TODO: This seems somewhat buggy, both setting and showing - right now, ignoring that
return Lists.newArrayList("set", "give", "show", "take", "reset");
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("give")) {
String levellessArg = args[1].toLowerCase(Locale.ENGLISH).replace("l", "");
if (NumberUtil.isInt(levellessArg)) {
return Lists.newArrayList(levellessArg, args[1] + "l");
} else {
return Collections.emptyList();
}
} else { // even without 'show'
return getPlayers(server, sender);
}
} else if (args.length == 3 && (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("give"))) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,177 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.MetaItemStack;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.Server;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Firework;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.FireworkMeta;
import org.bukkit.util.Vector;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
//This command has quite a complicated syntax, in theory it has 4 seperate syntaxes which are all variable:
//
//1: /firework clear - This clears all of the effects on a firework stack
//
//2: /firework power <int> - This changes the base power of a firework
//
//3: /firework fire - This 'fires' a copy of the firework held.
//3: /firework fire <int> - This 'fires' a number of copies of the firework held.
//3: /firework fire <other> - This 'fires' a copy of the firework held, in the direction you are looking, #easteregg
//
//4: /firework [meta] - This will add an effect to the firework stack held
//4: /firework color:<color> - The minimum you need to set an effect is 'color'
//4: Full Syntax: color:<color[,color,..]> [fade:<color[,color,..]>] [shape:<shape>] [effect:<effect[,effect]>]
//4: Possible Shapes: star, ball, large, creeper, burst
//4: Possible Effects trail, twinkle
public class Commandfirework extends EssentialsCommand {
public Commandfirework() {
super("firework");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final ItemStack stack = user.getItemInHand();
if (MaterialUtil.isFirework(stack.getType())) {
if (args.length > 0) {
if (args[0].equalsIgnoreCase("clear")) {
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
fmeta.clearEffects();
stack.setItemMeta(fmeta);
user.sendMessage(tl("fireworkEffectsCleared"));
} else if (args.length > 1 && (args[0].equalsIgnoreCase("power") || (args[0].equalsIgnoreCase("p")))) {
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
try {
int power = Integer.parseInt(args[1]);
fmeta.setPower(power > 3 ? 4 : power);
} catch (NumberFormatException e) {
throw new Exception(tl("invalidFireworkFormat", args[1], args[0]));
}
stack.setItemMeta(fmeta);
} else if ((args[0].equalsIgnoreCase("fire") || (args[0].equalsIgnoreCase("f"))) && user.isAuthorized("essentials.firework.fire")) {
int amount = 1;
boolean direction = false;
if (args.length > 1) {
if (NumberUtil.isInt(args[1])) {
final int serverLimit = ess.getSettings().getSpawnMobLimit();
amount = Integer.parseInt(args[1]);
if (amount > serverLimit) {
amount = serverLimit;
user.sendMessage(tl("mobSpawnLimit"));
}
} else {
direction = true;
}
}
for (int i = 0; i < amount; i++) {
Firework firework = (Firework) user.getWorld().spawnEntity(user.getLocation(), EntityType.FIREWORK);
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
if (direction) {
final Vector vector = user.getBase().getEyeLocation().getDirection().multiply(0.070);
if (fmeta.getPower() > 1) {
fmeta.setPower(1);
}
firework.setVelocity(vector);
}
firework.setFireworkMeta(fmeta);
}
} else {
final MetaItemStack mStack = new MetaItemStack(stack);
for (String arg : args) {
try {
mStack.addFireworkMeta(user.getSource(), true, arg, ess);
} catch (Exception e) {
user.sendMessage(tl("fireworkSyntax"));
throw e;
}
}
if (mStack.isValidFirework()) {
FireworkMeta fmeta = (FireworkMeta) mStack.getItemStack().getItemMeta();
FireworkEffect effect = mStack.getFireworkBuilder().build();
if (fmeta.getEffects().size() > 0 && !user.isAuthorized("essentials.firework.multiple")) {
throw new Exception(tl("multipleCharges"));
}
fmeta.addEffect(effect);
stack.setItemMeta(fmeta);
} else {
user.sendMessage(tl("fireworkSyntax"));
throw new Exception(tl("fireworkColor"));
}
}
} else {
throw new NotEnoughArgumentsException();
}
} else {
throw new Exception(tl("holdFirework"));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
// Note: this enforces an order of color fade shape effect, which the actual command doesn't have. But that's fine.
if (args.length == 1) {
List<String> options = Lists.newArrayList();
if (args[0].startsWith("color:")) {
String prefix;
if (args[0].contains(",")) {
prefix = args[0].substring(0, args[0].lastIndexOf(',') + 1);
} else {
prefix = "color:";
}
for (DyeColor color : DyeColor.values()) {
options.add(prefix + color.name().toLowerCase() + ",");
}
return options;
}
options.add("clear");
options.add("power");
options.add("color:");
if (user.isAuthorized("essentials.firework.fire")) {
options.add("fire");
}
return options;
} else if (args.length == 2) {
if (args[0].equals("power")) {
return Lists.newArrayList("1", "2", "3", "4");
} else if (args[0].equals("fire")) {
return Lists.newArrayList("1");
} else if (args[0].startsWith("color:")) {
List<String> options = Lists.newArrayList();
if (!args[1].startsWith("fade:")) {
args[1] = "fade:";
}
String prefix;
if (args[1].contains(",")) {
prefix = args[1].substring(0, args[1].lastIndexOf(',') + 1);
} else {
prefix = "fade:";
}
for (DyeColor color : DyeColor.values()) {
options.add(prefix + color.name().toLowerCase() + ",");
}
return options;
} else {
return Collections.emptyList();
}
} else if (args.length == 3 && args[0].startsWith("color:")) {
return Lists.newArrayList("shape:star", "shape:ball", "shape:large", "shape:creeper", "shape:burst");
} else if (args.length == 4 && args[0].startsWith("color:")) {
return Lists.newArrayList("effect:trail", "effect:twinkle", "effect:trail,twinkle", "effect:twinkle,trail");
} else {
return Collections.emptyList();
}
}
}
@@ -1,102 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.InventoryWorkaround;
import com.earth2me.essentials.utils.VersionUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandhat extends EssentialsCommand {
public Commandhat() {
super("hat");
}
/**
* The prefix for hat prevention commands
*/
private static final String PERM_PREFIX = "essentials.hat.prevent-type.";
/**
* Register permissions used by this command.
*
* @param toRegister The plugin manager to register permissions in.
*/
public static void registerPermissionsIfNecessary(PluginManager toRegister) {
Permission hatPerm = toRegister.getPermission(PERM_PREFIX + "*");
if (hatPerm != null) {
return;
}
ImmutableMap.Builder<String, Boolean> children = ImmutableMap.builder();
for (Material mat : Material.values()) {
final String matPerm = PERM_PREFIX + mat.name().toLowerCase();
children.put(matPerm, true);
toRegister.addPermission(new Permission(matPerm, "Prevent using " + mat + " as a type of hat.", PermissionDefault.FALSE));
}
toRegister.addPermission(new Permission(PERM_PREFIX + "*", "Prevent all types of hats", PermissionDefault.FALSE, children.build()));
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length > 0 && (args[0].contains("rem") || args[0].contains("off") || args[0].equalsIgnoreCase("0"))) {
final PlayerInventory inv = user.getBase().getInventory();
final ItemStack head = inv.getHelmet();
if (head == null || head.getType() == Material.AIR) {
user.sendMessage(tl("hatEmpty"));
} else if (VersionUtil.getServerBukkitVersion().isHigherThan(VersionUtil.v1_9_4_R01) && head.getEnchantments().containsKey(Enchantment.BINDING_CURSE) && !user.isAuthorized("essentials.hat.ignore-binding")) {
user.sendMessage(tl("hatCurse"));
} else {
final ItemStack air = new ItemStack(Material.AIR);
inv.setHelmet(air);
InventoryWorkaround.addItems(user.getBase().getInventory(), head);
user.sendMessage(tl("hatRemoved"));
}
} else {
final ItemStack hand = user.getItemInHand();
if (hand != null && hand.getType() != Material.AIR) {
if (user.isAuthorized("essentials.hat.prevent-type." + hand.getType().name().toLowerCase())) {
user.sendMessage(tl("hatFail"));
return;
}
if (hand.getType().getMaxDurability() == 0) {
final PlayerInventory inv = user.getBase().getInventory();
final ItemStack head = inv.getHelmet();
if (VersionUtil.getServerBukkitVersion().isHigherThan(VersionUtil.v1_9_4_R01) && head != null && head.getEnchantments().containsKey(Enchantment.BINDING_CURSE) && !user.isAuthorized("essentials.hat.ignore-binding")) {
user.sendMessage(tl("hatCurse"));
return;
}
inv.setHelmet(hand);
inv.setItemInHand(head);
user.sendMessage(tl("hatPlaced"));
} else {
user.sendMessage(tl("hatArmor"));
}
} else {
user.sendMessage(tl("hatFail"));
}
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
return Lists.newArrayList("remove", "wear"); // "wear" isn't real
} else {
return Collections.emptyList();
}
}
}
@@ -1,102 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.potion.PotionEffect;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandheal extends EssentialsLoopCommand {
public Commandheal() {
super("heal");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (!user.isAuthorized("essentials.heal.cooldown.bypass")) {
user.healCooldown();
}
if (args.length > 0 && user.isAuthorized("essentials.heal.others")) {
loopOnlinePlayers(server, user.getSource(), true, true, args[0], null);
return;
}
healPlayer(user);
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
loopOnlinePlayers(server, sender, true, true, args[0], null);
}
@Override
protected void updatePlayer(final Server server, final CommandSource sender, final User player, final String[] args) throws PlayerExemptException {
try {
healPlayer(player);
sender.sendMessage(tl("healOther", player.getDisplayName()));
} catch (QuietAbortException e) {
//Handle Quietly
}
}
private void healPlayer(final User user) throws PlayerExemptException, QuietAbortException {
final Player player = user.getBase();
if (player.getHealth() == 0) {
throw new PlayerExemptException(tl("healDead"));
}
final double amount = player.getMaxHealth() - player.getHealth();
final EntityRegainHealthEvent erhe = new EntityRegainHealthEvent(player, amount, RegainReason.CUSTOM);
ess.getServer().getPluginManager().callEvent(erhe);
if (erhe.isCancelled()) {
throw new QuietAbortException();
}
double newAmount = player.getHealth() + erhe.getAmount();
if (newAmount > player.getMaxHealth()) {
newAmount = player.getMaxHealth();
}
player.setHealth(newAmount);
player.setFoodLevel(20);
player.setFireTicks(0);
user.sendMessage(tl("heal"));
if (ess.getSettings().isRemovingEffectsOnHeal()) {
for (PotionEffect effect : player.getActivePotionEffects()) {
player.removePotionEffect(effect.getType());
}
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1 && user.isAuthorized("essentials.heal.others")) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,62 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.*;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commandhelp extends EssentialsCommand {
public Commandhelp() {
super("help");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
IText output;
String pageStr = args.length > 0 ? args[0] : null;
String chapterPageStr = args.length > 1 ? args[1] : null;
String command = commandLabel;
final IText input = new TextInput(user.getSource(), "help", false, ess);
if (input.getLines().isEmpty()) {
if (NumberUtil.isInt(pageStr) || pageStr == null) {
output = new HelpInput(user, "", ess);
} else {
if (pageStr.length() > 26) {
pageStr = pageStr.substring(0, 25);
}
output = new HelpInput(user, pageStr.toLowerCase(Locale.ENGLISH), ess);
command = command.concat(" ").concat(pageStr);
pageStr = chapterPageStr;
}
chapterPageStr = null;
} else {
user.setDisplayNick();
output = new KeywordReplacer(input, user.getSource(), ess);
}
final TextPager pager = new TextPager(output);
pager.showPage(pageStr, chapterPageStr, command, user.getSource());
}
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sender.sendMessage(tl("helpConsole"));
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getCommands(server);
} else {
return Collections.emptyList();
}
}
}
@@ -1,48 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Console;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Server;
import java.util.List;
import java.util.logging.Level;
import static com.earth2me.essentials.I18n.tl;
public class Commandhelpop extends EssentialsCommand {
public Commandhelpop() {
super("helpop");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
user.setDisplayNick();
final String message = sendMessage(server, user.getSource(), user.getDisplayName(), args);
if (!user.isAuthorized("essentials.helpop.receive")) {
user.sendMessage(message);
}
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sendMessage(server, sender, Console.NAME, args);
}
private String sendMessage(final Server server, final CommandSource sender, final String from, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
final String message = tl("helpOp", from, FormatUtil.stripFormat(getFinalArg(args, 0)));
server.getLogger().log(Level.INFO, message);
ess.broadcastMessage("essentials.helpop.receive", message);
return message;
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
return null; // Use vanilla handler for message
}
}
@@ -1,60 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
public class Commandignore extends EssentialsCommand {
public Commandignore() {
super("ignore");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
StringBuilder sb = new StringBuilder();
for (UUID uuid : user._getIgnoredPlayers()) {
User curUser = ess.getUser(uuid);
if (curUser != null && curUser.getName() != null && !curUser.getName().trim().isEmpty()) {
sb.append(curUser.getName()).append(" ");
}
}
String ignoredList = sb.toString().trim();
user.sendMessage(ignoredList.length() > 0 ? tl("ignoredList", ignoredList) : tl("noIgnored"));
} else {
User player;
try {
player = getPlayer(server, args, 0, true, true);
} catch (PlayerNotFoundException ex) {
player = ess.getOfflineUser(args[0]);
}
if (player == null) {
throw new PlayerNotFoundException();
}
if (player.isIgnoreExempt()) {
user.sendMessage(tl("ignoreExempt"));
} else if (user.isIgnoredPlayer(player)) {
user.setIgnoredPlayer(player, false);
user.sendMessage(tl("unignorePlayer", player.getName()));
} else {
user.setIgnoredPlayer(player, true);
user.sendMessage(tl("ignorePlayer", player.getName()));
}
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
}
@@ -1,70 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.utils.VersionUtil;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commanditemdb extends EssentialsCommand {
public Commanditemdb() {
super("itemdb");
}
@Override
protected void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception {
ItemStack itemStack = null;
boolean itemHeld = false;
if (args.length < 1) {
if (sender.isPlayer() && sender.getPlayer() != null) {
itemHeld = true;
itemStack = ess.getUser(sender.getPlayer()).getItemInHand();
}
if (itemStack == null) {
throw new NotEnoughArgumentsException();
}
} else {
itemStack = ess.getItemDb().get(args[0]);
}
String itemId = "none";
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01)) {
itemId = itemStack.getType().getId() + ":" + itemStack.getDurability();
}
sender.sendMessage(tl("itemType", itemStack.getType().toString(), itemId));
// Don't send IDs twice
if (!tl("itemType").contains("{1}") && !itemId.equals("none")) {
sender.sendMessage(tl("itemId", itemId));
}
if (itemHeld && itemStack.getType() != Material.AIR) {
int maxuses = itemStack.getType().getMaxDurability();
int durability = ((maxuses + 1) - itemStack.getDurability());
if (maxuses != 0) {
sender.sendMessage(tl("durability", Integer.toString(durability)));
}
}
final String itemNameList = ess.getItemDb().names(itemStack);
if (itemNameList != null) {
sender.sendMessage(tl("itemNames", ess.getItemDb().names(itemStack)));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getItems();
} else {
return Collections.emptyList();
}
}
}
@@ -1,75 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commanditemlore extends EssentialsCommand {
public Commanditemlore() {
super("itemlore");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
ItemStack item = user.getBase().getItemInHand();
if (item.getType().name().contains("AIR")) {
throw new Exception(tl("itemloreInvalidItem"));
}
if (args.length == 0) {
throw new NotEnoughArgumentsException();
}
ItemMeta im = item.getItemMeta();
if (args[0].equalsIgnoreCase("add") && args.length > 1) {
String line = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 1)).trim();
List<String> lore = im.hasLore() ? im.getLore() : new ArrayList<>();
lore.add(line);
im.setLore(lore);
item.setItemMeta(im);
user.sendMessage(tl("itemloreSuccess", line));
} else if (args[0].equalsIgnoreCase("clear")) {
im.setLore(new ArrayList<>());
item.setItemMeta(im);
user.sendMessage(tl("itemloreClear"));
} else if (args[0].equalsIgnoreCase("set") && args.length > 2 && NumberUtil.isInt(args[1])) {
if (!im.hasLore()) {
throw new Exception(tl("itemloreNoLore"));
}
int line = Integer.parseInt(args[1]);
String newLine = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 2)).trim();
List<String> lore = im.getLore();
try {
lore.set(line - 1, newLine);
} catch (Exception e) {
throw new Exception(tl("itemloreNoLine", line), e);
}
im.setLore(lore);
item.setItemMeta(im);
user.sendMessage(tl("itemloreSuccessLore", line, newLine));
} else {
throw new NotEnoughArgumentsException();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return Lists.newArrayList("add", "set", "clear");
} else {
return Collections.emptyList();
}
}
}
@@ -1,35 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commanditemname extends EssentialsCommand {
public Commanditemname() {
super("itemname");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
ItemStack item = user.getBase().getItemInHand();
if (item.getType().name().contains("AIR")) {
user.sendMessage(tl("itemnameInvalidItem", item.getType().toString().toLowerCase(Locale.ENGLISH).replace('_', ' ')));
return;
}
String name = FormatUtil.formatString(user, "essentials.itemname", getFinalArg(args, 0)).trim();
if (name.isEmpty()) name = null;
ItemMeta im = item.getItemMeta();
im.setDisplayName(name);
item.setItemMeta(im);
user.sendMessage(name == null ? tl("itemnameClear") : tl("itemnameSuccess", name));
}
}
@@ -1,128 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.PlayerList;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class Commandlist extends EssentialsCommand {
public Commandlist() {
super("list");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
boolean showHidden = true;
User user = null;
if (sender.isPlayer()) {
user = ess.getUser(sender.getPlayer());
showHidden = user.isAuthorized("essentials.list.hidden") || user.canInteractVanished();
}
sender.sendMessage(PlayerList.listSummary(ess, user, showHidden));
final Map<String, List<User>> playerList = PlayerList.getPlayerLists(ess, user, showHidden);
if (args.length > 0) {
sender.sendMessage(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase()));
} else {
sendGroupedList(sender, commandLabel, playerList);
}
}
// Output the standard /list output, when no group is specified
private void sendGroupedList(CommandSource sender, String commandLabel, Map<String, List<User>> playerList) {
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
final List<String> asterisk = new ArrayList<>();
// Loop through the custom defined groups and display them
for (String oConfigGroup : configGroups) {
String groupValue = ess.getSettings().getListGroupConfig().get(oConfigGroup).toString().trim();
String configGroup = oConfigGroup.toLowerCase();
// If the group value is an asterisk, then skip it, and handle it later
if (groupValue.equals("*")) {
asterisk.add(oConfigGroup);
continue;
}
// If the group value is hidden, we don't need to display it
if (groupValue.equalsIgnoreCase("hidden")) {
playerList.remove(configGroup);
continue;
}
List<User> outputUserList;
final List<User> matchedList = playerList.get(configGroup);
// If the group value is an int, then we might need to truncate it
if (NumberUtil.isInt(groupValue)) {
if (matchedList != null && !matchedList.isEmpty()) {
playerList.remove(configGroup);
outputUserList = new ArrayList<>(matchedList);
int limit = Integer.parseInt(groupValue);
if (matchedList.size() > limit) {
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, tl("groupNumber", matchedList.size(), commandLabel, FormatUtil.stripFormat(configGroup))));
} else {
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
}
continue;
}
}
outputUserList = PlayerList.getMergedList(ess, playerList, configGroup);
// If we have no users, than we don't need to continue parsing this group
if (outputUserList == null || outputUserList.isEmpty()) {
continue;
}
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
}
Set<String> var = playerList.keySet();
String[] onlineGroups = var.toArray(new String[0]);
Arrays.sort(onlineGroups, String.CASE_INSENSITIVE_ORDER);
// If we have an asterisk group, then merge all remaining groups
if (!asterisk.isEmpty()) {
List<User> asteriskUsers = new ArrayList<>();
for (String onlineGroup : onlineGroups) {
asteriskUsers.addAll(playerList.get(onlineGroup));
}
for (String key : asterisk) {
playerList.put(key, asteriskUsers);
}
onlineGroups = asterisk.toArray(new String[0]);
}
// If we have any groups remaining after the custom groups loop through and display them
for (String onlineGroup : onlineGroups) {
List<User> users = playerList.get(onlineGroup);
String groupName = asterisk.isEmpty() ? users.get(0).getGroup() : onlineGroup;
if (ess.getPermissionsHandler().getName().equals("ConfigPermissions")) {
groupName = tl("connectedPlayers");
}
if (users == null || users.isEmpty()) {
continue;
}
sender.sendMessage(PlayerList.outputFormat(groupName, PlayerList.listUsers(ess, users, ", ")));
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return getGroups();
} else {
return Collections.emptyList();
}
}
}
@@ -1,201 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
public class Commandmail extends EssentialsCommand {
private static int mailsPerMinute = 0;
private static long timestamp = 0;
public Commandmail() {
super("mail");
}
//TODO: Tidy this up / TL these errors.
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
final List<String> mail = user.getMails();
if (mail.isEmpty()) {
user.sendMessage(tl("noMail"));
throw new NoChargeException();
}
IText input = new SimpleTextInput(mail);
final TextPager pager = new TextPager(input);
pager.showPage(args.length > 1 ? args[1] : null, null, commandLabel + " " + args[0], user.getSource());
user.sendMessage(tl("mailClear"));
return;
}
if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
if (!user.isAuthorized("essentials.mail.send")) {
throw new Exception(tl("noPerm", "essentials.mail.send"));
}
if (user.isMuted()) {
String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
if (dateDiff == null) {
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
}
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
}
User u = getPlayer(server, args[1], true, true);
if (u == null) {
throw new Exception(tl("playerNeverOnServer", args[1]));
}
String mail = tl("mailFormat", user.getName(), FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 2)))));
if (mail.length() > 1000) {
throw new Exception(tl("mailTooLong"));
}
if (!u.isIgnoredPlayer(user)) {
if (Math.abs(System.currentTimeMillis() - timestamp) > 60000) {
timestamp = System.currentTimeMillis();
mailsPerMinute = 0;
}
mailsPerMinute++;
if (mailsPerMinute > ess.getSettings().getMailsPerMinute()) {
throw new Exception(tl("mailDelay", ess.getSettings().getMailsPerMinute()));
}
u.addMail(tl("mailMessage", mail));
}
user.sendMessage(tl("mailSentTo", u.getDisplayName(), u.getName()));
user.sendMessage(mail);
return;
}
if (args.length > 1 && "sendall".equalsIgnoreCase(args[0])) {
if (!user.isAuthorized("essentials.mail.sendall")) {
throw new Exception(tl("noPerm", "essentials.mail.sendall"));
}
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", user.getName(),
FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 1)))))));
user.sendMessage(tl("mailSent"));
return;
}
if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
if (user.getMails() == null || user.getMails().isEmpty()) {
user.sendMessage(tl("noMail"));
throw new NoChargeException();
}
user.setMails(null);
user.sendMessage(tl("mailCleared"));
return;
}
throw new NotEnoughArgumentsException();
}
@Override
protected void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception {
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
throw new Exception(tl("onlyPlayers", commandLabel + " read"));
} else if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
throw new Exception(tl("onlyPlayers", commandLabel + " clear"));
} else if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
User u = getPlayer(server, args[1], true, true);
if (u == null) {
throw new Exception(tl("playerNeverOnServer", args[1]));
}
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 2))));
sender.sendMessage(tl("mailSent"));
return;
} else if (args.length >= 2 && "sendall".equalsIgnoreCase(args[0])) {
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1)))));
sender.sendMessage(tl("mailSent"));
return;
} else if (args.length >= 2) {
//allow sending from console without "send" argument, since it's the only thing the console can do
User u = getPlayer(server, args[0], true, true);
if (u == null) {
throw new Exception(tl("playerNeverOnServer", args[0]));
}
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1))));
sender.sendMessage(tl("mailSent"));
return;
}
throw new NotEnoughArgumentsException();
}
private class SendAll implements Runnable {
String message;
public SendAll(String message) {
this.message = message;
}
@Override
public void run() {
for (UUID userid : ess.getUserMap().getAllUniqueUsers()) {
User user = ess.getUserMap().getUser(userid);
if (user != null) {
user.addMail(message);
}
}
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList("read", "clear");
if (user.isAuthorized("essentials.mail.send")) {
options.add("send");
}
if (user.isAuthorized("essentials.mail.sendall")) {
options.add("sendall");
}
return options;
} else if (args.length == 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) {
return getPlayers(server, user);
} else if (args.length == 2 && args[0].equalsIgnoreCase("read")) {
final List<String> mail = user.getMails();
int pages = mail.size() / 9 + (mail.size() % 9 > 0 ? 1 : 0);
if (pages == 0) {
return Lists.newArrayList("0");
} else {
List<String> options = Lists.newArrayList("1");
if (pages > 1) {
options.add(String.valueOf(pages));
}
return options;
}
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall") && user.isAuthorized("essentials.mail.sendall"))) {
return null; // Use vanilla handler
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return Lists.newArrayList("send", "sendall");
} else if (args.length == 2 && args[0].equalsIgnoreCase("send")) {
return getPlayers(server, sender);
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall"))) {
return null; // Use vanilla handler
} else {
return Collections.emptyList();
}
}
}
@@ -1,106 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.earth2me.essentials.I18n.tl;
public class Commandme extends EssentialsCommand {
public Commandme() {
super("me");
}
@Override
public void run(Server server, User user, String commandLabel, String[] args) throws Exception {
if (user.isMuted()) {
String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
if (dateDiff == null) {
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
}
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
}
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
String message = getFinalArg(args, 0);
message = FormatUtil.formatMessage(user, "essentials.chat", message);
user.setDisplayNick();
int radius = ess.getSettings().getChatRadius();
String toSend = tl("action", user.getDisplayName(), message);
if (radius < 1) {
ess.broadcastMessage(user, toSend);
return;
}
World world = user.getWorld();
Location loc = user.getLocation();
Set<Player> outList = new HashSet<>();
for (Player player : Bukkit.getOnlinePlayers()) {
final User onlineUser = ess.getUser(player);
if (!onlineUser.equals(user)) {
boolean abort = false;
final Location playerLoc = onlineUser.getLocation();
if (playerLoc.getWorld() != world) {
abort = true;
} else if (onlineUser.isIgnoredPlayer(user)) {
abort = true;
} else {
final double delta = playerLoc.distanceSquared(loc);
if (delta > radius) {
abort = true;
}
}
if (abort) {
if (onlineUser.isAuthorized("essentials.chat.spy")) {
outList.add(player); // Just use the same list unless we wanted to format spyying for this.
}
} else {
outList.add(player);
}
} else {
outList.add(player); // Add yourself to the list.
}
}
if (outList.size() < 2) {
user.sendMessage(tl("localNoOne"));
}
for (Player onlinePlayer : outList) {
onlinePlayer.sendMessage(toSend);
}
}
@Override
public void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
String message = getFinalArg(args, 0);
message = FormatUtil.replaceFormat(message);
ess.getServer().broadcastMessage(tl("action", "@", message));
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
return null; // It's a chat message, use the default chat handler
}
}
@@ -1,61 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandnuke extends EssentialsCommand {
public Commandnuke() {
super("nuke");
}
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws NoSuchFieldException, NotEnoughArgumentsException {
Collection<Player> targets;
if (args.length > 0) {
targets = new ArrayList<>();
int pos = 0;
for (String arg : args) {
targets.add(getPlayer(server, sender, args, pos).getBase());
pos++;
}
} else {
targets = ess.getOnlinePlayers();
}
ess.getTNTListener().enable();
for (Player player : targets) {
if (player == null) {
continue;
}
player.sendMessage(tl("nuke"));
final Location loc = player.getLocation();
final World world = loc.getWorld();
for (int x = -10; x <= 10; x += 5) {
for (int z = -10; z <= 10; z += 5) {
final Location tntloc = new Location(world, loc.getBlockX() + x, world.getHighestBlockYAt(loc) + 64, loc.getBlockZ() + z);
final TNTPrimed tnt = world.spawn(tntloc, TNTPrimed.class);
}
}
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,102 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.ChargeException;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.collect.Lists;
import net.ess3.api.MaxMoneyException;
import net.ess3.api.events.UserBalanceUpdateEvent;
import org.bukkit.Server;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandpay extends EssentialsLoopCommand {
BigDecimal amount;
boolean informToConfirm;
public Commandpay() {
super("pay");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
informToConfirm = false;
if (args.length < 2) {
throw new NotEnoughArgumentsException();
}
if (args[1].contains("-")) {
throw new Exception(tl("payMustBePositive"));
}
String stringAmount = args[1].replaceAll("[^0-9\\.]", "");
if (stringAmount.length() < 1) {
throw new NotEnoughArgumentsException();
}
amount = new BigDecimal(stringAmount);
if (amount.compareTo(ess.getSettings().getMinimumPayAmount()) < 0) { // Check if amount is less than minimum-pay-amount
throw new Exception(tl("minimumPayAmount", NumberUtil.displayCurrencyExactly(ess.getSettings().getMinimumPayAmount(), ess)));
}
loopOnlinePlayers(server, user.getSource(), false, user.isAuthorized("essentials.pay.multiple"), args[0], args);
if (informToConfirm) {
String cmd = "/" + commandLabel + " " + StringUtil.joinList(" ", args);
user.sendMessage(tl("confirmPayment", NumberUtil.displayCurrency(amount, ess), cmd));
}
}
@Override
protected void updatePlayer(final Server server, final CommandSource sender, final User player, final String[] args) throws ChargeException {
User user = ess.getUser(sender.getPlayer());
try {
if (!player.isAcceptingPay() || (ess.getSettings().isPayExcludesIgnoreList() && player.isIgnoredPlayer(user))) {
sender.sendMessage(tl("notAcceptingPay", player.getDisplayName()));
return;
}
if (user.isPromptingPayConfirm() && !amount.equals(user.getConfirmingPayments().get(player))) { // checks if exists and if command needs to be repeated.
// Used to reset confirmations and inform to confirm when a new pay command has been inserted.
if (!informToConfirm) {
// User hasnt been asked to confirm payment to this player, reset all confirmed payments and ask to confirm again.
// Clear previous confirmations to ensure that a new confirmation message is brought up.
user.getConfirmingPayments().clear();
this.informToConfirm = true;
}
user.getConfirmingPayments().put(player, amount);
return;
}
user.payUser(player, amount, UserBalanceUpdateEvent.Cause.COMMAND_PAY);
user.getConfirmingPayments().remove(player);
Trade.log("Command", "Pay", "Player", user.getName(), new Trade(amount, ess), player.getName(), new Trade(amount, ess), user.getLocation(), ess);
} catch (MaxMoneyException ex) {
sender.sendMessage(tl("maxMoney"));
try {
user.setMoney(user.getMoney().add(amount));
} catch (MaxMoneyException ignored) {
// this should never happen
}
} catch (Exception e) {
sender.sendMessage(e.getMessage());
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else if (args.length == 2) {
return Lists.newArrayList(ess.getSettings().getMinimumPayAmount().toString());
} else {
return Collections.emptyList();
}
}
}
@@ -1,30 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandpaytoggle extends EssentialsCommand {
public Commandpaytoggle() {
super("paytoggle");
}
@Override
public void run(Server server, User user, String commandLabel, String[] args) throws Exception {
boolean acceptingPay = !user.isAcceptingPay();
if (commandLabel.contains("payon")) {
acceptingPay = true;
} else if (commandLabel.contains("payoff")) {
acceptingPay = false;
}
user.setAcceptingPay(acceptingPay);
if (acceptingPay) {
user.sendMessage(tl("payToggleOn"));
} else {
user.sendMessage(tl("payToggleOff"));
}
}
}
@@ -1,209 +0,0 @@
package com.earth2me.essentials.commands;
import com.google.common.collect.Lists;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DescParseTickFormat;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class Commandptime extends EssentialsCommand {
private static final Set<String> getAliases = new HashSet<>();
static {
getAliases.add("get");
getAliases.add("list");
getAliases.add("show");
getAliases.add("display");
}
public Commandptime() {
super("ptime");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
// Which Players(s) / Users(s) are we interested in?
String userSelector = null;
if (args.length == 2) {
userSelector = args[1];
}
Set<User> users = getUsers(server, sender, userSelector);
// If no arguments we are reading the time
if (args.length == 0) {
getUsersTime(sender, users);
return;
}
if (sender.isPlayer()) {
User user = ess.getUser(sender.getPlayer());
if (user != null && (!users.contains(user) || users.size() > 1) && !user.isAuthorized("essentials.ptime.others")) {
user.sendMessage(tl("pTimeOthersPermission"));
return;
}
}
Long ticks;
// Parse the target time int ticks from args[0]
String timeParam = args[0];
boolean relative = true;
if (timeParam.startsWith("@")) {
relative = false;
timeParam = timeParam.substring(1);
}
if (getAliases.contains(timeParam)) {
getUsersTime(sender, users);
return;
} else if (DescParseTickFormat.meansReset(timeParam)) {
ticks = null;
} else {
try {
ticks = DescParseTickFormat.parse(timeParam);
} catch (NumberFormatException e) {
throw new NotEnoughArgumentsException(e);
}
}
setUsersTime(sender, users, ticks, relative);
}
/**
* Used to get the time and inform
*/
private void getUsersTime(final CommandSource sender, final Collection<User> users) {
if (users.size() > 1) {
sender.sendMessage(tl("pTimePlayers"));
}
for (User user : users) {
if (user.getBase().getPlayerTimeOffset() == 0) {
sender.sendMessage(tl("pTimeNormal", user.getName()));
} else {
String time = DescParseTickFormat.format(user.getBase().getPlayerTime());
if (!user.getBase().isPlayerTimeRelative()) {
sender.sendMessage(tl("pTimeCurrentFixed", user.getName(), time));
} else {
sender.sendMessage(tl("pTimeCurrent", user.getName(), time));
}
}
}
}
/**
* Used to set the time and inform of the change
*/
private void setUsersTime(final CommandSource sender, final Collection<User> users, final Long ticks, Boolean relative) {
// Update the time
if (ticks == null) {
// Reset
for (User user : users) {
user.getBase().resetPlayerTime();
}
} else {
// Set
for (User user : users) {
final World world = user.getWorld();
long time = user.getBase().getPlayerTime();
time -= time % 24000;
time += 24000 + ticks;
if (relative) {
time -= world.getTime();
}
user.getBase().setPlayerTime(time, relative);
}
}
final StringBuilder msg = new StringBuilder();
for (User user : users) {
if (msg.length() > 0) {
msg.append(", ");
}
msg.append(user.getName());
}
// Inform the sender of the change
if (ticks == null) {
sender.sendMessage(tl("pTimeReset", msg.toString()));
} else {
String time = DescParseTickFormat.format(ticks);
if (!relative) {
sender.sendMessage(tl("pTimeSetFixed", time, msg.toString()));
} else {
sender.sendMessage(tl("pTimeSet", time, msg.toString()));
}
}
}
/**
* Used to parse an argument of the type "users(s) selector"
*/
private Set<User> getUsers(final Server server, final CommandSource sender, final String selector) throws Exception {
final Set<User> users = new TreeSet<>(new UserNameComparator());
// If there is no selector we want the sender itself. Or all users if sender isn't a user.
if (selector == null) {
if (sender.isPlayer()) {
final User user = ess.getUser(sender.getPlayer());
users.add(user);
} else {
for (User user : ess.getOnlineUsers()) {
users.add(user);
}
}
return users;
}
// Try to find the user with name = selector
User user = null;
final List<Player> matchedPlayers = server.matchPlayer(selector);
if (!matchedPlayers.isEmpty()) {
user = ess.getUser(matchedPlayers.get(0));
}
if (user != null) {
users.add(user);
}
// If that fails, Is the argument something like "*" or "all"?
else if (selector.equalsIgnoreCase("*") || selector.equalsIgnoreCase("all")) {
for (User u : ess.getOnlineUsers()) {
users.add(u);
}
}
// We failed to understand the world target...
else {
throw new PlayerNotFoundException();
}
return users;
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
final User user = ess.getUser(sender.getPlayer());
if (args.length == 1) {
return Lists.newArrayList("get", "reset", "sunrise", "day", "morning", "noon", "afternoon", "sunset", "night", "midnight");
} else if (args.length == 2 && (getAliases.contains(args[0]) || user == null || user.isAuthorized("essentials.ptime.others"))) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
class UserNameComparator implements Comparator<User> {
@Override
public int compare(User a, User b) {
return a.getName().compareTo(b.getName());
}
}
@@ -1,166 +0,0 @@
package com.earth2me.essentials.commands;
import com.google.common.collect.Lists;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import org.bukkit.WeatherType;
import org.bukkit.entity.Player;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class Commandpweather extends EssentialsCommand {
public static final Set<String> getAliases = new HashSet<>();
public static final Map<String, WeatherType> weatherAliases = new HashMap<>();
static {
getAliases.add("get");
getAliases.add("list");
getAliases.add("show");
getAliases.add("display");
weatherAliases.put("sun", WeatherType.CLEAR);
weatherAliases.put("clear", WeatherType.CLEAR);
weatherAliases.put("storm", WeatherType.DOWNFALL);
weatherAliases.put("thunder", WeatherType.DOWNFALL);
}
public Commandpweather() {
super("pweather");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
// Which Players(s) / Users(s) are we interested in?
String userSelector = null;
if (args.length == 2) {
userSelector = args[1];
}
Set<User> users = getUsers(server, sender, userSelector);
if (args.length == 0) {
getUsersWeather(sender, users);
return;
}
if (getAliases.contains(args[0])) {
getUsersWeather(sender, users);
return;
}
if (sender.isPlayer()) {
User user = ess.getUser(sender.getPlayer());
if (user != null && (!users.contains(user) || users.size() > 1) && !user.isAuthorized("essentials.pweather.others")) {
user.sendMessage(tl("pWeatherOthersPermission"));
return;
}
}
setUsersWeather(sender, users, args[0].toLowerCase());
}
/**
* Used to get the time and inform
*/
private void getUsersWeather(final CommandSource sender, final Collection<User> users) {
if (users.size() > 1) {
sender.sendMessage(tl("pWeatherPlayers"));
}
for (User user : users) {
if (user.getBase().getPlayerWeather() == null) {
sender.sendMessage(tl("pWeatherNormal", user.getName()));
} else {
sender.sendMessage(tl("pWeatherCurrent", user.getName(), user.getBase().getPlayerWeather().toString().toLowerCase(Locale.ENGLISH)));
}
}
}
/**
* Used to set the time and inform of the change
*/
private void setUsersWeather(final CommandSource sender, final Collection<User> users, final String weatherType) throws Exception {
final StringBuilder msg = new StringBuilder();
for (User user : users) {
if (msg.length() > 0) {
msg.append(", ");
}
msg.append(user.getName());
}
if (weatherType.equalsIgnoreCase("reset")) {
for (User user : users) {
user.getBase().resetPlayerWeather();
}
sender.sendMessage(tl("pWeatherReset", msg));
} else {
if (!weatherAliases.containsKey(weatherType)) {
throw new NotEnoughArgumentsException(tl("pWeatherInvalidAlias"));
}
for (User user : users) {
user.getBase().setPlayerWeather(weatherAliases.get(weatherType));
}
sender.sendMessage(tl("pWeatherSet", weatherType, msg.toString()));
}
}
/**
* Used to parse an argument of the type "users(s) selector"
*/
private Set<User> getUsers(final Server server, final CommandSource sender, final String selector) throws Exception {
final Set<User> users = new TreeSet<>(new UserNameComparator());
// If there is no selector we want the sender itself. Or all users if sender isn't a user.
if (selector == null) {
if (sender.isPlayer()) {
final User user = ess.getUser(sender.getPlayer());
users.add(user);
} else {
for (User user : ess.getOnlineUsers()) {
users.add(user);
}
}
return users;
}
// Try to find the user with name = selector
User user = null;
final List<Player> matchedPlayers = server.matchPlayer(selector);
if (!matchedPlayers.isEmpty()) {
user = ess.getUser(matchedPlayers.get(0));
}
if (user != null) {
users.add(user);
}
// If that fails, Is the argument something like "*" or "all"?
else if (selector.equalsIgnoreCase("*") || selector.equalsIgnoreCase("all")) {
for (User u : ess.getOnlineUsers()) {
users.add(u);
}
}
// We failed to understand the world target...
else {
throw new PlayerNotFoundException();
}
return users;
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return Lists.newArrayList("get", "reset", "storm", "sun");
} else if (args.length == 2 && (getAliases.contains(args[0]) || user == null || user.isAuthorized("essentials.pweather.others"))) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
}
@@ -1,72 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.VersionUtil;
import org.bukkit.Server;
import org.bukkit.Statistic;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandrest extends EssentialsLoopCommand {
public Commandrest() {
super("rest");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01)) {
user.sendMessage(tl("unsupportedFeature"));
return;
}
if (args.length > 0 && user.isAuthorized("essentials.rest.others")) {
loopOnlinePlayers(server, user.getSource(), true, true, args[0], null);
return;
}
restPlayer(user);
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01)) {
sender.sendMessage(tl("unsupportedFeature"));
return;
}
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
loopOnlinePlayers(server, sender, true, true, args[0], null);
}
@Override
protected void updatePlayer(final Server server, final CommandSource sender, final User player, final String[] args) throws PlayerExemptException {
restPlayer(player);
sender.sendMessage(tl("restOther", player.getDisplayName()));
}
private void restPlayer(final User user) {
user.getBase().setStatistic(Statistic.TIME_SINCE_REST, 0);
user.sendMessage(tl("rest"));
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1 && user.isAuthorized("essentials.rest.others")) {
return getPlayers(server, user);
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,236 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.UserMap;
import com.earth2me.essentials.craftbukkit.BanLookup;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.StringUtil;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.Location;
import org.bukkit.Server;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.Collections;
import static com.earth2me.essentials.I18n.tl;
public class Commandseen extends EssentialsCommand {
public Commandseen() {
super("seen");
}
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
seen(server, sender, commandLabel, args, true, true, true, true);
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
seen(server, user.getSource(), commandLabel, args, user.isAuthorized("essentials.seen.banreason"), user.isAuthorized("essentials.seen.ip"), user.isAuthorized("essentials.seen.location"), user.isAuthorized("essentials.seen.ipsearch"));
}
protected void seen(final Server server, final CommandSource sender, final String commandLabel, final String[] args,
final boolean showBan, final boolean showIp, final boolean showLocation, final boolean ipLookup) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
User player;
// Check by uuid, if it fails check by name.
try {
UUID uuid = UUID.fromString(args[0]);
player = ess.getUser(uuid);
}catch (IllegalArgumentException ignored) { // Thrown if invalid UUID from string, check by name.
player = ess.getOfflineUser(args[0]);
}
if (player == null) {
if (ipLookup && FormatUtil.validIP(args[0])) {
seenIP(server, sender, args[0]);
return;
} else if (ess.getServer().getBanList(BanList.Type.IP).isBanned(args[0])) {
sender.sendMessage(tl("isIpBanned", args[0]));
return;
} else if (BanLookup.isBanned(ess, args[0])) {
sender.sendMessage(tl("whoisBanned", showBan ? BanLookup.getBanEntry(ess, args[0]).getReason() : tl("true")));
return;
}
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
@Override
public void run() {
User userFromBukkit = ess.getUserMap().getUserFromBukkit(args[0]);
try {
if (userFromBukkit != null) {
showUserSeen(userFromBukkit);
} else {
showUserSeen(getPlayer(server, sender, args, 0));
}
} catch (Exception e) {
ess.showError(sender, e, commandLabel);
}
}
private void showUserSeen(User user) throws Exception {
if (user == null) {
throw new PlayerNotFoundException();
}
showSeenMessage(server, sender, user, showBan, showIp, showLocation);
}
});
} else {
showSeenMessage(server, sender, player, showBan, showIp, showLocation);
}
}
private void showSeenMessage(Server server, CommandSource sender, User player, boolean showBan, boolean showIp, boolean showLocation) throws Exception {
if (player.getBase().isOnline() && canInteractWith(sender, player)) {
seenOnline(server, sender, player, showBan, showIp, showLocation);
} else {
seenOffline(server, sender, player, showBan, showIp, showLocation);
}
}
private void seenOnline(final Server server, final CommandSource sender, final User user, final boolean showBan, final boolean showIp, final boolean showLocation) throws Exception {
user.setDisplayNick();
sender.sendMessage(tl("seenOnline", user.getDisplayName(), DateUtil.formatDateDiff(user.getLastLogin())));
if (ess.getSettings().isDebug()) {
ess.getLogger().info("UUID: " + user.getBase().getUniqueId().toString());
}
List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
if (history != null && history.size() > 1) {
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
}
if (user.isAfk()) {
sender.sendMessage(tl("whoisAFK", tl("true")));
}
if (user.isJailed()) {
sender.sendMessage(tl("whoisJail", (user.getJailTimeout() > 0 ? DateUtil.formatDateDiff(user.getJailTimeout()) : tl("true"))));
}
if (user.isMuted()) {
long muteTimeout = user.getMuteTimeout();
if (!user.hasMuteReason()) {
sender.sendMessage(tl("whoisMuted", (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true"))));
} else {
sender.sendMessage(tl("whoisMutedReason", (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")), user.getMuteReason()));
}
}
final String location = user.getGeoLocation();
if (location != null && (!(sender.isPlayer()) || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
sender.sendMessage(tl("whoisGeoLocation", location));
}
if (showIp) {
sender.sendMessage(tl("whoisIPAddress", user.getBase().getAddress().getAddress().toString()));
}
}
private void seenOffline(final Server server, final CommandSource sender, User user, final boolean showBan, final boolean showIp, final boolean showLocation) throws Exception {
user.setDisplayNick();
if (user.getLastLogout() > 0) {
sender.sendMessage(tl("seenOffline", user.getName(), DateUtil.formatDateDiff(user.getLastLogout())));
} else {
sender.sendMessage(tl("userUnknown", user.getName()));
}
if (ess.getSettings().isDebug()) {
ess.getLogger().info("UUID: " + user.getBase().getUniqueId().toString());
}
List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
if (history != null && history.size() > 1) {
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
}
if (BanLookup.isBanned(ess, user)) {
final BanEntry banEntry = BanLookup.getBanEntry(ess, user.getName());
final String reason = showBan ? banEntry.getReason() : tl("true");
sender.sendMessage(tl("whoisBanned", reason));
if (banEntry.getExpiration() != null) {
Date expiry = banEntry.getExpiration();
String expireString = tl("now");
if (expiry.after(new Date())) {
expireString = DateUtil.formatDateDiff(expiry.getTime());
}
sender.sendMessage(tl("whoisTempBanned", expireString));
}
}
if (user.isMuted()) {
long muteTimeout = user.getMuteTimeout();
if (!user.hasMuteReason()) {
sender.sendMessage(tl("whoisMuted", (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true"))));
} else {
sender.sendMessage(tl("whoisMutedReason", (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")), user.getMuteReason()));
}
}
final String location = user.getGeoLocation();
if (location != null && (!(sender.isPlayer()) || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
sender.sendMessage(tl("whoisGeoLocation", location));
}
if (showIp) {
if (!user.getLastLoginAddress().isEmpty()) {
sender.sendMessage(tl("whoisIPAddress", user.getLastLoginAddress()));
}
}
if (showLocation) {
final Location loc = user.getLogoutLocation();
if (loc != null) {
sender.sendMessage(tl("whoisLocation", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
}
}
private void seenIP(final Server server, final CommandSource sender, final String ipAddress) throws Exception {
final UserMap userMap = ess.getUserMap();
if (ess.getServer().getBanList(BanList.Type.IP).isBanned(ipAddress)) {
sender.sendMessage(tl("isIpBanned", ipAddress));
}
sender.sendMessage(tl("runningPlayerMatch", ipAddress));
ess.runTaskAsynchronously(() -> {
final List<String> matches = new ArrayList<>();
for (final UUID u : userMap.getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user == null) {
continue;
}
final String uIPAddress = user.getLastLoginAddress();
if (!uIPAddress.isEmpty() && uIPAddress.equalsIgnoreCase(ipAddress)) {
matches.add(user.getName());
}
}
if (matches.size() > 0) {
sender.sendMessage(tl("matchingIPAddress"));
sender.sendMessage(StringUtil.joinList(matches));
} else {
sender.sendMessage(tl("noMatchingPlayers"));
}
});
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,43 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.RandomTeleport;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandsettpr extends EssentialsCommand {
public Commandsettpr() {
super("settpr");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
RandomTeleport randomTeleport = ess.getRandomTeleport();
randomTeleport.getCachedLocations().clear();
if (args.length == 0 || "center".equalsIgnoreCase(args[0])) {
randomTeleport.setCenter(user.getLocation());
user.sendMessage(tl("settpr"));
} else if (args.length > 1) {
if ("minrange".equalsIgnoreCase(args[0])) {
randomTeleport.setMinRange(Double.parseDouble(args[1]));
} else if ("maxrange".equalsIgnoreCase(args[0])) {
randomTeleport.setMaxRange(Double.parseDouble(args[1]));
}
user.sendMessage(tl("settprValue", args[0].toLowerCase(), args[1].toLowerCase()));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return Arrays.asList("center", "minrange", "maxrange");
}
return Collections.emptyList();
}
}
@@ -1,45 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.api.IWarps;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.StringUtil;
import net.ess3.api.InvalidWorldException;
import org.bukkit.Location;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandsetwarp extends EssentialsCommand {
public Commandsetwarp() {
super("setwarp");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
if (NumberUtil.isInt(args[0]) || args[0].isEmpty()) {
throw new NoSuchFieldException(tl("invalidWarpName"));
}
final Location loc = user.getLocation();
final IWarps warps = ess.getWarps();
Location warpLoc = null;
try {
warpLoc = warps.getWarp(args[0]);
} catch (WarpNotFoundException | InvalidWorldException ignored) {
}
if (warpLoc == null || user.isAuthorized("essentials.warp.overwrite." + StringUtil.safeString(args[0]))) {
warps.setWarp(user, args[0], loc);
} else {
throw new Exception(tl("warpOverwrite"));
}
user.sendMessage(tl("warpSet", args[0]));
}
}
@@ -1,83 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.InventoryWorkaround;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.google.common.collect.Lists;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandskull extends EssentialsCommand {
private static final Material SKULL_ITEM = EnumUtil.getMaterial("PLAYER_HEAD", "SKULL_ITEM");
public Commandskull() {
super("skull");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
String owner;
if (args.length > 0 && user.isAuthorized("essentials.skull.others")) {
if (!args[0].matches("^[A-Za-z0-9_]+$")) {
throw new IllegalArgumentException(tl("alphaNames"));
}
owner = args[0];
} else {
owner = user.getName();
}
ItemStack itemSkull = user.getItemInHand();
SkullMeta metaSkull = null;
boolean spawn = false;
if (itemSkull != null && MaterialUtil.isPlayerHead(itemSkull.getType(), itemSkull.getDurability())) {
metaSkull = (SkullMeta) itemSkull.getItemMeta();
} else if (user.isAuthorized("essentials.skull.spawn")) {
itemSkull = new ItemStack(SKULL_ITEM, 1, (byte) 3);
metaSkull = (SkullMeta) itemSkull.getItemMeta();
spawn = true;
} else {
throw new Exception(tl("invalidSkull"));
}
if (metaSkull.hasOwner() && !user.isAuthorized("essentials.skull.modify")) {
throw new Exception(tl("noPermissionSkull"));
}
metaSkull.setDisplayName("§fSkull of " + owner);
metaSkull.setOwner(owner);
itemSkull.setItemMeta(metaSkull);
if (spawn) {
InventoryWorkaround.addItems(user.getBase().getInventory(), itemSkull);
user.sendMessage(tl("givenSkull", owner));
} else {
user.sendMessage(tl("skullChanged", owner));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
if (user.isAuthorized("essentials.skull.others")) {
return getPlayers(server, user);
} else {
return Lists.newArrayList(user.getName());
}
} else {
return Collections.emptyList();
}
}
}
@@ -1,203 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DescParseTickFormat;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.World;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class Commandtime extends EssentialsCommand {
public Commandtime() {
super("time");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
boolean add = false;
final List<String> argList = new ArrayList<>(Arrays.asList(args));
if (argList.remove("set") && !argList.isEmpty() && NumberUtil.isInt(argList.get(0))) {
argList.set(0, argList.get(0) + "t");
}
if (argList.remove("add") && !argList.isEmpty() && NumberUtil.isInt(argList.get(0))) {
add = true;
argList.set(0, argList.get(0) + "t");
}
final String[] validArgs = argList.toArray(new String[0]);
// Which World(s) are we interested in?
String worldSelector = null;
if (validArgs.length == 2) {
worldSelector = validArgs[1];
}
final Set<World> worlds = getWorlds(server, sender, worldSelector);
final String setTime;
// If no arguments we are reading the time
if (validArgs.length == 0) {
if (commandLabel.equalsIgnoreCase("day") || commandLabel.equalsIgnoreCase("eday")) {
setTime = "day";
} else if (commandLabel.equalsIgnoreCase("night") || commandLabel.equalsIgnoreCase("enight")) {
setTime = "night";
} else {
getWorldsTime(sender, worlds);
throw new NoChargeException();
}
} else {
setTime = validArgs[0];
}
final User user = ess.getUser(sender.getPlayer());
if (user != null && !user.isAuthorized("essentials.time.set")) {
throw new Exception(tl("timeSetPermission"));
}
// Parse the target time int ticks from args[0]
long ticks;
try {
ticks = DescParseTickFormat.parse(setTime);
} catch (NumberFormatException e) {
throw new NotEnoughArgumentsException(e);
}
setWorldsTime(sender, worlds, ticks, add);
}
/**
* Used to get the time and inform
*/
private void getWorldsTime(final CommandSource sender, final Collection<World> worlds) {
if (worlds.size() == 1) {
final Iterator<World> iter = worlds.iterator();
sender.sendMessage(DescParseTickFormat.format(iter.next().getTime()));
return;
}
for (World world : worlds) {
sender.sendMessage(tl("timeWorldCurrent", world.getName(), DescParseTickFormat.format(world.getTime())));
}
}
/**
* Used to set the time and inform of the change
*/
private void setWorldsTime(final CommandSource sender, final Collection<World> worlds, final long ticks, final boolean add) throws Exception {
User user = ess.getUser(sender.getPlayer());
for (World world : worlds) {
if (!canUpdateWorld(user, world)) {
throw new Exception(tl("timeSetWorldPermission", user.getWorld().getName()));
}
}
// Update the time
for (World world : worlds) {
long time = world.getTime();
if (!add) {
time -= time % 24000;
}
world.setTime(time + (add ? 0 : 24000) + ticks);
}
final StringBuilder output = new StringBuilder();
for (World world : worlds) {
if (output.length() > 0) {
output.append(", ");
}
output.append(world.getName());
}
sender.sendMessage(tl("timeWorldSet", DescParseTickFormat.format(ticks), output.toString()));
}
/**
* Used to parse an argument of the type "world(s) selector"
*/
private Set<World> getWorlds(final Server server, final CommandSource sender, final String selector) throws Exception {
final Set<World> worlds = new TreeSet<>(new WorldNameComparator());
// If there is no selector we want the world the user is currently in. Or all worlds if it isn't a user.
if (selector == null) {
if (sender.isPlayer()) {
final User user = ess.getUser(sender.getPlayer());
worlds.add(user.getWorld());
} else {
worlds.addAll(server.getWorlds());
}
return worlds;
}
// Try to find the world with name = selector
final World world = server.getWorld(selector);
if (world != null) {
worlds.add(world);
}
// If that fails, Is the argument something like "*" or "all"?
else if (selector.equalsIgnoreCase("*") || selector.equalsIgnoreCase("all")) {
worlds.addAll(server.getWorlds());
}
// We failed to understand the world target...
else {
throw new Exception(tl("invalidWorld"));
}
return worlds;
}
private boolean canUpdateAll(User user) {
return !ess.getSettings().isWorldTimePermissions() // First check if per world permissions are enabled, if not, return true.
|| user == null || user.isAuthorized("essentials.time.world.all");
}
private boolean canUpdateWorld(User user, World world) {
return canUpdateAll(user) || user.isAuthorized("essentials.time.world." + normalizeWorldName(world));
}
private String normalizeWorldName(World world) {
return world.getName().toLowerCase().replaceAll("\\s+", "_");
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
final User user = ess.getUser(sender.getPlayer());
if (args.length == 1) {
if (user == null || user.isAuthorized("essentials.time.set")) {
return Lists.newArrayList("set", "add");
} else {
return Collections.emptyList();
}
} else if (args.length == 2 && args[0].equalsIgnoreCase("set")) {
return Lists.newArrayList("sunrise", "day", "morning", "noon", "afternoon", "sunset", "night", "midnight");
// TODO: handle tab completion for add
} else if (args.length == 3 && (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("add"))) {
List<String> worlds = Lists.newArrayList();
for (World world : server.getWorlds()) {
if (user == null || user.isAuthorized("essentials.time.world." + normalizeWorldName(world))) {
worlds.add(world.getName());
}
}
if (user == null || user.isAuthorized("essentials.time.world.all")) {
worlds.add("*");
}
return worlds;
} else {
return Collections.emptyList();
}
}
}
class WorldNameComparator implements Comparator<World> {
@Override
public int compare(final World a, final World b) {
return a.getName().compareTo(b.getName());
}
}
@@ -1,133 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DateUtil;
import net.ess3.api.events.JailStatusChangeEvent;
import org.bukkit.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class Commandtogglejail extends EssentialsCommand {
public Commandtogglejail() {
super("togglejail");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
final User player = getPlayer(server, args, 0, true, true);
if (args.length >= 2 && !player.isJailed()) {
if (!player.getBase().isOnline()) {
if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.togglejail.offline")) {
sender.sendMessage(tl("mayNotJailOffline"));
return;
}
} else {
if (player.isAuthorized("essentials.jail.exempt")) {
sender.sendMessage(tl("mayNotJail"));
return;
}
}
final User controller = sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null;
final JailStatusChangeEvent event = new JailStatusChangeEvent(player, controller, true);
ess.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
long preTimeDiff = 0;
if (args.length > 2) {
final String time = getFinalArg(args, 2);
preTimeDiff = DateUtil.parseDateDiff(time, true);
}
final long timeDiff = preTimeDiff;
CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
future.thenAccept(success -> {
if (success) {
player.setJailed(true);
player.sendMessage(tl("userJailed"));
player.setJail(null);
player.setJail(args[1]);
if (args.length > 2) {
player.setJailTimeout(timeDiff);
}
sender.sendMessage((timeDiff > 0 ? tl("playerJailedFor", player.getName(), DateUtil.formatDateDiff(timeDiff)) : tl("playerJailed", player.getName())));
}
});
if (player.getBase().isOnline()) {
ess.getJails().sendToJail(player, args[1], future);
} else {
// Check if jail exists
ess.getJails().getJail(args[1]);
future.complete(true);
}
}
return;
}
if (args.length >= 2 && player.isJailed() && !args[1].equalsIgnoreCase(player.getJail())) {
sender.sendMessage(tl("jailAlreadyIncarcerated", player.getJail()));
return;
}
if (args.length >= 2 && player.isJailed() && args[1].equalsIgnoreCase(player.getJail())) {
final String time = getFinalArg(args, 2);
final long timeDiff = DateUtil.parseDateDiff(time, true);
player.setJailTimeout(timeDiff);
sender.sendMessage(tl("jailSentenceExtended", DateUtil.formatDateDiff(timeDiff)));
return;
}
if (args.length == 1 || (args.length == 2 && args[1].equalsIgnoreCase(player.getJail()))) {
if (!player.isJailed()) {
throw new NotEnoughArgumentsException();
}
final User controller = sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null;
final JailStatusChangeEvent event = new JailStatusChangeEvent(player, controller, false);
ess.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
player.setJailed(false);
player.setJailTimeout(0);
player.sendMessage(tl("jailReleasedPlayerNotify"));
player.setJail(null);
if (player.getBase().isOnline()) {
CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
player.getAsyncTeleport().back(future);
future.thenAccept(success -> {
if (success) {
sender.sendMessage(tl("jailReleased", player.getName()));
}
});
return;
}
sender.sendMessage(tl("jailReleased", player.getName()));
}
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else if (args.length == 2) {
try {
return new ArrayList<>(ess.getJails().getList());
} catch (Exception e) {
return Collections.emptyList();
}
} else {
return COMMON_DATE_DIFFS;
}
}
}
@@ -1,56 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpacancel extends EssentialsCommand {
public Commandtpacancel() {
super("tpacancel");
}
/**
* Cancel {@link User}'s tp request if its {@code requester} is equal to the given {@code requester}.
* @param ess ess instance
* @param user user holding tp request
* @param requester tp requester
* @return whether tp was cancelled
*/
public static boolean cancelTeleportRequest(IEssentials ess, User user, User requester) throws Exception {
if (user.getTeleportRequest() != null) {
User userRequester = ess.getUser(user.getTeleportRequest());
if (requester.equals(userRequester)) {
user.requestTeleport(null, false);
return true;
}
}
return false;
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length == 0) {
int cancellations = 0;
for (User onlineUser : ess.getOnlineUsers()) {
if (onlineUser == user) continue;
if (cancelTeleportRequest(ess, onlineUser, user)) {
cancellations++;
}
}
if (cancellations > 0) {
user.sendMessage(tl("teleportRequestAllCancelled", cancellations));
} else {
throw new Exception(tl("noPendingRequest"));
}
} else {
User targetPlayer = getPlayer(server, user, args, 0);
if (cancelTeleportRequest(ess, targetPlayer, user)) {
user.sendMessage(tl("teleportRequestSpecificCancelled", targetPlayer.getName()));
}
}
}
}
@@ -1,81 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.AsyncTeleport;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpaccept extends EssentialsCommand {
public Commandtpaccept() {
super("tpaccept");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final User requester;
try {
requester = ess.getUser(user.getTeleportRequest());
} catch (Exception ex) {
throw new Exception(tl("noPendingRequest"));
}
if (!requester.getBase().isOnline()) {
throw new Exception(tl("noPendingRequest"));
}
if (user.isTpRequestHere() && ((!requester.isAuthorized("essentials.tpahere") && !requester.isAuthorized("essentials.tpaall")) || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + user.getWorld().getName())))) {
throw new Exception(tl("noPendingRequest"));
}
if (!user.isTpRequestHere() && (!requester.isAuthorized("essentials.tpa") || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + requester.getWorld().getName())))) {
throw new Exception(tl("noPendingRequest"));
}
if (args.length > 0 && !requester.getName().contains(args[0])) {
throw new Exception(tl("noPendingRequest"));
}
if (!user.hasOutstandingTeleportRequest()) {
user.requestTeleport(null, false);
throw new Exception(tl("requestTimedOut"));
}
final Trade charge = new Trade(this.getName(), ess);
user.sendMessage(tl("requestAccepted"));
requester.sendMessage(tl("requestAcceptedFrom", user.getDisplayName()));
CompletableFuture<Boolean> future = getNewExceptionFuture(requester.getSource(), commandLabel);
future.exceptionally(e -> {
user.sendMessage(tl("pendingTeleportCancelled"));
return false;
});
future.thenAccept(success -> {
if (success) {
user.requestTeleport(null, false);
}
});
if (user.isTpRequestHere()) {
final Location loc = user.getTpRequestLocation();
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
future.thenAccept(success -> {
if (success) {
requester.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
});
teleport.teleportPlayer(user, user.getTpRequestLocation(), charge, TeleportCause.COMMAND, future);
} else {
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
teleport.teleport(user.getBase(), charge, TeleportCause.COMMAND, future);
}
}
}
@@ -1,28 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpdeny extends EssentialsCommand {
public Commandtpdeny() {
super("tpdeny");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (user.getTeleportRequest() == null) {
throw new Exception(tl("noPendingRequest"));
}
final User player = ess.getUser(user.getTeleportRequest());
if (player == null) {
throw new Exception(tl("noPendingRequest"));
}
user.sendMessage(tl("requestDenied"));
player.sendMessage(tl("requestDeniedFrom", user.getDisplayName()));
user.requestTeleport(null, false);
}
}
@@ -1,35 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpoffline extends EssentialsCommand {
public Commandtpoffline() {
super("tpoffline");
}
@Override
public void run(final Server server, final User user, final String label, final String[] args) throws Exception {
switch (args.length) {
case 0:
throw new NotEnoughArgumentsException();
default:
final User target = getPlayer(server, args, 0, true, true);
final Location logout = target.getLogoutLocation();
if (user.getWorld() != logout.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + logout.getWorld().getName())) {
throw new Exception(tl("noPerm", "essentials.worlds." + logout.getWorld().getName()));
}
user.sendMessage(tl("teleporting", logout.getWorld().getName(), logout.getBlockX(), logout.getBlockY(), logout.getBlockZ()));
user.getAsyncTeleport().now(logout, false, PlayerTeleportEvent.TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), label));
}
}
}
@@ -1,48 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.RandomTeleport;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import net.ess3.api.events.UserRandomTeleportEvent;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpr extends EssentialsCommand {
public Commandtpr() {
super("tpr");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
final Trade charge = new Trade(this.getName(), ess);
charge.isAffordableFor(user);
RandomTeleport randomTeleport = ess.getRandomTeleport();
UserRandomTeleportEvent event = new UserRandomTeleportEvent(user, randomTeleport.getCenter(), randomTeleport.getMinRange(), randomTeleport.getMaxRange());
server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
randomTeleport.getRandomLocation(event.getCenter(), event.getMinRange(), event.getMaxRange()).thenAccept(location -> {
CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
user.getAsyncTeleport().teleport(location, charge, PlayerTeleportEvent.TeleportCause.COMMAND, future);
future.thenAccept(success -> {
if (success) {
user.sendMessage(tl("tprSuccess"));
}
});
});
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
return Collections.emptyList();
}
}
@@ -1,85 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.SetExpFix;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import org.bukkit.Statistic;
import java.util.Locale;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commandwhois extends EssentialsCommand {
// For some reason, in 1.13 PLAY_ONE_MINUTE = ticks played = what used to be PLAY_ONE_TICK
// https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/b848d8ce633871b52115247b089029749c02f579
private static final Statistic PLAY_ONE_TICK = EnumUtil.getStatistic("PLAY_ONE_MINUTE", "PLAY_ONE_TICK");
public Commandwhois() {
super("whois");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 1) {
throw new NotEnoughArgumentsException();
}
User user = getPlayer(server, sender, args, 0);
sender.sendMessage(tl("whoisTop", user.getName()));
user.setDisplayNick();
sender.sendMessage(tl("whoisNick", user.getDisplayName()));
sender.sendMessage(tl("whoisUuid", user.getBase().getUniqueId().toString()));
sender.sendMessage(tl("whoisHealth", user.getBase().getHealth()));
sender.sendMessage(tl("whoisHunger", user.getBase().getFoodLevel(), user.getBase().getSaturation()));
sender.sendMessage(tl("whoisExp", SetExpFix.getTotalExperience(user.getBase()), user.getBase().getLevel()));
sender.sendMessage(tl("whoisLocation", user.getLocation().getWorld().getName(), user.getLocation().getBlockX(), user.getLocation().getBlockY(), user.getLocation().getBlockZ()));
long playtimeMs = System.currentTimeMillis() - (user.getBase().getStatistic(PLAY_ONE_TICK) * 50);
sender.sendMessage(tl("whoisPlaytime", DateUtil.formatDateDiff(playtimeMs)));
if (!ess.getSettings().isEcoDisabled()) {
sender.sendMessage(tl("whoisMoney", NumberUtil.displayCurrency(user.getMoney(), ess)));
}
if (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.whois.ip")) {
sender.sendMessage(tl("whoisIPAddress", user.getBase().getAddress().getAddress().toString()));
}
final String location = user.getGeoLocation();
if (location != null && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
sender.sendMessage(tl("whoisGeoLocation", location));
}
sender.sendMessage(tl("whoisGamemode", tl(user.getBase().getGameMode().toString().toLowerCase(Locale.ENGLISH))));
sender.sendMessage(tl("whoisGod", (user.isGodModeEnabled() ? tl("true") : tl("false"))));
sender.sendMessage(tl("whoisOp", (user.getBase().isOp() ? tl("true") : tl("false"))));
sender.sendMessage(tl("whoisFly", user.getBase().getAllowFlight() ? tl("true") : tl("false"), user.getBase().isFlying() ? tl("flying") : tl("notFlying")));
sender.sendMessage(tl("whoisSpeed", user.getBase().isFlying() ? user.getBase().getFlySpeed() : user.getBase().getWalkSpeed()));
if (user.isAfk()) {
sender.sendMessage(tl("whoisAFKSince", tl("true"), DateUtil.formatDateDiff(user.getAfkSince())));
} else {
sender.sendMessage(tl("whoisAFK", tl("false")));
}
sender.sendMessage(tl("whoisJail", (user.isJailed() ? user.getJailTimeout() > 0 ? DateUtil.formatDateDiff(user.getJailTimeout()) : tl("true") : tl("false"))));
long muteTimeout = user.getMuteTimeout();
if (!user.hasMuteReason()) {
sender.sendMessage(tl("whoisMuted", (user.isMuted() ? (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")) : tl("false"))));
} else {
sender.sendMessage(tl("whoisMutedReason", (user.isMuted() ? (muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")) : tl("false")),
user.getMuteReason()));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getPlayers(server, sender);
} else {
return Collections.emptyList();
}
}
}
@@ -1,7 +0,0 @@
package com.earth2me.essentials.commands;
public class PlayerExemptException extends NoSuchFieldException {
public PlayerExemptException(String message) {
super(message);
}
}
@@ -1,9 +0,0 @@
package com.earth2me.essentials.commands;
import static com.earth2me.essentials.I18n.tl;
public class PlayerNotFoundException extends NoSuchFieldException {
public PlayerNotFoundException() {
super(tl("playerNotFound"));
}
}
@@ -1,13 +0,0 @@
package com.earth2me.essentials.commands;
import static com.earth2me.essentials.I18n.tl;
public class WarpNotFoundException extends Exception {
public WarpNotFoundException() {
super(tl("warpNotExist"));
}
public WarpNotFoundException(String message) {
super(message);
}
}
@@ -1,30 +0,0 @@
package com.earth2me.essentials.craftbukkit;
import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import java.util.Set;
public class BanLookup {
public static Boolean isBanned(IEssentials ess, User user) {
return isBanned(ess, user.getName());
}
public static Boolean isBanned(IEssentials ess, String name) {
return getBanEntry(ess, name) != null;
}
public static BanEntry getBanEntry(IEssentials ess, String name) {
Set<BanEntry> benteries = ess.getServer().getBanList(BanList.Type.NAME).getBanEntries();
for (BanEntry banEnt : benteries) {
if (banEnt.getTarget().equals(name)) {
return banEnt;
}
}
return null;
}
}
@@ -1,273 +0,0 @@
package com.earth2me.essentials.craftbukkit;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/*
* This class can be removed when https://github.com/Bukkit/CraftBukkit/pull/193 is accepted to CraftBukkit
*/
public final class InventoryWorkaround {
private InventoryWorkaround() {
}
private static int firstPartial(final Inventory inventory, final ItemStack item, final int maxAmount) {
if (item == null) {
return -1;
}
final ItemStack[] stacks = inventory.getContents();
for (int i = 0; i < stacks.length; i++) {
final ItemStack cItem = stacks[i];
if (cItem != null && cItem.getAmount() < maxAmount && cItem.isSimilar(item)) {
return i;
}
}
return -1;
}
/*
Spigot 1.9, for whatever reason, decided to merge the armor and main player inventories without providing a way
to access the main inventory. There's lots of ugly code in here to work around that.
*/
private static final int USABLE_PLAYER_INV_SIZE = 36;
private static boolean isCombinedInventory(Inventory inventory) {
return inventory instanceof PlayerInventory && inventory.getContents().length > USABLE_PLAYER_INV_SIZE;
}
// Clears inventory without clearing armor
public static void clearInventoryNoArmor(PlayerInventory inventory) {
if (isCombinedInventory(inventory)) {
for (int i = 0; i < USABLE_PLAYER_INV_SIZE; i++) {
inventory.setItem(i, null);
}
} else {
inventory.clear();
}
}
private static Inventory makeTruncatedPlayerInventory(PlayerInventory playerInventory) {
Inventory fakeInventory = Bukkit.getServer().createInventory(null, USABLE_PLAYER_INV_SIZE);
fakeInventory.setContents(Arrays.copyOf(playerInventory.getContents(), fakeInventory.getSize()));
return fakeInventory;
}
// Returns what it couldn't store
// This will will abort if it couldn't store all items
public static Map<Integer, ItemStack> addAllItems(final Inventory inventory, final ItemStack... items) {
ItemStack[] contents = inventory.getContents();
final Inventory fakeInventory;
if (isCombinedInventory(inventory)) {
fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
} else {
fakeInventory = Bukkit.getServer().createInventory(null, inventory.getType());
fakeInventory.setContents(contents);
}
Map<Integer, ItemStack> overflow = addItems(fakeInventory, items);
if (overflow.isEmpty()) {
addItems(inventory, items);
return null;
}
return addItems(fakeInventory, items);
}
public static Map<Integer, ItemStack> addAllOversizedItems(final Inventory inventory, final int oversizedStacks, final ItemStack... items) {
ItemStack[] contents = inventory.getContents();
final Inventory fakeInventory;
if (isCombinedInventory(inventory)) {
fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
} else {
fakeInventory = Bukkit.getServer().createInventory(null, inventory.getType());
fakeInventory.setContents(contents);
}
Map<Integer, ItemStack> overflow = addOversizedItems(fakeInventory, oversizedStacks, items);
if (overflow.isEmpty()) {
addOversizedItems(inventory, oversizedStacks, items);
return null;
}
return overflow;
}
// Returns what it couldn't store
public static Map<Integer, ItemStack> addItems(final Inventory inventory, final ItemStack... items) {
return addOversizedItems(inventory, 0, items);
}
// Returns what it couldn't store
// Set oversizedStack to below normal stack size to disable oversized stacks
public static Map<Integer, ItemStack> addOversizedItems(final Inventory inventory, final int oversizedStacks, final ItemStack... items) {
if (isCombinedInventory(inventory)) {
Inventory fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
Map<Integer, ItemStack> overflow = addOversizedItems(fakeInventory, oversizedStacks, items);
for (int i = 0; i < fakeInventory.getContents().length; i++) {
inventory.setItem(i, fakeInventory.getContents()[i]);
}
return overflow;
}
final Map<Integer, ItemStack> leftover = new HashMap<>();
/*
* TODO: some optimization - Create a 'firstPartial' with a 'fromIndex' - Record the lastPartial per Material -
* Cache firstEmpty result
*/
// combine items
final ItemStack[] combined = new ItemStack[items.length];
for (ItemStack item : items) {
if (item == null || item.getAmount() < 1) {
continue;
}
for (int j = 0; j < combined.length; j++) {
if (combined[j] == null) {
combined[j] = item.clone();
break;
}
if (combined[j].isSimilar(item)) {
combined[j].setAmount(combined[j].getAmount() + item.getAmount());
break;
}
}
}
for (int i = 0; i < combined.length; i++) {
final ItemStack item = combined[i];
if (item == null || item.getType() == Material.AIR) {
continue;
}
while (true) {
// Do we already have a stack of it?
final int maxAmount = Math.max(oversizedStacks, item.getType().getMaxStackSize());
final int firstPartial = firstPartial(inventory, item, maxAmount);
// Drat! no partial stack
if (firstPartial == -1) {
// Find a free spot!
final int firstFree = inventory.firstEmpty();
if (firstFree == -1) {
// No space at all!
leftover.put(i, item);
break;
} else {
// More than a single stack!
if (item.getAmount() > maxAmount) {
final ItemStack stack = item.clone();
stack.setAmount(maxAmount);
inventory.setItem(firstFree, stack);
item.setAmount(item.getAmount() - maxAmount);
} else {
// Just store it
inventory.setItem(firstFree, item);
break;
}
}
} else {
// So, apparently it might only partially fit, well lets do just that
final ItemStack partialItem = inventory.getItem(firstPartial);
final int amount = item.getAmount();
final int partialAmount = partialItem.getAmount();
// Check if it fully fits
if (amount + partialAmount <= maxAmount) {
partialItem.setAmount(amount + partialAmount);
break;
}
// It fits partially
partialItem.setAmount(maxAmount);
item.setAmount(amount + partialAmount - maxAmount);
}
}
}
return leftover;
}
// Hot-ish code so cache
private static Boolean hasMainHandSupport = null;
@SuppressWarnings("deprecation")
public static void setItemInMainHand(Player p, ItemStack item) {
if (hasMainHandSupport == null) {
try {
p.getInventory().setItemInMainHand(item);
hasMainHandSupport = true;
} catch (Throwable e) {
p.setItemInHand(item);
hasMainHandSupport = false;
}
} else {
if (hasMainHandSupport) {
p.getInventory().setItemInMainHand(item);
} else {
p.setItemInHand(item);
}
}
}
@SuppressWarnings("deprecation")
public static void setItemInMainHand(EntityEquipment invent, ItemStack item) {
if (hasMainHandSupport == null) {
try {
invent.setItemInMainHand(item);
hasMainHandSupport = true;
} catch (Throwable e) {
invent.setItemInHand(item);
hasMainHandSupport = false;
}
} else {
if (hasMainHandSupport) {
invent.setItemInMainHand(item);
} else {
invent.setItemInHand(item);
}
}
}
@SuppressWarnings("deprecation")
public static void setItemInMainHandDropChance(EntityEquipment invent, float chance) {
if (hasMainHandSupport == null) {
try {
invent.setItemInMainHandDropChance(chance);
hasMainHandSupport = true;
} catch (Throwable e) {
invent.setItemInHandDropChance(chance);
hasMainHandSupport = false;
}
} else {
if (hasMainHandSupport) {
invent.setItemInMainHandDropChance(chance);
} else {
invent.setItemInHandDropChance(chance);
}
}
}
public static void setItemInOffHand(Player p, ItemStack item) {
// This assumes that all builds that support a main hand also support an off hand.
if (hasMainHandSupport == null || hasMainHandSupport) {
try {
p.getInventory().setItemInOffHand(item);
hasMainHandSupport = true;
} catch (Throwable e) {
hasMainHandSupport = false;
}
}
}
}
@@ -1,349 +0,0 @@
package com.earth2me.essentials.items;
import com.earth2me.essentials.IConf;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.api.IEssentials;
import net.ess3.api.PluginKey;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.Material;
import org.bukkit.block.Banner;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import java.util.*;
import java.util.function.Function;
import static com.earth2me.essentials.I18n.tl;
public abstract class AbstractItemDb implements IConf, net.ess3.api.IItemDb {
protected final IEssentials ess;
protected boolean ready = false;
private final Map<PluginKey, ItemResolver> resolverMap = new HashMap<>();
AbstractItemDb(IEssentials ess) {
this.ess = ess;
}
@Override
public void registerResolver(Plugin plugin, String name, ItemResolver resolver) throws Exception {
PluginKey key = PluginKey.fromKey(plugin, name);
if (resolverMap.containsKey(key)) {
throw new Exception("Tried to add a duplicate resolver with name " + key.toString());
}
resolverMap.put(key, resolver);
}
@Override
public void unregisterResolver(Plugin plugin, String name) throws Exception {
PluginKey key = PluginKey.fromKey(plugin, name);
if (!resolverMap.containsKey(key)) {
throw new Exception("Tried to remove nonexistent resolver with name " + key.toString());
}
resolverMap.remove(key);
}
@Override
public boolean isResolverPresent(Plugin plugin, String name) {
return resolverMap.containsKey(PluginKey.fromKey(plugin, name));
}
@Override
public Map<PluginKey, ItemResolver> getResolvers() {
return new HashMap<>(resolverMap);
}
@Override
public Map<PluginKey, ItemResolver> getResolvers(Plugin plugin) {
Map<PluginKey, ItemResolver> matchingResolvers = new HashMap<>();
for (PluginKey key : resolverMap.keySet()) {
if (key.getPlugin().equals(plugin)) {
matchingResolvers.put(key, resolverMap.get(key));
}
}
return matchingResolvers;
}
@Override
public ItemResolver getResolver(Plugin plugin, String name) {
return resolverMap.get(PluginKey.fromKey(plugin, name));
}
@Override
public ItemStack get(String id) throws Exception {
return get(id, true);
}
ItemStack tryResolverDeserialize(String id) {
for (PluginKey key : resolverMap.keySet()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info(String.format("Trying to deserialize item '%s' with resolver '%s'...", id, key));
}
Function<String, ItemStack> resolver = resolverMap.get(key);
ItemStack stack = resolver.apply(id);
if (stack != null) {
return stack;
}
}
return null;
}
String tryResolverSerialize(ItemStack stack) {
for (PluginKey key : resolverMap.keySet()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info(String.format("Trying to serialize '%s' with resolver '%s'...", stack.toString(), key));
}
ItemResolver resolver = resolverMap.get(key);
String serialized = resolver.serialize(stack);
if (serialized != null) {
return serialized;
}
}
return null;
}
Collection<String> getResolverNames() {
List<String> result = new ArrayList<>();
for (ItemResolver resolver : resolverMap.values()) {
Collection<String> resolverNames = resolver.getNames();
if (resolverNames != null) {
result.addAll(resolverNames);
}
}
return result;
}
@Override
public List<ItemStack> getMatching(User user, String[] args) throws Exception {
List<ItemStack> is = new ArrayList<>();
if (args.length < 1) {
is.add(user.getItemInHand().clone());
} else if (args[0].equalsIgnoreCase("hand")) {
is.add(user.getItemInHand().clone());
} else if (args[0].equalsIgnoreCase("inventory") || args[0].equalsIgnoreCase("invent") || args[0].equalsIgnoreCase("all")) {
for (ItemStack stack : user.getBase().getInventory().getContents()) {
if (stack == null || stack.getType() == Material.AIR) {
continue;
}
is.add(stack.clone());
}
} else if (args[0].equalsIgnoreCase("blocks")) {
for (ItemStack stack : user.getBase().getInventory().getContents()) {
if (stack == null || stack.getType() == Material.AIR) {
continue;
}
is.add(stack.clone());
}
} else {
is.add(get(args[0]));
}
if (is.isEmpty() || is.get(0).getType() == Material.AIR) {
throw new Exception(tl("itemSellAir"));
}
return is;
}
@Override
public String serialize(ItemStack is) {
return serialize(is, true);
}
@Override
public String serialize(ItemStack is, boolean useResolvers) {
if (useResolvers) {
String serialized = tryResolverSerialize(is);
if (serialized != null) {
return serialized;
}
}
String mat = name(is);
if (VersionUtil.getServerBukkitVersion().isLowerThanOrEqualTo(VersionUtil.v1_12_2_R01) && is.getData().getData() != 0) {
mat = mat + ":" + is.getData().getData();
}
int quantity = is.getAmount();
StringBuilder sb = new StringBuilder(); // Add space AFTER you add something. We can trim at end.
sb.append(mat).append(" ").append(quantity).append(" ");
// ItemMeta applies to anything.
if (is.hasItemMeta()) {
ItemMeta meta = is.getItemMeta();
if (meta.hasDisplayName()) {
sb.append("name:").append(meta.getDisplayName().replaceAll(" ", "_")).append(" ");
}
if (meta.hasLore()) {
sb.append("lore:");
boolean first = true;
for (String s : meta.getLore()) {
// Add | before the line if it's not the first one. Easy but weird way
// to do this since we need each line separated by |
if (!first) {
sb.append("|");
}
first = false;
sb.append(s.replaceAll(" ", "_"));
}
sb.append(" ");
}
if (meta.hasEnchants()) {
for (Enchantment e : meta.getEnchants().keySet()) {
sb.append(e.getName().toLowerCase()).append(":").append(meta.getEnchantLevel(e)).append(" ");
}
}
Set<ItemFlag> flags = meta.getItemFlags();
if (flags != null && !flags.isEmpty()) {
sb.append("itemflags:");
boolean first = true;
for (ItemFlag flag : flags) {
if (!first) {
sb.append(",");
}
sb.append(flag.name());
first = false;
}
}
}
final Material material = is.getType();
switch (material) {
case WRITTEN_BOOK:
// Everything from http://wiki.ess3.net/wiki/Item_Meta#Books in that order.
// Interesting as I didn't see a way to do pages or chapters.
BookMeta bookMeta = (BookMeta) is.getItemMeta();
if (bookMeta.hasTitle()) {
sb.append("title:").append(bookMeta.getTitle()).append(" ");
}
if (bookMeta.hasAuthor()) {
sb.append("author:").append(bookMeta.getAuthor()).append(" ");
}
// Only other thing it could have is lore but that's done up there ^^^
break;
case ENCHANTED_BOOK:
EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) is.getItemMeta();
for (Enchantment e : enchantmentStorageMeta.getStoredEnchants().keySet()) {
sb.append(e.getName().toLowerCase()).append(":").append(enchantmentStorageMeta.getStoredEnchantLevel(e)).append(" ");
}
break;
}
if (MaterialUtil.isFirework(material)) {
// Everything from http://wiki.ess3.net/wiki/Item_Meta#Fireworks in that order.
FireworkMeta fireworkMeta = (FireworkMeta) is.getItemMeta();
if (fireworkMeta.hasEffects()) {
for (FireworkEffect effect : fireworkMeta.getEffects()) {
if (effect.getColors() != null && !effect.getColors().isEmpty()) {
sb.append("color:");
boolean first = true;
for (Color c : effect.getColors()) {
if (!first) {
sb.append(","); // same thing as above.
}
sb.append(c.toString());
first = false;
}
sb.append(" ");
}
sb.append("shape:").append(effect.getType().name()).append(" ");
if (effect.getFadeColors() != null && !effect.getFadeColors().isEmpty()) {
sb.append("fade:");
boolean first = true;
for (Color c : effect.getFadeColors()) {
if (!first) {
sb.append(","); // same thing as above.
}
sb.append(c.toString());
first = false;
}
sb.append(" ");
}
}
sb.append("power:").append(fireworkMeta.getPower()).append(" ");
}
} else if (MaterialUtil.isPotion(material)) {
Potion potion = Potion.fromItemStack(is);
for (PotionEffect e : potion.getEffects()) {
// long but needs to be effect:speed power:2 duration:120 in that order.
sb.append("splash:").append(potion.isSplash()).append(" ").append("effect:").append(e.getType().getName().toLowerCase()).append(" ").append("power:").append(e.getAmplifier()).append(" ").append("duration:").append(e.getDuration() / 20).append(" ");
}
} else if (MaterialUtil.isPlayerHead(material, is.getData().getData())) {
// item stack with meta
SkullMeta skullMeta = (SkullMeta) is.getItemMeta();
if (skullMeta != null && skullMeta.hasOwner()) {
sb.append("player:").append(skullMeta.getOwner()).append(" ");
}
} else if (MaterialUtil.isBanner(material)) {
if (material.toString().contains("SHIELD")) {
// Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795
BlockStateMeta shieldMeta = (BlockStateMeta) is.getItemMeta();
Banner shieldBannerMeta = (Banner) shieldMeta.getBlockState();
DyeColor baseDyeColor = shieldBannerMeta.getBaseColor();
if (baseDyeColor != null) {
int basecolor = baseDyeColor.getColor().asRGB();
sb.append("basecolor:").append(basecolor).append(" ");
}
for (org.bukkit.block.banner.Pattern p : shieldBannerMeta.getPatterns()) {
String type = p.getPattern().getIdentifier();
int color = p.getColor().getColor().asRGB();
sb.append(type).append(",").append(color).append(" ");
}
} else {
BannerMeta bannerMeta = (BannerMeta) is.getItemMeta();
if (bannerMeta != null) {
DyeColor baseDyeColor = bannerMeta.getBaseColor();
if (baseDyeColor == null) {
baseDyeColor = MaterialUtil.getColorOf(material);
}
if (baseDyeColor != null) {
int basecolor = baseDyeColor
.getColor()
.asRGB();
sb.append("basecolor:").append(basecolor).append(" ");
}
for (org.bukkit.block.banner.Pattern p : bannerMeta.getPatterns()) {
String type = p.getPattern().getIdentifier();
int color = p.getColor().getColor().asRGB();
sb.append(type).append(",").append(color).append(" ");
}
}
}
} else if (MaterialUtil.isLeatherArmor(material)) {
LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) is.getItemMeta();
int rgb = leatherArmorMeta.getColor().asRGB();
sb.append("color:").append(rgb).append(" ");
}
return sb.toString().trim().replaceAll("§", "&");
}
@Override
public boolean isReady() {
return ready;
}
}
@@ -1,89 +0,0 @@
package com.earth2me.essentials.items;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.EssentialsConf;
import com.earth2me.essentials.IConf;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import net.ess3.api.IItemDb;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
public class CustomItemResolver implements IItemDb.ItemResolver, IConf {
private final EssentialsConf config;
private final Essentials ess;
private final HashMap<String, String> map = new HashMap<>();
public CustomItemResolver(Essentials ess) {
config = new EssentialsConf(new File(ess.getDataFolder(), "custom_items.yml"));
this.ess = ess;
config.setTemplateName("/custom_items.yml");
}
@Override
public ItemStack apply(String item) {
if (map.containsKey(item)) {
try {
return ess.getItemDb().get(map.get(item));
} catch (Exception ignored) {}
}
return null;
}
@Override
public Collection<String> getNames() {
return map.keySet();
}
@Override
public void reloadConfig() {
map.clear();
config.load();
ConfigurationSection section = config.getConfigurationSection("aliases");
if (section == null || section.getKeys(false).isEmpty()) {
ess.getLogger().warning("No aliases found in custom_items.yml.");
return;
}
for (String alias : section.getKeys(false)) {
if (!section.isString(alias)) continue;
String target = section.getString(alias);
if (target != null && !section.contains(target) && existsInItemDb(target)) {
map.put(alias, target);
}
}
}
public void setAlias(String alias, String target) {
if (map.containsKey(alias) && map.get(alias).equalsIgnoreCase(target)) {
return;
}
map.put(alias, target);
save();
}
public void removeAlias(String alias) {
if (map.remove(alias) != null) {
save();
}
}
private void save() {
config.setProperty("aliases", map);
config.save();
}
private boolean existsInItemDb(String item) {
try {
ess.getItemDb().get(item);
return true;
} catch (Exception e) {
return false;
}
}
}
Loaded 100 of 918 files, more files were not shown because too many files have changed in this diff. Show more