Compare commits

...
158 Commits
Author SHA1 Message Date
Warrior 736171a3ae Run smite with entity scheduler 2026-05-03 13:35:38 -07:00
Warrior eb9b7dc74c Modify thunder on global thread 2026-05-03 13:35:38 -07:00
Warrior bb0f9f3c51 Fix retrieving tile entity count for gc command 2026-05-03 13:35:38 -07:00
Warrior 853ac1ea4b Fix merge artifact (?) 2026-05-03 13:35:38 -07:00
Warrior e7461bfd38 Run kill command on entity thread 2026-05-03 13:35:38 -07:00
JRoy 63dcfa2333 sure 2026-02-28 15:20:36 -08:00
JRoy 4423bc824e Merge branch '2.x' into fork/Warriorrrr/feat/folia/merge-2.x-changes
# Conflicts:
#	Essentials/src/main/java/com/earth2me/essentials/Essentials.java
#	Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java
2026-02-28 15:19:54 -08:00
Josh Roy e9b8343751 Move away from adventure-platform for Paper (#6220)
Makes us no longer dependent on adventure-platform for Paper servers as
we will use their native adventure apis.

Few hacks needed to be done to not remap things in places but this is
the best we can do

The PaperAdventureFacet has to be in PaperProvider to avoid the kyori
remapping that happens on the entire EssentialsX module (we already did
this for PaperChatListenerProvider)

Fixes #6215
2026-02-22 19:53:38 +00:00
RVSkeLe 99f7a49f28 Improve gc command performance (#6439)
Updates /gc to use the new provider methods, allowing Paper to use
getTileEntities(false) and avoid unnecessary snapshots while maintaining
spigot compatibility.

"Benchmarks:"
Before, Paper: https://spark.lucko.me/kf2MVRQ5td - 76mspt
After, Paper: https://spark.lucko.me/NhZQXtLqvh - 1mspt

Spigot: https://spark.lucko.me/toXyvx0yq0
2026-02-22 03:40:39 +00:00
Josh Roy bc6acfe1dc Command Trees v2 (#6230)
My take on command trees which fit the vibe of our codebase better than
#5384 imo.

Closes #5384
2026-02-21 21:13:48 +00:00
Vishwas Kumar d16aec1525 Fixed unreachable nickNoMore Message in /nick command. Not a huge change. (#6442)
The display name was comparing getDisplayName() to itself, making it
always true which made the nickNoMore message unreachable. Now it uses
FormatUtil.stripFormat() to strip color codes before comparing against
getName(), also has a null-safety guard.

I have built it locally and tested it - it works well. Verified: "Nick
set normally", "Nick equal to Username", "Colored nick equal to
Username". Had no Errors.
2026-02-21 08:45:17 +00:00
Warrior b30021321a Move economy layers initialization to after providers
EconomyLayers relies on the scheduler to be initialized to enable properly
2026-02-13 20:12:00 +01:00
Warrior 00e35092ad Update scheduling providers to work with new provider system 2026-02-13 19:59:33 +01:00
Warrior 25685a8a64 Merge branch '2.x' into feat/folia/merge-2.x-changes 2026-02-13 19:39:46 +01:00
Nassim JahnkeandJRoy 787f539921 Prevent NPE in getPlayerLocale for offline players (#6435)
I hope this message finds you well. This pull requests prevents an NPE
in getPlayerLocale for offline players.

Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2026-02-09 19:38:47 +00:00
TiagoFar78andJosh Roy 6ab56d2fe8 Implement kits placing items in specific slots (#5794)
Closes #5535

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2026-02-07 05:50:39 +00:00
Ville Blixt e5c9074b7c Fix preventing joins when server isn't actually full (#6426)
Fixes #6424

Caused by https://github.com/PaperMC/Paper/pull/13601
2026-02-07 05:14:10 +00:00
ivall 77756620cf Show expiration on temporary IP ban kick message (#6407)
Show tempban IP expiration on player join, matching standard tempban
behavior.
2026-01-17 19:54:28 +00:00
Josh RoyandMD 0bd4c24c8b Update dump url (#6283)
new site doesn't require .html extension, we can avoid the redirect.

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2026-01-17 19:49:41 +00:00
Phinner 18233d3f04 Fix NPE in PermissionsHandler#getGroups(OfflinePlayer) (#6398) 2026-01-11 00:59:40 +00:00
Phinner 5ed1d1e04a Fix UtilTest tests failing if locale is not English (#6397) 2026-01-11 00:58:18 +00:00
The_Dalek 9ad754b6d9 Use Discord name instead of tag for /msg sender (#6383)
Fixes #5520
2026-01-03 16:40:51 +00:00
benniofficialdevandJosh Roy 68c304e6b6 Fix /balance behavior for vanished players (#6386)
Fixes #6085

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2026-01-03 16:34:45 +00:00
benniofficialdevandJosh Roy ac666186f5 Fix error when running /helpop from console (#6385)
Fixes #6342

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2026-01-03 15:58:43 +00:00
Stackulate b34951757b Fix Spigot 1.21.10+ ReflServerStateProvider (#6377) 2026-01-03 15:48:20 +00:00
Josh RoyandMD 000e6c551d Mirror dumps on gist (#6193)
This will allow us to backup dumps while issues are still open since
bytebins expire after a bit.

We will need to set `GIST_TOKEN` to a PAT for @Flask-Bot.

obviously will require new website and shouldn't be merged till.

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-12-20 20:28:08 +00:00
Josh Roy b0ef56802a Update to Minecraft 1.21.11 (#6364)
Aliases TBD
2025-12-09 17:22:25 +00:00
ThiagoROX faa02b5c49 Fix event thread assignment in TeleportWarmupCancelledEvent (#6371)
Adds a call to super with the correct async flag based on Bukkit's
primary thread status, ensuring proper event handling for
TeleportWarmupCancelledEvent.

fix this error:

```
[18:40:31 WARN]: [Essentials] Plugin Essentials v2.22.0-dev+278-984a0c1 generated an exception while executing task 156164
java.lang.IllegalStateException: TeleportWarmupCancelledEvent may only be triggered synchronously.
        at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:42) ~[paper-1.21.10.jar:1.21.10-117-df4b668]
        at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:131) ~[paper-1.21.10.jar:1.21.10-117-df4b668]
        at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:628) ~[paper-api-1.21.10-R0.1-SNAPSHOT.jar:?]
        at EssentialsX-2.22.0-278.jar/com.earth2me.essentials.AsyncTimedTeleport.cancelTimer(AsyncTimedTeleport.java:160) ~[EssentialsX-2.22.0-278.jar:?]
        at EssentialsX-2.22.0-278.jar/com.earth2me.essentials.AsyncTimedTeleport.run(AsyncTimedTeleport.java:96) ~[EssentialsX-2.22.0-278.jar:?]
        at org.bukkit.craftbukkit.scheduler.CraftTask.run(CraftTask.java:78) ~[paper-1.21.10.jar:1.21.10-117-df4b668]
        at org.bukkit.craftbukkit.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:57) ~[paper-1.21.10.jar:1.21.10-117-df4b668]
        at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22) ~[paper-1.21.10.jar:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
```
2025-12-02 21:50:32 +00:00
Golfing7andJosh Roy 0fa6f70b13 Add ability to register custom item transformers. (#4871)
.

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-11-29 17:41:22 +00:00
Pierre-Olivier GOINandJosh Roy af44749256 Add WarmUpCancelled Event & Expose TPType (#6351)
<!--

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

### Details

**Proposed feature:**  
This PR add a new event when a warmpup teleport is cancelled, and it
also expose the tpType


**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: Debian 12

<!-- Type the JDK version (from java -version) you have used below. -->
Java version: OpenJDK Runtime Environment Corretto-8.422.05.1 (build
1.8.0_422-b05)

<!--
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.
-->
- [x] Most recent Paper version (1.21.8, git-Paper-60-main@29c8822)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [x] Paper 1.8.8

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-11-27 15:14:53 +00:00
Josh Roy 87dfe3793a Update to adventure 4.25.0 (#6331) 2025-11-21 21:12:57 +00:00
EesaandJosh Roy 9f57b3e476 Add config option for logging console/RCON commands (#6337)
<!--

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

Closes #6336. 

### Details

<!-- Type a description of your proposed feature below this line. -->
This adds a config option that allows disabling the logging of console
and RCON commands.

**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: Arch Linux

<!-- Type the JDK version (from java -version) you have used below. -->
Java version:  openjdk 21.0.8 2025-07-15

<!--
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.
-->
- [x] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
- [x] CraftBukkit/Spigot/Paper 1.12.2
- [x] 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.
-->
Without these changes, with the config option not set:
<img width="680" height="69" alt="image"
src="https://github.com/user-attachments/assets/92647fc0-c0c5-4e7b-9f61-953fcadd7f93"
/>

With these changes, with the config option set to false:
<img width="635" height="68" alt="image"
src="https://github.com/user-attachments/assets/fd11572c-dfe2-4b79-89ab-69e2275d2c9f"
/>

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-11-09 23:52:34 +00:00
1wairesdandJosh Roy 9985dbdd3a Fix double messages when running /heal on self (#6347)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-10-26 23:50:50 +00:00
Warrior bb47c45a60 Buffer reads/writes in uuid cache loading/saving (#6338)
Buffers read and writes to uuid cache
2025-10-23 11:29:19 +00:00
Josh Roy 150dabbe24 Bump to JDA 6 (#6320) 2025-10-17 19:04:22 +00:00
Josh Roy adf77dc4ba Fix exception during first time joins (#6327)
Fixes an issue during first join with the player configuration events on
Paper. The usermap will refuse to implicitly store new users into the
usermap unless it can get a Bukkit player object for it, which is only
created during the PlayerJoinEvent. So for new users, we must defer to
then. The code that was being run (activity/mute checks) was probably
incorrect to be placed inside of configure event anyway.

Also force loads the locale bundle into memory to ensure it's ready for
servers with slow i/o.

Fixes #6315
2025-10-10 15:22:31 +00:00
Josh Roy 321c911222 Update to Minecraft 1.21.10 (#6324) 2025-10-07 19:16:17 +00:00
Josh Roy 264b0ed660 Improve memory resilience with Configurate (#6266)
Hopefully should cut down on memory usage with configurate.

Closes #6118
2025-10-03 14:32:08 +00:00
Josh Roy 4dabeb57db Fix GeoIP dependencies (#6314)
During the gradle/shadow upgrades during the 1.21.9 update, shadow
removed implicit wildcards. Explicitly declare all shadow depends for
jackson in geoip.

Also get rid of mavenCentral since paper mirrors it, was causing
resolution issues for mockbukkit

Fixes #6313
2025-10-03 14:31:44 +00:00
Josh Roy 8982aaec1d Fix updating join message when not needed (#6312)
Prevents calling setJoinMessage when the message is unchanged causing
Bukkit to no longer send the translation key to the client (nuking
vanilla translation)

Fixes #6307
2025-10-03 11:16:41 +00:00
CopilotandJRoy 22b497558a Fixes stale cached locations after using /settpr (#6305)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-10-03 11:05:45 +00:00
Josh Roy 65120e9d2c Update to Minecraft 1.21.9 (#6299) 2025-09-30 15:42:02 +00:00
Jason 893bae02e6 Change baltop entry limit wording (#6306)
Ensures consistency with the rest of the config
2025-09-29 03:56:42 +00:00
maxcom1andJosh Roy ca7f3b5d5d Add option to limit baltop entries (#6300)
```yml
# Allows to limit cached balance top (/baltop) entries. This is especially recommended if
# you have a large number of players, then only a certain number of entries will be stored
# in the server's memory. Set to -1 to disable the limit
baltop-entry-limit: -1
```

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-09-29 02:35:59 +00:00
Josh Roy 973e5f8b27 Add support for transmute recipes in /recipe (#6302)
Fixes #6297
2025-09-27 16:18:16 +00:00
Josh Roy 37e10546ea Switch to configuration based events on Paper (#6248)
Moves away from PlayerLoginEvent on Paper and makes use of the new
configuration state to load user/locale data during configuration phase.
Not only does this ensure all I/O is done async before the player even
joins but also finally allows us to actually set the join message in the
PlayerJoinEvent.

This also fixes #5796 and closes #5848
2025-09-19 14:46:49 +00:00
SpottedleafandJosh Roy 0aa25c80b9 Fix issue with "Fix issue with offline player data" (#6286)
Fixes #6277

Fixes the bad fix in e9da11624a

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-09-08 01:56:58 +00:00
Josh Roy ca07b531e4 Add message when discord bot is missing permissions (#6228)
Closes #5450
2025-09-07 15:35:12 +00:00
0ff1andJosh Roy e08606432c Adds support for armor trims inside kits (#6155)
ref #5799

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-09-06 18:49:37 +00:00
Josh Roy 364c87986f Add @s and @p selector support (#6110)
Adds support for @s and @p player selectors (which both select the
source player).

Closes #5925
Partially Addresses #6006
2025-09-06 14:16:30 +00:00
Josh Roy 427f60eb57 Fix matching players with similar names (#6263)
Fixes an issue where matchPlayer didn't always match exact matches
first.

Also adds a test for this method to ensure stuff like this doesn't
happen in the future.

Fixes #6246
2025-09-06 14:13:46 +00:00
Josh Roy efbcfb3bd6 AntiBuild: Properly handle shear interactions (#6262)
Fixes #6257
2025-09-06 14:13:37 +00:00
Josh Roy 19f7e99d95 Add support for LuckPerms group names (#6229)
Closes #5463
2025-09-06 14:13:28 +00:00
SpottedleafandJRoy e9da11624a Fix issue with offline player data (#6284)
Fixes #6277

Totally overlooked the invalidate call here

Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-09-04 15:17:05 +00:00
Josh Roy 81ac6f7283 Fix potion command not supporting infinite durations (#6236)
Minecraft understands infinite durations to be `-1` and only `-1`. Our
second -> tick conversion was changing this to `-20` causing a potion
command with a `-1` duration to end up as `-20` and only causing the
potion to occur for 1 tick.

Fixes #5558
2025-09-01 13:09:38 +00:00
Gufeng ShenandJosh Roy 7bc91f1c65 Discord: Add setting to configure an HTTP Proxy for JDA (#6275)
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-08-18 15:21:12 +00:00
Josh RoyandMD 7c89592bbc Add setting to preserve fly on gamemode change (#6238)
Adds new option to preserve flight on gamemode change.

```yml
# Should Essentials preserve a player's flight status when they change game modes?
# When enabled, if a player is flying when switching game modes, their flight will be maintained.
# This will only work if the player has the 'essentials.fly' permission.
gamemode-change-preserve-flying: false
```

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-08-16 22:15:41 +00:00
Josh Roy 302dd3fe35 Maintain strong reference for online users (#6247)
Possibly fixes #6241
2025-08-16 21:39:03 +00:00
Josh Roy 69fd7bc8ff Fix missing powertoollist permission (#6250) 2025-08-16 21:22:39 +00:00
Josh Roy 5ed43f339b Do not lowercase afk timeout commands (#6255)
Fixes #6254
2025-08-14 21:07:57 +00:00
Josh RoyandMD 8ba7a44a7d Add support for snapshots in VersionUtil (#6265)
We will always assume that a snapshot version is the most recent version

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-08-11 05:35:47 +00:00
hello09xandJRoy e0e8d4432a Prevent dragging items outside of invsee (#5902)
Fixes #3065

---------

Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-08-10 19:11:19 +00:00
Adrián Oliva Del Río 2ccfad189b Add first login date to /seen (#6258) 2025-08-09 22:49:24 +00:00
Adrián Oliva Del RíoandJRoy 58726fbc90 Add option to use UUIDs over usernames in trade.log (#6252)
<!--

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 #6073. 

### Details

**Proposed feature:**  
<!-- Type a description of your proposed feature below this line. -->
Add a setting to replace usernames with UUIDs in the trade.log file


**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: Windows 11 Pro

<!-- Type the JDK version (from java -version) you have used below. -->
Java version:  JDK 17

<!--
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.
-->
- [X] 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.
-->

**Before:**
<img width="1299" height="33" alt="image"
src="https://github.com/user-attachments/assets/6f34baf2-b5f5-46f6-adab-0ca0c0bf71c5"
/>

**After:**
<img width="1346" height="18" alt="image"
src="https://github.com/user-attachments/assets/672619ef-1215-4833-8320-e24bbb1c7ec0"
/>

**Command execution:**
<img width="491" height="107" alt="image"
src="https://github.com/user-attachments/assets/688efa80-3239-4bfe-8509-c998d3981637"
/>

---------

Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-08-06 15:02:38 +00:00
Josh Roy 025ca4895f Discord: Respect game rule for advancement message type (#6195)
Fixes #4865
2025-08-05 18:34:59 +00:00
Josh Roy 8bcf03d61b Discord: Add PAPI support to avatar-url (#6189)
Closes #6160
2025-08-05 18:33:29 +00:00
Mickey42302andJRoy 689c35fb23 Add bypass permission node for the whitelist. (#6232)
I would like to suggest adding an "essentials.whitelist.bypass"
permission node. This permission node allows users to bypass the
whitelist requirement on servers that have the whitelist enabled,
similar to how the "essentials.joinfullserver" permission node allows
users to bypass the player limit.

---------

Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com>
2025-08-04 02:42:21 +00:00
JustinandJosh Roy 03d89380cc feat: Add /powertoollist (#6096)
<!--

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 #6095. 

### Details

**Proposed feature:**  
<!-- Type a description of your proposed feature below this line. -->
This PR implements /powertoollist, which lists out all of a user's set
powertools.

Since all of a user's powertools reside within `UserData`, I utilized
this Map to query through all of a user's powertools.

**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: MacOS Apple chip

<!-- Type the JDK version (from java -version) you have used below. -->
Java version: 22.0.2

<!--
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
```
[20:36:06] [Server thread/INFO]: This server is running CraftBukkit version 4457-Spigot-73860e0-f0c8dd3 (MC: 1.21.4) (Implementing API version 1.21.4-R0.1-SNAPSHOT)
[20:36:06] [Server thread/INFO]: Checking version, please wait...
[20:36:06] [Thread-9/INFO]: You are running the latest version
```

**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.
-->
https://youtu.be/tgOr-fbHRAg

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-08-03 21:48:54 +00:00
Jsinco 790644b1ab Ignore cancelled PlayerDeathEvents (#6179)
<!--

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 #6178 . 

### Details

**Proposed feature:**  
<!-- Type a description of your proposed feature below this line. -->
Essentials should ignore cancelled PlayerDeathEvents.


**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: Linux

<!-- Type the JDK version (from java -version) you have used below. -->
Java version:  21

<!--
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.
-->
- [x] Most recent Paper version (1.21.6, @aa6cd74 build: 42)
- [ ] 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.
-->
2025-08-03 21:44:47 +00:00
334c5a4884 New config.yml option: afk-timeout-command: (#6169)
Implements this https://github.com/EssentialsX/Essentials/issues/2864

**Specifically, this functionality:**

`afk-timeout-command: "server afk {USERNAME}"`
Sends the player to the AFK server on the server-network.

`afk-timeout-command: "litebans:kick {USERNAME} You were kicked after
{KICK_TIME} minutes of inactivity."`
Kicks the player using litebans so that it shows up in their litebans
history.

`afk-timeout-command: "none"`
Default behaviour of essentials kicking the player on afk-timeout.

---------

Co-authored-by: Jason <51760372+JasonHorkles@users.noreply.github.com>
Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-08-03 21:41:50 +00:00
Josh Roy d402f17318 Reset oxygen with /heal (#6201)
Closes #6197
2025-08-03 21:41:08 +00:00
Josh Roy 636bb097fe Add tipped arrow support to /potion (#6191)
Closes #5576
2025-08-03 21:40:58 +00:00
Josh Roy 74337fff0f Add wind charge explosion protect config (#6183)
Adds new protect config to cancel the knockback effect from windcharges

```yml
protect:

  # General physics/behavior modifications. Set these to true to disable behaviors.
  prevent:
    windcharge-explosion: false
```

Closes #6181
2025-08-03 21:40:51 +00:00
Josh Roy b36d43380c Prepare for 2.22.0 Dev Builds (#6209) 2025-08-03 18:33:12 +00:00
JRoy eb781a192b Release 2.21.2 2025-08-03 19:21:20 +01:00
Flask BotandMD 16f643a41f New Crowdin updates (#6225)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-08-03 18:01:24 +00:00
Josh Roy c01ba4eec4 Bump adventure dependencies (#6243)
Fixes #6215
2025-07-30 19:17:53 +00:00
Josh Roy f4cd1c92e2 Discord: Fix custom bot name requiring show-avatar (#6239)
Fixes #5878. Caused by a regression from
384f63bf92, we were checking for for the
legacy options when we should really be checking if
`mc-to-discord-name-format` is anything other than the bot name.
2025-07-28 05:38:13 +00:00
Josh Roy d002c34fe8 Add version data extraction (#6235)
Will allow me to dynamically fetch the version for the site making it so
we don't have to update it every mc release
2025-07-27 07:10:47 +00:00
Josh Roy ff7c952715 Fix per-player locale for messages with arguments (#6227)
Fixes #6029
2025-07-20 19:01:58 +00:00
Josh Roy 0417624bda Fix local chat spy formatting (#6226)
Fixes #5828
2025-07-20 08:56:46 +00:00
Josh Roy 9ef4fc34c3 Update to Minecraft 1.21.8 (#6224) 2025-07-17 21:55:01 +00:00
Flask BotandMD 73da278b02 New Crowdin updates (#6143)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-07-13 14:19:48 +00:00
Josh Roy e04f4888cd Fix inventory behavior with new body/saddle slot (#6218)
There are new slots that are persistent in the player inventory, the
saddle slot + the body slot, ignore them from our inventory
calculations.

Fixes #6216
2025-07-12 14:10:16 +00:00
Josh RoyandMD dfd83cfb0b Fix /tpr on legacy versions (#6212)
Commodore breaks this on legacy likely, use a provider to ensure we use
the old ABI...................i love legacy

Fixes #6211

---------

Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2025-07-12 14:09:07 +00:00
Josh Roy cc25a798fc Fix player commands option not working (#6214)
Fixes regression from 8d44d1ae7e
2025-07-10 06:24:24 +00:00
Josh Roy e39b61447b Fix numerous antibuild bypasses with signs (#6186)
Fix numerous antibuild bypasses when it came to breaking blocks that
signs were attached to

BOY DO WE NEED BALLOON TO CLEAN ALL THIS STUFF UP

Fixes #5784
Fixes #6173
2025-07-09 19:44:44 +00:00
b996a9f143 Fix formatCommand to handle empty args correctly (#6213)
<!--

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. -->
Allows creating a clickable confirmation button for the clear command
message.
The space prevents the command from being executed properly, as this
tries to run `'/clear '` instead of `'/clear'`.

**Environments tested:**    

<!-- Type the OS you have used below. -->
OS: Linux

<!-- Type the JDK version (from java -version) you have used below. -->
Java version:  21

<!--
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.
-->
- [X] 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.
-->

---------

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-09 18:11:18 +00:00
Josh Roy 3563b41556 Fix command data task (#6207) 2025-07-05 21:54:18 +00:00
Josh Roy ed3c9b83b5 Command + Permission Data Extraction (#6083)
for the new site, we're gonna have our own command and permissions page
and to make this less of a pain, this PR adds a gradle task which will
export that data. the resulting files end up in the root directory under
the generated folder.


gotta figure out how we automatically fetch this for the site, maybe we
make the generated json files part of the jenkins build process?
2025-07-05 21:43:29 +00:00
Josh Roy 6ad737d87a Fix various TPR issues (#6205)
Fixes various issues from the TPR improvements PR

Fixes #6035
2025-07-05 07:17:53 +00:00
Josh Roy 8d44d1ae7e Fix bstats error with unsupported permission plugins (#6190)
Fixes #5225
2025-07-04 15:35:00 +00:00
Josh Roy dae9e3d091 Trim chat messages with shout/question prefix (#6192)
Fixes #5333
2025-07-04 15:33:11 +00:00
Josh Roy 573aae01ee Fix /skull issues on 1.20.4+ (#6188)
Fixes #6124
2025-07-04 15:33:09 +00:00
Josh Roy c43ad269a8 Fix chat url parsing for tlds >3 chars (#6184)
Mojang only looks for urls with 2,3 chars but if we don't block urls
with >3 their regex will still parse the url trimming the extra chars
off the tld *sigh*

Fixes #6156
2025-07-04 15:33:06 +00:00
Josh Roy e3b4e7723f Fix PlaceholderAPI maven repo url (#6204) 2025-07-04 15:33:04 +00:00
Josh Roy 6100cedc58 Fix max nick length with hex color codes (#6185)
Hex color codes convert to the obnoxious md5 format which massively
inflates the character count than what the user entered. Unformat the
nickname before getting the length to remain true to what the user
entered as the command argument.

Fixes #6174
2025-07-04 15:21:57 +00:00
Josh Roy e6b8493749 Fix misleading whois command description (#6187)
Fixes #5616
2025-07-04 15:21:53 +00:00
MD fa4c6b66d5 Add merge queue trigger to PR workflow (#6203) 2025-07-04 15:17:39 +00:00
JRoy 715589c071 Add 1.21.7 Aliases 2025-06-30 08:45:26 -07:00
JRoy 4a06fd1138 Update to Minecraft 1.21.7 2025-06-30 08:45:26 -07:00
Josh Roy 25f2f76302 Update paste link inside of issue template (#6182)
paste.gg is kill
2025-06-28 07:03:27 +01:00
Josh Roy ff1b8b8615 Add 1.21.6 Aliases 2025-06-21 13:55:02 -07:00
Josh Roy 31ea9036cf Move tests to MockBukkit 2025-06-21 13:55:02 -07:00
Josh Roy 2796399268 Add 1.21.6 Mobs 2025-06-21 13:55:02 -07:00
Josh Roy 4057326b4f Update to Minecraft 1.21.6 2025-06-21 13:55:02 -07:00
Josh Roy b44a9b2256 Initial Move to Paper 1.21.6 2025-06-21 13:55:02 -07:00
Ahmed Waleed 4ab3bbca95 [ci skip] Update setup-gradle version (#6165) 2025-06-07 09:19:56 -07:00
Josh Roy f5c06b9676 Fix javadoc publish (#6164)
Netlify now requires a modern node version
2025-06-07 12:10:13 +01:00
Ahmed WaleedandJosh Roy 997f600c15 Update adventure dependencies (#6163)
Fixes issues with clickable chat components on 1.21.5

Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com>
2025-06-05 22:17:44 -07:00
ThiagoROX d42347fb09 Fix sending message during /editsign paste if SignChangeEvent is cancelled (#6136) 2025-05-25 13:27:48 -07:00
Josh Roy e836747244 Prepare for 2.21.2 dev builds (#6128) 2025-05-25 20:35:54 +01:00
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 d73ed137fa Fix time/weather commands 2023-08-22 12:12:33 -04:00
Josh Roy d0493ccd2a Merge remote-tracking branch 'upstream/2.x' into refactor/folia
# Conflicts:
#	Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java
2023-08-22 11:37:36 -04:00
Josh Roy 0c8bc16c3d Fix time command 2023-07-27 19:59:56 -04:00
Josh Roy d4ebaf61d4 Fix weather command 2023-07-27 19:38:32 -04:00
Josh Roy 1a108b5e2e Bump folia version 2023-07-27 19:35:45 -04:00
Josh Roy 10fc005652 Merge remote-tracking branch 'upstream/2.x' into refactor/folia
# Conflicts:
#	EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java
2023-07-27 19:30:00 -04:00
Josh Roy 4256909eee Fix folia init tasks never getting called LOLW 2023-04-26 21:28:33 -04:00
Josh Roy 9133dce3eb Merge branch '2.x' into refactor/folia 2023-04-15 20:34:56 -04:00
Josh Roy ec5007c07e fix wrong period/timeunit for folia scheduling 2023-04-12 18:45:22 -04:00
Josh Roy 4cb861badc Run interaction commands async for now (hopefully okay) 2023-04-02 21:11:55 -04:00
Josh Roy 7279be7850 allow DiscordMessageEvent calls from any context 2023-04-02 20:58:56 -04:00
Josh Roy 2ba1eb0cf9 fix "Fix moo" 2023-04-01 17:42:38 -04:00
Josh Roy 7216fddfbe Add helper methods for ensuring thread contexts 2023-03-30 13:40:13 -04:00
Josh Roy 2d8cc827b9 Add method for checking the global thread 2023-03-30 13:31:16 -04:00
Josh Roy 4ff9286a79 Move over some discord things 2023-03-30 13:28:41 -04:00
Josh Roy 9a5ac3cad8 Fix tests 2023-03-30 13:20:29 -04:00
Josh Roy 05a8aa8883 Switch to Paper repo 2023-03-30 13:06:35 -04:00
Josh Roy 1d93ebfddd Fix moo 2023-03-29 19:59:30 -04:00
Josh Roy 04683ee43b Fix async entity dismounts 2023-03-29 19:57:57 -04:00
Josh Roy a649f978c5 Add some thread checking helper methods
pending PaperMC/Folia#5 for global region helpers
2023-03-28 21:57:24 -04:00
Josh Roy 20882940c5 Actually fire init tasks in Folia 2023-03-28 21:30:51 -04:00
Josh Roy 07fbf93001 Ensure nuke commands runs on correct threads 2023-03-28 21:23:27 -04:00
Josh Roy 0e4bab24cb Get like 70% of the plugin to work 2023-03-27 23:31:39 -04:00
Josh Roy 15c48d457a Add Folia providers 2023-03-27 23:29:42 -04:00
316 changed files with 15225 additions and 6060 deletions

No files matched your search

+2 -2
View File
@@ -32,8 +32,8 @@ body:
- 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"
description: If you are reporting a console error, upload any relevant log excerpts to either https://pastes.dev 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://pastes.dev/UYL3ASLsee"
- type: textarea
attributes:
+3 -3
View File
@@ -27,7 +27,7 @@ jobs:
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: |
@@ -35,7 +35,7 @@ jobs:
./gradlew build --stacktrace
- name: Publish JUnit report
uses: mikepenz/action-junit-report@v4
uses: mikepenz/action-junit-report@v5
if: success() || failure() # Run even if the previous step fails
with:
report_paths: '**/build/test-results/test*/TEST-*.xml'
@@ -82,7 +82,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 22
- name: Download Javadocs
uses: actions/download-artifact@v4
+3 -1
View File
@@ -10,6 +10,8 @@ on:
- 2.x
- mc/*
- dev/*
merge_group:
types: [checks_requested]
permissions:
checks: write
@@ -32,7 +34,7 @@ jobs:
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: |
+105
View File
@@ -0,0 +1,105 @@
name: Mirror EssentialsX dumps to Gist
on:
issues:
types: [opened, edited, closed]
issue_comment:
types: [created]
permissions:
issues: write
contents: read
env:
DUMP_REGEX: 'https?://essentialsx\.net/dump\?bytebin=([A-Za-z0-9_-]+)'
GIST_LINK_FMT: 'https://essentialsx.net/dump?gist='
jobs:
handle-dump:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Mirror dump to a private Gist
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GIST_TOKEN }}
script: |
const issue = context.payload.issue ?? context.payload.comment.issue;
const bodySrc = context.payload.comment ? context.payload.comment.body : issue.body || '';
const regex = new RegExp(process.env.DUMP_REGEX);
const m = bodySrc.match(regex);
if (!m) {
core.info('No EssentialsX dump link found – abort.');
return;
}
const key = m[1];
const dump = await fetch(`https://api.pastes.dev/${key}`);
if (!dump.ok) throw new Error(`pastes.dev fetch failed (${dump.status})`);
const text = await dump.text();
const { data: gist } = await github.rest.gists.create({
files: { [`${key}.txt`]: { content: text } },
description: `EssentialsX dump for issue #${issue.number}`,
public: false
});
const link = `${process.env.GIST_LINK_FMT}${gist.id}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `📋 Essentials Dump Backup → ${link}`
});
cleanup-gist:
if: github.event_name == 'issues' && github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete gist and hide bot comment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GIST_TOKEN }}
script: |
const issue = context.payload.issue;
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, per_page: 100 }
);
const re = /https?:\/\/essentialsx\.net\/dump\?gist=([A-Za-z0-9]+)/;
const hit = comments.reverse().find(c => re.test(c.body || ''));
if (!hit) {
core.info('No gist link comment found – nothing to clean up.');
return;
}
const gistId = (hit.body.match(re))[1];
const commentNodeId = hit.node_id;
try {
await github.request('DELETE /gists/{gist_id}', { gist_id: gistId });
core.info(`Deleted Gist ${gistId}`);
} catch (err) {
core.warning(`Could not delete Gist ${gistId}: ${err.message}`);
}
try {
await github.graphql(
`mutation($id:ID!){
minimizeComment(input:{subjectId:$id, classifier:OUTDATED}) {
minimizedComment { id }
}
}`,
{ id: commentNodeId }
);
core.info(`Minimised comment ${hit.id} as OUTDATED`);
} catch (err) {
core.warning(`Could not minimise comment: ${err.message}`);
}
+43
View File
@@ -0,0 +1,43 @@
name: Command and Permissions Data
on:
push:
branches:
- 2.x
jobs:
data:
name: Generate and Upload
runs-on: ubuntu-latest
steps:
- name: Checkout Git Repo
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Generate Data
run: |
chmod +x gradlew
./gradlew commandData
./gradlew versionData
- name: Upload Data
uses: ryand56/r2-upload-action@v1
with:
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
r2-bucket: ${{ secrets.R2_BUCKET }}
source-dir: generated
destination-dir: ./
+5
View File
@@ -21,10 +21,15 @@
# Build files
.gradle/
/jars/
generated/
out/
build/
target/
*.class
build-logic/.kotlin/
build-logic/bin/
bin/
test-tmp/
# Run directory
/run/
+30 -9
View File
@@ -14,21 +14,26 @@ dependencies {
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'
implementation 'net.kyori:adventure-api:4.25.0'
implementation 'net.kyori:adventure-text-minimessage:4.25.0'
implementation 'net.kyori:adventure-platform-bukkit:4.4.1'
// Providers
api project(':providers:BaseProviders')
api project(path: ':providers:PaperProvider', configuration: 'shadow')
api(project(':providers:BaseProviders')) {
exclude(module: 'spigot-api')
}
api(project(path: ':providers:PaperProvider', configuration: 'shadow')){
exclude(module: 'paper-api')
}
api project(':providers:FoliaProvider')
api(project(':providers:NMSReflectionProvider')) {
exclude group: "org.bukkit", module: "bukkit"
exclude(module: 'bukkit')
}
api(project(':providers:1_8Provider')) {
exclude group: "org.spigotmc", module: "spigot"
exclude(module: 'spigot-api')
}
api(project(':providers:1_12Provider')) {
exclude group: "org.bukkit", module: "bukkit"
exclude(module: 'bukkit')
}
}
@@ -36,6 +41,10 @@ test {
testLogging.showStandardStreams = true
}
tasks.register('versionData', VersionDataTask) {
destination.set(rootProject.layout.projectDirectory.dir("generated").file("version-data.json"))
}
shadowJar {
dependencies {
include (dependency('io.papermc:paperlib'))
@@ -65,9 +74,21 @@ shadowJar {
include (dependency('net.kyori:adventure-text-minimessage'))
include (project(':providers:BaseProviders'))
include (project(path: ':providers:PaperProvider', configuration: 'shadow'))
include (project(':providers:FoliaProvider'))
include (project(':providers:NMSReflectionProvider'))
include (project(':providers:1_8Provider'))
include (project(':providers:1_12Provider'))
// Paper in-dev adventure versions (for snapshot/pre-releases)
include (dependency('io.papermc.adventure:adventure-api'))
include (dependency('io.papermc.adventure:adventure-key'))
include (dependency('io.papermc.adventure:adventure-nbt'))
include (dependency('io.papermc.adventure:adventure-text-serializer-gson'))
include (dependency('io.papermc.adventure:adventure-text-serializer-gson-legacy-impl'))
include (dependency('io.papermc.adventure:adventure-text-serializer-json'))
include (dependency('io.papermc.adventure:adventure-text-serializer-json-legacy-impl'))
include (dependency('io.papermc.adventure:adventure-text-serializer-legacy'))
include (dependency('io.papermc.adventure:adventure-text-minimessage'))
}
relocate 'io.papermc.lib', 'com.earth2me.essentials.paperlib'
relocate 'org.bstats', 'com.earth2me.essentials.libs.bstats'
@@ -76,7 +97,7 @@ shadowJar {
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'
relocate 'net.essentialsx.temp.adventure', 'net.kyori.adventure' // Undo temporary relocation for Paper Provider
minimize {
include(dependency('org.checkerframework:checker-qual'))
@@ -139,19 +139,6 @@ public class AsyncTeleport implements IAsyncTeleport {
paperFuture.exceptionally(future::completeExceptionally);
}
private void runOnMain(final Runnable runnable) throws ExecutionException, InterruptedException {
if (Bukkit.isPrimaryThread()) {
runnable.run();
return;
}
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
Bukkit.getScheduler().runTask(ess, () -> {
runnable.run();
taskLock.complete(new Object());
});
taskLock.get();
}
protected void nowAsync(final IUser teleportee, final ITarget target, final TeleportCause cause, final CompletableFuture<Boolean> future) {
cancel(false);
@@ -169,8 +156,8 @@ public class AsyncTeleport implements IAsyncTeleport {
}
try {
runOnMain(() -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
} catch (final ExecutionException | InterruptedException e) {
ess.ensureEntity(teleportee.getBase(), () -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
} catch (final RuntimeException e) {
future.completeExceptionally(e);
return;
}
@@ -190,8 +177,7 @@ public class AsyncTeleport implements IAsyncTeleport {
if (LocationUtil.isBlockUnsafeForUser(ess, teleportee, chunk.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
if (ess.getSettings().isTeleportSafetyEnabled()) {
if (ess.getSettings().isForceDisableTeleportSafety()) {
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.
teleportee.getBase().teleport(loc, cause);
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
} else {
try {
//There's a chance the safer location is outside the loaded chunk so still teleport async here.
@@ -207,8 +193,7 @@ public class AsyncTeleport implements IAsyncTeleport {
}
} else {
if (ess.getSettings().isForceDisableTeleportSafety()) {
//The chunk we're teleporting to is 100% going to be loaded here, no need to teleport async.
teleportee.getBase().teleport(loc, cause);
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
} else {
if (ess.getSettings().isTeleportToCenterLocation()) {
loc = LocationUtil.getRoundedDestination(loc);
@@ -457,6 +442,10 @@ public class AsyncTeleport implements IAsyncTeleport {
nowAsync(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND, future);
}
public TeleportType getTpType() {
return this.tpType;
}
public void setTpType(final TeleportType tpType) {
this.tpType = tpType;
}
@@ -2,12 +2,16 @@ package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import net.ess3.provider.SchedulingProvider;
import org.bukkit.Location;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.essentialsx.api.v2.events.TeleportWarmupCancelledEvent;
import net.essentialsx.api.v2.events.TeleportWarmupCancelledEvent.CancelReason;
public class AsyncTimedTeleport implements Runnable {
private static final double MOVE_CONSTANT = 0.3;
private final IUser teleportOwner;
@@ -28,7 +32,7 @@ public class AsyncTimedTeleport implements Runnable {
private final boolean timer_canMove;
private final Trade timer_chargeFor;
private final TeleportCause timer_cause;
private int timer_task;
private SchedulingProvider.EssentialsTask timer_task;
private double timer_health;
AsyncTimedTeleport(final IUser user, final IEssentials ess, final AsyncTeleport teleport, final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) {
@@ -52,7 +56,7 @@ public class AsyncTimedTeleport implements Runnable {
this.timer_respawn = respawn;
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20);
if (future != null) {
this.parentFuture = future;
@@ -139,16 +143,24 @@ public class AsyncTimedTeleport implements Runnable {
}
}
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
ess.scheduleEntityDelayedTask(teleportOwner.getBase(), new DelayedTeleportTask());
}
//If we need to cancelTimer a pending teleportPlayer call this method
void cancelTimer(final boolean notifyUser) {
if (timer_task == -1) {
if (timer_task == null) {
return;
}
try {
ess.getServer().getScheduler().cancelTask(timer_task);
timer_task.cancel();
final IUser teleportUser = ess.getUser(this.timer_teleportee);
if (teleportUser != null && teleportUser.getBase() != null) {
final TeleportWarmupCancelledEvent.CancelReason cancelReason = teleportUser.getBase().isOnline() ? CancelReason.MOVE : CancelReason.LEAVE;
final TeleportWarmupCancelledEvent event = new TeleportWarmupCancelledEvent(teleportUser.getBase(), this.teleport.getTpType(), cancelReason, notifyUser);
ess.getServer().getPluginManager().callEvent(event);
}
if (notifyUser) {
teleportOwner.sendTl("pendingTeleportCancelled");
if (timer_teleportee != null && !timer_teleportee.equals(teleportOwner.getBase().getUniqueId())) {
@@ -156,7 +168,7 @@ public class AsyncTimedTeleport implements Runnable {
}
}
} finally {
timer_task = -1;
timer_task = null;
}
}
}
@@ -1,7 +1,7 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import net.ess3.api.IEssentials;
import net.ess3.provider.SchedulingProvider;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
@@ -19,7 +19,7 @@ public class Backup implements Runnable {
private transient final IEssentials ess;
private final AtomicBoolean pendingShutdown = new AtomicBoolean(false);
private transient boolean running = false;
private transient int taskId = -1;
private transient SchedulingProvider.EssentialsTask task = null;
private transient boolean active = false;
private transient CompletableFuture<Object> taskLock = null;
@@ -37,10 +37,10 @@ public class Backup implements Runnable {
public synchronized void stopTask() {
running = false;
if (taskId != -1) {
server.getScheduler().cancelTask(taskId);
if (task != null) {
task.cancel();
}
taskId = -1;
task = null;
}
private synchronized void startTask() {
@@ -49,7 +49,7 @@ public class Backup implements Runnable {
if (interval < 1200) {
return;
}
taskId = ess.scheduleSyncRepeatingTask(this, interval, interval);
task = ess.scheduleGlobalRepeatingTask(this, interval, interval);
running = true;
}
}
@@ -80,7 +80,7 @@ public class Backup implements Runnable {
taskLock.complete(new Object());
return;
}
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("backupStarted")));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("backupStarted")));
final CommandSender cs = server.getConsoleSender();
server.dispatchCommand(cs, "save-all");
server.dispatchCommand(cs, "save-off");
@@ -119,12 +119,12 @@ public class Backup implements Runnable {
}
active = false;
taskLock.complete(new Object());
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("backupFinished")));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("backupFinished")));
}
}
if (!pendingShutdown.get()) {
ess.scheduleSyncDelayedTask(new BackupEnableSaveTask());
ess.scheduleGlobalDelayedTask(new BackupEnableSaveTask());
}
}
});
@@ -53,7 +53,10 @@ public class BalanceTopImpl implements BalanceTop {
}
final LinkedHashMap<UUID, Entry> sortedMap = new LinkedHashMap<>();
entries.sort((entry1, entry2) -> entry2.getBalance().compareTo(entry1.getBalance()));
for (Entry entry : entries) {
final int entryLimit = ess.getSettings().getBaltopEntryLimit();
final int limit = entryLimit == -1 ? entries.size() : Math.min(entryLimit, entries.size());
for (int i = 0; i < limit; i++) {
final Entry entry = entries.get(i);
sortedMap.put(entry.getUuid(), entry);
}
topCache = sortedMap;
@@ -1,8 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import com.earth2me.essentials.adventure.ComponentHolder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -37,7 +35,7 @@ public class CommandSource {
final String translation = tlLiteral(tlKey, args);
if (!translation.isEmpty()) {
sendComponent(AdventureUtil.miniMessage().deserialize(translation));
sendComponent(ess.getAdventureFacet().deserializeMiniMessage(translation));
}
}
@@ -49,18 +47,17 @@ public class CommandSource {
return tlLiteral(tlKey, args);
}
public Component tlComponent(final String tlKey, final Object... args) {
public ComponentHolder tlComponent(final String tlKey, final Object... args) {
if (isPlayer()) {
//noinspection ConstantConditions
return getUser().tlComponent(tlKey, args);
}
final String translation = tlLiteral(tlKey, args);
return AdventureUtil.miniMessage().deserialize(translation);
return ess.getAdventureFacet().deserializeMiniMessage(translation);
}
public void sendComponent(final Component component) {
final BukkitAudiences audiences = ess.getBukkitAudience();
audiences.sender(sender).sendMessage(component);
public void sendComponent(final ComponentHolder component) {
ess.getAdventureFacet().send(sender, component);
}
public final net.ess3.api.IUser getUser() {
@@ -1,10 +1,8 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureFacet;
import com.earth2me.essentials.messaging.IMessageRecipient;
import com.earth2me.essentials.messaging.SimpleMessageRecipient;
import com.earth2me.essentials.utils.AdventureUtil;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -73,10 +71,8 @@ public final class Console implements IMessageRecipient {
return;
}
final Audience consoleAudience = ((Essentials) ess).getBukkitAudience().sender(getCommandSender());
final Component component = AdventureUtil.miniMessage()
.deserialize(translation);
consoleAudience.sendMessage(component);
final AdventureFacet adventureFacet = ess.getAdventureFacet();
adventureFacet.send(getCommandSender(), adventureFacet.deserializeMiniMessage(translation));
}
@Override
@@ -17,12 +17,18 @@
*/
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureFacet;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.adventure.ComponentHolder;
import com.earth2me.essentials.adventure.PaperAdventureFacet;
import com.earth2me.essentials.adventure.SpigotAdventureFacet;
import com.earth2me.essentials.commands.EssentialsCommand;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.commands.NoChargeException;
import com.earth2me.essentials.commands.NotEnoughArgumentsException;
import com.earth2me.essentials.commands.PlayerNotFoundException;
import com.earth2me.essentials.commands.QuietAbortException;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.economy.EconomyLayers;
import com.earth2me.essentials.economy.vault.VaultEconomyProvider;
import com.earth2me.essentials.items.AbstractItemDb;
@@ -40,7 +46,6 @@ import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.updatecheck.UpdateChecker;
import com.earth2me.essentials.userstorage.ModernUserMap;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
@@ -63,16 +68,21 @@ import net.ess3.provider.InventoryViewProvider;
import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.PlayerLocaleProvider;
import net.ess3.provider.ProviderListener;
import net.ess3.provider.SchedulingProvider;
import net.ess3.provider.ServerStateProvider;
import net.ess3.provider.providers.BaseBannerDataProvider;
import net.ess3.provider.providers.BaseInventoryViewProvider;
import net.ess3.provider.providers.BaseLoggerProvider;
import net.ess3.provider.providers.BlockMetaSpawnerItemProvider;
import net.ess3.provider.providers.BukkitMaterialTagProvider;
import net.ess3.provider.providers.BukkitSchedulingProvider;
import net.ess3.provider.providers.BukkitSpawnerBlockProvider;
import net.ess3.provider.providers.BukkitTileEntityProvider;
import net.ess3.provider.providers.BukkitWorldTileEntityCountProvider;
import net.ess3.provider.providers.FixedHeightWorldInfoProvider;
import net.ess3.provider.providers.FlatSpawnEggProvider;
import net.ess3.provider.providers.FoliaSchedulingProvider;
import net.ess3.provider.providers.LegacyBannerDataProvider;
import net.ess3.provider.providers.LegacyBiomeNameProvider;
import net.ess3.provider.providers.LegacyDamageEventProvider;
import net.ess3.provider.providers.LegacyInventoryViewProvider;
import net.ess3.provider.providers.LegacyItemUnbreakableProvider;
@@ -95,12 +105,13 @@ import net.ess3.provider.providers.PaperRecipeBookListener;
import net.ess3.provider.providers.PaperSerializationProvider;
import net.ess3.provider.providers.PaperServerStateProvider;
import net.ess3.provider.providers.PaperTickCountProvider;
import net.ess3.provider.providers.PaperTileEntityProvider;
import net.ess3.provider.providers.PaperWorldTileEntityCountProvider;
import net.ess3.provider.providers.PrehistoricPotionMetaProvider;
import net.essentialsx.api.v2.services.BalanceTop;
import net.essentialsx.api.v2.services.mail.MailService;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.block.Block;
@@ -110,6 +121,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
@@ -120,18 +132,11 @@ import org.bukkit.event.player.PlayerEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.InvalidDescriptionException;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -143,6 +148,8 @@ import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -181,58 +188,17 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
private transient Kits kits;
private transient RandomTeleport randomTeleport;
private transient UpdateChecker updateChecker;
private transient BukkitAudiences bukkitAudience;
private transient AdventureFacet adventureFacet;
static {
EconomyLayers.init();
}
public Essentials() {
}
protected Essentials(final JavaPluginLoader loader, final PluginDescriptionFile description, final File dataFolder, final File file) {
super(loader, description, dataFolder, file);
}
public Essentials(final Server server) {
super(new JavaPluginLoader(server), new PluginDescriptionFile("Essentials", "", "com.earth2me.essentials.Essentials"), null, null);
}
@Override
public ISettings getSettings() {
return settings;
}
public void setupForTesting(final Server server) throws IOException, InvalidDescriptionException {
TESTING = true;
LOGGER = new BaseLoggerProvider(this, BUKKIT_LOGGER);
final File dataFolder = File.createTempFile("essentialstest", "");
if (!dataFolder.delete()) {
throw new IOException();
}
if (!dataFolder.mkdir()) {
throw new IOException();
}
i18n = new I18n(this);
i18n.onEnable();
i18n.updateLocale("en");
Console.setInstance(this);
LOGGER.log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("usingTempFolderForTesting")));
LOGGER.log(Level.INFO, dataFolder.toString());
settings = new Settings(this);
mail = new MailServiceImpl(this);
userMap = new ModernUserMap(this);
balanceTop = new BalanceTopImpl(this);
permissionsHandler = new PermissionsHandler(this, false);
Economy.setEss(this);
confList = new ArrayList<>();
jails = new Jails(this);
registerListeners(server.getPluginManager());
kits = new Kits(this);
bukkitAudience = BukkitAudiences.create(this);
}
@Override
public void onLoad() {
try {
@@ -253,6 +219,8 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
LOGGER = EssentialsLogger.getLoggerProvider(this);
EssentialsLogger.updatePluginLogger(this);
initAdventureFacet();
execTimer = new ExecuteTimer();
execTimer.start();
@@ -268,33 +236,37 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
switch (VersionUtil.getServerSupportStatus()) {
case NMS_CLEANROOM:
getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedCleanroom")));
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedCleanroom")));
break;
case DANGEROUS_FORK:
getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedDangerous")));
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedDangerous")));
break;
case STUPID_PLUGIN:
getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedDumbPlugins")));
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedDumbPlugins")));
break;
case UNSTABLE:
getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedMods")));
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedMods")));
break;
case OUTDATED:
getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupported")));
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupported")));
break;
case LIMITED:
getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedLimitedApi")));
getLogger().info(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedLimitedApi")));
break;
}
if (VersionUtil.getSupportStatusClass() != null) {
getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedClass", VersionUtil.getSupportStatusClass())));
getLogger().info(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedClass", VersionUtil.getSupportStatusClass())));
}
if (VersionUtil.getServerBukkitVersion().isSnapshot()) {
getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverSnapshot")));
}
final PluginManager pm = getServer().getPluginManager();
for (final Plugin plugin : pm.getPlugins()) {
if (plugin.getDescription().getName().startsWith("Essentials") && !plugin.getDescription().getVersion().equals(this.getDescription().getVersion()) && !plugin.getDescription().getName().equals("EssentialsAntiCheat")) {
getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("versionMismatch", plugin.getDescription().getName())));
getLogger().warning(getAdventureFacet().miniToLegacy(tlLiteral("versionMismatch", plugin.getDescription().getName())));
}
}
@@ -357,9 +329,6 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
confList.add(jails);
execTimer.mark("Init(Jails)");
EconomyLayers.onEnable(this);
execTimer.mark("Init(EconomyLayers)");
// Spawner item provider only uses one, but it's here for legacy...
providerFactory.registerProvider(BlockMetaSpawnerItemProvider.class);
@@ -420,13 +389,27 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
// Inventory View Provider
providerFactory.registerProvider(LegacyInventoryViewProvider.class, BaseInventoryViewProvider.class);
// Biome Name Provider
providerFactory.registerProvider(LegacyBiomeNameProvider.class);
// Biome Key Provider
providerFactory.registerProvider(PaperBiomeKeyProvider.class);
// Tile Entity Provider
providerFactory.registerProvider(BukkitTileEntityProvider.class, PaperTileEntityProvider.class);
// World Tile Entity Count Provider
providerFactory.registerProvider(BukkitWorldTileEntityCountProvider.class, PaperWorldTileEntityCountProvider.class);
// Tick Count Provider
providerFactory.registerProvider(PaperTickCountProvider.class);
providerFactory.finalizeRegistration();
// Scheduling provider
providerFactory.registerProvider(FoliaSchedulingProvider.class, BukkitSchedulingProvider.class);
if (!TESTING || true) { // FIXME: The scheduling provider is necessary for tests to pass without adding checks for TESTING everywhere
providerFactory.finalizeRegistration();
}
// Event Providers
if (PaperLib.isPaper()) {
@@ -445,6 +428,12 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
execTimer.mark("Init(Providers)");
EconomyLayers.onEnable(this);
execTimer.mark("Init(EconomyLayers)");
registerListeners(getServer().getPluginManager());
initAdventureFacet();
reload();
// The item spawn blacklist is loaded with all other settings, before the item
@@ -455,7 +444,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
alternativeCommandsHandler = new AlternativeCommandsHandler(this);
timer = new EssentialsTimer(this);
scheduleSyncRepeatingTask(timer, 1000, 50);
scheduleGlobalRepeatingTask(timer, 1000, 50);
Economy.setEss(this);
execTimer.mark("RegHandler");
@@ -464,15 +453,17 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
PermissionsDefaults.registerAllBackDefaults();
PermissionsDefaults.registerAllHatDefaults();
updateChecker = new UpdateChecker(this);
runTaskAsynchronously(() -> {
getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("versionFetching")));
for (final Component component : updateChecker.getVersionMessages(false, true, new CommandSource(this, Bukkit.getConsoleSender()))) {
getLogger().log(getSettings().isUpdateCheckEnabled() ? Level.WARNING : Level.INFO, AdventureUtil.adventureToLegacy(component));
}
});
if (!TESTING) {
updateChecker = new UpdateChecker(this);
runTaskAsynchronously(() -> {
getLogger().log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("versionFetching")));
for (final ComponentHolder component : updateChecker.getVersionMessages(false, true, new CommandSource(this, Bukkit.getConsoleSender()))) {
getLogger().log(getSettings().isUpdateCheckEnabled() ? Level.WARNING : Level.INFO, getAdventureFacet().adventureToLegacy(component));
}
});
metrics = new MetricsWrapper(this, 858, true);
metrics = new MetricsWrapper(this, 858, true);
}
execTimer.mark("Init(External)");
@@ -486,7 +477,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
handleCrash(ex);
throw ex;
}
getBackup().setPendingShutdown(false);
if (!TESTING) {
getBackup().setPendingShutdown(false);
}
}
// Returns our provider logger if available
@@ -504,8 +497,6 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
private void registerListeners(final PluginManager pm) {
HandlerList.unregisterAll(this);
if (getSettings().isDebug()) {
LOGGER.log(Level.INFO, "Registering Listeners");
}
@@ -554,11 +545,19 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
@Override
public void onDisable() {
final boolean stopping = provider(ServerStateProvider.class).isStopping();
if (!stopping) {
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("serverReloading")));
if (adventureFacet != null) {
adventureFacet.close();
}
getBackup().setPendingShutdown(true);
final boolean stopping = TESTING || provider(ServerStateProvider.class).isStopping();
if (!stopping) {
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("serverReloading")));
}
if (!TESTING) {
getBackup().setPendingShutdown(true);
}
for (final User user : getOnlineUsers()) {
if (user.isVanished()) {
user.setVanished(false);
@@ -575,8 +574,8 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
}
cleanupOpenInventories();
if (getBackup().getTaskLock() != null && !getBackup().getTaskLock().isDone()) {
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("backupInProgress")));
if (!TESTING && getBackup().getTaskLock() != null && !getBackup().getTaskLock().isDone()) {
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("backupInProgress")));
getBackup().getTaskLock().join();
}
if (i18n != null) {
@@ -586,12 +585,16 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
backup.stopTask();
}
this.getPermissionsHandler().unregisterContexts();
if (!TESTING) {
this.getPermissionsHandler().unregisterContexts();
}
Economy.setEss(null);
Trade.closeLog();
getUsers().shutdown();
EssentialsConfiguration.shutdownExecutor();
HandlerList.unregisterAll(this);
}
@@ -599,11 +602,6 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
public void reload() {
Trade.closeLog();
if (bukkitAudience != null) {
bukkitAudience.close();
bukkitAudience = null;
}
for (final IConf iConf : confList) {
iConf.reloadConfig();
execTimer.mark("Reload(" + iConf.getClass().getSimpleName() + ")");
@@ -613,16 +611,26 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
for (final String commandName : this.getDescription().getCommands().keySet()) {
final Command command = this.getCommand(commandName);
if (command != null) {
command.setDescription(AdventureUtil.miniToLegacy(tlLiteral(commandName + "CommandDescription")));
command.setUsage(AdventureUtil.miniToLegacy(tlLiteral(commandName + "CommandUsage")));
command.setDescription(getAdventureFacet().miniToLegacy(tlLiteral(commandName + "CommandDescription")));
command.setUsage(getAdventureFacet().miniToLegacy(tlLiteral(commandName + "CommandUsage")));
}
}
final PluginManager pm = getServer().getPluginManager();
registerListeners(pm);
initAdventureFacet();
}
AdventureUtil.setEss(this);
bukkitAudience = BukkitAudiences.create(this);
private void initAdventureFacet() {
if (adventureFacet != null) {
adventureFacet.close();
}
if (VersionUtil.isPaper() && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_5_R01)) {
adventureFacet = new PaperAdventureFacet(getSettings() != null ? getSettings().getPrimaryColor() : null, getSettings() != null ? getSettings().getSecondaryColor() : null);
} else {
adventureFacet = new SpigotAdventureFacet(this);
}
AdventureUtil.setAdventureFacet(adventureFacet);
}
private IEssentialsCommand loadCommand(final String path, final String name, final IEssentialsModule module, final ClassLoader classLoader) throws Exception {
@@ -687,7 +695,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
cmd = loadCommand(commandPath, command.getName(), module, classLoader);
} catch (final Exception ex) {
sender.sendTl("commandNotLoaded", commandLabel);
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex);
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex);
return Collections.emptyList();
}
@@ -710,11 +718,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
} catch (final Exception ex) {
showError(sender, ex, commandLabel);
// Tab completion shouldn't fail
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
return Collections.emptyList();
}
} catch (final Throwable ex) {
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
return Collections.emptyList();
}
}
@@ -741,7 +749,8 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
if (cSender instanceof Player) {
final PlayerLocaleProvider localeProvider = provider(PlayerLocaleProvider.class);
getBukkitAudience().sender(cSender).sendMessage(AdventureUtil.miniMessage().deserialize(tlLocale(I18n.getLocale(localeProvider.getLocale((Player) cSender)), "internalError")));
final String miniMessageStr = tlLocale(I18n.getLocale(localeProvider.getLocale((Player) cSender)), "internalError");
getAdventureFacet().send(cSender, getAdventureFacet().deserializeMiniMessage(miniMessageStr));
} else {
cSender.sendMessage(tlLiteral("internalError"));
}
@@ -766,7 +775,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
LOGGER.log(Level.INFO, "CommandBlock at " + bSenderBlock.getX() + "," + bSenderBlock.getY() + "," + bSenderBlock.getZ() + " issued server command: /" + commandLabel + " " + EssentialsCommand.getFinalArg(args, 0));
}
} else if (user == null) {
LOGGER.log(Level.INFO, cSender.getName()+ " issued server command: /" + commandLabel + " " + EssentialsCommand.getFinalArg(args, 0));
if (getSettings().logConsoleCommands()) {
LOGGER.log(Level.INFO, cSender.getName()+ " issued server command: /" + commandLabel + " " + EssentialsCommand.getFinalArg(args, 0));
}
}
final CommandSource sender = new CommandSource(this, cSender);
@@ -797,13 +808,13 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
cmd = loadCommand(commandPath, command.getName(), module, classLoader);
} catch (final Exception ex) {
sender.sendTl("commandNotLoaded", commandLabel);
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex);
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex);
return true;
}
// Check authorization
if (user != null && !user.isAuthorized(cmd, permissionPrefix)) {
LOGGER.log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("deniedAccessCommand", user.getName())));
LOGGER.log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("deniedAccessCommand", user.getName())));
user.sendTl("noAccessCommand");
return true;
}
@@ -844,7 +855,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
sender.sendMessage(command.getUsage().replace("<command>", commandLabel));
}
if (!ex.getMessage().isEmpty()) {
sender.sendComponent(AdventureUtil.miniMessage().deserialize(ex.getMessage()));
sender.sendComponent(getAdventureFacet().deserializeMiniMessage(ex.getMessage()));
}
if (ex.getCause() != null && settings.isDebug()) {
ex.getCause().printStackTrace();
@@ -858,7 +869,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
return true;
}
} catch (final Throwable ex) {
LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex);
return true;
}
}
@@ -894,15 +905,10 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
sender.sendTl("errorWithMessage", exception.getMessage());
}
if (getSettings().isDebug()) {
LOGGER.log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("errorCallingCommand", commandLabel)), exception);
LOGGER.log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("errorCallingCommand", commandLabel)), exception);
}
}
@Override
public BukkitScheduler getScheduler() {
return this.getServer().getScheduler();
}
@Override
public IJails getJails() {
return jails;
@@ -979,13 +985,23 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
final User user;
Player exPlayer;
if (sourceUser != null && (searchTerm.equals("@p") || searchTerm.equals("@s"))) {
return sourceUser;
}
try {
exPlayer = server.getPlayer(UUID.fromString(searchTerm));
} catch (final IllegalArgumentException ex) {
// Prefer exact online name match first always
if (getOffline) {
// When offline lookups are allowed, do not pick partial online matches here; allow exact offline match later
exPlayer = server.getPlayerExact(searchTerm);
} else {
exPlayer = server.getPlayer(searchTerm);
exPlayer = server.getPlayerExact(searchTerm);
if (exPlayer == null) {
// Only consider partial/prefix online match when not explicitly doing an offline-capable lookup
exPlayer = server.getPlayer(searchTerm);
}
}
}
@@ -1022,6 +1038,16 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
}
} else {
// Prefer exact username match among the matched players
for (final Player player : matches) {
if (player.getName().equalsIgnoreCase(searchTerm)) {
final User userMatch = getUser(player);
if (getHidden || canInteractWith(sourceUser, userMatch)) {
return userMatch;
}
}
}
// Then prefer display name/prefix match as before
for (final Player player : matches) {
final User userMatch = getUser(player);
if (userMatch.getDisplayName().startsWith(searchTerm) && (getHidden || canInteractWith(sourceUser, userMatch))) {
@@ -1143,7 +1169,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
final Collection<Player> players = getOnlinePlayers();
for (final Player player : players) {
final User user = getUser(player);
if ((permission == null && (sender == null || !user.isIgnoredPlayer(sender))) || (permission != null && user.isAuthorized(permission))) {
if (permission == null && (sender == null || !user.isIgnoredPlayer(sender)) || permission != null && user.isAuthorized(permission)) {
if (shouldExclude != null && shouldExclude.test(user)) {
continue;
}
@@ -1206,33 +1232,135 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
}
@Override
public BukkitTask runTaskAsynchronously(final Runnable run) {
return this.getScheduler().runTaskAsynchronously(this, run);
public void scheduleInitTask(Runnable runnable) {
provider(SchedulingProvider.class).registerInitTask(runnable);
}
@Override
public BukkitTask runTaskLaterAsynchronously(final Runnable run, final long delay) {
return this.getScheduler().runTaskLaterAsynchronously(this, run, delay);
public void runTaskAsynchronously(final Runnable run) {
provider(SchedulingProvider.class).runAsyncTask(run);
}
@Override
public BukkitTask runTaskTimerAsynchronously(final Runnable run, final long delay, final long period) {
return this.getScheduler().runTaskTimerAsynchronously(this, run, delay, period);
public void runTaskLaterAsynchronously(final Runnable run, final long delay) {
provider(SchedulingProvider.class).runAsyncTaskLater(run, delay);
}
@Override
public int scheduleSyncDelayedTask(final Runnable run) {
return this.getScheduler().scheduleSyncDelayedTask(this, run);
public SchedulingProvider.EssentialsTask runTaskTimerAsynchronously(final Runnable run, final long delay, final long period) {
return provider(SchedulingProvider.class).runAsyncTaskRepeating(run, delay, period);
}
@Override
public int scheduleSyncDelayedTask(final Runnable run, final long delay) {
return this.getScheduler().scheduleSyncDelayedTask(this, run, delay);
public void scheduleEntityDelayedTask(Entity entity, Runnable run) {
provider(SchedulingProvider.class).runEntityTask(entity, run);
}
@Override
public int scheduleSyncRepeatingTask(final Runnable run, final long delay, final long period) {
return this.getScheduler().scheduleSyncRepeatingTask(this, run, delay, period);
public SchedulingProvider.EssentialsTask scheduleEntityDelayedTask(Entity entity, Runnable run, long delay) {
return provider(SchedulingProvider.class).runEntityTask(entity, run, delay);
}
@Override
public SchedulingProvider.EssentialsTask scheduleEntityRepeatingTask(Entity entity, Runnable run, long delay, long period) {
return provider(SchedulingProvider.class).runEntityTaskRepeating(entity, run, delay, period);
}
@Override
public void scheduleLocationDelayedTask(Location location, Runnable run) {
provider(SchedulingProvider.class).runLocationalTask(location, run);
}
@Override
public void scheduleLocationDelayedTask(Location location, Runnable run, long delay) {
provider(SchedulingProvider.class).runLocationalTask(location, run, delay);
}
@Override
public SchedulingProvider.EssentialsTask scheduleLocationRepeatingTask(Location location, Runnable run, long delay, long period) {
return provider(SchedulingProvider.class).runLocationalTaskRepeating(location, run, delay, period);
}
@Override
public void scheduleGlobalDelayedTask(Runnable run, long delay) {
provider(SchedulingProvider.class).runGlobalLocationalTask(run, delay);
}
@Override
public SchedulingProvider.EssentialsTask scheduleGlobalRepeatingTask(Runnable run, long delay, long period) {
return provider(SchedulingProvider.class).runGlobalLocationalTaskRepeating(run, delay, period);
}
@Override
public boolean isEntityThread(Entity entity) {
return provider(SchedulingProvider.class).isEntityThread(entity);
}
@Override
public boolean isRegionThread(Location location) {
return provider(SchedulingProvider.class).isRegionThread(location);
}
@Override
public boolean isGlobalThread() {
return provider(SchedulingProvider.class).isGlobalThread();
}
@Override
public void ensureEntity(Entity entity, Runnable runnable) {
if (isEntityThread(entity)) {
runnable.run();
return;
}
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
scheduleEntityDelayedTask(entity, () -> {
runnable.run();
taskLock.complete(new Object());
});
try {
taskLock.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public void ensureRegion(Location location, Runnable runnable) {
if (isRegionThread(location)) {
runnable.run();
return;
}
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
scheduleLocationDelayedTask(location, () -> {
runnable.run();
taskLock.complete(new Object());
});
try {
taskLock.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public void ensureGlobal(Runnable runnable) {
if (isGlobalThread()) {
runnable.run();
return;
}
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
scheduleGlobalDelayedTask(() -> {
runnable.run();
taskLock.complete(new Object());
});
try {
taskLock.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
@@ -1315,8 +1443,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
return this.getCommand(cmd);
}
public BukkitAudiences getBukkitAudience() {
return bukkitAudience;
@Override
public AdventureFacet getAdventureFacet() {
return adventureFacet;
}
private AbstractItemDb getItemDbFromConfig() {
@@ -44,7 +44,7 @@ public class EssentialsBlockListener implements Listener {
final User user = ess.getUser(event.getPlayer());
if (user.hasUnlimited(is) && user.getBase().getGameMode() == GameMode.SURVIVAL) {
ess.scheduleSyncDelayedTask(() -> {
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
if (is != null && is.getType() != null && !MaterialUtil.isAir(is.getType())) {
final ItemStack cloneIs = is.clone();
cloneIs.setAmount(1);
@@ -110,7 +110,7 @@ public class EssentialsEntityListener implements Listener {
}
}
ess.scheduleSyncDelayedTask(new PowerToolInteractTask());
ess.scheduleEntityDelayedTask(attacker.getBase(), new PowerToolInteractTask());
event.setCancelled(true);
return;
@@ -156,7 +156,7 @@ public class EssentialsEntityListener implements Listener {
}
}
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerDeathEvent(final PlayerDeathEvent event) {
final Entity entity = event.getEntity();
if (entity.hasMetadata("NPC")) {
@@ -1,12 +1,14 @@
package com.earth2me.essentials;
import com.destroystokyo.paper.ClientOption;
import com.earth2me.essentials.adventure.ComponentHolder;
import com.earth2me.essentials.commands.Commandfireball;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.TextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.userstorage.ModernUserMap;
import com.earth2me.essentials.utils.CommonPlaceholders;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
@@ -14,17 +16,21 @@ import com.earth2me.essentials.utils.LocationUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
import io.papermc.paper.ban.BanListType;
import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent;
import io.papermc.paper.event.player.PlayerServerFullCheckEvent;
import net.ess3.api.IEssentials;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.ess3.provider.CommandSendListenerProvider;
import net.ess3.provider.SchedulingProvider;
import net.ess3.provider.FormattedCommandAliasProvider;
import net.ess3.provider.InventoryViewProvider;
import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.TickCountProvider;
import net.ess3.provider.providers.BukkitCommandSendListenerProvider;
import net.ess3.provider.providers.PaperCommandSendListenerProvider;
import net.essentialsx.PaperAdventureSmuggler;
import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent;
import net.kyori.adventure.text.Component;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.GameMode;
@@ -43,17 +49,19 @@ import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerEggThrowEvent;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
@@ -73,19 +81,21 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tlLiteral;
public class EssentialsPlayerListener implements Listener, FakeAccessor {
public class EssentialsPlayerListener implements Listener {
private final transient IEssentials ess;
private final ConcurrentHashMap<UUID, Integer> pendingMotdTasks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<UUID, SchedulingProvider.EssentialsTask> pendingMotdTasks = new ConcurrentHashMap<>();
public EssentialsPlayerListener(final IEssentials parent) {
this.ess = parent;
@@ -158,6 +168,14 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
} else if (isCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new BukkitCommandSendListenerProvider(new CommandSendFilter()), ess);
}
if (VersionUtil.isPaper() && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_21_8_R01)) {
ess.getServer().getPluginManager().registerEvents(new LoginListener1_21(), ess);
ess.getServer().getPluginManager().registerEvents(new JoinListener1_21(), ess);
} else {
ess.getServer().getPluginManager().registerEvents(new LoginListenerPre1_21(), ess);
ess.getServer().getPluginManager().registerEvents(new JoinListenerPre1_21(), ess);
}
}
@EventHandler(priority = EventPriority.NORMAL)
@@ -192,7 +210,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("mutedUserSpeaks", user.getName(), event.getMessage())));
ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", user.getName(), event.getMessage())));
}
try {
final Iterator<Player> it = event.getRecipients().iterator();
@@ -272,12 +290,12 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
public void onPlayerQuit(final PlayerQuitEvent event) {
final User user = ess.getUser(event.getPlayer());
final Integer pendingId = pendingMotdTasks.remove(user.getUUID());
if (pendingId != null) {
ess.getScheduler().cancelTask(pendingId);
final SchedulingProvider.EssentialsTask pendingTask = pendingMotdTasks.remove(user.getUUID());
if (pendingTask != null) {
pendingTask.cancel();
}
if (hideJoinQuitMessages() || (ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit"))) {
if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit")) {
event.setQuitMessage(null);
} else if (ess.getSettings().isCustomQuitMessage() && event.getQuitMessage() != null) {
final Player player = event.getPlayer();
@@ -324,8 +342,72 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
user.dispose();
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(final PlayerJoinEvent event) {
@SuppressWarnings("UnstableApiUsage")
private final class JoinListener1_21 implements Listener {
private final Map<UUID, String> newUserLocales = new ConcurrentHashMap<>();
@EventHandler
public void onPlayerConfigure(final AsyncPlayerConnectionConfigureEvent event) {
ess.getBackup().onPlayerJoin();
final User dUser = ess.getUser(event.getConnection().getProfile().getId());
// Force loading of the locale bundle while we are async to ensure it's ready when the player joins.
final String locale = event.getConnection().getClientOption(ClientOption.LOCALE);
ess.getI18n().blockingLoadBundle(I18n.getLocale(locale));
// This is a first time join, we have to wait until the bukkit Player object is created to create the User object.
// So we store the locale here and apply it when the PlayerJoinEvent is fired.
if (dUser == null) {
newUserLocales.put(event.getConnection().getProfile().getId(), locale);
return;
}
// Set the locale for player to preload the language bundle.
dUser.getPlayerLocale(locale);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(final PlayerJoinEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user == null) {
legacyJoinFlow(event);
return;
}
user.update(event.getPlayer());
// If this is a new user, we set their locale that we stored earlier.
final String locale = newUserLocales.remove(user.getUUID());
if (locale != null) {
user.getPlayerLocale(locale);
}
final long currentTime = System.currentTimeMillis();
user.startTransaction();
if (user.isNPC()) {
user.setNPC(false);
}
user.checkMuteTimeout(currentTime);
user.updateActivity(false, AfkStatusChangeEvent.Cause.JOIN);
user.stopTransaction();
joinFlow(user, currentTime, event.getJoinMessage(), event::setJoinMessage);
}
}
private final class JoinListenerPre1_21 implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(final PlayerJoinEvent event) {
legacyJoinFlow(event);
}
}
private boolean hideJoinQuitMessages() {
return ess.getSettings().hasJoinQuitMessagePlayerCount() && ess.getServer().getOnlinePlayers().size() > ess.getSettings().getJoinQuitMessagePlayerCount();
}
private void legacyJoinFlow(final PlayerJoinEvent event) {
final String joinMessage = event.getJoinMessage();
ess.runTaskAsynchronously(() -> delayedJoin(event.getPlayer(), joinMessage));
@@ -334,17 +416,160 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
private boolean hideJoinQuitMessages() {
return ess.getSettings().hasJoinQuitMessagePlayerCount() && ess.getServer().getOnlinePlayers().size() > ess.getSettings().getJoinQuitMessagePlayerCount();
private void joinFlow(final User user, final long currentTime, final String message, final Consumer<String> joinMessageConsumer) {
user.startTransaction();
final String lastAccountName = user.getLastAccountName(); // For comparison
user.setLastAccountName(user.getBase().getName());
user.setLastLogin(currentTime);
user.setDisplayNick();
updateCompass(user);
user.setLeavingHidden(false);
// Check for new username. If they don't want the message, let's just say it's false.
final boolean newUsername = ess.getSettings().isCustomNewUsernameMessage() && lastAccountName != null && !lastAccountName.equals(user.getBase().getName());
if (!ess.getVanishedPlayersNew().isEmpty() && !user.isAuthorized("essentials.vanish.see")) {
for (final String p : ess.getVanishedPlayersNew()) {
final Player toVanish = ess.getServer().getPlayerExact(p);
if (toVanish != null && toVanish.isOnline()) {
user.getBase().hidePlayer(toVanish);
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Hiding vanished player: " + p);
}
}
}
}
if (user.isAuthorized("essentials.sleepingignored")) {
user.getBase().setSleepingIgnored(true);
}
final String effectiveMessage;
if (ess.getSettings().allowSilentJoinQuit() && (user.isAuthorized("essentials.silentjoin") || user.isAuthorized("essentials.silentjoin.vanish"))) {
if (user.isAuthorized("essentials.silentjoin.vanish")) {
user.setVanished(true);
}
effectiveMessage = null;
} else if (message == null || hideJoinQuitMessages()) {
effectiveMessage = null;
} else if (ess.getSettings().isCustomJoinMessage()) {
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
.replace("{PLAYER}", user.getDisplayName()).replace("{USERNAME}", user.getName())
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUsers().getUserCount()))
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()))
.replace("{PREFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(user.getBase())))
.replace("{SUFFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getSuffix(user.getBase())))
.replace("{OLDUSERNAME}", lastAccountName == null ? "" : lastAccountName);
effectiveMessage = msg.isEmpty() ? null : msg;
} else if (ess.getSettings().allowSilentJoinQuit()) {
effectiveMessage = message;
} else {
effectiveMessage = message;
}
// Only change the vanilla join message if it's different to avoid nuking client translation
if (effectiveMessage != null && !effectiveMessage.equals(message) || message != null && effectiveMessage == null) {
joinMessageConsumer.accept(effectiveMessage);
}
ess.runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage)));
if (ess.getSettings().getMotdDelay() >= 0) {
final int motdDelay = ess.getSettings().getMotdDelay() / 50;
final Runnable motdTask = () -> motdFlow(user);
if (motdDelay > 0) {
pendingMotdTasks.put(user.getUUID(), ess.scheduleEntityDelayedTask(user.getBase(), motdTask, motdDelay));
} else {
motdTask.run();
}
}
if (!ess.getSettings().isCommandDisabled("mail") && user.isAuthorized("essentials.mail")) {
if (user.getUnreadMailAmount() == 0) {
if (ess.getSettings().isNotifyNoNewMail()) {
user.sendTl("noNewMail"); // Only notify if they want us to.
}
} else {
user.notifyOfMail();
}
}
if (user.isAuthorized("essentials.updatecheck")) {
ess.runTaskAsynchronously(() -> {
for (final ComponentHolder component : ess.getUpdateChecker().getVersionMessages(false, false, user.getSource())) {
user.sendComponent(component);
}
});
}
if (user.isAuthorized("essentials.fly.safelogin")) {
user.getBase().setFallDistance(0);
if (LocationUtil.shouldFly(ess, user.getLocation())) {
user.getBase().setAllowFlight(true);
user.getBase().setFlying(true);
if (ess.getSettings().isSendFlyEnableOnJoin()) {
user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName());
}
}
}
if (!user.isAuthorized("essentials.speed")) {
user.getBase().setFlySpeed(0.1f);
user.getBase().setWalkSpeed(0.2f);
}
if (user.isSocialSpyEnabled() && !user.isAuthorized("essentials.socialspy")) {
user.setSocialSpyEnabled(false);
ess.getLogger().log(Level.INFO, "Set socialspy to false for {0} because they had it enabled without permission.", user.getName());
}
if (user.isGodModeEnabled() && !user.isAuthorized("essentials.god")) {
user.setGodModeEnabled(false);
ess.getLogger().log(Level.INFO, "Set god mode to false for {0} because they had it enabled without permission.", user.getName());
}
user.setConfirmingClearCommand(null);
user.getConfirmingPayments().clear();
user.stopTransaction();
}
public void delayedJoin(final Player player, final String message) {
private void motdFlow(final User user) {
pendingMotdTasks.remove(user.getUUID());
IText tempInput = null;
if (!ess.getSettings().isCommandDisabled("motd")) {
try {
tempInput = new TextInput(user.getSource(), "motd", true, ess);
} catch (final IOException ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.WARNING, ex.getMessage(), ex);
} else {
ess.getLogger().log(Level.WARNING, ex.getMessage());
}
}
}
final IText input = tempInput;
if (input != null && !input.getLines().isEmpty() && user.isAuthorized("essentials.motd")) {
final IText output = new KeywordReplacer(input, user.getSource(), ess);
final TextPager pager = new TextPager(output, true);
pager.showPage("1", null, "motd", user.getSource());
}
}
private void delayedJoin(final Player player, final String message) {
if (!player.isOnline()) {
return;
}
ess.getBackup().onPlayerJoin();
final User dUser = ess.getUser(player);
((ModernUserMap) ess.getUsers()).getOnlineUserCache().put(player.getUniqueId(), dUser);
dUser.update(player);
dUser.startTransaction();
@@ -357,170 +582,19 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
dUser.updateActivity(false, AfkStatusChangeEvent.Cause.JOIN);
dUser.stopTransaction();
class DelayJoinTask implements Runnable {
@Override
public void run() {
final User user = ess.getUser(player);
ess.scheduleEntityDelayedTask(player, () -> {
final User user = ess.getUser(player);
if (!user.getBase().isOnline()) {
return;
}
user.startTransaction();
final String lastAccountName = user.getLastAccountName(); // For comparison
user.setLastAccountName(user.getBase().getName());
user.setLastLogin(currentTime);
user.setDisplayNick();
updateCompass(user);
user.setLeavingHidden(false);
// Check for new username. If they don't want the message, let's just say it's false.
final boolean newUsername = ess.getSettings().isCustomNewUsernameMessage() && lastAccountName != null && !lastAccountName.equals(user.getBase().getName());
if (!ess.getVanishedPlayersNew().isEmpty() && !user.isAuthorized("essentials.vanish.see")) {
for (final String p : ess.getVanishedPlayersNew()) {
final Player toVanish = ess.getServer().getPlayerExact(p);
if (toVanish != null && toVanish.isOnline()) {
user.getBase().hidePlayer(toVanish);
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Hiding vanished player: " + p);
}
}
}
}
if (user.isAuthorized("essentials.sleepingignored")) {
user.getBase().setSleepingIgnored(true);
}
final String effectiveMessage;
if (ess.getSettings().allowSilentJoinQuit() && (user.isAuthorized("essentials.silentjoin") || user.isAuthorized("essentials.silentjoin.vanish"))) {
if (user.isAuthorized("essentials.silentjoin.vanish")) {
user.setVanished(true);
}
effectiveMessage = null;
} else if (message == null || hideJoinQuitMessages()) {
effectiveMessage = null;
} else if (ess.getSettings().isCustomJoinMessage()) {
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUsers().getUserCount()))
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()))
.replace("{PREFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player)))
.replace("{SUFFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getSuffix(player)))
.replace("{OLDUSERNAME}", lastAccountName == null ? "" : lastAccountName);
if (!msg.isEmpty()) {
ess.getServer().broadcastMessage(msg);
}
effectiveMessage = msg.isEmpty() ? null : msg;
} else if (ess.getSettings().allowSilentJoinQuit()) {
ess.getServer().broadcastMessage(message);
effectiveMessage = message;
} else {
effectiveMessage = message;
}
ess.runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage)));
if (ess.getSettings().getMotdDelay() >= 0) {
final int motdDelay = ess.getSettings().getMotdDelay() / 50;
final DelayMotdTask motdTask = new DelayMotdTask(user);
if (motdDelay > 0) {
pendingMotdTasks.put(user.getUUID(), ess.scheduleSyncDelayedTask(motdTask, motdDelay));
} else {
motdTask.run();
}
}
if (!ess.getSettings().isCommandDisabled("mail") && user.isAuthorized("essentials.mail")) {
if (user.getUnreadMailAmount() == 0) {
if (ess.getSettings().isNotifyNoNewMail()) {
user.sendTl("noNewMail"); // Only notify if they want us to.
}
} else {
user.notifyOfMail();
}
}
if (user.isAuthorized("essentials.updatecheck")) {
ess.runTaskAsynchronously(() -> {
for (final Component component : ess.getUpdateChecker().getVersionMessages(false, false, user.getSource())) {
user.sendComponent(component);
}
});
}
if (user.isAuthorized("essentials.fly.safelogin")) {
user.getBase().setFallDistance(0);
if (LocationUtil.shouldFly(ess, user.getLocation())) {
user.getBase().setAllowFlight(true);
user.getBase().setFlying(true);
if (ess.getSettings().isSendFlyEnableOnJoin()) {
user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName());
}
}
}
if (!user.isAuthorized("essentials.speed")) {
user.getBase().setFlySpeed(0.1f);
user.getBase().setWalkSpeed(0.2f);
}
if (user.isSocialSpyEnabled() && !user.isAuthorized("essentials.socialspy")) {
user.setSocialSpyEnabled(false);
ess.getLogger().log(Level.INFO, "Set socialspy to false for {0} because they had it enabled without permission.", user.getName());
}
if (user.isGodModeEnabled() && !user.isAuthorized("essentials.god")) {
user.setGodModeEnabled(false);
ess.getLogger().log(Level.INFO, "Set god mode to false for {0} because they had it enabled without permission.", user.getName());
}
user.setConfirmingClearCommand(null);
user.getConfirmingPayments().clear();
user.stopTransaction();
if (!user.getBase().isOnline()) {
return;
}
class DelayMotdTask implements Runnable {
private final User user;
DelayMotdTask(final User user) {
this.user = user;
joinFlow(user, currentTime, message, msg -> {
if (msg != null && !msg.isEmpty()) {
ess.getServer().broadcastMessage(msg);
}
@Override
public void run() {
pendingMotdTasks.remove(user.getUUID());
IText tempInput = null;
if (!ess.getSettings().isCommandDisabled("motd")) {
try {
tempInput = new TextInput(user.getSource(), "motd", true, ess);
} catch (final IOException ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.WARNING, ex.getMessage(), ex);
} else {
ess.getLogger().log(Level.WARNING, ex.getMessage());
}
}
}
final IText input = tempInput;
if (input != null && !input.getLines().isEmpty() && user.isAuthorized("essentials.motd")) {
final IText output = new KeywordReplacer(input, user.getSource(), ess);
final TextPager pager = new TextPager(output, true);
pager.showPage("1", null, "motd", user.getSource());
}
}
}
}
ess.scheduleSyncDelayedTask(new DelayJoinTask());
});
});
}
// Makes the compass item ingame always point to the first essentials home. #EasterEgg
@@ -540,38 +614,95 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
user.getBase().setCompassTarget(loc);
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLoginBanned(final PlayerLoginEvent event) {
if (event.getResult() == Result.KICK_BANNED) {
BanEntry banEntry = ess.getServer().getBanList(BanList.Type.NAME).getBanEntry(event.getPlayer().getName());
if (banEntry != null) {
final Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason())));
} else {
event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banJoin", banEntry.getReason())));
}
} else {
banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress());
private final class LoginListenerPre1_21 implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLoginBanned(final PlayerLoginEvent event) {
if (event.getResult() == PlayerLoginEvent.Result.KICK_BANNED) {
BanEntry banEntry = ess.getServer().getBanList(BanList.Type.NAME).getBanEntry(event.getPlayer().getName());
if (banEntry != null) {
event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason())));
final Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason())));
} else {
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banJoin", banEntry.getReason())));
}
} else {
banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress());
if (banEntry != null) {
final Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason())));
} else {
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason())));
}
}
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerLogin(final PlayerLoginEvent event) {
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL) {
final User kfuser = ess.getUser(event.getPlayer());
kfuser.update(event.getPlayer());
if (kfuser.isAuthorized("essentials.joinfullserver")) {
event.allow();
return;
}
if (ess.getSettings().isCustomServerFullMessage()) {
event.disallow(PlayerLoginEvent.Result.KICK_FULL, tlLiteral("serverFull"));
}
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerLogin(final PlayerLoginEvent event) {
if (event.getResult() == Result.KICK_FULL) {
final User kfuser = ess.getUser(event.getPlayer());
kfuser.update(event.getPlayer());
if (kfuser.isAuthorized("essentials.joinfullserver")) {
event.allow();
private final class LoginListener1_21 implements Listener {
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerListFull(final PlayerServerFullCheckEvent event) {
if (ess.getPermissionsHandler().isOfflinePermissionSet(event.getPlayerProfile().getId(), "essentials.joinfullserver")) {
event.allow(true);
return;
}
if (ess.getSettings().isCustomServerFullMessage()) {
event.disallow(Result.KICK_FULL, tlLiteral("serverFull"));
if (!event.isAllowed() && ess.getSettings().isCustomServerFullMessage()) {
PaperAdventureSmuggler.smugglePlayerServerFullCheckEvent(event, ess.getAdventureFacet().miniToLegacy(tlLiteral("serverFull")));
}
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerKickBanned(final AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() == AsyncPlayerPreLoginEvent.Result.KICK_BANNED) {
BanEntry<?> banEntry = ess.getServer().getBanList(BanListType.PROFILE).getBanEntry(event.getPlayerProfile());
if (banEntry != null) {
final Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason())));
} else {
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banJoin", banEntry.getReason())));
}
} else {
banEntry = ess.getServer().getBanList(BanListType.IP).getBanEntry(event.getAddress());
if (banEntry != null) {
final Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason())));
} else {
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason())));
}
}
}
} else if (event.getLoginResult() == AsyncPlayerPreLoginEvent.Result.KICK_WHITELIST) {
if (ess.getPermissionsHandler().isOfflinePermissionSet(event.getUniqueId(), "essentials.whitelist.bypass")) {
event.allow();
return;
}
if (ess.getSettings().isCustomWhitelistMessage()) {
event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("whitelistKick")));
}
}
}
}
@@ -621,7 +752,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
final User user = ess.getUser(event.getPlayer());
if (user.hasUnlimited(new ItemStack(event.getBucket()))) {
event.getItemStack().setType(event.getBucket());
ess.scheduleSyncDelayedTask(user.getBase()::updateInventory);
ess.scheduleEntityDelayedTask(user.getBase(), user.getBase()::updateInventory);
}
}
@@ -655,16 +786,19 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
if (ess.getSettings().getSocialSpyCommands().contains(cmd) || ess.getSettings().getSocialSpyCommands().contains("*")) {
if (pluginCommand == null
|| (!pluginCommand.getName().equals("msg") && !pluginCommand.getName().equals("r"))) { // /msg and /r are handled in SimpleMessageRecipient
|| !pluginCommand.getName().equals("msg") && !pluginCommand.getName().equals("r")) { // /msg and /r are handled in SimpleMessageRecipient
final User user = ess.getUser(player);
if (!user.isAuthorized("essentials.chat.spy.exempt")) {
final String playerName = ess.getSettings().isSocialSpyDisplayNames() ? player.getDisplayName() : player.getName();
for (final User spyer : ess.getOnlineUsers()) {
if (spyer.isSocialSpyEnabled() && !player.equals(spyer.getBase())) {
final Component base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers())
final ComponentHolder base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers())
? spyer.tlComponent("socialSpyMutedPrefix")
: spyer.tlComponent("socialSpyPrefix");
spyer.sendComponent(base.append(AdventureUtil.legacyToAdventure(playerName)).append(Component.text(": " + event.getMessage())));
final ComponentHolder nameComponent = ess.getAdventureFacet().legacyToAdventure(playerName);
final ComponentHolder messageComponent = ess.getAdventureFacet().text(": " + event.getMessage());
spyer.sendComponent(ess.getAdventureFacet().append(base, nameComponent, messageComponent));
}
}
}
@@ -688,7 +822,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
user.sendTl("voiceSilencedTime", dateDiff);
}
}
ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("mutedUserSpeaks", player.getName(), event.getMessage())));
ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", player.getName(), event.getMessage())));
return;
}
@@ -879,7 +1013,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
ess.scheduleSyncDelayedTask(new DelayedClickJumpTask());
ess.scheduleEntityDelayedTask(user.getBase(), new DelayedClickJumpTask());
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.WARNING, ex.getMessage(), ex);
@@ -910,7 +1044,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
}
ess.scheduleSyncDelayedTask(new PowerToolUseTask());
ess.scheduleEntityDelayedTask(user.getBase(), new PowerToolUseTask());
}
}
@@ -972,7 +1106,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
if (refreshPlayer != null) {
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
ess.scheduleEntityDelayedTask(refreshPlayer, refreshPlayer::updateInventory, 1);
}
}
@@ -984,6 +1118,26 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
return false;
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onInventoryDragEvent(final InventoryDragEvent event) {
final InventoryViewProvider provider = ess.provider(InventoryViewProvider.class);
final Inventory top = provider.getTopInventory(event.getView());
if (top.getType() != InventoryType.PLAYER) {
return;
}
final User user = ess.getUser((Player) event.getWhoClicked());
if (!user.isInvSee()) {
return;
}
for (int slot : event.getNewItems().keySet()) {
if (Inventories.isBottomInventorySlot(slot)) {
event.setCancelled(true);
break;
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onInventoryCloseEvent(final InventoryCloseEvent event) {
Player refreshPlayer = null;
@@ -1015,7 +1169,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
}
if (refreshPlayer != null) {
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
ess.scheduleEntityDelayedTask(refreshPlayer, refreshPlayer::updateInventory, 1);
}
}
@@ -1025,6 +1179,27 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
user.updateActivityOnInteract(true);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerGameModeChange(final PlayerGameModeChangeEvent event) {
if (!ess.getSettings().isGamemodeChangePreserveFlying()) {
return;
}
final User user = ess.getUser(event.getPlayer());
if (!user.isAuthorized("essentials.fly")) {
return;
}
final Player player = event.getPlayer();
if (player.isFlying() && player.getAllowFlight() && user.isAuthorized("essentials.fly")) {
// The gamemode change happens after the event, so we need to delay the flight enable
ess.scheduleEntityDelayedTask(player, () -> {
player.setAllowFlight(true);
player.setFlying(true);
}, 1);
}
}
private static final class ArrowPickupListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onArrowPickup(final org.bukkit.event.player.PlayerPickupArrowEvent event) {
@@ -1100,12 +1275,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
return command != null
&& (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials") || command.getPlugin().getClass().getName().startsWith("net.essentialsx"))
&& (ess.getSettings().isCommandOverridden(label) || (ess.getAlternativeCommandsHandler().getAlternative(label) == null));
&& (ess.getSettings().isCommandOverridden(label) || ess.getAlternativeCommandsHandler().getAlternative(label) == null);
}
}
@Override
public void getUser(Player player) {
ess.getUser(player);
}
}
@@ -5,7 +5,6 @@ import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
@@ -13,36 +12,19 @@ import java.util.logging.Level;
public class EssentialsTimer implements Runnable {
private final transient IEssentials ess;
private final transient Set<UUID> onlineUsers = new HashSet<>(); // Field is necessary for hidden users
private final LinkedList<Double> history = new LinkedList<>();
@SuppressWarnings("FieldCanBeLocal")
private final long maxTime = 10 * 1000000;
@SuppressWarnings("FieldCanBeLocal")
private final long tickInterval = 50;
private transient long lastPoll = System.nanoTime();
private static final long maxTime = 10 * 1000000;
private int skip1 = 0;
private int skip2 = 0;
EssentialsTimer(final IEssentials ess) {
this.ess = ess;
history.add(20d);
}
@Override
public void run() {
final long startTime = System.nanoTime();
final long currentTime = System.currentTimeMillis();
long timeSpent = (startTime - lastPoll) / 1000;
if (timeSpent == 0) {
timeSpent = 1;
}
if (history.size() > 10) {
history.remove();
}
final double tps = tickInterval * 1000000.0 / timeSpent;
if (tps <= 21) {
history.add(tps);
}
lastPoll = startTime;
int count = 0;
onlineUsers.clear();
for (final Player player : ess.getOnlinePlayers()) {
@@ -99,14 +81,4 @@ public class EssentialsTimer implements Runnable {
user.resetInvulnerabilityAfterTeleport();
}
}
public double getAverageTPS() {
double avg = 0;
for (final Double f : history) {
if (f != null) {
avg += f;
}
}
return avg / history.size();
}
}
@@ -6,7 +6,6 @@ import com.earth2me.essentials.config.EssentialsUserConfiguration;
import com.earth2me.essentials.config.entities.LazyLocation;
import com.earth2me.essentials.craftbukkit.BanLookup;
import com.earth2me.essentials.userstorage.ModernUUIDCache;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
@@ -72,7 +71,7 @@ public class EssentialsUpgrade {
doneFile.load();
}
public static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUFCache) {
private static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUFCache) {
ess.getLogger().info("Starting Essentials UUID userdata conversion");
final File userdir = new File(ess.getDataFolder(), "userdata");
@@ -157,7 +156,7 @@ public class EssentialsUpgrade {
ess.getLogger().info("To rerun the conversion type /essentials uuidconvert");
}
public void updateRandomTeleport() {
private void updateRandomTeleport() {
if (doneFile.getBoolean("updateRandomTeleport", false)) {
return;
}
@@ -192,7 +191,7 @@ public class EssentialsUpgrade {
ess.getLogger().info("Done converting random teleport config.");
}
public void convertMailList() {
private void convertMailList() {
if (doneFile.getBoolean("updateUsersMailList", false)) {
return;
}
@@ -232,7 +231,7 @@ public class EssentialsUpgrade {
ess.getLogger().info("Done converting mail list.");
}
public void convertStupidCamelCaseUserdataKeys() {
private void convertStupidCamelCaseUserdataKeys() {
if (doneFile.getBoolean("updateUsersStupidLegacyPathNames", false)) {
return;
}
@@ -312,7 +311,7 @@ public class EssentialsUpgrade {
* horrible economy code, as any operation which loads all user data into memory will load all these NPC accounts
* and spam the console with warnings."
*/
public void purgeBrokenNpcAccounts() {
private void purgeBrokenNpcAccounts() {
if (doneFile.getBoolean("updatePurgeBrokenNpcAccounts", false)) {
return;
}
@@ -423,7 +422,7 @@ public class EssentialsUpgrade {
ess.getLogger().info("#===========================================================================#");
}
public void convertIgnoreList() {
private void convertIgnoreList() {
final 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;
@@ -474,6 +473,10 @@ public class EssentialsUpgrade {
}
public void convertKits() {
if (Essentials.TESTING) {
return;
}
final Kits kits = ess.getKits();
final EssentialsConfiguration config = kits.getRootConfig();
if (doneFile.getBoolean("kitsyml", false)) {
@@ -531,7 +534,7 @@ public class EssentialsUpgrade {
doneFile.setProperty("move" + name + "ToFile", true);
doneFile.save();
} catch (final IOException e) {
ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("upgradingFilesError")), e);
ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("upgradingFilesError")), e);
}
}
@@ -694,15 +697,15 @@ public class EssentialsUpgrade {
final File tmpFile = new File(listOfFile.getParentFile(), sanitizedFilename + ".tmp");
final File newFile = new File(listOfFile.getParentFile(), sanitizedFilename);
if (!listOfFile.renameTo(tmpFile)) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("userdataMoveError", filename, sanitizedFilename)));
ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("userdataMoveError", filename, sanitizedFilename)));
continue;
}
if (newFile.exists()) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("duplicatedUserdata", filename, sanitizedFilename)));
ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("duplicatedUserdata", filename, sanitizedFilename)));
continue;
}
if (!tmpFile.renameTo(newFile)) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("userdataMoveBackError", sanitizedFilename, sanitizedFilename)));
ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("userdataMoveBackError", sanitizedFilename, sanitizedFilename)));
}
}
doneFile.setProperty("sanitizeAllUserFilenames", true);
@@ -718,7 +721,7 @@ public class EssentialsUpgrade {
return null;
}
public Location getFakeLocation(final CommentedConfigurationNode config, final String path) {
private Location getFakeLocation(final CommentedConfigurationNode config, final String path) {
final String worldName = config.getString((path != null ? path + "." : "") + "world");
if (worldName == null || worldName.isEmpty()) {
return null;
@@ -882,7 +885,7 @@ public class EssentialsUpgrade {
doneFile.save();
}
public void banFormatChange() {
private void banFormatChange() {
if (doneFile.getBoolean("banFormatChange", false)) {
return;
}
@@ -952,7 +955,7 @@ public class EssentialsUpgrade {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, banTimeout == 0 ? null : new Date(banTimeout), Console.NAME);
}
public void generateUidCache() {
private void generateUidCache() {
if (doneFile.getBoolean("newUidCacheBuilt", false)) {
return;
}
@@ -992,7 +995,7 @@ public class EssentialsUpgrade {
if (name != null) {
if (nameToUuidMap.containsKey(name)) {
final UUID oldUuid = nameToUuidMap.get(name);
if (oldUuid.version() < uuid.version() || (oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time)) {
if (oldUuid.version() < uuid.version() || oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time) {
ess.getLogger().warning("New UUID found for " + name + ": " + uuid + " (old: " + oldUuid + "). Replacing.");
uuids.remove(oldUuid);
} else {
@@ -1060,7 +1063,7 @@ public class EssentialsUpgrade {
properties.load(Files.newReader(backup, Charsets.UTF_8));
for (final String key : properties.stringPropertyNames()) {
final String value = properties.getProperty(key);
properties.setProperty(key, AdventureUtil.legacyToMini(AdventureUtil.miniMessage().escapeTags(value), true));
properties.setProperty(key, ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(value), true));
}
properties.store(Files.newWriter(newFile, Charsets.UTF_8), null);
}
@@ -1079,6 +1082,10 @@ public class EssentialsUpgrade {
}
public void beforeSettings() {
if (Essentials.TESTING) {
return;
}
if (!ess.getDataFolder().exists()) {
ess.getDataFolder().mkdirs();
}
@@ -1087,10 +1094,17 @@ public class EssentialsUpgrade {
}
public void preModules() {
if (Essentials.TESTING) {
return;
}
generateUidCache();
}
public void afterSettings() {
if (Essentials.TESTING) {
return;
}
sanitizeAllUserFilenames();
updateUsersPowerToolsFormat();
updateUsersHomesFormat();
@@ -1,6 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import net.ess3.api.IEssentials;
import org.jetbrains.annotations.NotNull;
@@ -92,7 +92,7 @@ public class I18n implements net.ess3.api.II18n {
if (objects.length == 0) {
return NODOUBLEMARK.matcher(instance.translate(locale, tlKey)).replaceAll("'");
} else {
return instance.format(tlKey, objects);
return instance.format(locale, tlKey, objects);
}
}
@@ -125,18 +125,7 @@ public class I18n implements net.ess3.api.II18n {
if (!loadingBundles.contains(locale)) {
loadingBundles.add(locale);
BUNDLE_LOADER_EXECUTOR.submit(() -> {
ResourceBundle bundle;
try {
bundle = ResourceBundle.getBundle(MESSAGES, locale, new FileResClassLoader(I18n.class.getClassLoader(), ess), new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
try {
bundle = ResourceBundle.getBundle(MESSAGES, locale, new UTF8PropertiesControl());
} catch (MissingResourceException ex2) {
bundle = NULL_BUNDLE;
}
}
loadedBundles.put(locale, bundle);
blockingLoadBundle(locale);
synchronized (loadingBundles) {
loadingBundles.remove(locale);
}
@@ -147,6 +136,23 @@ public class I18n implements net.ess3.api.II18n {
}
}
public void blockingLoadBundle(final Locale locale) {
if (!loadedBundles.containsKey(locale)) {
ResourceBundle bundle;
try {
bundle = ResourceBundle.getBundle(MESSAGES, locale, new FileResClassLoader(I18n.class.getClassLoader(), ess), new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
try {
bundle = ResourceBundle.getBundle(MESSAGES, locale, new UTF8PropertiesControl());
} catch (MissingResourceException ex2) {
bundle = NULL_BUNDLE;
}
}
loadedBundles.put(locale, bundle);
}
}
private String translate(final Locale locale, final String string) {
try {
try {
@@ -162,10 +168,6 @@ public class I18n implements net.ess3.api.II18n {
}
}
private String format(final String string, final Object... objects) {
return format(currentLocale, string, objects);
}
private String format(final Locale locale, final String string, final Object... objects) {
String format = translate(locale, string);
@@ -185,7 +187,7 @@ public class I18n implements net.ess3.api.II18n {
if (arg instanceof AdventureUtil.ParsedPlaceholder) {
return arg.toString();
}
return AdventureUtil.legacyToMini(AdventureUtil.miniMessage().escapeTags(arg.toString()));
return ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(arg.toString()));
});
return messageFormat.format(processedArgs).replace(' ', ' '); // replace nbsp with a space
@@ -281,7 +283,7 @@ public class I18n implements net.ess3.api.II18n {
* 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 {
private static final class UTF8PropertiesControl extends ResourceBundle.Control {
public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, final ClassLoader loader, final boolean reload) throws IOException {
final String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
ResourceBundle bundle = null;
@@ -1,5 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureFacet;
import com.earth2me.essentials.api.IItemDb;
import com.earth2me.essentials.api.IJails;
import com.earth2me.essentials.api.IWarps;
@@ -8,18 +9,19 @@ import com.earth2me.essentials.commands.PlayerNotFoundException;
import com.earth2me.essentials.perm.PermissionsHandler;
import com.earth2me.essentials.updatecheck.UpdateChecker;
import com.earth2me.essentials.userstorage.IUserMap;
import net.ess3.provider.SchedulingProvider;
import net.ess3.provider.Provider;
import net.essentialsx.api.v2.services.BalanceTop;
import net.essentialsx.api.v2.services.mail.MailService;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.Collection;
import java.util.List;
@@ -79,8 +81,6 @@ public interface IEssentials extends Plugin {
ISettings getSettings();
BukkitScheduler getScheduler();
IJails getJails();
IWarps getWarps();
@@ -95,17 +95,45 @@ public interface IEssentials extends Plugin {
UpdateChecker getUpdateChecker();
BukkitTask runTaskAsynchronously(Runnable run);
void runTaskAsynchronously(Runnable run);
BukkitTask runTaskLaterAsynchronously(Runnable run, long delay);
void runTaskLaterAsynchronously(Runnable run, long delay);
BukkitTask runTaskTimerAsynchronously(Runnable run, long delay, long period);
SchedulingProvider.EssentialsTask runTaskTimerAsynchronously(Runnable run, long delay, long period);
int scheduleSyncDelayedTask(Runnable run);
void scheduleEntityDelayedTask(Entity entity, Runnable run);
int scheduleSyncDelayedTask(Runnable run, long delay);
SchedulingProvider.EssentialsTask scheduleEntityDelayedTask(Entity entity, Runnable run, long delay);
int scheduleSyncRepeatingTask(Runnable run, long delay, long period);
SchedulingProvider.EssentialsTask scheduleEntityRepeatingTask(Entity entity, Runnable run, long delay, long period);
void scheduleLocationDelayedTask(Location location, Runnable run);
void scheduleLocationDelayedTask(Location location, Runnable run, long delay);
SchedulingProvider.EssentialsTask scheduleLocationRepeatingTask(Location location, Runnable run, long delay, long period);
default void scheduleGlobalDelayedTask(Runnable run) {
scheduleGlobalDelayedTask(run, 1);
}
void scheduleGlobalDelayedTask(Runnable run, long delay);
SchedulingProvider.EssentialsTask scheduleGlobalRepeatingTask(Runnable run, long delay, long period);
void scheduleInitTask(Runnable runnable);
boolean isEntityThread(Entity entity);
boolean isRegionThread(Location location);
boolean isGlobalThread();
void ensureEntity(Entity entity, Runnable runnable);
void ensureRegion(Location location, Runnable runnable);
void ensureGlobal(Runnable runnable);
PermissionsHandler getPermissionsHandler();
@@ -146,4 +174,6 @@ public interface IEssentials extends Plugin {
default <P extends Provider> P provider(final Class<P> providerClass) {
return getProviders().get(providerClass);
}
AdventureFacet getAdventureFacet();
}
@@ -4,7 +4,6 @@ import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.signs.EssentialsSign;
import com.earth2me.essentials.textreader.IText;
import net.essentialsx.api.v2.ChatType;
import net.kyori.adventure.text.minimessage.tag.Tag;
import org.bukkit.Material;
import org.bukkit.event.EventPriority;
import org.spongepowered.configurate.CommentedConfigurationNode;
@@ -184,6 +183,8 @@ public interface ISettings extends IConf {
boolean isEcoLogEnabled();
boolean isEcoLogUUIDEnabled();
boolean isEcoLogUpdateEnabled();
boolean realNamesOnList();
@@ -208,7 +209,9 @@ public interface ISettings extends IConf {
long getAutoAfk();
long getAutoAfkKick();
long getAutoAfkTimeout();
List<String> getAfkTimeoutCommands();
boolean getFreezeAfkPlayers();
@@ -316,6 +319,8 @@ public interface ISettings extends IConf {
boolean isCustomServerFullMessage();
boolean isCustomWhitelistMessage();
boolean isNotifyNoNewMail();
boolean isDropItemsIfFull();
@@ -350,6 +355,8 @@ public interface ISettings extends IConf {
boolean isWorldChangePreserveFlying();
boolean isGamemodeChangePreserveFlying();
boolean isWorldChangeSpeedResetEnabled();
long getCommandCooldownMs(String label);
@@ -406,6 +413,8 @@ public interface ISettings extends IConf {
boolean logCommandBlockCommands();
boolean logConsoleCommands();
Set<Predicate<String>> getNickBlacklist();
double getMaxProjectileSpeed();
@@ -424,18 +433,22 @@ public interface ISettings extends IConf {
boolean showZeroBaltop();
String getNickRegex();
BigDecimal getMultiplier(final User user);
int getMaxItemLore();
Tag getPrimaryColor();
String getPrimaryColor();
Tag getSecondaryColor();
String getSecondaryColor();
BigDecimal getBaltopMinBalance();
long getBaltopMinPlaytime();
int getBaltopEntryLimit();
enum KeepInvPolicy {
KEEP,
DELETE,
@@ -1,5 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.ComponentHolder;
import com.earth2me.essentials.api.IAsyncTeleport;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.config.entities.CommandCooldown;
@@ -7,8 +8,6 @@ import net.ess3.api.MaxMoneyException;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.essentialsx.api.v2.services.mail.MailMessage;
import net.essentialsx.api.v2.services.mail.MailSender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
@@ -138,9 +137,9 @@ public interface IUser {
void sendMessage(String message);
void sendComponent(ComponentLike component);
void sendComponent(ComponentHolder component);
Component tlComponent(String tlKey, Object... args);
ComponentHolder tlComponent(String tlKey, Object... args);
String playerTl(String tlKey, Object... args);
@@ -177,6 +176,17 @@ public interface IUser {
String getFormattedJailTime();
/**
* Returns last activity time.
* <p>
* It is used internally to determine if user's afk status should be set to
* true because of ACTIVITY {@link AfkStatusChangeEvent.Cause}, or the player
* should be kicked for being afk too long.
*
* @return Last activity time (Epoch Milliseconds)
*/
long getLastActivityTime();
@Deprecated
List<String> getMails();
@@ -3,7 +3,6 @@ package com.earth2me.essentials;
import com.earth2me.essentials.config.ConfigurateUtil;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.config.entities.LazyLocation;
import com.earth2me.essentials.utils.AdventureUtil;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import net.ess3.api.TranslatableException;
@@ -273,9 +272,9 @@ public class Jails implements net.ess3.api.IJails {
event.setRespawnLocation(getJail(user.getJail()));
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
} else {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
}
}
}
@@ -295,9 +294,9 @@ public class Jails implements net.ess3.api.IJails {
event.setTo(getJail(user.getJail()));
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
} else {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
}
}
user.sendTl("jailMessage");
@@ -319,9 +318,9 @@ public class Jails implements net.ess3.api.IJails {
final CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(ex -> {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex);
} else {
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())));
}
return false;
});
@@ -6,7 +6,6 @@ import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.NumberUtil;
import net.ess3.api.IEssentials;
@@ -26,6 +25,7 @@ import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.logging.Level;
import static com.earth2me.essentials.I18n.tlLiteral;
@@ -174,10 +174,11 @@ public class Kit {
final boolean allowUnsafe = ess.getSettings().allowUnsafeEnchantments();
final boolean autoEquip = ess.getSettings().isKitAutoEquip();
final List<ItemStack> itemList = new ArrayList<>();
final Map<Integer, ItemStack> itemsWithSlot = new HashMap<>();
final List<String> commandQueue = new ArrayList<>();
final List<String> moneyQueue = new ArrayList<>();
final String currencySymbol = ess.getSettings().getCurrencySymbol().isEmpty() ? "$" : ess.getSettings().getCurrencySymbol();
for (final String kitItem : output.getLines()) {
for (String kitItem : output.getLines()) {
if (kitItem.startsWith("$") || kitItem.startsWith(currencySymbol)) {
moneyQueue.add(NumberUtil.sanitizeCurrencyString(kitItem, ess));
continue;
@@ -191,12 +192,22 @@ public class Kit {
continue;
}
int itemSlot = -1;
if (kitItem.startsWith("slot:")) {
final int spaceIndex = kitItem.indexOf(" ");
if (spaceIndex != -1) {
final String slotStr = kitItem.substring("slot:".length(), spaceIndex);
itemSlot = NumberUtil.isInt(slotStr) ? Integer.parseInt(slotStr) : -1;
kitItem = kitItem.substring(spaceIndex + 1);
}
}
final ItemStack stack;
final SerializationProvider serializationProvider = ess.provider(SerializationProvider.class);
if (kitItem.startsWith("@")) {
if (serializationProvider == null) {
ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("kitError3", kitName, user.getName())));
ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("kitError3", kitName, user.getName())));
continue;
}
stack = serializationProvider.deserializeItem(Base64Coder.decodeLines(kitItem.substring(1)));
@@ -218,22 +229,35 @@ public class Kit {
stack = metaStack.getItemStack();
}
itemList.add(stack);
if (itemSlot == -1 || itemsWithSlot.containsKey(itemSlot)) {
itemList.add(stack);
} else {
itemsWithSlot.put(itemSlot, stack);
}
}
final int maxStackSize = user.isAuthorized("essentials.oversizedstacks") ? ess.getSettings().getOversizedStackSize() : 0;
final boolean isDropItemsIfFull = ess.getSettings().isDropItemsIfFull();
final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, itemList);
final List<ItemStack> totalItems = new ArrayList<>(itemList);
totalItems.addAll(itemsWithSlot.values());
final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, totalItems);
Bukkit.getPluginManager().callEvent(itemsEvent);
final ItemStack[] itemArray = itemList.toArray(new ItemStack[0]);
if (!isDropItemsIfFull && !Inventories.hasSpace(user.getBase(), maxStackSize, autoEquip, itemArray)) {
final ItemStack[] totalItemsArray = totalItems.toArray(new ItemStack[0]);
if (!isDropItemsIfFull && !Inventories.hasSpace(user.getBase(), maxStackSize, autoEquip, totalItemsArray)) {
user.sendTl("kitInvFullNoDrop");
return false;
}
for (Map.Entry<Integer, ItemStack> itemWithSlot : itemsWithSlot.entrySet()) {
final ItemStack leftover = Inventories.addItem(user.getBase(), maxStackSize, itemWithSlot.getValue(), itemWithSlot.getKey());
if (leftover != null) {
itemList.add(leftover);
}
}
final ItemStack[] itemArray = itemList.toArray(new ItemStack[0]);
final Map<Integer, ItemStack> leftover = Inventories.addItem(user.getBase(), maxStackSize, autoEquip, itemArray);
if (!isDropItemsIfFull && !leftover.isEmpty()) {
// Inventories#hasSpace should prevent this state from EVER being reached; If it does, something has gone terribly wrong, and we should just give up and hope people report it :(
@@ -1,6 +1,5 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import net.ess3.api.IEssentials;
import java.io.BufferedInputStream;
@@ -46,7 +45,7 @@ public class ManagedFile {
try {
copyResourceAscii("/" + filename, file);
} catch (final IOException ex) {
Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("itemsCsvNotLoaded", filename)), ex);
Essentials.getWrappedLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("itemsCsvNotLoaded", filename)), ex);
}
}
}
@@ -1,5 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.items.transform.PluginItemTransformer;
import com.earth2me.essentials.textreader.BookInput;
import com.earth2me.essentials.textreader.BookPager;
import com.earth2me.essentials.textreader.IText;
@@ -20,6 +21,8 @@ import org.bukkit.Material;
import org.bukkit.block.Banner;
import org.bukkit.block.banner.PatternType;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.Registry;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BannerMeta;
@@ -32,6 +35,10 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.inventory.meta.ArmorMeta;
import org.bukkit.inventory.meta.trim.ArmorTrim;
import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
@@ -47,6 +54,7 @@ import java.util.regex.Pattern;
public class MetaItemStack {
private static final Map<String, DyeColor> colorMap = new HashMap<>();
private static final Map<String, FireworkEffect.Type> fireworkShape = new HashMap<>();
private static final transient Map<String, PluginItemTransformer> itemTransformers = new HashMap<>();
private static boolean useNewSkullMethod = true;
static {
@@ -78,6 +86,32 @@ public class MetaItemStack {
this.stack = stack.clone();
}
/**
* Registers an item transformer, belonging to a plugin, that can manipulate certain item metadata.
* @param key the key for the transformer.
* @param itemTransformer the actual transformer.
*/
public static void registerItemTransformer(final String key, final PluginItemTransformer itemTransformer) {
//Warn people if they're trying to register over top of someone else.
if (itemTransformers.containsKey(key)) {
Essentials.getWrappedLogger().log(Level.WARNING, String.format("Plugin transformer registered to \"%s\" attempted to register already existing item transformer \"%s\" belonging to \"%s\"!",
itemTransformer.getPlugin().getName(),
key,
itemTransformers.get(key).getPlugin().getName()));
return;
}
itemTransformers.put(key, itemTransformer);
}
/**
* Unregisters a certain item transformer under key "key".
* @param key the transformer key.
*/
public static void unregisterItemTransformer(final String key) {
itemTransformers.remove(key);
}
private static void setSkullOwner(final IEssentials ess, final ItemStack stack, final String owner) {
if (!(stack.getItemMeta() instanceof SkullMeta)) return;
@@ -343,11 +377,37 @@ public class MetaItemStack {
} else {
throw new TranslatableException("leatherSyntax");
}
} else if (MaterialUtil.isArmor(stack.getType()) && split.length > 1 && split[0].equalsIgnoreCase("trim")) {
final ArmorMeta armorMeta = (ArmorMeta) stack.getItemMeta();
final String[] trimData = split[1].split("\\|");
final TrimPattern pattern = Registry.TRIM_PATTERN.getOrThrow(NamespacedKey.minecraft(trimData[0].toLowerCase()));
final TrimMaterial material = Registry.TRIM_MATERIAL.getOrThrow(NamespacedKey.minecraft(trimData[1].toLowerCase()));
armorMeta.setTrim(new ArmorTrim(material, pattern));
stack.setItemMeta(armorMeta);
} else if (split.length > 1 && itemTransformers.containsKey(split[0])) {
transformItem(split[0], split[1]);
} else {
parseEnchantmentStrings(sender, allowUnsafe, split, ess);
}
}
private void transformItem(final String key, final String data){
final PluginItemTransformer transformer = itemTransformers.get(key);
//Ignore, the plugin is disabled.
if (!transformer.getPlugin().isEnabled()) {
return;
}
try {
stack = transformer.apply(data, stack);
} catch(final Throwable thr) {
Essentials.getWrappedLogger().log(Level.SEVERE, String.format("Error applying data \"%s\" to itemstack! Plugin: %s, Key: %s", data, transformer.getPlugin().getName(), key), thr);
}
}
public void addItemFlags(final String string) throws Exception {
final String[] separate = splitPattern.split(string, 2);
if (separate.length != 2) {
@@ -549,7 +609,8 @@ public class MetaItemStack {
} 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
final int parsed = Integer.parseInt(split[1]);
duration = parsed == -1 ? -1 : parsed * 20; //Duration is in ticks by default, converted to seconds
} else {
throw new TranslatableException("invalidPotionMeta", split[1]);
}
@@ -1,6 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.EnumUtil;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -124,7 +124,13 @@ public enum Mob {
ARMADILLO("Armadillo", Enemies.FRIENDLY, "ARMADILLO"),
BREEZE("Breeze", Enemies.ENEMY, "BREEZE"),
BOGGED("Bogged", Enemies.ENEMY, "BOGGED"),
CREAKING("Creaking", Enemies.ENEMY, "CREAKING")
CREAKING("Creaking", Enemies.ENEMY, "CREAKING"),
HAPPY_GHAST("HappyGhast", Enemies.FRIENDLY, "HAPPY_GHAST"),
COPPER_GOLEM("CopperGolem", Enemies.FRIENDLY, "COPPER_GOLEM"),
CAMEL_HUSK("CamelHusk", Enemies.NEUTRAL, "CAMEL_HUSK"),
NAUTILUS("Nautilus", Enemies.NEUTRAL, "NAUTILUS"),
ZOMBIE_NAUTILUS("ZombieNautilus", Enemies.NEUTRAL, "ZOMBIE_NAUTILUS"),
PARCHED("Parched", Enemies.ENEMY, "PARCHED"),
;
private static final Map<String, Mob> hashMap = new HashMap<>();
@@ -185,7 +191,7 @@ public enum Mob {
public Entity spawn(final World world, final Server server, final Location loc) throws MobException {
final Entity entity = world.spawn(loc, this.bukkitType.getEntityClass());
if (entity == null) {
Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("unableToSpawnMob")));
Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("unableToSpawnMob")));
throw new MobException();
}
return entity;
@@ -6,9 +6,12 @@ import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.nms.refl.ReflUtil;
import org.bukkit.Material;
import org.bukkit.TreeSpecies;
import org.bukkit.entity.AbstractNautilus;
import org.bukkit.entity.Axolotl;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Camel;
import org.bukkit.entity.Chicken;
import org.bukkit.entity.Cow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Fox;
@@ -18,13 +21,18 @@ import org.bukkit.entity.MushroomCow;
import org.bukkit.entity.Ocelot;
import org.bukkit.entity.Panda;
import org.bukkit.entity.Parrot;
import org.bukkit.entity.Pig;
import org.bukkit.entity.Player;
import org.bukkit.entity.Salmon;
import org.bukkit.entity.TropicalFish;
import org.bukkit.entity.Villager;
import org.bukkit.entity.Wolf;
import org.bukkit.entity.ZombieNautilus;
import org.bukkit.inventory.ItemStack;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import static com.earth2me.essentials.utils.EnumUtil.getEntityType;
@@ -32,8 +40,21 @@ import static com.earth2me.essentials.utils.EnumUtil.getEntityType;
public final class MobCompat {
// Constants for mob interfaces added in later versions
@SuppressWarnings("rawtypes")
public static final Class RAIDER = ReflUtil.getClassCached("org.bukkit.entity.Raider");
public static final Class<?> RAIDER = ReflUtil.getClassCached("org.bukkit.entity.Raider");
// Stupid hacks to avoid Commodore rewrites.
private static final Class<?> COW = ReflUtil.getClassCached("org.bukkit.entity.Cow");
private static final Class<?> COW_VARIANT = ReflUtil.getClassCached("org.bukkit.entity.Cow$Variant");
private static final MethodHandle COW_VARIANT_HANDLE;
static {
MethodHandle handle = null;
try {
handle = MethodHandles.lookup().findVirtual(COW, "setVariant", MethodType.methodType(void.class, COW_VARIANT));
} catch (final Throwable ignored) {
}
COW_VARIANT_HANDLE = handle;
}
// Constants for mobs added in later versions
public static final EntityType LLAMA = getEntityType("LLAMA");
@@ -50,6 +71,9 @@ public final class MobCompat {
public static final EntityType FROG = getEntityType("FROG");
public static final EntityType CAMEL = getEntityType("CAMEL");
public static final EntityType SALMON = getEntityType("SALMON");
public static final EntityType CAMEL_HUSK = getEntityType("CAMEL_HUSK");
public static final EntityType NAUTILUS = getEntityType("NAUTILUS");
public static final EntityType ZOMBIE_NAUTILUS = getEntityType("ZOMBIE_NAUTILUS");
// Constants for mobs that have changed since earlier versions
public static final EntityType CAT = getEntityType("CAT", "OCELOT");
@@ -250,6 +274,65 @@ public final class MobCompat {
}
}
public static void setCowVariant(final Entity spawned, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_21_5_R01) || COW_VARIANT_HANDLE == null) {
return;
}
if (spawned instanceof Cow) {
try {
COW_VARIANT_HANDLE.invoke(spawned, RegistryUtil.valueOf(COW_VARIANT, variant));
} catch (Throwable ignored) {
}
}
}
public static void setChickenVariant(final Entity spawned, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_21_5_R01)) {
return;
}
if (spawned instanceof Chicken) {
//noinspection DataFlowIssue
((Chicken) spawned).setVariant(RegistryUtil.valueOf(Chicken.Variant.class, variant));
}
}
public static void setPigVariant(final Entity spawned, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_21_5_R01)) {
return;
}
if (spawned instanceof Pig) {
//noinspection DataFlowIssue
((Pig) spawned).setVariant(RegistryUtil.valueOf(Pig.Variant.class, variant));
}
}
public static void setZombieNautilusVariant(final Entity spawned, final String variant) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_21_11_R01)) {
return;
}
if (spawned instanceof ZombieNautilus) {
//noinspection DataFlowIssue
((ZombieNautilus) spawned).setVariant(RegistryUtil.valueOf(ZombieNautilus.Variant.class, variant));
}
}
public static void setNautilusSaddle(final Entity entity, final Player target) {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_21_11_R01)) {
return;
}
if (entity instanceof AbstractNautilus) {
final AbstractNautilus nautilus = (AbstractNautilus) entity;
nautilus.setTamed(true);
nautilus.setOwner(target);
nautilus.getInventory().setSaddle(new ItemStack(Material.SADDLE, 1));
}
}
public enum CatType {
// These are (loosely) Mojang names for the cats
SIAMESE("SIAMESE", "SIAMESE_CAT"),
@@ -10,6 +10,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Boat;
import org.bukkit.entity.ChestedHorse;
import org.bukkit.entity.Chicken;
import org.bukkit.entity.Creeper;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@@ -81,6 +82,7 @@ public enum MobData {
SADDLE_HORSE("saddle", EntityType.HORSE, Data.HORSESADDLE, true),
GOLD_ARMOR_HORSE("goldarmor", EntityType.HORSE, EnumUtil.getMaterial("GOLDEN_HORSE_ARMOR", "GOLD_BARDING"), true),
DIAMOND_ARMOR_HORSE("diamondarmor", EntityType.HORSE, EnumUtil.getMaterial("DIAMOND_HORSE_ARMOR", "DIAMOND_BARDING"), true),
NETHERITE_HORSE_ARMOR("netheritearmor", EntityType.HORSE, EnumUtil.getMaterial("NETHERITE_HORSE_ARMOR"), true),
ARMOR_HORSE("armor", EntityType.HORSE, EnumUtil.getMaterial("IRON_HORSE_ARMOR", "IRON_BARDING"), true),
SIAMESE_CAT("siamese", MobCompat.CAT, MobCompat.CatType.SIAMESE, true),
WHITE_CAT("white", MobCompat.CAT, MobCompat.CatType.WHITE, false),
@@ -221,6 +223,20 @@ public enum MobData {
SMALL_SALMON("small", MobCompat.SALMON, "salmon:SMALL", true),
MEDIUM_SALMON("medium", MobCompat.SALMON, "salmon:MEDIUM", true),
LARGE_SALMON("large", MobCompat.SALMON, "salmon:LARGE", true),
TEMPERATE_COW("temperate", EntityType.COW.getEntityClass(), "cow:TEMPERATE", true),
WARM_COW("warm", EntityType.COW.getEntityClass(), "cow:WARM", true),
COLD_COW("cold", EntityType.COW.getEntityClass(), "cow:COLD", true),
TEMPERATE_CHICKEN("temperate", Chicken.class, "chicken:TEMPERATE", true),
WARM_CHICKEN("warm", Chicken.class, "chicken:WARM", true),
COLD_CHICKEN("cold", Chicken.class, "chicken:COLD", true),
TEMPERATE_PIG("temperate", Pig.class, "pig:TEMPERATE", true),
WARM_PIG("warm", Pig.class, "pig:WARM", true),
COLD_PIG("cold", Pig.class, "pig:COLD", true),
SADDLE_CAMEL_HUSK("saddle", MobCompat.CAMEL_HUSK, Data.CAMELHUSKSADDLE, true),
TEMPERATE_ZOMBIE_NAUTILUS("temperate", MobCompat.ZOMBIE_NAUTILUS, "zombienautilus:TEMPERATE", true),
WARM_ZOMBIE_NAUTILUS("warm", MobCompat.ZOMBIE_NAUTILUS, "zombienautilus:WARM", true),
SADDLE_NAUTILUS("saddle", MobCompat.NAUTILUS, Data.NAUTILUSSADDLE, true),
SADDLE_ZOMBIE_NAUTILUS("saddle", MobCompat.ZOMBIE_NAUTILUS, Data.NAUTILUSSADDLE, true)
;
final private String nickname;
@@ -401,6 +417,12 @@ public enum MobData {
((Goat) spawned).setScreaming(true);
} else if (this.value.equals(Data.CAMELSADDLE)) {
MobCompat.setCamelSaddle(spawned, target);
} else if (this.value.equals(Data.CAMELHUSKSADDLE)) {
MobCompat.setCamelSaddle(spawned, target);
} else if (this.value.equals(Data.NAUTILUSSADDLE)) {
MobCompat.setNautilusSaddle(spawned, target);
} else if (this.value.equals(Data.ZOMBIENAUTILUSSADDLE)) {
MobCompat.setNautilusSaddle(spawned, target);
} else if (this.value instanceof MobCompat.BoatVariant) {
MobCompat.setBoatVariant(spawned, (MobCompat.BoatVariant) this.value);
} else if (this.value instanceof String) {
@@ -442,6 +464,18 @@ public enum MobData {
case "salmon":
MobCompat.setSalmonSize(spawned, split[1]);
break;
case "cow":
MobCompat.setCowVariant(spawned, split[1]);
break;
case "chicken":
MobCompat.setChickenVariant(spawned, split[1]);
break;
case "pig":
MobCompat.setPigVariant(spawned, split[1]);
break;
case "zombienautilus":
MobCompat.setZombieNautilusVariant(spawned, split[1]);
break;
}
} else {
Essentials.getWrappedLogger().warning("Unknown mob data type: " + this.toString());
@@ -467,5 +501,8 @@ public enum MobData {
FISH_PATTERN_COLOR,
GOAT_SCREAMING,
CAMELSADDLE,
CAMELHUSKSADDLE,
NAUTILUSSADDLE,
ZOMBIENAUTILUSSADDLE,
}
}
@@ -1,6 +1,5 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import net.ess3.api.TranslatableException;
@@ -39,7 +38,7 @@ public final class PlayerList {
groupString.append(tlLiteral("listHiddenTag"));
}
user.setDisplayNick();
groupString.append(AdventureUtil.legacyToMini(user.getDisplayName()));
groupString.append(ess.getAdventureFacet().legacyToMini(user.getDisplayName()));
final String strippedNick = FormatUtil.stripFormat(user.getNickname());
if (ess.getSettings().realNamesOnList() && strippedNick != null && !strippedNick.equals(user.getName())) {
@@ -161,6 +161,14 @@ public final class Potions {
ALIASPOTIONS.put("wind", PotionEffectType.WIND_CHARGED);
} catch (final Throwable ignored) {
}
// 1.21.11
try {
POTIONS.put("breathofthenautilus", PotionEffectType.BREATH_OF_THE_NAUTILUS);
ALIASPOTIONS.put("nautilusbreath", PotionEffectType.BREATH_OF_THE_NAUTILUS);
ALIASPOTIONS.put("nautilus", PotionEffectType.BREATH_OF_THE_NAUTILUS);
} catch (final Throwable ignored) {
}
}
private Potions() {
@@ -7,6 +7,7 @@ import com.earth2me.essentials.utils.LocationUtil;
import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
import net.ess3.provider.BiomeKeyProvider;
import net.ess3.provider.BiomeNameProvider;
import net.ess3.provider.WorldInfoProvider;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -68,6 +69,8 @@ public class RandomTeleport implements IConf {
public void setCenter(final String name, final Location center) {
config.setProperty(locationKey(name, "center"), center);
config.save();
// Clear cached locations since center changed
this.getCachedLocations(name).clear();
}
public double getMinRange(final String name) {
@@ -77,6 +80,8 @@ public class RandomTeleport implements IConf {
public void setMinRange(final String name, final double minRange) {
config.setProperty(locationKey(name, "min-range"), minRange);
config.save();
// Clear cached locations since min range changed
this.getCachedLocations(name).clear();
}
public double getMaxRange(final String name) {
@@ -86,6 +91,8 @@ public class RandomTeleport implements IConf {
public void setMaxRange(final String name, final double maxRange) {
config.setProperty(locationKey(name, "max-range"), maxRange);
config.save();
// Clear cached locations since max range changed
this.getCachedLocations(name).clear();
}
public String getDefaultLocation() {
@@ -149,7 +156,7 @@ public class RandomTeleport implements IConf {
// Prompts caching random valid locations, up to a maximum number of attempts.
public void cacheRandomLocations(final String name) {
ess.getServer().getScheduler().scheduleSyncDelayedTask(ess, () -> {
ess.runTaskAsynchronously(() -> {
for (int i = 0; i < this.getFindAttempts(); ++i) {
calculateRandomLocation(getCenter(name), getMinRange(name), getMaxRange(name)).thenAccept(location -> {
if (isValidRandomLocation(location)) {
@@ -179,7 +186,6 @@ public class RandomTeleport implements IConf {
// Calculates a random location asynchronously.
private CompletableFuture<Location> calculateRandomLocation(final Location center, final double minRange, final double maxRange) {
final CompletableFuture<Location> future = new CompletableFuture<>();
// Find an equally distributed offset by randomly rotating a point inside a rectangle about the origin
final double rectX = RANDOM.nextDouble() * (maxRange - minRange) + minRange;
final double rectZ = RANDOM.nextDouble() * (maxRange + minRange) - minRange;
@@ -207,15 +213,14 @@ public class RandomTeleport implements IConf {
360 * RANDOM.nextFloat() - 180,
0
);
PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
return PaperLib.getChunkAtAsync(location).thenApply(chunk -> { // FIXME: use thenApplyAsync + an Executor that runs tasks on the correct region thread to be fully safe.
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 location;
});
return future;
}
// Returns an appropriate elevation for a given location in the nether, or MIN_VALUE if none is found
@@ -238,8 +243,9 @@ public class RandomTeleport implements IConf {
// Exclude biome if enum or namespaced key matches
private boolean isExcludedBiome(final Location location) {
final BiomeNameProvider biomeNameProvider = ess.provider(BiomeNameProvider.class);
final Set<String> excluded = getExcludedBiomes();
final String enumKey = location.getBlock().getBiome().name().toLowerCase();
final String enumKey = biomeNameProvider.getBiomeName(location.getBlock());
// Try with good old bukkit enum
if (excluded.contains(enumKey)) {
return true;
@@ -1,5 +1,6 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.api.IItemDb;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.config.ConfigurateUtil;
@@ -8,7 +9,6 @@ import com.earth2me.essentials.signs.EssentialsSign;
import com.earth2me.essentials.signs.Signs;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.LocationUtil;
@@ -18,8 +18,6 @@ import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.SyncCommandsProvider;
import net.essentialsx.api.v2.ChatType;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.tag.Tag;
import org.bukkit.ChatColor;
import org.bukkit.Color;
import org.bukkit.Material;
@@ -46,6 +44,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
@@ -59,8 +58,8 @@ import static com.earth2me.essentials.I18n.tlLiteral;
public class Settings implements net.ess3.api.ISettings {
private static final BigDecimal DEFAULT_MAX_MONEY = new BigDecimal("10000000000000");
private static final BigDecimal DEFAULT_MIN_MONEY = new BigDecimal("-10000000000000");
private static final Tag DEFAULT_PRIMARY_COLOR = Tag.styling(NamedTextColor.GOLD);
private static final Tag DEFAULT_SECONDARY_COLOR = Tag.styling(NamedTextColor.RED);
private static final String DEFAULT_PRIMARY_COLOR = NamedTextColor.GOLD.toString();
private static final String DEFAULT_SECONDARY_COLOR = NamedTextColor.RED.toString();
private final transient EssentialsConfiguration config;
private final transient IEssentials ess;
private final transient AtomicInteger reloadCount = new AtomicInteger(0);
@@ -92,6 +91,7 @@ public class Settings implements net.ess3.api.ISettings {
private BigDecimal maxMoney = DEFAULT_MAX_MONEY;
private BigDecimal minMoney = DEFAULT_MIN_MONEY;
private boolean economyLog = false;
private boolean economyLogUUID = false;
// #easteregg
private boolean economyLogUpdate = false;
private boolean changeDisplayName = true;
@@ -145,14 +145,16 @@ public class Settings implements net.ess3.api.ISettings {
private boolean isWaterSafe;
private boolean isSafeUsermap;
private boolean logCommandBlockCommands;
private boolean logConsoleCommands;
private Set<Predicate<String>> nickBlacklist;
private double maxProjectileSpeed;
private boolean removeEffectsOnHeal;
private Map<String, String> worldAliases;
private Tag primaryColor = DEFAULT_PRIMARY_COLOR;
private Tag secondaryColor = DEFAULT_SECONDARY_COLOR;
private String primaryColor = DEFAULT_PRIMARY_COLOR;
private String secondaryColor = DEFAULT_SECONDARY_COLOR;
private Set<String> multiplierPerms;
private BigDecimal defaultMultiplier;
private List<String> afkTimeoutCommands = Collections.emptyList();
public Settings(final IEssentials ess) {
this.ess = ess;
@@ -835,7 +837,7 @@ public class Settings implements net.ess3.api.ISettings {
if (reloadCount.get() < 2) {
// on startup: add plugins again in case they registered commands with the new API
// we need to schedule this task before any of the below tasks using _addAlternativeCommand.
ess.scheduleSyncDelayedTask(() -> {
ess.scheduleGlobalDelayedTask(() -> {
for (final Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
if (plugin.isEnabled()) {
ess.getAlternativeCommandsHandler().addPlugin(plugin);
@@ -861,7 +863,7 @@ public class Settings implements net.ess3.api.ISettings {
// This is 2 because Settings are reloaded twice in the startup lifecycle
if (reloadCount.get() < 2) {
ess.scheduleSyncDelayedTask(() -> _addAlternativeCommand(effectiveAlias, toDisable));
ess.scheduleGlobalDelayedTask(() -> _addAlternativeCommand(effectiveAlias, toDisable));
} else {
_addAlternativeCommand(effectiveAlias, toDisable);
}
@@ -876,7 +878,7 @@ public class Settings implements net.ess3.api.ISettings {
ess.getLogger().log(Level.INFO, "Syncing commands");
}
if (reloadCount.get() < 2) {
ess.scheduleSyncDelayedTask(syncCommandsProvider::syncCommands);
ess.scheduleGlobalDelayedTask(syncCommandsProvider::syncCommands);
} else {
syncCommandsProvider.syncCommands();
}
@@ -903,6 +905,7 @@ public class Settings implements net.ess3.api.ISettings {
permissionsLagWarning = _getPermissionsLagWarning();
economyLagWarning = _getEconomyLagWarning();
economyLog = _isEcoLogEnabled();
economyLogUUID = _isEcoLogUUIDEnabled();
economyLogUpdate = _isEcoLogUpdateEnabled();
economyDisabled = _isEcoDisabled();
allowSilentJoin = _allowSilentJoinQuit();
@@ -927,6 +930,7 @@ public class Settings implements net.ess3.api.ISettings {
isWaterSafe = _isWaterSafe();
isSafeUsermap = _isSafeUsermap();
logCommandBlockCommands = _logCommandBlockCommands();
logConsoleCommands = _logConsoleCommands();
nickBlacklist = _getNickBlacklist();
maxProjectileSpeed = _getMaxProjectileSpeed();
removeEffectsOnHeal = _isRemovingEffectsOnHeal();
@@ -938,6 +942,7 @@ public class Settings implements net.ess3.api.ISettings {
secondaryColor = _getSecondaryColor();
multiplierPerms = _getMultiplierPerms();
defaultMultiplier = _getDefaultMultiplier();
afkTimeoutCommands = _getAfkTimeoutCommands();
reloadCount.incrementAndGet();
}
@@ -968,7 +973,7 @@ public class Settings implements net.ess3.api.ISettings {
final ItemStack iStack = itemDb.get(itemName);
epItemSpwn.add(iStack.getType());
} catch (final Exception ex) {
ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", itemName, "item-spawn-blacklist")), ex);
ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", itemName, "item-spawn-blacklist")), ex);
}
}
return epItemSpwn;
@@ -996,7 +1001,7 @@ public class Settings implements net.ess3.api.ISettings {
try {
newSigns.add(Signs.valueOf(signName).getSign());
} catch (final Exception ex) {
ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", signName, "enabledSigns")));
ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", signName, "enabledSigns")));
continue;
}
signsEnabled = true;
@@ -1124,7 +1129,7 @@ public class Settings implements net.ess3.api.ISettings {
}
if (mat == null) {
ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", itemName, configName)));
ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", itemName, configName)));
} else {
list.add(mat);
}
@@ -1173,6 +1178,14 @@ public class Settings implements net.ess3.api.ISettings {
return config.getBoolean("economy-log-enabled", false);
}
public boolean _isEcoLogUUIDEnabled() {
return config.getBoolean("economy-log-uuids", false);
}
public boolean isEcoLogUUIDEnabled() {
return economyLogUUID;
}
@Override
public boolean isEcoLogUpdateEnabled() {
return economyLogUpdate;
@@ -1262,8 +1275,17 @@ public class Settings implements net.ess3.api.ISettings {
}
@Override
public long getAutoAfkKick() {
return config.getLong("auto-afk-kick", -1);
public long getAutoAfkTimeout() {
return config.getLong("auto-afk-timeout", config.getLong("auto-afk-kick", -1));
}
private List<String> _getAfkTimeoutCommands() {
return new ArrayList<>(config.getList("afk-timeout-commands", String.class));
}
@Override
public List<String> getAfkTimeoutCommands() {
return afkTimeoutCommands;
}
@Override
@@ -1619,6 +1641,11 @@ public class Settings implements net.ess3.api.ISettings {
return config.getBoolean("use-custom-server-full-message", true);
}
@Override
public boolean isCustomWhitelistMessage() {
return config.getBoolean("use-custom-whitelist-message", true);
}
@Override
public int getJoinQuitMessagePlayerCount() {
return config.getInt("hide-join-quit-messages-above", -1);
@@ -1872,7 +1899,7 @@ public class Settings implements net.ess3.api.ISettings {
try {
newSigns.add(Signs.valueOf(signName).getSign());
} catch (final Exception ex) {
ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", signName, "unprotected-sign-names")));
ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", signName, "unprotected-sign-names")));
}
}
return newSigns;
@@ -1938,6 +1965,11 @@ public class Settings implements net.ess3.api.ISettings {
return config.getBoolean("world-change-preserve-flying", true);
}
@Override
public boolean isGamemodeChangePreserveFlying() {
return config.getBoolean("gamemode-change-preserve-flying", false);
}
@Override
public boolean isWorldChangeSpeedResetEnabled() {
return config.getBoolean("world-change-speed-reset", true);
@@ -2053,6 +2085,15 @@ public class Settings implements net.ess3.api.ISettings {
return logCommandBlockCommands;
}
private boolean _logConsoleCommands() {
return config.getBoolean("log-console-commands", true);
}
@Override
public boolean logConsoleCommands() {
return logConsoleCommands;
}
private Set<Predicate<String>> _getNickBlacklist() {
final Set<Predicate<String>> blacklist = new HashSet<>();
@@ -2120,6 +2161,11 @@ public class Settings implements net.ess3.api.ISettings {
return config.getBoolean("show-zero-baltop", true);
}
@Override
public String getNickRegex() {
return config.getString("allowed-nicks-regex", "^[a-zA-Z_0-9§]+$");
}
@Override
public BigDecimal getMultiplier(final User user) {
BigDecimal multiplier = defaultMultiplier;
@@ -2153,39 +2199,40 @@ public class Settings implements net.ess3.api.ISettings {
}
@Override
public Tag getPrimaryColor() {
public String getPrimaryColor() {
return primaryColor;
}
private Tag _getPrimaryColor() {
private String _getPrimaryColor() {
final String color = config.getString("message-colors.primary", "#ffaa00");
final TextColor textColor = _getTagColor(color);
return textColor != null ? Tag.styling(textColor) : DEFAULT_PRIMARY_COLOR;
final String textColor = _getTagColor(color);
return textColor != null ? textColor : DEFAULT_PRIMARY_COLOR;
}
@Override
public Tag getSecondaryColor() {
public String getSecondaryColor() {
return secondaryColor;
}
private Tag _getSecondaryColor() {
private String _getSecondaryColor() {
final String color = config.getString("message-colors.secondary", "#ff5555");
final TextColor textColor = _getTagColor(color);
return textColor != null ? Tag.styling(textColor) : DEFAULT_SECONDARY_COLOR;
final String textColor = _getTagColor(color);
return textColor != null ? textColor : DEFAULT_SECONDARY_COLOR;
}
private TextColor _getTagColor(final String color) {
private String _getTagColor(final String color) {
try {
if (color.startsWith("#") && color.length() == 7 && NumberUtil.isHexadecimal(color.substring(1))) {
return TextColor.color(Color.fromRGB(Integer.decode(color)).asRGB());
Color.fromRGB(Integer.decode(color));
return color;
}
if (color.length() == 1) {
return AdventureUtil.fromChar(color.charAt(0));
return Objects.requireNonNull(AdventureUtil.fromChar(color.charAt(0))).toString();
}
return NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH));
} catch (IllegalArgumentException ignored) {
return Objects.requireNonNull(NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH))).toString();
} catch (NullPointerException | IllegalArgumentException ignored) {
}
return null;
}
@@ -2195,6 +2242,11 @@ public class Settings implements net.ess3.api.ISettings {
return config.getBigDecimal("baltop-requirements.minimum-balance", BigDecimal.ZERO);
}
@Override
public int getBaltopEntryLimit() {
return config.getInt("baltop-entry-limit", -1);
}
@Override
public long getBaltopMinPlaytime() {
return config.getLong("baltop-requirements.minimum-playtime", 0);
@@ -2,7 +2,7 @@ package com.earth2me.essentials;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.craftbukkit.SetExpFix;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.api.IEssentials;
@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
@@ -86,7 +87,14 @@ public class Trade {
sb.append(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date()));
sb.append("\",\"");
if (sender != null) {
sb.append(sender);
String senderIdentifier = sender;
if (ess.getSettings().isEcoLogUUIDEnabled()) {
final UUID uuid = ess.getUsers().getNameCache().get(sender);
if (uuid != null) {
senderIdentifier = uuid.toString();
}
}
sb.append(senderIdentifier);
}
sb.append("\",");
if (charge == null) {
@@ -112,7 +120,14 @@ public class Trade {
}
sb.append(",\"");
if (receiver != null) {
sb.append(receiver);
String receiverIdentifier = receiver;
if (ess.getSettings().isEcoLogUUIDEnabled()) {
final UUID uuid = ess.getUsers().getNameCache().get(receiver);
if (uuid != null) {
receiverIdentifier = uuid.toString();
}
}
sb.append(receiverIdentifier);
}
sb.append("\",");
if (pay == null) {
@@ -145,7 +160,7 @@ public class Trade {
sb.append(loc.getBlockY()).append(",");
sb.append(loc.getBlockZ()).append(",");
}
if (endBalance == null) {
sb.append(",");
} else {
@@ -1,12 +1,13 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.adventure.ComponentHolder;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.economy.EconomyLayer;
import com.earth2me.essentials.economy.EconomyLayers;
import com.earth2me.essentials.messaging.IMessageRecipient;
import com.earth2me.essentials.messaging.SimpleMessageRecipient;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.FormatUtil;
@@ -24,8 +25,6 @@ import net.ess3.api.events.UserBalanceUpdateEvent;
import net.ess3.provider.PlayerLocaleProvider;
import net.essentialsx.api.v2.events.TransactionEvent;
import net.essentialsx.api.v2.services.mail.MailSender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Statistic;
@@ -136,6 +135,10 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
@Override
public boolean isAuthorized(final String node) {
if (Essentials.TESTING) {
return false;
}
final boolean result = isAuthorizedCheck(node);
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "checking if " + base.getName() + " has " + node + " - " + result);
@@ -145,6 +148,10 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
@Override
public boolean isPermissionSet(final String node) {
if (Essentials.TESTING) {
return false;
}
final boolean result = isPermSetCheck(node);
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "checking if " + base.getName() + " has " + node + " (set-explicit) - " + result);
@@ -640,7 +647,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
return;
}
this.getBase().setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") || (set && ess.getSettings().sleepIgnoresAfkPlayers()));
this.getBase().setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") || set && ess.getSettings().sleepIgnoresAfkPlayers());
if (set && !isAfk()) {
afkPosition = this.getLocation();
this.afkSince = System.currentTimeMillis();
@@ -775,6 +782,11 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
return false;
}
@Override
public long getLastActivityTime() {
return this.lastActivity;
}
@Deprecated
public void updateActivity(final boolean broadcast) {
updateActivity(broadcast, AfkStatusChangeEvent.Cause.UNKNOWN);
@@ -809,7 +821,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
public void updateActivityOnChat(final boolean broadcast) {
if (ess.getSettings().cancelAfkOnChat()) {
//Chat happens async, make sure we have a sync context
ess.scheduleSyncDelayedTask(() -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT));
ess.scheduleEntityDelayedTask(base, () -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT));
}
}
@@ -819,19 +831,34 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
return;
}
final long autoafkkick = ess.getSettings().getAutoAfkKick();
if (autoafkkick > 0
&& lastActivity > 0 && (lastActivity + (autoafkkick * 1000)) < System.currentTimeMillis()
final long autoafktimeout = ess.getSettings().getAutoAfkTimeout();
// Checks if the player has been inactive for longer than the configured auto-afk-timeout time.
if (autoafktimeout > 0
&& lastActivity > 0 && (lastActivity + (autoafktimeout * 1000)) < System.currentTimeMillis()
&& !isAuthorized("essentials.kick.exempt")
&& !isAuthorized("essentials.afk.kickexempt")) {
lastActivity = 0;
final double kickTime = autoafkkick / 60.0;
final double kickTime = autoafktimeout / 60.0;
this.getBase().kickPlayer(AdventureUtil.miniToLegacy(playerTl("autoAfkKickReason", kickTime)));
// If `afk-timeout-command` in config.yml is empty, use default Essentials kicking behaviour instead of executing a command.
if (ess.getSettings().getAfkTimeoutCommands().isEmpty()) {
this.getBase().kickPlayer(ess.getAdventureFacet().miniToLegacy(playerTl("autoAfkKickReason", kickTime)));
for (final User user : ess.getOnlineUsers()) {
if (user.isAuthorized("essentials.kick.notify")) {
user.sendTl("playerKicked", Console.DISPLAY_NAME, getName(), user.playerTl("autoAfkKickReason", kickTime));
for (final User user : ess.getOnlineUsers()) {
if (user.isAuthorized("essentials.kick.notify")) {
user.sendTl("playerKicked", Console.DISPLAY_NAME, getName(), user.playerTl("autoAfkKickReason", kickTime));
}
}
} else {
// If `afk-timeout-commands` in config.yml is populated, execute the command(s) instead of kicking the player.
for (final String command : ess.getSettings().getAfkTimeoutCommands()) {
if (command == null || command.isEmpty()){
continue;
}
// Replace placeholders in the command with actual values.
final String cmd = command.replace("{USERNAME}", getName()).replace("{KICKTIME}", String.valueOf(kickTime));
ess.getServer().dispatchCommand(ess.getServer().getConsoleSender(), cmd);
}
}
}
@@ -1046,14 +1073,14 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
}
@Override
public void sendComponent(ComponentLike component) {
ess.getBukkitAudience().player(base).sendMessage(component);
public void sendComponent(ComponentHolder component) {
ess.getAdventureFacet().send(base, component);
}
@Override
public Component tlComponent(String tlKey, Object... args) {
public ComponentHolder tlComponent(String tlKey, Object... args) {
final String translation = playerTl(tlKey, args);
return AdventureUtil.miniMessage().deserialize(translation);
return ess.getAdventureFacet().deserializeMiniMessage(translation);
}
@Override
@@ -1063,14 +1090,17 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
return;
}
sendComponent(AdventureUtil.miniMessage().deserialize(translation));
sendComponent(ess.getAdventureFacet().deserializeMiniMessage(translation));
}
@Override
public String playerTl(String tlKey, Object... args) {
if (ess.getSettings().isPerPlayerLocale()) {
final PlayerLocaleProvider provider = ess.provider(PlayerLocaleProvider.class);
return tlLocale(getPlayerLocale(provider.getLocale(base)), tlKey, args);
final Locale locale = base != null ? getPlayerLocale(provider.getLocale(base)) : playerLocale;
if (locale != null) {
return tlLocale(locale, tlKey, args);
}
}
return tlLiteral(tlKey, args);
}
@@ -1081,7 +1111,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
}
public Locale getPlayerLocale(final String locale) {
if (locale.equals(lastLocaleString)) {
if (locale == null || locale.equals(lastLocaleString)) {
return playerLocale;
}
lastLocaleString = locale;
@@ -74,6 +74,7 @@ public abstract class UserData extends PlayerExtension implements IConf {
public final void cleanup() {
config.blockingSave();
ess.getUsers().removeCache(getConfigUUID());
}
@Override
@@ -271,6 +272,10 @@ public abstract class UserData extends PlayerExtension implements IConf {
return !holder.powertools().isEmpty();
}
public Map<String, List<String>> getAllPowertools() {
return holder.powertools();
}
public Location getLastLocation() {
final LazyLocation lastLocation = holder.lastLocation();
return lastLocation != null ? lastLocation.location() : null;
@@ -1,8 +1,8 @@
package com.earth2me.essentials;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.commands.WarpNotFoundException;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.StringUtil;
import net.ess3.api.InvalidNameException;
import net.ess3.api.TranslatableException;
@@ -134,7 +134,7 @@ public class Warps implements IConf, net.ess3.api.IWarps {
warpPoints.put(new StringIgnoreCase(name), conf);
}
} catch (final Exception ex) {
Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("loadWarpError", filename)), ex);
Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("loadWarpError", filename)), ex);
}
}
}
@@ -0,0 +1,62 @@
package com.earth2me.essentials.adventure;
import net.kyori.adventure.text.format.NamedTextColor;
public final class AdventureUtil {
private static final String LOOKUP = "0123456789abcdefklmnor";
private static final NamedTextColor[] COLORS = new NamedTextColor[]{NamedTextColor.BLACK, NamedTextColor.DARK_BLUE, NamedTextColor.DARK_GREEN, NamedTextColor.DARK_AQUA, NamedTextColor.DARK_RED, NamedTextColor.DARK_PURPLE, NamedTextColor.GOLD, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY, NamedTextColor.BLUE, NamedTextColor.GREEN, NamedTextColor.AQUA, NamedTextColor.RED, NamedTextColor.LIGHT_PURPLE, NamedTextColor.YELLOW, NamedTextColor.WHITE};
private static AdventureFacet ADVENTURE_FACET_INSTANCE = null;
private AdventureUtil() {
}
public static void setAdventureFacet(AdventureFacet adventureFacetInstance) {
ADVENTURE_FACET_INSTANCE = adventureFacetInstance;
}
public static AdventureFacet getAdventureFacet() {
return ADVENTURE_FACET_INSTANCE;
}
/**
* Get the {@link NamedTextColor} from its associated section sign char.
*/
public static NamedTextColor fromChar(final char c) {
final int index = LOOKUP.indexOf(c);
if (index == -1 || index > 15) {
return null;
}
return COLORS[index];
}
public static String fromCharName(final char c) {
final NamedTextColor namedTextColor = fromChar(c);
if (namedTextColor == null) {
return null;
}
return namedTextColor.toString();
}
/**
* Parameters for a translation message are not parsed for MiniMessage by default to avoid injection. If you want
* a parameter to be parsed for MiniMessage you must wrap it in a ParsedPlaceholder by using this method.
*/
public static ParsedPlaceholder parsed(final String literal) {
return new ParsedPlaceholder(literal);
}
public static class ParsedPlaceholder {
private final String value;
protected ParsedPlaceholder(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
}
@@ -0,0 +1,149 @@
package com.earth2me.essentials.adventure;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.provider.AbstractChatEvent;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Color;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Locale;
public class SpigotAdventureFacet implements AdventureFacet {
private static final LegacyComponentSerializer LEGACY_SERIALIZER;
private static final MiniMessage MINI_MESSAGE_NO_TAGS;
static {
final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder()
.flattener(ComponentFlattener.basic())
.extractUrls(AbstractChatEvent.URL_PATTERN)
.useUnusualXRepeatedCharacterHexFormat();
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) {
builder.hexColors();
}
LEGACY_SERIALIZER = builder.build();
MINI_MESSAGE_NO_TAGS = MiniMessage.builder().strict(true).build();
}
private final Essentials ess;
private final BukkitAudiences bukkitAudiences;
private final MiniMessage miniMessageInstance;
public SpigotAdventureFacet(final Essentials ess) {
this.ess = ess.getSettings() == null ? null : ess;
bukkitAudiences = BukkitAudiences.create(ess);
miniMessageInstance = MiniMessage.builder()
.tags(TagResolver.builder()
.resolvers(TagResolver.standard())
.resolver(TagResolver.resolver("primary", supplyTag(true)))
.resolver(TagResolver.resolver("secondary", supplyTag(false)))
.build())
.build();
}
private Tag supplyTag(final boolean primary) {
final String color = primary ? (ess != null ? ess.getSettings().getPrimaryColor() : "gold") : (ess != null ? ess.getSettings().getSecondaryColor() : "red");
final TextColor textColor;
if (color.startsWith("#") && color.length() == 7 && NumberUtil.isHexadecimal(color.substring(1))) {
textColor = TextColor.color(Color.fromRGB(Integer.decode(color)).asRGB());
} else if (color.length() == 1) {
textColor = AdventureUtil.fromChar(color.charAt(0));
} else {
textColor = NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH));
}
return ess != null && textColor != null ? Tag.styling(textColor) : Tag.styling(primary ? NamedTextColor.GOLD : NamedTextColor.RED);
}
@Override
public ComponentHolder deserializeMiniMessage(String message) {
return new ComponentHolder(miniMessageInstance.deserialize(message));
}
@Override
public String legacyToMini(String message) {
return legacyToMini(message, true);
}
@Override
public void send(CommandSender sender, ComponentHolder component) {
bukkitAudiences.sender(sender).sendMessage((Component) component.getComponent());
}
@Override
public void send(Player player, ComponentHolder component) {
bukkitAudiences.player(player).sendMessage((Component) component.getComponent());
}
@Override
public String adventureToLegacy(ComponentHolder component) {
return adventureToLegacy((Component) component.getComponent());
}
private String adventureToLegacy(Component component) {
return LEGACY_SERIALIZER.serialize(component);
}
@Override
public ComponentHolder legacyToAdventure(String message) {
return new ComponentHolder(LEGACY_SERIALIZER.deserialize(message));
}
@Override
public ComponentHolder text(String message) {
return new ComponentHolder(Component.text(message));
}
@Override
public String legacyToMini(String message, boolean useCustomTags) {
final Component deserializedText = LEGACY_SERIALIZER.deserialize(message);
if (useCustomTags) {
return miniMessageInstance.serialize(deserializedText);
} else {
return MINI_MESSAGE_NO_TAGS.serialize(deserializedText);
}
}
@Override
public String miniToLegacy(String message) {
return adventureToLegacy(miniMessageInstance.deserialize(message));
}
@Override
public String stripTags(String input) {
return miniMessageInstance.stripTags(input);
}
@Override
public String escapeTags(String input) {
return miniMessageInstance.escapeTags(input);
}
@Override
public ComponentHolder append(ComponentHolder base, ComponentHolder... addition) {
Component baseComponent = (Component) base.getComponent();
for (ComponentHolder holder : addition) {
final Component additionComponent = (Component) holder.getComponent();
baseComponent = baseComponent.append(additionComponent);
}
return new ComponentHolder(baseComponent);
}
@Override
public void close() {
bukkitAudiences.close();
}
}
@@ -106,7 +106,7 @@ public class Commandafk extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.afk.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -77,7 +77,7 @@ public class Commandback extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (user.isAuthorized("essentials.back.others") && args.length == 1) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -2,7 +2,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
@@ -20,14 +20,14 @@ public class Commandbalance extends EssentialsCommand {
throw new NotEnoughArgumentsException();
}
final User target = getPlayer(server, args, 0, false, true);
final User target = getPlayer(server, args, 0, true, true);
sender.sendTl("balanceOther", target.isHidden() ? target.getName() : target.getDisplayName(), AdventureUtil.parsed(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 User target = getPlayer(server, args, 0, false, true);
user.sendTl("balanceOther", target.isHidden() ? target.getName() : target.getDisplayName(), AdventureUtil.parsed(NumberUtil.displayCurrency(target.getMoney(), ess)));
} else if (args.length < 2) {
user.sendTl("balance", AdventureUtil.parsed(NumberUtil.displayCurrency(user.getMoney(), ess)));
@@ -39,7 +39,7 @@ public class Commandbalance extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.balance.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -4,9 +4,10 @@ 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.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.VersionUtil;
import com.google.common.collect.Lists;
import net.essentialsx.api.v2.services.BalanceTop;
import org.bukkit.Bukkit;
@@ -43,7 +44,7 @@ public class Commandbalancetop extends EssentialsCommand {
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
};
if (sender.getSender() instanceof BlockCommandSender) {
ess.scheduleSyncDelayedTask(runnable);
ess.scheduleGlobalDelayedTask(runnable);
} else {
runnable.run();
}
@@ -114,16 +115,21 @@ public class Commandbalancetop extends EssentialsCommand {
future.thenRun(() -> {
if (fresh) {
final SimpleTextInput newCache = new SimpleTextInput();
newCache.getLines().add(AdventureUtil.miniToLegacy(tlLiteral("serverTotal", AdventureUtil.parsed(NumberUtil.displayCurrency(ess.getBalanceTop().getBalanceTopTotal(), ess)))));
newCache.getLines().add(ess.getAdventureFacet().miniToLegacy(tlLiteral("serverTotal", AdventureUtil.parsed(NumberUtil.displayCurrency(ess.getBalanceTop().getBalanceTopTotal(), ess)))));
int pos = 1;
for (final Map.Entry<UUID, BalanceTop.Entry> entry : ess.getBalanceTop().getBalanceTopCache().entrySet()) {
final BigDecimal balance = entry.getValue().getBalance();
final User user = ess.getUser(entry.getKey());
final Statistic PLAY_ONE_TICK = EnumUtil.getStatistic("PLAY_ONE_MINUTE", "PLAY_ONE_TICK");
final boolean offlineStatisticSupported = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_15_2_R01);
final long playtime;
if (user.getBase() == null || !user.getBase().isOnline()) {
playtime = Bukkit.getServer().getOfflinePlayer(entry.getKey()).getStatistic(PLAY_ONE_TICK);
if (offlineStatisticSupported) {
playtime = Bukkit.getServer().getOfflinePlayer(entry.getKey()).getStatistic(PLAY_ONE_TICK);
} else {
playtime = -1;
}
} else {
playtime = user.getBase().getStatistic(PLAY_ONE_TICK);
}
@@ -133,8 +139,9 @@ public class Commandbalancetop extends EssentialsCommand {
// Checking if player meets the requirements of minimum balance and minimum playtime to be listed in baltop list
if ((ess.getSettings().showZeroBaltop() || balance.compareTo(BigDecimal.ZERO) > 0)
&& balance.compareTo(ess.getSettings().getBaltopMinBalance()) >= 0 &&
playTimeSecs >= ess.getSettings().getBaltopMinPlaytime()) {
newCache.getLines().add(AdventureUtil.miniToLegacy(tlLiteral("balanceTopLine", pos, entry.getValue().getDisplayName(), AdventureUtil.parsed(NumberUtil.displayCurrency(balance, ess)))));
// Skip playtime check for offline players on versions below 1.15.2
(playtime == -1 || playTimeSecs >= ess.getSettings().getBaltopMinPlaytime())) {
newCache.getLines().add(ess.getAdventureFacet().miniToLegacy(tlLiteral("balanceTopLine", pos, entry.getValue().getDisplayName(), AdventureUtil.parsed(NumberUtil.displayCurrency(balance, ess)))));
}
pos++;
}
@@ -4,7 +4,6 @@ import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Console;
import com.earth2me.essentials.OfflinePlayerStub;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import net.ess3.api.TranslatableException;
import org.bukkit.BanList;
@@ -55,8 +54,8 @@ public class Commandban extends EssentialsCommand {
final String banDisplay = tlLiteral("banFormat", banReason, senderDisplayName);
user.getBase().kickPlayer(AdventureUtil.miniToLegacy(banDisplay));
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerBanned", senderDisplayName, user.getName(), banDisplay)));
user.getBase().kickPlayer(ess.getAdventureFacet().miniToLegacy(banDisplay));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerBanned", senderDisplayName, user.getName(), banDisplay)));
if (nomatch) {
sender.sendTl("userUnknown", user.getName());
@@ -68,7 +67,7 @@ public class Commandban extends EssentialsCommand {
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -3,7 +3,6 @@ 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.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.BanList;
import org.bukkit.Server;
@@ -52,10 +51,10 @@ public class Commandbanip extends EssentialsCommand {
banReason = tlLiteral("defaultBanReason");
}
final String banDisplay = AdventureUtil.miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName));
final String banDisplay = ess.getAdventureFacet().miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName));
ess.getServer().getBanList(BanList.Type.IP).addBan(ipAddress, banReason, null, senderName);
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerBanIpAddress", senderDisplayName, ipAddress, banReason)));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerBanIpAddress", senderDisplayName, ipAddress, banReason)));
for (final Player player : ess.getServer().getOnlinePlayers()) {
if (player.getAddress().getAddress().getHostAddress().equalsIgnoreCase(ipAddress)) {
@@ -70,7 +69,7 @@ public class Commandbanip extends EssentialsCommand {
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
// TODO: Also list IP addresses?
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -23,7 +23,7 @@ public class Commandbeezooka extends EssentialsCommand {
final Entity bee = Mob.BEE.spawn(user.getWorld(), server, user.getBase().getEyeLocation());
bee.setVelocity(user.getBase().getEyeLocation().getDirection().multiply(2));
ess.scheduleSyncDelayedTask(() -> {
ess.scheduleEntityDelayedTask(bee, () -> {
final Location loc = bee.getLocation();
bee.remove();
loc.getWorld().createExplosion(loc, 0F);
@@ -106,7 +106,7 @@ public class Commandbook extends EssentialsCommand {
}
return options;
} else if (args.length == 2 && args[0].equalsIgnoreCase("author") && user.isAuthorized("essentials.book.author")) {
final List<String> options = getPlayers(server, user);
final List<String> options = getPlayers(user);
options.add("Herobrine"); // #EasterEgg
return options;
} else {
@@ -2,7 +2,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.google.common.collect.Lists;
import net.ess3.api.TranslatableException;
@@ -54,7 +54,7 @@ public class Commandbroadcastworld extends EssentialsCommand {
if (message.isEmpty()) {
throw new NotEnoughArgumentsException();
}
ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(AdventureUtil.legacyToMini(name)));
ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name)));
}
@Override
@@ -26,7 +26,7 @@ public class Commandburn extends EssentialsCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return COMMON_DURATIONS;
} else {
@@ -153,7 +153,7 @@ public class Commandclearinventory extends EssentialsCommand {
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (user.isAuthorized("essentials.clearinventory.others")) {
if (args.length == 1) {
final List<String> options = getPlayers(server, user);
final List<String> options = getPlayers(user);
if (user.isAuthorized("essentials.clearinventory.all") || user.isAuthorized("essentials.clearinventory.multiple")) {
// Assume that nobody will have the 'all' permission without the 'others' permission
options.add("*");
@@ -182,7 +182,7 @@ public class Commandclearinventory extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
final List<String> options = getPlayers(server, sender);
final List<String> options = getPlayers(sender);
options.add("*");
return options;
} else if (args.length == 2) {
@@ -196,6 +196,9 @@ public class Commandclearinventory extends EssentialsCommand {
}
private String formatCommand(final String commandLabel, final String[] args) {
if (args == null || args.length == 0) {
return "/" + commandLabel;
}
return "/" + commandLabel + " " + StringUtil.joinList(" ", (Object[]) args);
}
@@ -51,13 +51,14 @@ public class Commandcreatekit extends EssentialsCommand {
useSerializationProvider = false;
}
for (ItemStack is : items) {
for (int i = 0; i < items.length; i++) {
final ItemStack is = items[i];
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
final String serialized;
if (useSerializationProvider) {
serialized = "@" + Base64Coder.encodeLines(serializationProvider.serializeItem(is));
serialized = "slot:" + i + " @" + Base64Coder.encodeLines(serializationProvider.serializeItem(is));
} else {
serialized = ess.getItemDb().serialize(is);
serialized = "slot:" + i + " " + ess.getItemDb().serialize(is);
}
list.add(serialized);
}
@@ -30,10 +30,10 @@ public class Commanddelhome extends EssentialsCommand {
try {
user.delHome(home);
sender.sendTl("deleteHome", home);
} catch (Exception e) {
sender.sendTl("invalidHome", home);
}
sender.sendTl("deleteHome", home);
}
@Override
@@ -87,7 +87,7 @@ public class Commanddelhome extends EssentialsCommand {
if (canDelOthers) {
final int sepIndex = args[0].indexOf(':');
if (sepIndex < 0) {
getPlayers(server, sender).forEach(player -> homes.add(player + ":"));
getPlayers(sender).forEach(player -> homes.add(player + ":"));
} else {
final String namePart = args[0].substring(0, sepIndex);
final User otherUser;
@@ -3,7 +3,7 @@ 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.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import net.ess3.api.MaxMoneyException;
@@ -87,7 +87,7 @@ public class Commandeco extends EssentialsLoopCommand {
}
return options;
} else if (args.length == 2) {
return getPlayers(server, sender);
return getPlayers(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());
@@ -17,7 +17,6 @@ import org.bukkit.block.data.type.HangingSign;
import org.bukkit.block.data.type.WallHangingSign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.block.sign.Side;
import org.bukkit.entity.Player;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.util.Vector;
@@ -49,7 +48,7 @@ public class Commandeditsign extends EssentialsCommand {
throw new TranslatableException("editsignCommandLimit");
}
existingLines[line] = text;
if (callSignEvent(sign, user.getBase(), existingLines)) {
if (callSignEvent(sign, user, existingLines)) {
return;
}
@@ -61,7 +60,7 @@ public class Commandeditsign extends EssentialsCommand {
existingLines[i] = "";
}
if (callSignEvent(sign, user.getBase(), existingLines)) {
if (callSignEvent(sign, user, existingLines)) {
return;
}
@@ -71,7 +70,7 @@ public class Commandeditsign extends EssentialsCommand {
final int line = Integer.parseInt(args[1]) - 1;
existingLines[line] = "";
if (callSignEvent(sign, user.getBase(), existingLines)) {
if (callSignEvent(sign, user, existingLines)) {
return;
}
@@ -100,13 +99,17 @@ public class Commandeditsign extends EssentialsCommand {
for (int i = 0; i < 4; i++) {
existingLines[i] = FormatUtil.formatString(user, "essentials.editsign", user.getSignCopy().get(i));
}
if (callSignEvent(sign, user, existingLines)) {
return;
}
user.sendTl("editsignPaste", commandLabel);
} else {
existingLines[line] = FormatUtil.formatString(user, "essentials.editsign", user.getSignCopy().get(line));
if (callSignEvent(sign, user, existingLines)) {
return;
}
user.sendTl("editsignPasteLine", line + 1, commandLabel);
}
callSignEvent(sign, user.getBase(), existingLines);
} else {
throw new NotEnoughArgumentsException();
}
@@ -115,22 +118,22 @@ public class Commandeditsign extends EssentialsCommand {
}
}
private boolean callSignEvent(final ModifiableSign sign, final Player player, final String[] lines) {
private boolean callSignEvent(final ModifiableSign sign, final User user, final String[] lines) {
final SignChangeEvent event;
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_20_1_R01)) {
if (sign.isWaxed() && !player.hasPermission("essentials.editsign.waxed.exempt")) {
if (sign.isWaxed() && !user.isAuthorized("essentials.editsign.waxed.exempt")) {
return true;
}
event = new SignChangeEvent(sign.getBlock(), player, lines, sign.isFront() ? Side.FRONT : Side.BACK);
event = new SignChangeEvent(sign.getBlock(), user.getBase(), lines, sign.isFront() ? Side.FRONT : Side.BACK);
} else {
//noinspection deprecation
event = new SignChangeEvent(sign.getBlock(), player, lines);
event = new SignChangeEvent(sign.getBlock(), user.getBase(), lines);
}
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("SignChangeEvent canceled for /editsign execution by " + player.getName());
ess.getLogger().info("SignChangeEvent canceled for /editsign execution by " + user.getName());
}
return true;
}
@@ -26,7 +26,7 @@ public class Commandenderchest extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1 && user.isAuthorized("essentials.enderchest.others")) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -1,962 +1,29 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.EssentialsUpgrade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.economy.EconomyLayer;
import com.earth2me.essentials.economy.EconomyLayers;
import com.earth2me.essentials.userstorage.ModernUserMap;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.CommandMapUtil;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FloatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.PasteUtil;
import com.earth2me.essentials.utils.RegistryUtil;
import com.earth2me.essentials.utils.VersionUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.ess3.api.TranslatableException;
import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.OnlineModeProvider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static com.earth2me.essentials.I18n.tlLiteral;
import com.earth2me.essentials.commands.essentials.CleanupCommand;
import com.earth2me.essentials.commands.essentials.CommandMapCommand;
import com.earth2me.essentials.commands.essentials.DebugCommand;
import com.earth2me.essentials.commands.essentials.DumpCommand;
import com.earth2me.essentials.commands.essentials.HomesCommand;
import com.earth2me.essentials.commands.essentials.ItemTestCommand;
import com.earth2me.essentials.commands.essentials.MooCommand;
import com.earth2me.essentials.commands.essentials.NyanCommand;
import com.earth2me.essentials.commands.essentials.UsermapCommand;
import com.earth2me.essentials.commands.essentials.VersionCommand;
// This command has 4 undocumented behaviours #EasterEgg
public class Commandessentials extends EssentialsCommand {
private static final Sound NOTE_HARP = RegistryUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO");
private static final Sound MOO_SOUND = RegistryUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK");
private static final String HOMES_USAGE = "/<command> homes (fix | delete [world])";
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?\"..."};
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)
"DiscordSRV", // potential for issues if EssentialsXDiscord is installed
// Chat signing bypass plugins that can potentially break EssentialsChat
"AntiPopup",
"NoChatReports",
"NoEncryption"
);
private static final List<String> officialPlugins = Arrays.asList(
"EssentialsAntiBuild",
"EssentialsChat",
"EssentialsDiscord",
"EssentialsDiscordLink",
"EssentialsGeoIP",
"EssentialsProtect",
"EssentialsSpawn",
"EssentialsXMPP"
);
private static final List<String> warnPlugins = Arrays.asList(
"PermissionsEx",
"GroupManager",
"bPermissions",
// Brain-dead chat signing bypass that break EssentialsChat
"NoChatReports",
"NoEncryption"
);
private transient TuneRunnable currentTune = null;
public class Commandessentials extends EssentialsTreeCommand {
public Commandessentials() {
super("essentials");
}
@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;
case "dump":
runDump(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 "homes":
runHomes(server, sender, commandLabel, args);
break;
case "usermap":
runUserMap(sender, args);
break;
case "itemtest":
runItemTest(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);
break;
}
}
public void runItemTest(Server server, CommandSource sender, String commandLabel, String[] args) {
if (!sender.isAuthorized("essentials.itemtest") || args.length < 2 || !sender.isPlayer()) {
return;
}
final Player player = sender.getPlayer();
assert player != null;
switch (args[1]) {
case "slot": {
if (args.length < 3) {
return;
}
player.getInventory().setItem(Integer.parseInt(args[2]), new ItemStack(Material.DIRT));
break;
}
case "overfill": {
sender.sendMessage(Inventories.addItem(player, 42, false, new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32)).toString());
break;
}
case "overfill2": {
if (args.length < 4) {
return;
}
final boolean armor = Boolean.parseBoolean(args[2]);
final boolean add = Boolean.parseBoolean(args[3]);
final ItemStack[] items = new ItemStack[]{new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIAMOND_HELMET, 4), new ItemStack(Material.CHAINMAIL_LEGGINGS, 1)};
if (Inventories.hasSpace(player, 0, armor, items)) {
if (add) {
sender.sendMessage(Inventories.addItem(player, 0, armor, items).toString());
}
sender.sendMessage("SO MUCH SPACE!");
} else {
sender.sendMessage("No space!");
}
break;
}
case "remove": {
if (args.length < 3) {
return;
}
Inventories.removeItemExact(player, new ItemStack(Material.PUMPKIN, 1), Boolean.parseBoolean(args[2]));
break;
}
default: {
break;
}
}
}
// Displays the command's usage.
private void showUsage(final CommandSource sender) throws Exception {
throw new NotEnoughArgumentsException();
}
// 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) {
if (ess.getAlternativeCommandsHandler().disabledCommands().size() == 0) {
sender.sendTl("blockListEmpty");
return;
}
sender.sendTl("blockList");
for (final Map.Entry<String, String> entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) {
sender.sendMessage(entry.getKey() + " => " + entry.getValue());
}
}
// Generates a paste of useful information
private void runDump(Server server, CommandSource sender, String commandLabel, String[] args) {
sender.sendTl("dumpCreating");
final JsonObject dump = new JsonObject();
final JsonObject meta = new JsonObject();
meta.addProperty("timestamp", Instant.now().toEpochMilli());
meta.addProperty("sender", sender.getPlayer() != null ? sender.getPlayer().getName() : null);
meta.addProperty("senderUuid", sender.getPlayer() != null ? sender.getPlayer().getUniqueId().toString() : null);
dump.add("meta", meta);
final JsonObject serverData = new JsonObject();
serverData.addProperty("bukkit-version", Bukkit.getBukkitVersion());
serverData.addProperty("server-version", Bukkit.getVersion());
serverData.addProperty("server-brand", Bukkit.getName());
serverData.addProperty("online-mode", ess.provider(OnlineModeProvider.class).getOnlineModeString());
final JsonObject supportStatus = new JsonObject();
final VersionUtil.SupportStatus status = VersionUtil.getServerSupportStatus();
supportStatus.addProperty("status", status.name());
supportStatus.addProperty("supported", status.isSupported());
supportStatus.addProperty("trigger", VersionUtil.getSupportStatusClass());
serverData.add("support-status", supportStatus);
dump.add("server-data", serverData);
final JsonObject environment = new JsonObject();
environment.addProperty("java-version", System.getProperty("java.version"));
environment.addProperty("operating-system", System.getProperty("os.name"));
environment.addProperty("uptime", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()));
environment.addProperty("allocated-memory", (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");
dump.add("environment", environment);
final JsonObject essData = new JsonObject();
essData.addProperty("version", ess.getDescription().getVersion());
final JsonObject updateData = new JsonObject();
updateData.addProperty("id", ess.getUpdateChecker().getVersionIdentifier());
updateData.addProperty("branch", ess.getUpdateChecker().getVersionBranch());
updateData.addProperty("dev", ess.getUpdateChecker().isDevBuild());
essData.add("update-data", updateData);
final JsonObject econLayer = new JsonObject();
econLayer.addProperty("enabled", !ess.getSettings().isEcoDisabled());
econLayer.addProperty("selected-layer", EconomyLayers.isLayerSelected());
final EconomyLayer layer = EconomyLayers.getSelectedLayer();
econLayer.addProperty("name", layer == null ? "null" : layer.getName());
econLayer.addProperty("layer-version", layer == null ? "null" : layer.getPluginVersion());
econLayer.addProperty("backend-name", layer == null ? "null" : layer.getBackendName());
essData.add("economy-layer", econLayer);
final JsonArray addons = new JsonArray();
final JsonArray plugins = new JsonArray();
final ArrayList<Plugin> alphabetical = new ArrayList<>();
Collections.addAll(alphabetical, Bukkit.getPluginManager().getPlugins());
alphabetical.sort(Comparator.comparing(o -> o.getName().toUpperCase(Locale.ENGLISH)));
for (final Plugin plugin : alphabetical) {
final JsonObject pluginData = new JsonObject();
final PluginDescriptionFile info = plugin.getDescription();
final String name = info.getName();
pluginData.addProperty("name", name);
pluginData.addProperty("version", info.getVersion());
pluginData.addProperty("description", info.getDescription());
pluginData.addProperty("main", info.getMain());
pluginData.addProperty("enabled", plugin.isEnabled());
pluginData.addProperty("official", plugin == ess || officialPlugins.contains(name));
pluginData.addProperty("unsupported", warnPlugins.contains(name));
final JsonArray authors = new JsonArray();
for (final String author : info.getAuthors()) {
authors.add(author == null ? JsonNull.INSTANCE : new JsonPrimitive(author));
}
pluginData.add("authors", authors);
if (name.startsWith("Essentials") && !name.equals("Essentials")) {
addons.add(pluginData);
}
plugins.add(pluginData);
}
essData.add("addons", addons);
dump.add("ess-data", essData);
dump.add("plugins", plugins);
final List<PasteUtil.PasteFile> files = new ArrayList<>();
files.add(new PasteUtil.PasteFile("dump.json", dump.toString()));
final Plugin essDiscord = Bukkit.getPluginManager().getPlugin("EssentialsDiscord");
final Plugin essDiscordLink = Bukkit.getPluginManager().getPlugin("EssentialsDiscordLink");
final Plugin essSpawn = Bukkit.getPluginManager().getPlugin("EssentialsSpawn");
final Map<String, Command> knownCommandsCopy = new HashMap<>(ess.provider(KnownCommandsProvider.class).getKnownCommands());
final Map<String, String> disabledCommandsCopy = new HashMap<>(ess.getAlternativeCommandsHandler().disabledCommands());
// Further operations will be heavy IO
ess.runTaskAsynchronously(() -> {
boolean config = false;
boolean discord = false;
boolean kits = false;
boolean log = false;
boolean worth = false;
boolean tpr = false;
boolean spawns = false;
boolean commands = false;
for (final String arg : args) {
if (arg.equals("*") || arg.equalsIgnoreCase("all")) {
config = true;
discord = true;
kits = true;
log = true;
worth = true;
tpr = true;
spawns = true;
commands = true;
break;
} else if (arg.equalsIgnoreCase("config")) {
config = true;
} else if (arg.equalsIgnoreCase("discord")) {
discord = true;
} else if (arg.equalsIgnoreCase("kits")) {
kits = true;
} else if (arg.equalsIgnoreCase("log")) {
log = true;
} else if (arg.equalsIgnoreCase("worth")) {
worth = true;
} else if (arg.equalsIgnoreCase("tpr")) {
tpr = true;
} else if (arg.equalsIgnoreCase("spawns")) {
spawns = true;
} else if (arg.equalsIgnoreCase("commands")) {
commands = true;
}
}
if (config) {
try {
files.add(new PasteUtil.PasteFile("config.yml", new String(Files.readAllBytes(ess.getSettings().getConfigFile().toPath()), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "config.yml", e.getMessage());
}
}
if (discord && essDiscord != null) {
try {
files.add(new PasteUtil.PasteFile("discord-config.yml",
new String(Files.readAllBytes(essDiscord.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8)
.replaceAll("[A-Za-z\\d]{24}\\.[\\w-]{6}\\.[\\w-]{27}", "<censored token>")));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "discord-config.yml", e.getMessage());
}
if (essDiscordLink != null) {
try {
files.add(new PasteUtil.PasteFile("discord-link-config.yml",
new String(Files.readAllBytes(essDiscordLink.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "discord-link-config.yml", e.getMessage());
}
}
}
if (kits) {
try {
files.add(new PasteUtil.PasteFile("kits.yml", new String(Files.readAllBytes(ess.getKits().getFile().toPath()), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "kits.yml", e.getMessage());
}
}
if (log) {
try {
files.add(new PasteUtil.PasteFile("latest.log", new String(Files.readAllBytes(Paths.get("logs", "latest.log")), StandardCharsets.UTF_8)
.replaceAll("(?m)^\\[\\d\\d:\\d\\d:\\d\\d] \\[.+/(?:DEBUG|TRACE)]: .+\\s(?:[A-Za-z.]+:.+\\s(?:\\t.+\\s)*)?\\s*(?:\"[A-Za-z]+\" : .+[\\s}\\]]+)*", "")
.replaceAll("(?:[0-9]{1,3}\\.){3}[0-9]{1,3}", "<censored ip address>")));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "latest.log", e.getMessage());
}
}
if (worth) {
try {
files.add(new PasteUtil.PasteFile("worth.yml", new String(Files.readAllBytes(ess.getWorth().getFile().toPath()), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "worth.yml", e.getMessage());
}
}
if (tpr) {
try {
files.add(new PasteUtil.PasteFile("tpr.yml", new String(Files.readAllBytes(ess.getRandomTeleport().getFile().toPath()), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "tpr.yml", e.getMessage());
}
}
if (spawns && essSpawn != null) {
try {
files.add(new PasteUtil.PasteFile("spawn.yml", new String(Files.readAllBytes(ess.getDataFolder().toPath().resolve("spawn.yml")), StandardCharsets.UTF_8)));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "spawn.yml", e.getMessage());
}
}
if (commands) {
try {
files.add(new PasteUtil.PasteFile("commands.yml", new String(Files.readAllBytes(Paths.get("commands.yml")), StandardCharsets.UTF_8)));
files.add(new PasteUtil.PasteFile("commandmap.json", CommandMapUtil.toJsonPretty(ess, knownCommandsCopy)));
files.add(new PasteUtil.PasteFile("commandoverride.json", disabledCommandsCopy.toString()));
} catch (IOException e) {
sender.sendTl("dumpErrorUpload", "commands.yml", e.getMessage());
}
}
final CompletableFuture<PasteUtil.PasteResult> future = PasteUtil.createPaste(files);
future.thenAccept(result -> {
if (result != null) {
final String dumpUrl = "https://essentialsx.net/dump.html?bytebin=" + result.getPasteId();
sender.sendTl("dumpUrl", dumpUrl);
// pastes.dev doesn't support deletion keys
//sender.sendTl("dumpDeleteKey", result.getDeletionKey());
if (sender.isPlayer()) {
ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpConsoleUrl", dumpUrl)));
// pastes.dev doesn't support deletion keys
//ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpDeleteKey", result.getDeletionKey())));
}
}
files.clear();
});
future.exceptionally(throwable -> {
sender.sendTl("dumpError", throwable.getMessage());
return null;
});
});
}
// 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.sendTl("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 (final String s : CONSOLE_MOO) {
ess.getLogger().info(s);
}
for (final 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.sendTl("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;
ess.runTaskAsynchronously(() -> {
final long currTime = System.currentTimeMillis();
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
final User user = ess.getUsers().loadUncachedUser(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;
}
final long timeDiff = currTime - lastLog;
final long milliDays = daysArg * 24L * 60L * 60L * 1000L;
final int homeCount = user.getHomes().size();
final 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.sendTl("cleaned");
});
}
private void runHomes(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
sender.sendMessage("This sub-command provides a utility to mass-delete homes based on user options:");
sender.sendMessage("Use \"fix\" to delete all homes inside non-existent or unloaded worlds.");
sender.sendMessage("Use \"delete\" to delete all existing homes.");
sender.sendMessage("Use \"delete <worldname>\" to delete all homes inside a specific world.");
throw new Exception(HOMES_USAGE);
}
switch (args[1]) {
case "fix":
sender.sendTl("fixingHomes");
ess.runTaskAsynchronously(() -> {
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
final User user = ess.getUsers().loadUncachedUser(u);
if (user == null) {
continue;
}
for (String homeName : user.getHomes()) {
try {
if (user.getHome(homeName) == null) {
user.delHome(homeName);
}
} catch (Exception e) {
ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName());
}
}
}
sender.sendTl("fixedHomes");
});
break;
case "delete":
final boolean filterByWorld = args.length >= 3;
if (filterByWorld && server.getWorld(args[2]) == null) {
throw new TranslatableException("invalidWorld");
}
if (filterByWorld) {
sender.sendTl("deletingHomesWorld", args[2]);
} else {
sender.sendTl("deletingHomes");
}
ess.runTaskAsynchronously(() -> {
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
final User user = ess.getUsers().loadUncachedUser(u);
if (user == null) {
continue;
}
for (String homeName : user.getHomes()) {
try {
final Location home = user.getHome(homeName);
if (!filterByWorld || (home != null && home.getWorld() != null && home.getWorld().getName().equals(args[2]))) {
user.delHome(homeName);
}
} catch (Exception e) {
ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName());
}
}
}
if (filterByWorld) {
sender.sendTl("deletedHomesWorld", args[2]);
} else {
sender.sendTl("deletedHomes");
}
});
break;
default:
throw new Exception(HOMES_USAGE);
}
}
// Gets information about cached users
private void runUserMap(final CommandSource sender, final String[] args) {
if (!sender.isAuthorized("essentials.usermap")) {
return;
}
final ModernUserMap userMap = (ModernUserMap) ess.getUsers();
sender.sendTl("usermapSize", userMap.getCachedCount(), userMap.getUserCount(), ess.getSettings().getMaxUserCacheCount());
if (args.length > 1) {
if (args[1].equals("full")) {
for (final Map.Entry<String, UUID> entry : userMap.getNameCache().entrySet()) {
sender.sendTl("usermapEntry", entry.getKey(), entry.getValue().toString());
}
} else if (args[1].equals("purge")) {
final boolean seppuku = args.length > 2 && args[2].equals("iknowwhatimdoing");
sender.sendTl("usermapPurge", String.valueOf(seppuku));
final Set<UUID> uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs());
ess.runTaskAsynchronously(() -> {
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup-boogaloo-" + System.currentTimeMillis());
if (!userdataFolder.isDirectory()) {
ess.getLogger().warning("Missing userdata folder, aborting usermap purge.");
return;
}
if (seppuku && !backupFolder.mkdir()) {
ess.getLogger().warning("Unable to create backup folder, aborting usermap purge.");
return;
}
int total = 0;
final File[] files = userdataFolder.listFiles(EssentialsUpgrade.YML_FILTER);
if (files != null) {
for (final File file : files) {
try {
final String fileName = file.getName();
final UUID uuid = UUID.fromString(fileName.substring(0, fileName.length() - 4));
if (!uuids.contains(uuid)) {
total++;
ess.getLogger().warning("Found orphaned userdata file: " + file.getName());
if (seppuku) {
try {
com.google.common.io.Files.move(file, new File(backupFolder, file.getName()));
} catch (IOException e) {
ess.getLogger().log(Level.WARNING, "Unable to move orphaned userdata file: " + file.getName(), e);
}
}
}
} catch (IllegalArgumentException ignored) {
}
}
}
ess.getLogger().info("Found " + total + " orphaned userdata files.");
});
} else if (args[1].equalsIgnoreCase("cache")) {
sender.sendTl("usermapKnown", ess.getUsers().getAllUserUUIDs().size(), ess.getUsers().getNameCache().size());
} else {
try {
final UUID uuid = UUID.fromString(args[1]);
for (final Map.Entry<String, UUID> entry : userMap.getNameCache().entrySet()) {
if (entry.getValue().equals(uuid)) {
sender.sendTl("usermapEntry", entry.getKey(), args[1]);
}
}
} catch (IllegalArgumentException ignored) {
final String sanitizedName = userMap.getSanitizedName(args[1]);
sender.sendTl("usermapEntry", sanitizedName, userMap.getNameCache().get(sanitizedName).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 VersionUtil.SupportStatus supportStatus = VersionUtil.getServerSupportStatus();
final PluginManager pm = server.getPluginManager();
final String essVer = pm.getPlugin("Essentials").getDescription().getVersion();
final String serverMessageKey;
if (supportStatus.isSupported()) {
serverMessageKey = "versionOutputFine";
} else if (supportStatus == VersionUtil.SupportStatus.UNSTABLE) {
serverMessageKey = "versionOutputUnsupported";
} else {
serverMessageKey = "versionOutputWarn";
}
sender.sendTl(serverMessageKey, "Server", server.getBukkitVersion() + " " + server.getVersion());
sender.sendTl(serverMessageKey, "Brand", server.getName());
sender.sendTl("versionOutputFine", "EssentialsX", essVer);
for (final Plugin plugin : pm.getPlugins()) {
final PluginDescriptionFile desc = plugin.getDescription();
String name = desc.getName();
final 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.sendTl("versionOutputWarn", name, version);
} else {
sender.sendTl("versionOutputFine", name, version);
}
} else {
sender.sendTl("versionOutputUnsupported", name, version);
isUnsupported = true;
}
}
if (versionPlugins.contains(name)) {
if (warnPlugins.contains(name)) {
sender.sendTl("versionOutputUnsupported", name, version);
isUnsupported = true;
} else {
sender.sendTl("versionOutputFine", name, version);
}
}
if (name.equals("Vault")) isVaultInstalled = true;
}
final String layer;
if (ess.getSettings().isEcoDisabled()) {
layer = "Disabled";
} else if (EconomyLayers.isLayerSelected()) {
final EconomyLayer economyLayer = EconomyLayers.getSelectedLayer();
layer = economyLayer.getName() + " (" + economyLayer.getBackendName() + ")";
} else {
layer = "None";
}
sender.sendTl("versionOutputEconLayer", layer);
if (isMismatched) {
sender.sendTl("versionMismatchAll");
}
if (!isVaultInstalled) {
sender.sendTl("versionOutputVaultMissing");
}
if (isUnsupported) {
sender.sendTl("versionOutputUnsupportedPlugins");
}
switch (supportStatus) {
case NMS_CLEANROOM:
sender.sendComponent(sender.tlComponent("serverUnsupportedCleanroom").color(NamedTextColor.DARK_RED));
break;
case DANGEROUS_FORK:
sender.sendComponent(sender.tlComponent("serverUnsupportedDangerous").color(NamedTextColor.DARK_RED));
break;
case STUPID_PLUGIN:
sender.sendComponent(sender.tlComponent("serverUnsupportedDumbPlugins").color(NamedTextColor.DARK_RED));
break;
case UNSTABLE:
sender.sendComponent(sender.tlComponent("serverUnsupportedMods").color(NamedTextColor.DARK_RED));
break;
case OUTDATED:
sender.sendComponent(sender.tlComponent("serverUnsupported").color(NamedTextColor.RED));
break;
case LIMITED:
sender.sendComponent(sender.tlComponent("serverUnsupportedLimitedApi").color(NamedTextColor.RED));
break;
}
if (VersionUtil.getSupportStatusClass() != null) {
sender.sendComponent(sender.tlComponent("serverUnsupportedClass").color(NamedTextColor.RED));
}
sender.sendTl("versionFetching");
ess.runTaskAsynchronously(() -> {
for (final Component component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) {
sender.sendComponent(component);
}
});
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
final List<String> options = Lists.newArrayList();
options.add("reload");
options.add("version");
options.add("dump");
options.add("commands");
options.add("debug");
options.add("reset");
options.add("cleanup");
options.add("homes");
//options.add("uuidconvert");
//options.add("nya");
//options.add("moo");
return options;
}
switch (args[0]) {
case "moo":
if (args.length == 2) {
return Lists.newArrayList("moo");
}
break;
case "reset":
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 "homes":
if (args.length == 2) {
return Lists.newArrayList("fix", "delete");
} else if (args.length == 3 && args[1].equalsIgnoreCase("delete")) {
return server.getWorlds().stream().map(World::getName).collect(Collectors.toList());
}
break;
case "dump":
final List<String> list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all");
for (String arg : args) {
if (arg.equals("*") || arg.equalsIgnoreCase("all")) {
list.clear();
return list;
}
list.remove(arg.toLowerCase(Locale.ENGLISH));
}
return list;
}
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 (final Player onlinePlayer : players.get()) {
onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note));
}
}
registerNode(new VersionCommand());
registerNode(new DebugCommand());
registerNode(new CommandMapCommand());
registerNode(new DumpCommand());
registerNode(new CleanupCommand());
registerNode(new HomesCommand());
registerNode(new UsermapCommand());
registerNode(new ItemTestCommand());
registerNode(new NyanCommand());
registerNode(new MooCommand());
}
}
@@ -154,7 +154,7 @@ public class Commandexp extends EssentialsLoopCommand {
}
}
if (user.isAuthorized("essentials.exp.others")) {
return getPlayers(server, user);
return getPlayers(user);
}
} else if (args.length == 3 && !(args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("reset"))) {
final String levellessArg = args[2].toLowerCase(Locale.ENGLISH).replaceAll("l", "");
@@ -182,10 +182,10 @@ public class Commandexp extends EssentialsLoopCommand {
return Collections.emptyList();
}
} else { // even without 'show'
return getPlayers(server, sender);
return getPlayers(sender);
}
} else if (args.length == 3 && (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("give"))) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -46,7 +46,7 @@ public class Commandext extends EssentialsLoopCommand {
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -65,7 +65,7 @@ public class Commandfeed extends EssentialsLoopCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.feed.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -105,12 +105,12 @@ public class Commandgamemode extends EssentialsLoopCommand {
try {
// Direct command? Don't ask for the mode
matchGameMode(commandLabel);
return getPlayers(server, sender);
return getPlayers(sender);
} catch (final NotEnoughArgumentsException e) {
return STANDARD_OPTIONS;
}
} else if (args.length == 2) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -128,12 +128,12 @@ public class Commandgamemode extends EssentialsLoopCommand {
}
if (args.length == 1) {
if (user.isAuthorized("essentials.gamemode.others") && isDirectGamemodeCommand) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return STANDARD_OPTIONS;
}
} else if (args.length == 2 && user.isAuthorized("essentials.gamemode.others") && !isDirectGamemodeCommand) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -3,14 +3,13 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.NumberUtil;
import net.ess3.provider.WorldTileEntityCountProvider;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.Server;
import org.bukkit.World;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.logging.Level;
public class Commandgc extends EssentialsCommand {
public Commandgc() {
@@ -19,7 +18,7 @@ public class Commandgc extends EssentialsCommand {
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
final double tps = ess.getTimer().getAverageTPS();
final double tps = 20d; //TODO
final ChatColor color;
if (tps >= 18.0) {
color = ChatColor.GREEN;
@@ -36,6 +35,7 @@ public class Commandgc extends EssentialsCommand {
sender.sendTl("gcfree", Runtime.getRuntime().freeMemory() / 1024 / 1024);
final List<World> worlds = server.getWorlds();
final WorldTileEntityCountProvider worldTileEntityCountProvider = ess.provider(WorldTileEntityCountProvider.class);
for (final World w : worlds) {
String worldType = "World";
switch (w.getEnvironment()) {
@@ -47,15 +47,7 @@ public class Commandgc extends EssentialsCommand {
break;
}
int tileEntities = 0;
try {
for (final Chunk chunk : w.getLoadedChunks()) {
tileEntities += chunk.getTileEntities().length;
}
} catch (final java.lang.ClassCastException ex) {
ess.getLogger().log(Level.SEVERE, "Corrupted chunk data on world " + w, ex);
}
final int tileEntities = worldTileEntityCountProvider.getTileEntityCount(w);
sender.sendTl("gcWorld", worldType, w.getName(), w.getLoadedChunks().length, w.getEntities().size(), tileEntities);
}
@@ -47,7 +47,7 @@ public class Commandgetpos extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.getpos.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -99,7 +99,7 @@ public class Commandgive extends EssentialsLoopCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return getItems();
} else if (args.length == 3) {
@@ -63,13 +63,16 @@ public class Commandheal extends EssentialsLoopCommand {
player.setHealth(newAmount);
player.setFoodLevel(20);
player.setFireTicks(0);
player.setRemainingAir(player.getMaximumAir());
user.sendTl("heal");
if (ess.getSettings().isRemovingEffectsOnHeal()) {
for (final PotionEffect effect : player.getActivePotionEffects()) {
player.removePotionEffect(effect.getType());
}
}
sender.sendTl("healOther", user.getDisplayName());
if (!sender.isPlayer() || !user.getBase().equals(sender.getPlayer())) {
sender.sendTl("healOther", user.getDisplayName());
}
} catch (final QuietAbortException e) {
//Handle Quietly
}
@@ -78,7 +81,7 @@ public class Commandheal extends EssentialsLoopCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.heal.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
@@ -7,7 +7,7 @@ import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.TextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import net.ess3.provider.KnownCommandsProvider;
import org.bukkit.Server;
@@ -4,7 +4,6 @@ import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Console;
import com.earth2me.essentials.User;
import com.earth2me.essentials.messaging.IMessageRecipient;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import net.ess3.api.IUser;
import net.essentialsx.api.v2.events.HelpopMessageSendEvent;
@@ -39,11 +38,11 @@ public class Commandhelpop extends EssentialsCommand {
}
final String message = FormatUtil.stripFormat(getFinalArg(args, 0));
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("helpOp", from.getDisplayName(), message)));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("helpOp", from.getDisplayName(), message)));
final List<IUser> recipients = new ArrayList<>();
for (IUser user : ess.getOnlineUsers()) {
if (user.getBase().hasPermission("essentials.helpop.receive")) {
if (user.isAuthorized("essentials.helpop.receive")) {
recipients.add(user);
}
}
@@ -51,9 +50,11 @@ public class Commandhelpop extends EssentialsCommand {
final HelpopMessageSendEvent sendEvent = new HelpopMessageSendEvent(from, recipients, message);
ess.getServer().getPluginManager().callEvent(sendEvent);
final IUser sender = (IUser) from;
if(!recipients.contains(sender)){
from.sendTl("helpOp", from.getDisplayName(), message);
if (from instanceof IUser) {
final IUser sender = (IUser) from;
if (!recipients.contains(sender)) {
from.sendTl("helpOp", from.getDisplayName(), message);
}
}
for (IUser recipient : sendEvent.getRecipients()) {
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.OfflinePlayerStub;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.StringUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.TranslatableException;
@@ -155,7 +155,7 @@ public class Commandhome extends EssentialsCommand {
if (canVisitOthers) {
final int sepIndex = args[0].indexOf(':');
if (sepIndex < 0) {
getPlayers(server, user).forEach(player -> homes.add(player + ":"));
getPlayers(user).forEach(player -> homes.add(player + ":"));
} else {
final String namePart = args[0].substring(0, sepIndex);
final User otherUser;
@@ -49,7 +49,7 @@ public class Commandice extends EssentialsLoopCommand {
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.ice.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -27,15 +27,7 @@ public class Commandignore extends EssentialsCommand {
return;
}
User player;
try {
player = getPlayer(server, args, 0, true, true);
} catch (final PlayerNotFoundException ex) {
player = ess.getOfflineUser(args[0]);
}
if (player == null) {
throw new PlayerNotFoundException();
}
final User player = getPlayer(server, args, 0, false, true);
if (player.isIgnoreExempt()) {
user.sendTl("ignoreExempt");
@@ -53,7 +45,7 @@ public class Commandignore extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -45,7 +45,7 @@ public class Commandinvsee extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
final List<String> suggestions = getPlayers(server, user);
final List<String> suggestions = getPlayers(user);
suggestions.remove(user.getName());
return suggestions;
} else {
@@ -3,7 +3,6 @@ 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.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import net.ess3.api.TranslatableException;
import net.essentialsx.api.v2.events.UserKickEvent;
@@ -39,7 +38,7 @@ public class Commandkick extends EssentialsCommand {
}
}
String kickReason = args.length > 1 ? getFinalArg(args, 1) : AdventureUtil.miniToLegacy(tlLiteral("kickDefault"));
String kickReason = args.length > 1 ? getFinalArg(args, 1) : ess.getAdventureFacet().miniToLegacy(tlLiteral("kickDefault"));
kickReason = FormatUtil.replaceFormat(kickReason.replace("\\n", "\n").replace("|", "\n"));
final UserKickEvent event = new UserKickEvent(user, target, kickReason);
@@ -55,14 +54,14 @@ public class Commandkick extends EssentialsCommand {
final String tlKey = "playerKicked";
final Object[] objects = {senderDisplayName, target.getName(), kickReason};
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects)));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects)));
ess.broadcastTl(null, "essentials.kick.notify", tlKey, objects);
}
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -1,7 +1,6 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@@ -15,7 +14,7 @@ public class Commandkickall extends EssentialsCommand {
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
String kickReason = args.length > 0 ? getFinalArg(args, 0) : AdventureUtil.miniToLegacy(tlLiteral("kickDefault"));
String kickReason = args.length > 0 ? getFinalArg(args, 0) : ess.getAdventureFacet().miniToLegacy(tlLiteral("kickDefault"));
kickReason = FormatUtil.replaceFormat(kickReason.replace("\\n", "\n").replace("|", "\n"));
for (final Player onlinePlayer : ess.getOnlinePlayers()) {
@@ -32,19 +32,21 @@ public class Commandkill extends EssentialsLoopCommand {
}
final DamageEventProvider provider = ess.provider(DamageEventProvider.class);
final EntityDamageEvent ede = provider.callDamageEvent(matchPlayer, sender.isPlayer() && sender.getPlayer().getName().equals(matchPlayer.getName()) ? EntityDamageEvent.DamageCause.SUICIDE : EntityDamageEvent.DamageCause.CUSTOM, Float.MAX_VALUE);
if (ede.isCancelled() && sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.kill.force")) {
return;
}
ede.getEntity().setLastDamageCause(ede);
matchPlayer.setHealth(0);
sender.sendTl("kill", matchPlayer.getDisplayName());
ess.scheduleEntityDelayedTask(matchPlayer, () -> {
final EntityDamageEvent ede = provider.callDamageEvent(matchPlayer, sender.isPlayer() && sender.getPlayer().getName().equals(matchPlayer.getName()) ? EntityDamageEvent.DamageCause.SUICIDE : EntityDamageEvent.DamageCause.CUSTOM, Float.MAX_VALUE);
if (ede.isCancelled() && sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.kill.force")) {
return;
}
ede.getEntity().setLastDamageCause(ede);
matchPlayer.setHealth(0);
sender.sendTl("kill", matchPlayer.getDisplayName());
});
}
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Kit;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.StringUtil;
import net.ess3.api.TranslatableException;
import org.bukkit.Server;
@@ -108,7 +108,7 @@ public class Commandkit extends EssentialsCommand {
}
return options;
} else if (args.length == 2 && user.isAuthorized("essentials.kit.others")) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -119,7 +119,7 @@ public class Commandkit extends EssentialsCommand {
if (args.length == 1) {
return new ArrayList<>(ess.getKits().getKitKeys()); // TODO: Move this to its own method
} else if (args.length == 2) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -59,7 +59,7 @@ public class Commandkitreset extends EssentialsCommand {
if (args.length == 1) {
return new ArrayList<>(ess.getKits().getKitKeys());
} else if (args.length == 2 && sender.isAuthorized("essentials.kitreset.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -49,7 +49,7 @@ public class Commandkittycannon extends EssentialsCommand {
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final Entity ocelot = Mob.CAT.getType() == null ? spawnOcelot(server, user) : spawnCat(server, user);
ess.scheduleSyncDelayedTask(() -> {
ess.scheduleEntityDelayedTask(ocelot, () -> {
final Location loc = ocelot.getLocation();
ocelot.remove();
loc.getWorld().createExplosion(loc, 0F);
@@ -33,15 +33,17 @@ public class Commandlightning extends EssentialsLoopCommand {
}
final int finalPower = power;
loopOnlinePlayersConsumer(server, sender, false, true, args[0], player -> {
sender.sendTl("lightningUse", player.getDisplayName());
final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation());
ess.scheduleEntityDelayedTask(player.getBase(), () -> {
sender.sendTl("lightningUse", player.getDisplayName());
final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation());
if (!player.isGodModeEnabled()) {
player.getBase().damage(finalPower, strike);
}
if (ess.getSettings().warnOnSmite()) {
player.sendTl("lightningSmited");
}
if (!player.isGodModeEnabled()) {
player.getBase().damage(finalPower, strike);
}
if (ess.getSettings().warnOnSmite()) {
player.sendTl("lightningSmited");
}
});
});
loopOnlinePlayers(server, sender, true, true, args[0], null);
}
@@ -63,7 +65,7 @@ public class Commandlightning extends EssentialsLoopCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return Lists.newArrayList("5");
} else {
@@ -3,7 +3,6 @@ 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.AdventureUtil;
import org.bukkit.Server;
import java.util.ArrayList;
@@ -24,11 +23,11 @@ public class Commandlist extends EssentialsCommand {
user = ess.getUser(sender.getPlayer());
showHidden = user.isAuthorized("essentials.list.hidden") || user.canInteractVanished();
}
sender.sendComponent(AdventureUtil.miniMessage().deserialize(PlayerList.listSummary(ess, user, showHidden)));
sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(PlayerList.listSummary(ess, user, showHidden)));
final Map<String, List<User>> playerList = PlayerList.getPlayerLists(ess, user, showHidden);
if (args.length > 0) {
sender.sendComponent(AdventureUtil.miniMessage().deserialize(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase())));
sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase())));
} else {
sendGroupedList(sender, commandLabel, playerList);
}
@@ -37,7 +36,7 @@ public class Commandlist extends EssentialsCommand {
// Output the standard /list output, when no group is specified
private void sendGroupedList(final CommandSource sender, final String commandLabel, final Map<String, List<User>> playerList) {
for (final String str : PlayerList.prepareGroupedList(ess, sender, commandLabel, playerList)) {
sender.sendComponent(AdventureUtil.miniMessage().deserialize(str));
sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(str));
}
}
@@ -334,7 +334,7 @@ public class Commandmail extends EssentialsCommand {
return options;
} else if (args.length == 2) {
if ((args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) || (args[0].equalsIgnoreCase("sendtemp") && user.isAuthorized("essentials.mail.sendtemp")) || ((args[0].equalsIgnoreCase("clear"))&& user.isAuthorized("essentials.mail.clear.others"))) {
return getPlayers(server, user);
return getPlayers(user);
} else if (args[0].equalsIgnoreCase("sendtempall") && user.isAuthorized("essentials.mail.sendtempall")) {
return COMMON_DATE_DIFFS;
} else if (args[0].equalsIgnoreCase("read")) {
@@ -374,7 +374,7 @@ public class Commandmail extends EssentialsCommand {
return Lists.newArrayList("send", "sendall", "sendtemp", "sendtempall", "clearall", "clear");
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("send") || args[0].equalsIgnoreCase("sendtemp") || args[0].equalsIgnoreCase("clear")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else if (args[0].equalsIgnoreCase("sendtempall")) {
return COMMON_DATE_DIFFS;
}
@@ -58,7 +58,7 @@ public class Commandmsg extends EssentialsLoopCommand {
@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);
return getPlayers(sender);
} else {
return Collections.emptyList(); // It's a chat message, send an empty list.
}
@@ -3,7 +3,6 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.OfflinePlayerStub;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.DateUtil;
import net.ess3.api.TranslatableException;
import net.ess3.api.events.MuteStatusChangeEvent;
@@ -117,7 +116,7 @@ public class Commandmute extends EssentialsCommand {
objects = new Object[]{sender.getSender().getName(), user.getName(), muteTime};
}
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects)));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects)));
ess.broadcastTl(null, "essentials.mute.notify", tlKey, objects);
} else {
sender.sendTl("unmutedPlayer", user.getDisplayName());
@@ -129,7 +128,7 @@ public class Commandmute extends EssentialsCommand {
@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);
return getPlayers(sender);
} else {
return COMMON_DATE_DIFFS; // Date diff can span multiple words
}
@@ -2,7 +2,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.google.common.collect.Lists;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -117,7 +117,7 @@ public class Commandnear extends EssentialsCommand {
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (user.isAuthorized("essentials.near.others")) {
if (args.length == 1) {
return getPlayers(server, user);
return getPlayers(user);
} else if (args.length == 2) {
return Lists.newArrayList(Integer.toString(ess.getSettings().getNearRadius()));
} else {
@@ -135,7 +135,7 @@ public class Commandnear extends EssentialsCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return Lists.newArrayList(Integer.toString(ess.getSettings().getNearRadius()));
} else {
@@ -5,7 +5,6 @@ import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import net.ess3.api.TranslatableException;
import net.ess3.api.events.NickChangeEvent;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import java.util.Collections;
@@ -49,7 +48,8 @@ public class Commandnick extends EssentialsLoopCommand {
target.sendTl("nickNoMore");
} else if (target.getName().equalsIgnoreCase(nick)) {
setNickname(server, sender, target, nick);
if (!target.getDisplayName().equalsIgnoreCase(target.getDisplayName())) {
final String strippedDisplay = FormatUtil.stripFormat(target.getDisplayName());
if (strippedDisplay != null && !strippedDisplay.equalsIgnoreCase(target.getName())) {
target.sendTl("nickNoMore");
}
target.sendTl("nickSet", ess.getSettings().changeDisplayName() ? target.getDisplayName() : nick);
@@ -63,7 +63,7 @@ public class Commandnick extends EssentialsLoopCommand {
private String formatNickname(final User user, final String nick) throws Exception {
final String newNick = user == null ? FormatUtil.replaceFormat(nick) : FormatUtil.formatString(user, "essentials.nick", nick);
if (!newNick.matches("^[a-zA-Z_0-9" + ChatColor.COLOR_CHAR + "]+$") && user != null && !user.isAuthorized("essentials.nick.allowunsafe")) {
if (!newNick.matches(ess.getSettings().getNickRegex()) && user != null && !user.isAuthorized("essentials.nick.allowunsafe")) {
throw new TranslatableException("nickNamesAlpha");
} else if (getNickLength(newNick) > ess.getSettings().getMaxNickLength()) {
throw new TranslatableException("nickTooLong");
@@ -87,7 +87,10 @@ public class Commandnick extends EssentialsLoopCommand {
}
private int getNickLength(final String nick) {
return ess.getSettings().ignoreColorsInMaxLength() ? ChatColor.stripColor(nick).length() : nick.length();
if (ess.getSettings().ignoreColorsInMaxLength()) {
return FormatUtil.stripFormat(nick).length();
}
return FormatUtil.unformatString(nick).length();
}
private boolean nickInUse(final User target, final String nick) {
@@ -118,7 +121,7 @@ public class Commandnick extends EssentialsLoopCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.nick.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -35,24 +35,27 @@ public class Commandnuke extends EssentialsCommand {
if (user == null) {
continue;
}
user.sendTl("nuke");
final Location loc = user.getLocation();
final World world = loc.getWorld();
if (world != null) {
for (int x = -10; x <= 10; x += 5) {
for (int z = -10; z <= 10; z += 5) {
final TNTPrimed entity = world.spawn(new Location(world, loc.getBlockX() + x, world.getHighestBlockYAt(loc) + 64, loc.getBlockZ() + z), TNTPrimed.class);
entity.setMetadata(NUKE_META_KEY, new FixedMetadataValue(ess, true));
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
user.sendTl("nuke");
final Location loc = user.getLocation();
final World world = loc.getWorld();
if (world != null) {
for (int x = -10; x <= 10; x += 5) {
for (int z = -10; z <= 10; z += 5) {
final TNTPrimed entity = world.spawn(new Location(world, loc.getBlockX() + x, world.getHighestBlockYAt(loc) + 64, loc.getBlockZ() + z), TNTPrimed.class);
entity.setMetadata(NUKE_META_KEY, new FixedMetadataValue(ess, true));
}
}
}
}
});
}
}
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.collect.Lists;
@@ -109,7 +109,7 @@ public class Commandpay extends EssentialsLoopCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return Lists.newArrayList(ess.getSettings().getMinimumPayAmount().toString());
} else {
@@ -2,7 +2,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.VersionUtil;
@@ -28,7 +28,7 @@ public class Commandplaytime extends EssentialsCommand {
String displayName;
long playtime;
final String key;
if (args.length > 0 && sender.isAuthorized("essentials.playtime.others")) {
try {
final IUser user = getPlayer(server, sender, args, 0);
@@ -57,16 +57,16 @@ public class Commandplaytime extends EssentialsCommand {
}
final long playtimeMs = System.currentTimeMillis() - (playtime * 50L);
sender.sendTl(key, DateUtil.formatDateDiff(playtimeMs), AdventureUtil.parsed(AdventureUtil.legacyToMini(displayName)));
sender.sendTl(key, DateUtil.formatDateDiff(playtimeMs), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(displayName)));
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.playtime.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
}
}
@@ -42,7 +42,7 @@ public class Commandpotion extends EssentialsCommand {
boolean holdingPotion = stack.getType() == Material.POTION;
if (!holdingPotion && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_9_R01)) {
holdingPotion = stack.getType() == Material.SPLASH_POTION || stack.getType() == Material.LINGERING_POTION;
holdingPotion = stack.getType() == Material.SPLASH_POTION || stack.getType() == Material.LINGERING_POTION || stack.getType() == Material.TIPPED_ARROW;
}
if (holdingPotion) {
PotionMeta pmeta = (PotionMeta) stack.getItemMeta();
@@ -75,7 +75,7 @@ public class Commandpotion extends EssentialsCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
// Note: this enforces an order of effect power duration splash, which the actual command doesn't have. But that's fine.
// Note: this enforces an order of effect power duration splash, which the actual command doesn't have. But that's fine.
if (args.length == 1) {
final List<String> options = Lists.newArrayList();
options.add("clear");
@@ -129,7 +129,7 @@ public class Commandpowertool extends EssentialsCommand {
@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);
return getPlayers(sender);
} else if (args.length == 2) {
return getItems();
} else if (args.length == 3) {
@@ -0,0 +1,29 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.StringUtil;
import org.bukkit.Server;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class Commandpowertoollist extends EssentialsCommand {
public Commandpowertoollist() {
super("powertoollist");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (!user.hasPowerTools()) {
user.sendTl("noPowerTools");
return;
}
final Map<String, List<String>> powertools = user.getAllPowertools();
for (Map.Entry<String, List<String>> entry : powertools.entrySet()) {
final String itemName = entry.getKey().toLowerCase(Locale.ENGLISH).replaceAll("_", " ");
final List<String> commands = entry.getValue();
user.sendTl("powerToolList", StringUtil.joinList(commands), itemName);
}
}
}
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.IUser;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.DescParseTickFormat;
import com.google.common.collect.Lists;
import org.bukkit.Server;
@@ -122,7 +122,7 @@ public class Commandptime extends EssentialsLoopCommand {
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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -107,7 +107,7 @@ public class Commandpweather extends EssentialsLoopCommand {
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);
return getPlayers(user);
} else {
return Collections.emptyList();
}
@@ -2,8 +2,8 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.VersionUtil;
@@ -11,14 +11,18 @@ import net.ess3.api.TranslatableException;
import net.ess3.provider.InventoryViewProvider;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.inventory.TransmuteRecipe;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -62,7 +66,7 @@ public class Commandrecipe extends EssentialsCommand {
if (!sender.isPlayer()) {
throw new TranslatableException("consoleCannotUseCommand");
}
itemType = Inventories.getItemInHand(sender.getPlayer());
} else {
itemType = ess.getItemDb().get(args[0]);
@@ -78,24 +82,42 @@ public class Commandrecipe extends EssentialsCommand {
}
}
final List<Recipe> recipesOfType = ess.getServer().getRecipesFor(itemType);
if (recipesOfType.size() < 1) {
final List<Recipe> bukkitRecipes = ess.getServer().getRecipesFor(itemType);
if (bukkitRecipes.isEmpty()) {
throw new TranslatableException("recipeNone", getMaterialName(sender, itemType));
}
if (recipeNo < 0 || recipeNo >= recipesOfType.size()) {
final List<Recipe> recipes = new ArrayList<>();
for (Recipe recipe : bukkitRecipes) {
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_21_3_R01) && recipe instanceof TransmuteRecipe) {
final TransmuteRecipe transmuteRecipe = (TransmuteRecipe) recipe;
for (ItemStack inputChoice : toChoices(transmuteRecipe.getInput())) {
for (ItemStack materialChoice : toChoices(transmuteRecipe.getMaterial())) {
final ShapelessRecipe shapelessRecipe = new ShapelessRecipe(new NamespacedKey(ess, "transmute"), itemType);
shapelessRecipe.addIngredient(inputChoice);
shapelessRecipe.addIngredient(materialChoice);
recipes.add(shapelessRecipe);
}
}
} else {
recipes.add(recipe);
}
}
if (recipeNo < 0 || recipeNo >= recipes.size()) {
throw new TranslatableException("recipeBadIndex");
}
final Recipe selectedRecipe = recipesOfType.get(recipeNo);
sender.sendTl("recipe", getMaterialName(sender, itemType), recipeNo + 1, recipesOfType.size());
final Recipe selectedRecipe = recipes.get(recipeNo);
sender.sendTl("recipe", getMaterialName(sender, itemType), recipeNo + 1, recipes.size());
if (selectedRecipe instanceof FurnaceRecipe) {
furnaceRecipe(sender, (FurnaceRecipe) selectedRecipe);
} else if (selectedRecipe instanceof ShapedRecipe) {
shapedRecipe(sender, (ShapedRecipe) selectedRecipe, sender.isPlayer());
} else if (selectedRecipe instanceof ShapelessRecipe) {
if (recipesOfType.size() == 1 && (itemType.getType() == FIREWORK_ROCKET)) {
if (recipes.size() == 1 && itemType.getType() == FIREWORK_ROCKET) {
final ShapelessRecipe shapelessRecipe = new ShapelessRecipe(itemType);
shapelessRecipe.addIngredient(GUNPOWDER);
shapelessRecipe.addIngredient(Material.PAPER);
@@ -106,11 +128,25 @@ public class Commandrecipe extends EssentialsCommand {
}
}
if (recipesOfType.size() > 1 && args.length == 1) {
if (recipes.size() > 1 && args.length == 1) {
sender.sendTl("recipeMore", commandLabel, args[0], getMaterialName(sender, itemType));
}
}
private List<ItemStack> toChoices(final RecipeChoice choice) {
if (choice instanceof RecipeChoice.MaterialChoice) {
final List<ItemStack> stacks = new ArrayList<>();
for (final Material material : ((RecipeChoice.MaterialChoice) choice).getChoices()) {
stacks.add(new ItemStack(material, 1));
}
return stacks;
} else if (choice instanceof RecipeChoice.ExactChoice) {
return ((RecipeChoice.ExactChoice) choice).getChoices();
} else {
return Collections.emptyList();
}
}
public void furnaceRecipe(final CommandSource sender, final FurnaceRecipe recipe) {
sender.sendTl("recipeFurnace", getMaterialName(sender, recipe.getInput()));
}
@@ -89,7 +89,7 @@ public class Commandrenamehome extends EssentialsCommand {
if (canRenameOthers) {
final int sepIndex = args[0].indexOf(':');
if (sepIndex < 0) {
getPlayers(server, sender).forEach(player -> homes.add(player + ":"));
getPlayers(sender).forEach(player -> homes.add(player + ":"));
} else {
final String namePart = args[0].substring(0, sepIndex);
final User otherUser;
@@ -45,7 +45,7 @@ public class Commandrest extends EssentialsLoopCommand {
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1 && sender.isAuthorized("essentials.rest.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.BanLookup;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.CommonPlaceholders;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
@@ -61,7 +61,7 @@ public class Commandseen extends EssentialsCommand {
return;
}
}
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
ess.runTaskAsynchronously(new Runnable() {
@Override
public void run() {
final User userFromBukkit = ess.getUsers().getUser(args[0]);
@@ -113,6 +113,10 @@ public class Commandseen extends EssentialsCommand {
sender.sendTl("whoisUuid", user.getBase().getUniqueId().toString());
}
if (sender.isAuthorized("essentials.seen.firstlogin")) {
sender.sendTl("whoisFirstLogin", DateUtil.formatDate(user.getBase().getFirstPlayed(), ess));
}
if (user.isAfk()) {
sender.sendTl("whoisAFK", CommonPlaceholders.trueFalse(sender, true));
}
@@ -197,7 +201,7 @@ public class Commandseen extends EssentialsCommand {
}
private void seenIP(final CommandSource sender, final String ipAddress, final String display) {
sender.sendTl("runningPlayerMatch", AdventureUtil.parsed(AdventureUtil.legacyToMini(display)));
sender.sendTl("runningPlayerMatch", AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(display)));
ess.runTaskAsynchronously(() -> {
final List<String> matches = new ArrayList<>();
@@ -228,7 +232,7 @@ public class Commandseen extends EssentialsCommand {
@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);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -3,7 +3,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.craftbukkit.Inventories;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import net.ess3.api.TranslatableException;
@@ -123,7 +123,7 @@ public class Commandsell extends EssentialsCommand {
final String typeName = is.getType().toString().toLowerCase(Locale.ENGLISH);
final AdventureUtil.ParsedPlaceholder worthDisplay = AdventureUtil.parsed(NumberUtil.displayCurrency(worth, ess));
user.sendTl("itemSold", AdventureUtil.parsed(NumberUtil.displayCurrency(result, ess)), amount, typeName, worthDisplay);
ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("itemSoldConsole", user.getName(), typeName, AdventureUtil.miniToLegacy(NumberUtil.displayCurrency(result, ess)), amount, AdventureUtil.miniToLegacy(worthDisplay.toString()), user.getDisplayName())));
ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("itemSoldConsole", user.getName(), typeName, ess.getAdventureFacet().miniToLegacy(NumberUtil.displayCurrency(result, ess)), amount, ess.getAdventureFacet().miniToLegacy(worthDisplay.toString()), user.getDisplayName())));
return result;
}
@@ -9,6 +9,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.ess3.api.TranslatableException;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
@@ -108,7 +109,7 @@ public class Commandskull extends EssentialsCommand {
ess.runTaskAsynchronously(() -> {
// Run this stuff async because it causes an HTTP request
final String shortOwnerName;
String shortOwnerName;
if (URL_VALUE_PATTERN.matcher(owner).matches()) {
if (!playerProfileSupported) {
user.sendTl("unsupportedFeature");
@@ -129,23 +130,46 @@ public class Commandskull extends EssentialsCommand {
shortOwnerName = owner.substring(0, 7);
} else {
//noinspection deprecation
skullMeta.setOwner(owner);
shortOwnerName = owner;
if (playerProfileSupported) {
try {
PlayerProfile profile = ess.getServer().createPlayerProfile(null, owner);
profile = profile.update().join();
if (profile != null) {
skullMeta.setOwnerProfile(profile);
}
if (skullMeta.getOwnerProfile() == null) {
final OfflinePlayer offline = ess.getServer().getOfflinePlayer(owner);
skullMeta.setOwningPlayer(offline);
}
shortOwnerName = owner;
} catch (final Exception e) {
//noinspection deprecation
skullMeta.setOwner(owner);
shortOwnerName = owner;
}
} else {
//noinspection deprecation
skullMeta.setOwner(owner);
shortOwnerName = owner;
}
}
skullMeta.setDisplayName("§fSkull of " + shortOwnerName);
ess.scheduleSyncDelayedTask(() -> {
final String shortNameFinal = shortOwnerName;
ess.scheduleEntityDelayedTask(user.getBase(), () -> {
stack.setItemMeta(skullMeta);
if (spawn) {
Inventories.addItem(receive.getBase(), stack);
receive.sendTl("givenSkull", shortOwnerName);
receive.sendTl("givenSkull", shortNameFinal);
if (user != receive) {
user.sendTl("givenSkullOther", receive.getDisplayName(), shortOwnerName);
user.sendTl("givenSkullOther", receive.getDisplayName(), shortNameFinal);
}
return;
}
user.sendTl("skullChanged", shortOwnerName);
user.sendTl("skullChanged", shortNameFinal);
});
});
}
@@ -154,13 +178,13 @@ public class Commandskull extends EssentialsCommand {
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
if (user.isAuthorized("essentials.skull.others")) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Lists.newArrayList(user.getName());
}
} else if (args.length == 2){
if (user.isAuthorized("essentials.skull.others")) {
return getPlayers(server, user);
return getPlayers(user);
} else {
return Lists.newArrayList(user.getName());
}
@@ -2,7 +2,7 @@ package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.AdventureUtil;
import com.earth2me.essentials.adventure.AdventureUtil;
import com.earth2me.essentials.utils.FloatUtil;
import org.bukkit.Server;
import org.bukkit.entity.Player;
@@ -140,7 +140,7 @@ public class Commandspeed extends EssentialsCommand {
} else if (args.length == 2) {
return speeds;
} else if (args.length == 3 && sender.isAuthorized("essentials.speed.others")) {
return getPlayers(server, sender);
return getPlayers(sender);
} else {
return Collections.emptyList();
}
@@ -53,7 +53,7 @@ public class Commandsudo extends EssentialsLoopCommand {
}
}
ess.scheduleSyncDelayedTask(new SudoCommandTask());
ess.scheduleEntityDelayedTask(user.getBase(), new SudoCommandTask());
}
}
}
Loaded 100 of 316 files, more files were not shown because too many files have changed in this diff. Show more