Compare commits

..
396 Commits
Author SHA1 Message Date
MD 3348bd9129 Release 2.19.3 2022-03-04 11:41:56 +00:00
Josh Roy 1a5526867c Fix IllegalAccessException with advancement listener on 1.12.2 (#4826)
Fixes #4809.
2022-03-03 20:38:39 +00:00
MD c5253bc5e2 Update to Minecraft 1.18.2 (#4821)
This PR updates the build setup to Gradle 7.4 and updates EssentialsX to Minecraft 1.18.2.
2022-02-28 20:51:26 +00:00
Flask Bot 29a2a865c5 New Crowdin updates (#4744) 2022-02-28 20:31:02 +00:00
MD c3b994808f Use GitHub proxy fallback for update checker (#4818) 2022-02-26 17:09:54 -05:00
triagonal bc2da98703 Make OfflinePlayer#setName protected for CMI Importer (#4805) 2022-02-14 07:40:48 -05:00
Josh Roy cbb012853c Fix toggleshout causing issues with question/local chat 2022-02-13 22:44:37 -05:00
Josh Roy 4bf4bd432a Add some color to the local prefix by default 2022-02-13 22:44:37 -05:00
Josh Roy 83d0e0d9a0 Add permissions to receive certain channel channels 2022-02-13 22:44:37 -05:00
Josh Roy 2f5c24d114 Add permission for local channel chat 2022-02-13 22:44:37 -05:00
Josh Roy 88c8ccd29b Add config for default shout state and for persisting shout state 2022-02-13 22:44:37 -05:00
Josh Roy 94edbcfeb1 Add chat question config option 2022-02-13 22:44:37 -05:00
Josh RoyandMD d5822e9a41 Add config for max /tree and /bigtree range (#4728)
Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
2022-02-13 16:46:18 -05:00
Josh Roy debf09437e Add UUID support to trade and protection signs (#4713) 2022-02-13 15:54:19 -05:00
Josh Roy a9e5f079a9 Fix trade sign length validation issues (#4748) 2022-02-13 15:50:43 -05:00
Josh Roy f00c2dcf92 Fix keepinv policies ignoring offhand (#4725) 2022-02-13 20:37:25 +00:00
Josh Roy 63cbf7e2da Prevent legacy material support from being initiated (#4697) 2022-02-13 15:33:51 -05:00
Josh Roy 19837f9309 Add some more dump files (#4785)
Adds worth, tpr, and spawns configs.

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

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

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

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

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

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

Closes #3769
Closes #1550

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

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

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

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

* Add emoji parsing/tab complete for discordbroadcast command

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

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

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

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

my magnum opus

---

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

* Add feedback for large migrations

* Improve feedback messages

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

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

* Add less ranty context to rant

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Defaults to not enabled

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Thanks to triagonal for reporting the exception caused by createkit.

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

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

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

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

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

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

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

Closes #2756. 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* Forces use of issue templates
* Auto-labels issue templates
* Links to discord in the issue-create matrix as a better form of troubleshooting
2020-08-29 17:21:22 +01:00
Josh Roy fa835e9c64 Fix tablist name getting reset after AFK status change (#3624)
Fixes #3606.
2020-08-29 16:03:49 +01:00
728 changed files with 70510 additions and 28327 deletions

No files matched your search

+183
View File
@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!-- based on https://github.com/KyoriPowered/adventure/blob/master/.checkstyle/checkstyle.xml -->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="fileExtensions" value="java, properties, xml"/>
<property name="severity" value="error"/>
<!-- https://checkstyle.org/config_filefilters.html#BeforeExecutionExclusionFileFilter -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#FileTabCharacter -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<!-- https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${configDirectory}/suppressions.xml"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter -->
<module name="SuppressWithPlainTextCommentFilter"/>
<module name="TreeWalker">
<!-- https://checkstyle.org/config_misc.html#ArrayTypeStyle -->
<module name="ArrayTypeStyle"/>
<!-- https://checkstyle.org/config_imports.html#AvoidStarImport -->
<module name="AvoidStarImport"/>
<!-- https://checkstyle.org/config_misc.html#AvoidEscapedUnicodeCharacters -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowByTailComment" value="true"/>
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
<property name="severity" value="info"/>
</module>
<!-- https://checkstyle.org/config_misc.html#CommentsIndentation -->
<module name="CommentsIndentation"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForInitializerPad -->
<module name="EmptyForInitializerPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyForIteratorPad -->
<module name="EmptyForIteratorPad"/>
<!-- https://checkstyle.org/config_whitespace.html#EmptyLineSeparator -->
<module name="EmptyLineSeparator">
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
<property name="tokens"
value="CLASS_DEF, CTOR_DEF, ENUM_DEF, IMPORT, INSTANCE_INIT, INTERFACE_DEF, METHOD_DEF, STATIC_IMPORT, STATIC_INIT, VARIABLE_DEF"/> <!-- remove PACKAGE_DEF, temporarily remove COMPACT_CTOR_DEF, RECORD_DEF -->
</module>
<!-- https://checkstyle.org/config_coding.html#FallThrough -->
<module name="FallThrough">
<property name="checkLastCaseGroup" value="true"/>
</module>
<!-- https://checkstyle.org/config_design.html#FinalClass -->
<module name="FinalClass"/>
<!-- https://checkstyle.org/config_coding.html#FinalLocalVariable -->
<module name="FinalLocalVariable"/>
<!-- https://checkstyle.org/config_whitespace.html#GenericWhitespace -->
<module name="GenericWhitespace"/>
<!-- https://checkstyle.org/config_design.html#HideUtilityClassConstructor -->
<module name="HideUtilityClassConstructor"/>
<!-- https://checkstyle.org/config_imports.html#IllegalImport -->
<module name="IllegalImport">
<property name="illegalPkgs" value="sun, jdk, com.sun"/>
</module>
<!-- https://checkstyle.org/config_coding.html#IllegalTokenText -->
<module name="IllegalTokenText">
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
<property name="tokens" value="CHAR_LITERAL, STRING_LITERAL"/>
</module>
<!-- https://checkstyle.org/config_misc.html#Indentation -->
<module name="Indentation"/>
<!-- https://checkstyle.org/config_blocks.html#LeftCurly -->
<module name="LeftCurly"/>
<!-- https://checkstyle.org/config_naming.html#MethodName -->
<module name="MethodName">
<property name="format" value="^[_]?[a-z][a-zA-Z0-9]*$"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#MethodParamPad -->
<module name="MethodParamPad"/>
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocMethod -->
<module name="MissingJavadocMethod">
<property name="allowMissingPropertyJavadoc" value="true"/>
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/> <!-- remove CTOR_DEF, COMPACT_CTOR_DEF -->
</module>
<!-- https://checkstyle.org/config_javadoc.html#MissingJavadocType -->
<module name="MissingJavadocType"/>
<!-- https://checkstyle.org/config_coding.html#NoFinalizer -->
<module name="NoFinalizer"/>
<!-- https://checkstyle.org/config_whitespace.html#NoLineWrap -->
<module name="NoLineWrap"/>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceAfter -->
<module name="NoWhitespaceAfter">
<property name="allowLineBreaks" value="false"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#NoWhitespaceBefore -->
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
</module>
<!-- https://checkstyle.org/config_coding.html#OneStatementPerLine -->
<module name="OneStatementPerLine"/>
<!-- https://checkstyle.org/config_misc.html#OuterTypeFilename -->
<module name="OuterTypeFilename"/>
<!-- https://checkstyle.org/config_imports.html#RedundantImport -->
<module name="RedundantImport"/>
<!-- https://checkstyle.org/config_modifier.html#RedundantModifier -->
<module name="RedundantModifier">
<property name="tokens"
value="ANNOTATION_FIELD_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INTERFACE_DEF, VARIABLE_DEF"/> <!-- remove METHOD_DEF and RESOURCE -->
</module>
<!-- https://checkstyle.org/config_coding.html#RequireThis -->
<module name="RequireThis"/>
<!-- https://checkstyle.org/config_blocks.html#RightCurly -->
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_DEF, INSTANCE_INIT, LITERAL_FOR, LITERAL_WHILE, METHOD_DEF, STATIC_INIT"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="option" value="same"/>
<property name="tokens"
value="LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF, LITERAL_TRY"/> <!-- add LITERAL_DO -->
</module>
<!-- https://checkstyle.org/config_whitespace.html#SingleSpaceSeparator -->
<module name="SingleSpaceSeparator">
<property name="validateComments" value="true"/>
</module>
<!-- https://checkstyle.org/config_whitespace.html#TypecastParenPad -->
<module name="TypecastParenPad"/>
<!-- https://checkstyle.org/config_coding.html#UnnecessaryParentheses -->
<module name="UnnecessaryParentheses"/>
<!-- https://checkstyle.org/config_imports.html#UnusedImports -->
<module name="UnusedImports"/>
</module>
</module>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "http://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<suppress files=".*[\\/]test[\\/]java[\\/].*" checks="RequireExplicitVisibilityModifier"/>
<!-- TODO: don't suppress I-prefixed API interfaces under com.earth2me.essentials -->
<suppress files="(com[\\/]earth2me[\\/]essentials[\\/]|net[\\/]ess3[\\/]|net[\\/]essentialsx[\\/])(?:[^\\/]+$|(?!api)[^\\/]+[\\/])" checks="MissingJavadoc(Method|Type)"/>
</suppressions>
+2 -2
View File
@@ -1,10 +1,10 @@
# Currently used
patreon: EssentialsX
ko_fi: essentialsx
github: EssentialsX
open_collective: EssentialsX
# Currently not used
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#open_collective: # Replace with a single Open Collective username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
+20
View File
@@ -0,0 +1,20 @@
blank_issues_enabled: false
contact_links:
- name: View common issues
url: https://github.com/EssentialsX/Essentials/issues/3956
about: Check the list of known common issues to see if it solves your problem.
- name: Check past help requests
url: https://github.com/EssentialsX/Essentials/discussions/categories/q-a
about: Visit the Q&A forum to search for previous help requests.
- name: Check past feature suggestions
url: https://github.com/EssentialsX/Essentials/discussions/categories/ideas-and-feature-suggestions
about: Visit the Feature Suggestions forum to view and discuss existing feature suggestions.
- name: Create help request
url: https://github.com/EssentialsX/Essentials/discussions/new?category=q-a
about: Create a support ticket to get help from developers.
- name: Suggest a feature
url: https://github.com/EssentialsX/Essentials/discussions/new?category=ideas-and-feature-suggestions
about: Suggest new features for EssentialsX!
- name: Get help from the community on Discord
url: https://discord.gg/casfFyh
about: Join the MOSS Discord for community-powered EssentialsX support!
-45
View File
@@ -1,45 +0,0 @@
---
name: Help!
about: Encountered a problem with EssentialsX? Not sure how to fix it?
---
<!-- EssentialsX help guide
Don't write inside the arrows as they will be hidden when you post your issue.
Want faster support? Come join our Discord server to get help sooner:
https://discord.gg/F7gexAQ
If you're happy to wait (or you were sent here from Discord), read on:
1. Fill out the template.
This will help us understand what problem you've encountered and help us
find a solution.
2. Check the Common Issues page.
Read through the wiki page to see if you've encountered a regular issue:
https://essentialsx.github.io/#/Common-Issues
3. Delete this line and all above lines before posting your issue! -->
### Information
Full output of `/ess version`:
<!-- Replace this with the command output -->
**Server log:** <!-- Upload `logs/latest.log` to https://gist.github.com/ -->
**EssentialsX config** <!-- Upload `plugins/Essentials/config.yml` to https://gist.github.com/ -->
### Help request
**Problem**
<!-- What problem did you encounter? -->
**What I have tried**
<!-- What have you tried so far? -->
**Screenshots**
<!-- If relevant, include any screenshots here. -->
-58
View File
@@ -1,58 +0,0 @@
---
name: Report a bug
about: Report an EssentialsX bug. Only use this if you're 100% sure there's something wrong with EssentialsX - otherwise, try "Help!".
---
<!-- EssentialsX bug reporting guide
Don't put anything inside this block, as it won't be included in the issue.
If you are reporting a bug, please follow the following steps:
1. Fill out the template in full.
Run the commands in the console. Don't just put "latest" as a version,
or we will ignore it.
2. When linking files, do not attach them to the post!
Copy and paste any logs into https://gist.github.com/, then paste a
link to them in the relevant parts of the template. Avoid using
Hastebin or Pastebin, as this makes providing support more difficult.
**DO NOT drag files into this box!**
3. If you are reporting a performance issue, please include a link to a
Timings and/or profiler report.
4. If you are reporting a bug with commands or something else in-game,
please include screenshots to help us diagnose the problem.
5. Make sure not to write between the arrows, as anything there will be
hidden.
6. Delete this line and all above lines before posting your issue! -->
### Information
**Full output of `/ess version`**:
```
<!-- Run the command in your console, and copy and paste the whole output here -->
```
**Server log**: <!-- Upload `logs/latest.log` to https://gist.github.com/, replace this with link -->
**EssentialsX config**: <!-- Upload `plugins/Essentials/config.yml` to https://gist.github.com/, replace this with link -->
### Details
**Description**
<!-- Replace this with a brief summary of the bug. -->
**Steps to reproduce**
<!-- Replace this with exactly what you did to cause the bug. -->
**Expected behavior**
<!-- Replace this with what you expected to happen. -->
**Screenshots**
<!-- Replace this with screenshots, if necessary. -->
+79
View File
@@ -0,0 +1,79 @@
name: Report bug
description: Report a bug in EssentialsX.
labels: 'bug: unconfirmed'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report an EssentialsX bug! Fill out the form below to provide us with info to help fix the bug.
Only use this if you're 100% sure you've found a bug and can reproduce it. If you're looking for general help with EssentialsX, try the Q&A forum or MOSS Discord server.
- type: dropdown
attributes:
label: Type of bug
description: What type of bug is this? Choose all that apply.
multiple: true
options:
- Performance issue or memory leak
- Data loss
- Exploit
- Compatibility issue
- Error in console
- Other unexpected behaviour
validations:
required: true
- type: input
attributes:
label: "`/ess dump all` output"
description: Run `/ess dump all` in the console, then copy and paste the link from the output of the command into this box. (If this command doesn't work, you should [update EssentialsX](https://essentialsx.net/downloads.html) and try again.)
validations:
required: true
- type: input
attributes:
label: Error log (if applicable)
description: If you are reporting a console error, upload any relevant log excerpts to either https://paste.gg or https://gist.github.com, save and the paste the link in this box. If you included those files in the same paste as your startup log, paste the same link here.
placeholder: "Example: https://paste.gg/p/anonymous/109dd6a10a734a3aa430d5a351ea5210"
- type: textarea
attributes:
label: Bug description
description: Describe roughly what the bug is here.
placeholder: |
Example: "When running /nuke after putting everyone into adventure mode, there aren't any explosions..."
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Provide an example of how to trigger the bug.
placeholder: |
Example:
1. Have at least 3 people online
2. Run `/gma *` to put everyone into adventure mode
3. Run `/nuke`
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: Explain what you should expect to happen.
placeholder: |
Example: "Everything should explode!"
validations:
required: true
- type: textarea
attributes:
label: Actual behaviour
description: Explain what actually happens.
placeholder: |
Example: "Everything doesn't explode :("
validations:
required: true
- type: markdown
attributes:
value: |
In the text box below, you can attach any relevant screenshots, files and links to Timings/spark profiler reports.
You can also include a link to a heapdump if necessary, but please make sure you don't include any private player data in the heapdump.
If you suspect this issue is related to a prior issue/PR/commit, please mention it here.
@@ -1,45 +0,0 @@
---
name: Request a feature
about: Suggest a feature you want to see in EssentialsX!
---
<!-- EssentialsX feature request guide
Fill out the template. Don't write inside the arrows as they will be hidden
when you post your issue.
Not sure if your feature fits in EssentialsX? Feel free to ask on our Discord
server: https://discord.gg/F7gexAQ
If you have a feature suggestion for EssentialsX, read the following tips:
1. Fill out the template.
This will help us understand what you're requesting and why you want us
to add it.
2. Keep it simple.
Make sure it's easy to understand what you're requesting. A good way is
to keep it to one request per GitHub issue, as we can then easily track
feature requests.
3. Check whether it has already been asked or added.
You can search the issue tracker to see if your feature has already been
requested at https://github.com/EssentialsX/Essentials/issues. You can
also check the wiki at https://essentialsx.github.io/ to see if the
feature was recently added.
4. Ask yourself: "Does this belong in EssentialsX?"
There are lots of features that we reject because most servers won't
need or use them. If your feature is very specific or already exists in
another plugin, it might not be a good fit for EssentialsX.
5. Delete this line and all above lines before posting your issue! -->
### Feature request
**Feature description**
<!-- What feature are you suggesting? -->
**How the feature is useful**
<!-- How is the feature useful to players, server owners and/or developers? -->
+94
View File
@@ -0,0 +1,94 @@
<!--
EssentialsX bug fix submission guide
====================================
NOTE: Failure to fill out this template properly may result in your PR being
delayed or ignored without warning.
NOTE: Don't type between any arrows in the template, as this text will be
hidden. This includes this header block and any other explanation text
blocks.
Want to discuss your PR before submitting it? Join the EssentialsX Development
server: https://discord.gg/CUN7qVb
EssentialsX is GPL
------------------
By contributing to EssentialsX, you agree to license your code under the
GNU General Public License version 3, which can be found at the link below:
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
Instructions
------------
If you are submitting a bug fix, please follow the following steps:
1. Fill out the template in full.
This includes providing screenshots and a link to the original bug
report. If there isn't an existing bug report, we recommend opening a new
detailed bug report BEFORE opening your PR to fix it, else your PR may be
delayed or rejected without warning.
You can open a new bug report by following this link:
https://github.com/EssentialsX/Essentials/issues/new/choose
2. When linking logs or config files, do not attach them to the post!
Copy and paste any logs into https://gist.github.com/, then paste a
link to them in the relevant parts of the template. Do not use Hastebin
or Pastebin, as this can cause issues with future reviews.
DO NOT drag logs directly into this text box, as we cannot read these!
3. If you are fixing a performance issue, please include a link to a
Timings and/or profiler report, both before and after your PR.
4. If you are fixing a visual bug, such as in commands, please include
screenshots so that we can more easily review the proposed fix.
(You can drag screenshots into the bottom of the editor.)
-->
### Information
<!--
Replace #nnnn with the number of the original issue. If this PR fixes
multiple issues, you should repeat the phrase "fixes #nnnn" for each issue.
-->
This PR fixes #nnnn.
### Details
**Proposed fix:**
<!-- Type a description of your proposed fix below this line. -->
**Environments tested:**
<!-- Type the OS you have used below. -->
OS:
<!-- Type the JDK version (from java -version) you have used below. -->
Java version:
<!--
Put an "x" inside the boxes for the server software you have tested this
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
environments, add a new line with relevant details.
-->
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [ ] CraftBukkit 1.8.8
**Demonstration:**
<!--
Below this block, include screenshots/log snippets from before and after as
necessary. If you have created or used a test case plugin, please link to a
download of the plugin, source code and exact version used where possible.
-->
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: MOSS Discord Server
url: https://discord.gg/casfFyh
about: Need help with using EssentialsX? Join the MOSS Discord server for help.
- name: EssentialsX Development Discord Server
url: https://discord.gg/CUN7qVb
about: Got questions or want to discuss changes before opening your PR? Join the EssentialsX Development Discord server. (Need help? Join MOSS instead.)
@@ -0,0 +1,93 @@
<!--
EssentialsX feature submission guide
====================================
NOTE: Failure to fill out this template properly may result in your PR being
delayed or ignored without warning.
NOTE: Don't type between any arrows in the template, as this text will be
hidden. This includes this header block and any other explanation text
blocks.
Want to discuss your PR before submitting it? Join the EssentialsX Development
server: https://discord.gg/CUN7qVb
EssentialsX is GPL
------------------
By contributing to EssentialsX, you agree to license your code under the
GNU General Public License version 3, which can be found at the link below:
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
Instructions
------------
If you are submitting a new feature, please follow the following steps:
1. Fill out the template in full.
This includes providing screenshots and a link to the original feature
request. If there isn't an existing feature request, we strongly
recommend opening a new feature request BEFORE opening your PR to
implement it, as this allows us to review whether we're likely to accept
your feature in advance, and also allows us to discuss possible
implementations for the feature. If there is no associated feature
request, your PR may be delayed or rejected without warning.
You can open a new feature request by following this link:
https://github.com/EssentialsX/Essentials/issues/new/choose
2. If you are fixing a performance issue, please use the "Bug fix" PR template
instead. The "bug fix" template is better suited to performance issues.
3. Include a demonstration.
If you are adding commands, please provide screenshots and/or a video
demonstration of the feature. Similarly, if you are adding a new API,
please include a link to example code that takes advantage of your
proposed API. This will aid us in reviewing PRs and speed up the process
significantly.
-->
### Information
<!--
Replace #nnnn with the number of the original issue. If this PR implements
features from multiple issues, you should repeat the phrase "closes #nnnn"
for each issue.
-->
This PR closes #nnnn.
### Details
**Proposed feature:**
<!-- Type a description of your proposed feature below this line. -->
**Environments tested:**
<!-- Type the OS you have used below. -->
OS:
<!-- Type the JDK version (from java -version) you have used below. -->
Java version:
<!--
Put an "x" inside the boxes for the server software you have tested this
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
environments, add a new line with relevant details.
-->
- [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [ ] CraftBukkit 1.8.8
**Demonstration:**
<!--
Below this block, include screenshots/log snippets from before and after as
necessary. If you have created or used a test case plugin, please link to a
download of the plugin, source code and exact version used where possible.
-->
+10
View File
@@ -0,0 +1,10 @@
You need to select a pull request template!
If you're adding a new feature, copy and paste the following parameter at the end of the URL:
?template=new-feature.md
If you're fixing a bug, copy and paste the following parameter at the end of the URL:
?template=bug-fix.md
For more information about contributing to EssentialsX, see CONTRIBUTING.md:
https://github.com/EssentialsX/Essentials/blob/2.x/CONTRIBUTING.md
+75
View File
@@ -0,0 +1,75 @@
name: Build EssentialsX
on:
push:
branches:
- 2.x
- dev/*
jobs:
build:
name: Build and upload
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Restore Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Set up JDK 16
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 16
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew build --stacktrace
- name: Archive plugin jars on GitHub
uses: actions/upload-artifact@master
with:
name: EssentialsX plugin jars
path: jars/
- name: Deploy to Maven repo
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
env:
ORG_GRADLE_PROJECT_essxUsername: ${{ secrets.ESSENTIALSX_DEPLOY_USERNAME }}
ORG_GRADLE_PROJECT_essxPassword: ${{ secrets.ESSENTIALSX_DEPLOY_PASSWORD }}
run: |
./gradlew publish
- name: Prepare Javadocs
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
run: |
mv Essentials/build/docs/javadoc/ javadocs/
cp -r EssentialsAntiBuild/build/docs/javadoc/ javadocs/EssentialsAntiBuild/
cp -r EssentialsChat/build/docs/javadoc/ javadocs/EssentialsChat/
cp -r EssentialsDiscord/build/docs/javadoc/ javadocs/EssentialsDiscord/
cp -r EssentialsGeoIP/build/docs/javadoc/ javadocs/EssentialsGeoIP/
cp -r EssentialsProtect/build/docs/javadoc/ javadocs/EssentialsProtect/
cp -r EssentialsSpawn/build/docs/javadoc/ javadocs/EssentialsSpawn/
cp -r EssentialsXMPP/build/docs/javadoc/ javadocs/EssentialsXMPP/
- name: Deploy Javadocs
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
uses: netlify/actions/cli@master
with:
args: deploy --dir=javadocs/ --prod --message="GitHubActionsDeploy"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_JD_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_JD_2X_SITE_ID }}
+51
View File
@@ -0,0 +1,51 @@
name: Build Pull Request
on:
push:
branches:
- mc/* # MC version updates
- pr/* # PR rebases
pull_request:
branches:
- 2.x
- mc/*
- dev/*
jobs:
build:
name: Build and upload
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Restore Gradle cache
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Set up JDK 16
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 16
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew build --stacktrace
- name: Archive plugin jars on GitHub
uses: actions/upload-artifact@master
with:
name: EssentialsX plugin jars
path: jars/
-38
View File
@@ -1,38 +0,0 @@
name: Build EssentialsX
on:
push:
branches:
- 2.x
- mc/*
pull_request:
branches:
- 2.x
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Git repo
uses: actions/checkout@v1
- name: Restore Maven cache
uses: actions/cache@v1
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn package --file pom.xml
- name: Copy artifacts
run: mkdir -p ./out/ && cp -t ./out/ **/target/Essentials*.jar
- uses: actions/upload-artifact@master
with:
name: Plugin jars
path: out/
+13 -4
View File
@@ -1,11 +1,14 @@
# IDE files
# Other IDE files
*.classpath
*.project
*.settings/
.idea/
.idea/*
*.iml
.DS_Store
# Checkstyle-IDEA config
!.idea/checkstyle-idea.xml
# Essentials files
/Essentials/userdata
/Essentials/config.yml
@@ -14,6 +17,12 @@
/Essentials/usermap.csv
# Build files
.gradle/
/jars/
out/
build/
target/
jars/
out/
*.class
# Run directory
/run/
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="active-configuration" value="PROJECT_RELATIVE:$PROJECT_DIR$/.checkstyle/checkstyle.xml:EssentialsX" />
<entry key="checkstyle-version" value="8.36.2" />
<entry key="copy-libs" value="true" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="location-2" value="PROJECT_RELATIVE:$PROJECT_DIR$/.checkstyle/checkstyle.xml:EssentialsX" />
<entry key="property-2.configDirectory" value=".checkstyle/" />
<entry key="scan-before-checkin" value="true" />
<entry key="scanscope" value="JavaOnly" />
<entry key="suppress-errors" value="false" />
</map>
</option>
</component>
</project>
+31
View File
@@ -0,0 +1,31 @@
Contributing to EssentialsX
===========================
Want to help improve EssentialsX? There are several ways you can support and contribute to the project.
If you'd like to make a financial contribution to the project, you can join our [Patreon](https://www.patreon.com/essentialsx/),
or to make a one-off donation you can visit our [Ko-fi page](https://ko-fi.com/essentialsx). If you can't make a
donation, don't worry! There are lots of other ways to contribute:
* Do you run a server? Take a look at our ["help wanted"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22)
and ["bug: unconfirmed"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22bug%3A+unconfirmed%22)
issues, where you can find issues that need extra testing and investigation.
* Do you speak multiple languages? If so, we always welcome contributions to our [Crowdin project](https://crowdin.com/project/essentialsx-official).
* Do you enjoy helping others? If so, why not contribute to the [EssentialsX documentation](https://github.com/EssentialsX/wiki)?
You can also join the [MOSS Discord community](https://discord.gg/casfFyh) and provide direct community support to
other EssentialsX users.
* If you're a developer, you could look through our ["open to PR"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22status%3A+open+to+PR%22)
issues. We're always happy to receive bug fixes and feature additions as pull requests.
Submitting a PR
---------------
EssentialsX has extensive templates for PRs that detail how to submit your PR. To find out more, see the PR templates for:
* [Bug fixes](https://github.com/EssentialsX/Essentials/blob/2.x/.github/PULL_REQUEST_TEMPLATE/bug-fix.md)
* [New features](https://github.com/EssentialsX/Essentials/blob/2.x/.github/PULL_REQUEST_TEMPLATE/new-feature.md)
Want to discuss a feature before opening a PR? Join the [EssentialsX Development Discord server](https://discord.gg/CUN7qVb). Note that this server is **not for end-users** - if you need support with EssentialsX, you should join [MOSS](https://discord.gg/casfFyh) instead.
By contributing to EssentialsX, you agree to license your code under the [GNU General Public License version 3](https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE).
+58
View File
@@ -0,0 +1,58 @@
plugins {
id("essentials.shadow-module")
}
dependencies {
compileOnly('com.github.milkbowl:VaultAPI:1.7') {
exclude group: "org.bukkit", module: "bukkit"
}
compileOnly 'net.luckperms:api:5.0'
api 'io.papermc:paperlib:1.0.6'
api 'org.bstats:bstats-bukkit:2.2.1'
implementation 'org.spongepowered:configurate-yaml:4.1.2'
implementation 'org.checkerframework:checker-qual:3.14.0'
// Providers
api project(':providers:BaseProviders')
api project(':providers:PaperProvider')
api(project(':providers:NMSReflectionProvider')) {
exclude group: "org.bukkit", module: "bukkit"
}
api(project(':providers:1_8Provider')) {
exclude group: "org.spigotmc", module: "spigot"
}
api(project(':providers:1_12Provider')) {
exclude group: "org.bukkit", module: "bukkit"
}
}
shadowJar {
dependencies {
include (dependency('io.papermc:paperlib'))
include (dependency('org.bstats:bstats-bukkit'))
include (dependency('org.bstats:bstats-base'))
include (dependency('org.spongepowered:configurate-yaml'))
include (dependency('org.spongepowered:configurate-core'))
include (dependency('org.yaml:snakeyaml'))
include (dependency('io.leangen.geantyref:geantyref'))
include (dependency('org.checkerframework:checker-qual'))
include (project(':providers:BaseProviders'))
include (project(':providers:PaperProvider'))
include (project(':providers:NMSReflectionProvider'))
include (project(':providers:1_8Provider'))
include (project(':providers:1_12Provider'))
}
relocate 'io.papermc.lib', 'com.earth2me.essentials.paperlib'
relocate 'org.bstats', 'com.earth2me.essentials.libs.bstats'
relocate 'org.spongepowered.configurate', 'com.earth2me.essentials.libs.configurate'
relocate 'org.yaml.snakeyaml', 'com.earth2me.essentials.libs.snakeyaml'
relocate 'io.leangen.geantyref', 'com.earth2me.essentials.libs.geantyref'
relocate 'org.checkerframework', 'com.earth2me.essentials.libs.checkerframework'
minimize {
include(dependency('org.checkerframework:checker-qual'))
}
}
-118
View File
@@ -1,118 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.ess3</groupId>
<artifactId>EssentialsXParent</artifactId>
<version>2.18.1</version>
</parent>
<artifactId>EssentialsX</artifactId>
<build>
<finalName>EssentialsX-${full.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<showDeprecation>false</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>com.earth2me.essentials.paperlib</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.github.milkbowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>BaseProviders</artifactId>
<version>2.18.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>NMSReflectionProvider</artifactId>
<version>2.18.1</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>PaperProvider</artifactId>
<version>2.18.1</version>
<exclusions>
<exclusion>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>1_8Provider</artifactId>
<version>2.18.1</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -1,104 +0,0 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.PluginCommandYamlParser;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AlternativeCommandsHandler {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private final transient Map<String, List<PluginCommand>> altcommands = new HashMap<>();
private final transient Map<String, String> disabledList = new HashMap<>();
private final transient IEssentials ess;
public AlternativeCommandsHandler(final IEssentials ess) {
this.ess = ess;
for (Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
if (plugin.isEnabled()) {
addPlugin(plugin);
}
}
}
public final void addPlugin(final Plugin plugin) {
if (plugin.getDescription().getMain().contains("com.earth2me.essentials")) {
return;
}
final List<Command> commands = PluginCommandYamlParser.parse(plugin);
final String pluginName = plugin.getDescription().getName().toLowerCase(Locale.ENGLISH);
for (Command command : commands) {
final PluginCommand pc = (PluginCommand) command;
final List<String> labels = new ArrayList<>(pc.getAliases());
labels.add(pc.getName());
PluginCommand reg = ess.getServer().getPluginCommand(pluginName + ":" + pc.getName().toLowerCase(Locale.ENGLISH));
if (reg == null) {
reg = ess.getServer().getPluginCommand(pc.getName().toLowerCase(Locale.ENGLISH));
}
if (reg == null || !reg.getPlugin().equals(plugin)) {
continue;
}
for (String label : labels) {
List<PluginCommand> plugincommands = altcommands.computeIfAbsent(label.toLowerCase(Locale.ENGLISH), k -> new ArrayList<>());
boolean found = false;
for (PluginCommand pc2 : plugincommands) {
if (pc2.getPlugin().equals(plugin)) {
found = true;
break;
}
}
if (!found) {
plugincommands.add(reg);
}
}
}
}
public void removePlugin(final Plugin plugin) {
final Iterator<Map.Entry<String, List<PluginCommand>>> iterator = altcommands.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<String, List<PluginCommand>> entry = iterator.next();
entry.getValue().removeIf(pc -> pc.getPlugin() == null || pc.getPlugin().equals(plugin));
if (entry.getValue().isEmpty()) {
iterator.remove();
}
}
}
public PluginCommand getAlternative(final String label) {
final List<PluginCommand> commands = altcommands.get(label);
if (commands == null || commands.isEmpty()) {
return null;
}
if (commands.size() == 1) {
return commands.get(0);
}
// return the first command that is not an alias
for (PluginCommand command : commands) {
if (command.getName().equalsIgnoreCase(label)) {
return command;
}
}
// return the first alias
return commands.get(0);
}
public void executed(final String label, final PluginCommand pc) {
final String altString = pc.getPlugin().getName() + ":" + pc.getLabel();
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, "Essentials: Alternative command " + label + " found, using " + altString);
}
disabledList.put(label, altString);
}
public Map<String, String> disabledCommands() {
return disabledList;
}
}
@@ -1,662 +0,0 @@
package com.earth2me.essentials;
import com.google.common.io.Files;
import net.ess3.api.InvalidWorldException;
import org.bukkit.OfflinePlayer;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.io.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsConf extends YamlConfiguration {
protected static final Logger LOGGER = Logger.getLogger("Essentials");
protected final File configFile;
protected String templateName = null;
protected static final Charset UTF8 = StandardCharsets.UTF_8;
private Class<?> resourceClass = EssentialsConf.class;
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
private final AtomicInteger pendingDiskWrites = new AtomicInteger(0);
private final AtomicBoolean transaction = new AtomicBoolean(false);
public EssentialsConf(final File configFile) {
super();
this.configFile = configFile.getAbsoluteFile();
}
private final byte[] bytebuffer = new byte[1024];
public synchronized void load() {
if (pendingDiskWrites.get() != 0) {
LOGGER.log(Level.INFO, "File {0} not read, because it''s not yet written to disk.", configFile);
return;
}
if (!configFile.getParentFile().exists()) {
if (!configFile.getParentFile().mkdirs()) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
}
}
// This will delete files where the first character is 0. In most cases they are broken.
if (configFile.exists() && configFile.length() != 0) {
try {
final InputStream input = new FileInputStream(configFile);
try {
if (input.read() == 0) {
input.close();
configFile.delete();
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
} finally {
try {
input.close();
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
} catch (FileNotFoundException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
if (!configFile.exists()) {
if (legacyFileExists()) {
convertLegacyFile();
} else if (altFileExists()) {
convertAltFile();
} else if (templateName != null) {
LOGGER.log(Level.INFO, tl("creatingConfigFromTemplate", configFile.toString()));
createFromTemplate();
} else {
return;
}
}
try {
try (FileInputStream inputStream = new FileInputStream(configFile)) {
long startSize = configFile.length();
if (startSize > Integer.MAX_VALUE) {
throw new InvalidConfigurationException("File too big");
}
ByteBuffer buffer = ByteBuffer.allocate((int) startSize);
int length;
while ((length = inputStream.read(bytebuffer)) != -1) {
if (length > buffer.remaining()) {
ByteBuffer resize = ByteBuffer.allocate(buffer.capacity() + length - buffer.remaining());
int resizePosition = buffer.position();
// Fix builds compiled against Java 9+ breaking on Java 8
((Buffer) buffer).rewind();
resize.put(buffer);
resize.position(resizePosition);
buffer = resize;
}
buffer.put(bytebuffer, 0, length);
}
((Buffer) buffer).rewind();
final CharBuffer data = CharBuffer.allocate(buffer.capacity());
CharsetDecoder decoder = UTF8.newDecoder();
CoderResult result = decoder.decode(buffer, data, true);
if (result.isError()) {
((Buffer) buffer).rewind();
((Buffer) data).clear();
LOGGER.log(Level.INFO, "File " + configFile.getAbsolutePath() + " is not utf-8 encoded, trying " + Charset.defaultCharset().displayName());
decoder = Charset.defaultCharset().newDecoder();
result = decoder.decode(buffer, data, true);
if (result.isError()) {
throw new InvalidConfigurationException("Invalid Characters in file " + configFile.getAbsolutePath());
} else {
decoder.flush(data);
}
} else {
decoder.flush(data);
}
final int end = data.position();
((Buffer) data).rewind();
super.loadFromString(data.subSequence(0, end).toString());
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
} catch (InvalidConfigurationException ex) {
File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
configFile.renameTo(broken);
LOGGER.log(Level.SEVERE, "The file " + configFile.toString() + " is broken, it has been renamed to " + broken.toString(), ex.getCause());
}
}
public boolean legacyFileExists() {
return false;
}
public void convertLegacyFile() {
LOGGER.log(Level.SEVERE, "Unable to import legacy config file.");
}
public boolean altFileExists() {
return false;
}
public void convertAltFile() {
LOGGER.log(Level.SEVERE, "Unable to import alt config file.");
}
private void createFromTemplate() {
InputStream istr = null;
OutputStream ostr = null;
try {
istr = resourceClass.getResourceAsStream(templateName);
if (istr == null) {
LOGGER.log(Level.SEVERE, tl("couldNotFindTemplate", templateName));
return;
}
ostr = new FileOutputStream(configFile);
byte[] buffer = new byte[1024];
int length = 0;
length = istr.read(buffer);
while (length > 0) {
ostr.write(buffer, 0, length);
length = istr.read(buffer);
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToWriteConfig", configFile.toString()), ex);
} finally {
try {
if (istr != null) {
istr.close();
}
} catch (IOException ex) {
Logger.getLogger(EssentialsConf.class.getName()).log(Level.SEVERE, null, ex);
}
try {
if (ostr != null) {
ostr.close();
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToCloseConfig", configFile.toString()), ex);
}
}
}
public void setTemplateName(final String templateName) {
this.templateName = templateName;
}
public File getFile() {
return configFile;
}
public void setTemplateName(final String templateName, final Class<?> resClass) {
this.templateName = templateName;
this.resourceClass = resClass;
}
public void startTransaction() {
transaction.set(true);
}
public void stopTransaction() {
transaction.set(false);
save();
}
public void save() {
try {
save(configFile);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
}
}
public void saveWithError() throws IOException {
save(configFile);
}
@Override
public synchronized void save(final File file) throws IOException {
if (!transaction.get()) {
delayedSave(file);
}
}
//This may be aborted if there are stagnant requests sitting in queue.
//This needs fixed to discard outstanding save requests.
public synchronized void forceSave() {
try {
Future<?> future = delayedSave(configFile);
if (future != null) {
future.get();
}
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
}
}
public synchronized void cleanup() {
forceSave();
}
private Future<?> delayedSave(final File file) {
if (file == null) {
throw new IllegalArgumentException("File cannot be null");
}
final String data = saveToString();
if (data.length() == 0) {
return null;
}
pendingDiskWrites.incrementAndGet();
return EXECUTOR_SERVICE.submit(new WriteRunner(configFile, data, pendingDiskWrites));
}
private static class WriteRunner implements Runnable {
private final File configFile;
private final String data;
private final AtomicInteger pendingDiskWrites;
private WriteRunner(final File configFile, final String data, final AtomicInteger pendingDiskWrites) {
this.configFile = configFile;
this.data = data;
this.pendingDiskWrites = pendingDiskWrites;
}
@Override
public void run() {
//long startTime = System.nanoTime();
synchronized (configFile) {
if (pendingDiskWrites.get() > 1) {
// Writes can be skipped, because they are stored in a queue (in the executor).
// Only the last is actually written.
pendingDiskWrites.decrementAndGet();
//LOGGER.log(Level.INFO, configFile + " skipped writing in " + (System.nanoTime() - startTime) + " nsec.");
return;
}
try {
Files.createParentDirs(configFile);
if (!configFile.exists()) {
try {
LOGGER.log(Level.INFO, tl("creatingEmptyConfig", configFile.toString()));
if (!configFile.createNewFile()) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
return;
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()), ex);
return;
}
}
try (FileOutputStream fos = new FileOutputStream(configFile)) {
try (OutputStreamWriter writer = new OutputStreamWriter(fos, UTF8)) {
writer.write(data);
}
}
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
} finally {
//LOGGER.log(Level.INFO, configFile + " written to disk in " + (System.nanoTime() - startTime) + " nsec.");
pendingDiskWrites.decrementAndGet();
}
}
}
}
public boolean hasProperty(final String path) {
return isSet(path);
}
public Location getLocation(final String path, final Server server) throws InvalidWorldException {
final String worldString = (path == null ? "" : path + ".") + "world";
final String worldName = getString(worldString);
if (worldName == null || worldName.isEmpty()) {
return null;
}
final World world = server.getWorld(worldName);
if (world == null) {
throw new InvalidWorldException(worldName);
}
return new Location(world, getDouble((path == null ? "" : path + ".") + "x", 0), getDouble((path == null ? "" : path + ".") + "y", 0), getDouble((path == null ? "" : path + ".") + "z", 0), (float) getDouble((path == null ? "" : path + ".") + "yaw", 0), (float) getDouble((path == null ? "" : path + ".") + "pitch", 0));
}
public void setProperty(final String path, final Location loc) {
set((path == null ? "" : path + ".") + "world", loc.getWorld().getName());
set((path == null ? "" : path + ".") + "x", loc.getX());
set((path == null ? "" : path + ".") + "y", loc.getY());
set((path == null ? "" : path + ".") + "z", loc.getZ());
set((path == null ? "" : path + ".") + "yaw", loc.getYaw());
set((path == null ? "" : path + ".") + "pitch", loc.getPitch());
}
@Override
public ItemStack getItemStack(final String path) {
final ItemStack stack = new ItemStack(Material.valueOf(getString(path + ".type", "AIR")), getInt(path + ".amount", 1), (short) getInt(path + ".damage", 0));
final ConfigurationSection enchants = getConfigurationSection(path + ".enchant");
if (enchants != null) {
for (String enchant : enchants.getKeys(false)) {
final Enchantment enchantment = Enchantment.getByName(enchant.toUpperCase(Locale.ENGLISH));
if (enchantment == null) {
continue;
}
final int level = getInt(path + ".enchant." + enchant, enchantment.getStartLevel());
stack.addUnsafeEnchantment(enchantment, level);
}
}
return stack;
/*
* ,
* (byte)getInt(path + ".data", 0)
*/
}
public void setProperty(final String path, final ItemStack stack) {
final Map<String, Object> map = new HashMap<>();
map.put("type", stack.getType().toString());
map.put("amount", stack.getAmount());
map.put("damage", stack.getDurability());
Map<Enchantment, Integer> enchantments = stack.getEnchantments();
if (!enchantments.isEmpty()) {
Map<String, Integer> enchant = new HashMap<>();
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
enchant.put(entry.getKey().getName().toLowerCase(Locale.ENGLISH), entry.getValue());
}
map.put("enchant", enchant);
}
// getData().getData() is broken
//map.put("data", stack.getDurability());
set(path, map);
}
public void setProperty(String path, List object) {
set(path, new ArrayList(object));
}
public void setProperty(String path, Map object) {
set(path, new LinkedHashMap(object));
}
public Object getProperty(String path) {
return get(path);
}
public void setProperty(final String path, final BigDecimal bigDecimal) {
set(path, bigDecimal.toString());
}
public void setProperty(String path, Object object) {
set(path, object);
}
public void removeProperty(String path) {
set(path, null);
}
@Override
public synchronized Object get(String path) {
return super.get(path);
}
@Override
public synchronized Object get(String path, Object def) {
return super.get(path, def);
}
public synchronized BigDecimal getBigDecimal(final String path, final BigDecimal def) {
final String input = super.getString(path);
return toBigDecimal(input, def);
}
public static BigDecimal toBigDecimal(final String input, final BigDecimal def) {
if (input == null || input.isEmpty()) {
return def;
} else {
try {
return new BigDecimal(input, MathContext.DECIMAL128);
} catch (NumberFormatException | ArithmeticException e) {
return def;
}
}
}
@Override
public synchronized boolean getBoolean(String path) {
return super.getBoolean(path);
}
@Override
public synchronized boolean getBoolean(String path, boolean def) {
return super.getBoolean(path, def);
}
@Override
public synchronized List<Boolean> getBooleanList(String path) {
return super.getBooleanList(path);
}
@Override
public synchronized List<Byte> getByteList(String path) {
return super.getByteList(path);
}
@Override
public synchronized List<Character> getCharacterList(String path) {
return super.getCharacterList(path);
}
@Override
public synchronized ConfigurationSection getConfigurationSection(String path) {
return super.getConfigurationSection(path);
}
@Override
public synchronized double getDouble(String path) {
return super.getDouble(path);
}
@Override
public synchronized double getDouble(final String path, final double def) {
return super.getDouble(path, def);
}
@Override
public synchronized List<Double> getDoubleList(String path) {
return super.getDoubleList(path);
}
@Override
public synchronized List<Float> getFloatList(String path) {
return super.getFloatList(path);
}
@Override
public synchronized int getInt(String path) {
return super.getInt(path);
}
@Override
public synchronized int getInt(String path, int def) {
return super.getInt(path, def);
}
@Override
public synchronized List<Integer> getIntegerList(String path) {
return super.getIntegerList(path);
}
@Override
public synchronized ItemStack getItemStack(String path, ItemStack def) {
return super.getItemStack(path, def);
}
@Override
public synchronized Set<String> getKeys(boolean deep) {
return super.getKeys(deep);
}
@Override
public synchronized List<?> getList(String path) {
return super.getList(path);
}
@Override
public synchronized List<?> getList(String path, List<?> def) {
return super.getList(path, def);
}
@Override
public synchronized long getLong(String path) {
return super.getLong(path);
}
@Override
public synchronized long getLong(final String path, final long def) {
return super.getLong(path, def);
}
@Override
public synchronized List<Long> getLongList(String path) {
return super.getLongList(path);
}
public synchronized Map<String, Object> getMap() {
return map;
}
@Override
public synchronized List<Map<?, ?>> getMapList(String path) {
return super.getMapList(path);
}
@Override
public synchronized OfflinePlayer getOfflinePlayer(String path) {
return super.getOfflinePlayer(path);
}
@Override
public synchronized OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def) {
return super.getOfflinePlayer(path, def);
}
@Override
public synchronized List<Short> getShortList(String path) {
return super.getShortList(path);
}
@Override
public synchronized String getString(String path) {
return super.getString(path);
}
@Override
public synchronized String getString(String path, String def) {
return super.getString(path, def);
}
@Override
public synchronized List<String> getStringList(String path) {
return super.getStringList(path);
}
@Override
public synchronized Map<String, Object> getValues(boolean deep) {
return super.getValues(deep);
}
@Override
public synchronized Vector getVector(String path) {
return super.getVector(path);
}
@Override
public synchronized Vector getVector(String path, Vector def) {
return super.getVector(path, def);
}
@Override
public synchronized boolean isBoolean(String path) {
return super.isBoolean(path);
}
@Override
public synchronized boolean isConfigurationSection(String path) {
return super.isConfigurationSection(path);
}
@Override
public synchronized boolean isDouble(String path) {
return super.isDouble(path);
}
@Override
public synchronized boolean isInt(String path) {
return super.isInt(path);
}
@Override
public synchronized boolean isItemStack(String path) {
return super.isItemStack(path);
}
@Override
public synchronized boolean isList(String path) {
return super.isList(path);
}
@Override
public synchronized boolean isLong(String path) {
return super.isLong(path);
}
@Override
public synchronized boolean isOfflinePlayer(String path) {
return super.isOfflinePlayer(path);
}
@Override
public synchronized boolean isSet(String path) {
return super.isSet(path);
}
@Override
public synchronized boolean isString(String path) {
return super.isString(path);
}
@Override
public synchronized boolean isVector(String path) {
return super.isVector(path);
}
@Override
public synchronized void set(String path, Object value) {
super.set(path, value);
}
}
@@ -1,134 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import java.io.File;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static com.earth2me.essentials.I18n.capitalCase;
import static com.earth2me.essentials.I18n.tl;
public class Kits implements IConf {
private final EssentialsConf config;
private ConfigurationSection kits;
public Kits(final IEssentials essentials) {
config = new EssentialsConf(new File(essentials.getDataFolder(), "kits.yml"));
config.setTemplateName("/kits.yml");
reloadConfig();
}
@Override
public void reloadConfig() {
config.load();
kits = _getKits();
}
private ConfigurationSection _getKits() {
if (config.isConfigurationSection("kits")) {
final ConfigurationSection section = config.getConfigurationSection("kits");
final ConfigurationSection newSection = new MemoryConfiguration();
for (String kitItem : section.getKeys(false)) {
if (section.isConfigurationSection(kitItem)) {
newSection.set(kitItem.toLowerCase(Locale.ENGLISH), section.getConfigurationSection(kitItem));
}
}
return newSection;
}
return null;
}
public EssentialsConf getConfig() {
return config;
}
public ConfigurationSection getKits() {
return kits;
}
public Map<String, Object> getKit(String name) {
name = name.replace('.', '_').replace('/', '_');
if (getKits() != null) {
final ConfigurationSection kits = getKits();
// For some reason, YAML doesn't sees keys as always lowercase even if they aren't defined like that.
// Workaround is to toLowercase when getting from the config, but showing normally elsewhere.
// ODDLY ENOUGH when you get the configuration section for ALL kits, it will return the proper
// case of each kit. But when you check for each kit's configuration section, it won't return the kit
// you just found if you don't toLowercase it.
if (kits.isConfigurationSection(name.toLowerCase())) {
return kits.getConfigurationSection(name.toLowerCase()).getValues(true);
}
}
return null;
}
// Tries to find an existing kit name that matches the given name, ignoring case. Returns null if no match.
public String matchKit(String name) {
if (config.isConfigurationSection("kits")) {
final ConfigurationSection section = config.getConfigurationSection("kits");
if (section != null) {
for (String kitName : section.getKeys(false)) {
if (kitName.equalsIgnoreCase(name)) {
return kitName;
}
}
}
}
return null;
}
public void addKit(String name, List<String> lines, long delay) {
// Will overwrite but w/e
config.set("kits." + name + ".delay", delay);
config.set("kits." + name + ".items", lines);
kits = _getKits();
config.save();
}
public void removeKit(String name) {
config.set("kits." + name, null);
kits = _getKits();
config.save();
}
public String listKits(final net.ess3.api.IEssentials ess, final User user) throws Exception {
try {
final ConfigurationSection kits = config.getConfigurationSection("kits");
final StringBuilder list = new StringBuilder();
for (String kitItem : kits.getKeys(false)) {
if (user == null) {
list.append(" ").append(capitalCase(kitItem));
} else if (user.isAuthorized("essentials.kits." + kitItem.toLowerCase(Locale.ENGLISH))) {
String cost = "";
String name = capitalCase(kitItem);
BigDecimal costPrice = new Trade("kit-" + kitItem.toLowerCase(Locale.ENGLISH), ess).getCommandCost(user);
if (costPrice.signum() > 0) {
cost = tl("kitCost", NumberUtil.displayCurrency(costPrice, ess));
}
Kit kit = new Kit(kitItem, ess);
double nextUse = kit.getNextUse(user);
if (nextUse == -1 && ess.getSettings().isSkippingUsedOneTimeKitsFromKitList()) {
continue;
} else if (nextUse != 0) {
name = tl("kitDelay", name);
}
list.append(" ").append(name).append(cost);
}
}
return list.toString().trim();
} catch (Exception ex) {
throw new Exception(tl("kitError"), ex);
}
}
}
@@ -1,119 +0,0 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class PlayerList {
// Cosmetic list formatting
public static String listUsers(final IEssentials ess, final List<User> users, final String seperator) {
final StringBuilder groupString = new StringBuilder();
Collections.sort(users);
boolean needComma = false;
for (User user : users) {
if (needComma) {
groupString.append(seperator);
}
needComma = true;
if (user.isAfk()) {
groupString.append(tl("listAfkTag"));
}
if (user.isHidden()) {
groupString.append(tl("listHiddenTag"));
}
user.setDisplayNick();
groupString.append(user.getDisplayName());
if (ess.getSettings().realNamesOnList() && !ChatColor.stripColor(user.getDisplayName()).equals(user.getName())) {
groupString.append(" (").append(user.getName()).append(")");
}
groupString.append("\u00a7f");
}
return groupString.toString();
}
// Produce a user summary: There are 5 out of maximum 10 players online.
public static String listSummary(final IEssentials ess, final User user, final boolean showHidden) {
Server server = ess.getServer();
int playerHidden = 0;
int hiddenCount = 0;
for (User onlinePlayer : ess.getOnlineUsers()) {
if (onlinePlayer.isHidden() || (user != null && !user.getBase().canSee(onlinePlayer.getBase()))) {
playerHidden++;
if (showHidden || user != null && user.getBase().canSee(onlinePlayer.getBase())) {
hiddenCount++;
}
}
}
String online;
if (hiddenCount > 0) {
online = tl("listAmountHidden", ess.getOnlinePlayers().size() - playerHidden, hiddenCount, server.getMaxPlayers());
} else {
online = tl("listAmount", ess.getOnlinePlayers().size() - playerHidden, server.getMaxPlayers());
}
return online;
}
// Build the basic player list, divided by groups.
public static Map<String, List<User>> getPlayerLists(final IEssentials ess, final IUser sender, final boolean showHidden) {
final Map<String, List<User>> playerList = new HashMap<>();
for (User onlineUser : ess.getOnlineUsers()) {
if ((sender == null && !showHidden && onlineUser.isHidden()) || (sender != null && !showHidden && !sender.getBase().canSee(onlineUser.getBase()))) {
continue;
}
final String group = FormatUtil.stripFormat(FormatUtil.stripEssentialsFormat(onlineUser.getGroup().toLowerCase()));
List<User> list = playerList.computeIfAbsent(group, k -> new ArrayList<>());
list.add(onlineUser);
}
return playerList;
}
// Handle the merging of groups
public static List<User> getMergedList(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) {
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
final List<User> users = new ArrayList<>();
for (String configGroup : configGroups) {
if (configGroup.equalsIgnoreCase(groupName)) {
String[] groupValues = ess.getSettings().getListGroupConfig().get(configGroup).toString().trim().split(" ");
for (String groupValue : groupValues) {
groupValue = groupValue.toLowerCase(Locale.ENGLISH);
if (groupValue.isEmpty()) {
continue;
}
List<User> u = playerList.get(groupValue.trim());
if (u == null || u.isEmpty()) {
continue;
}
playerList.remove(groupValue);
users.addAll(u);
}
}
}
return users;
}
// Output a playerlist of just a single group, /list <groupname>
public static String listGroupUsers(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) throws Exception {
final List<User> users = getMergedList(ess, playerList, groupName);
final List<User> groupUsers = playerList.get(groupName);
if (groupUsers != null && !groupUsers.isEmpty()) {
users.addAll(groupUsers);
}
if (users.isEmpty()) {
throw new Exception(tl("groupDoesNotExist"));
}
String displayGroupName = Character.toTitleCase(groupName.charAt(0)) +
groupName.substring(1);
return outputFormat(displayGroupName, listUsers(ess, users, ", "));
}
// Build the output string
public static String outputFormat(final String group, final String message) {
return tl("listGroupTag", FormatUtil.replaceFormat(group)) +
message;
}
}
File diff suppressed because it is too large. Load diff
@@ -1,123 +0,0 @@
package com.earth2me.essentials.api;
import com.earth2me.essentials.Trade;
import net.ess3.api.IUser;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.concurrent.CompletableFuture;
public interface IAsyncTeleport {
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param loc - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Location loc, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Used to skip teleportPlayer delay when teleporting someone to a location or player.
*
* @param entity - Where should the player end up
* @param cooldown - If cooldown should be enforced
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void now(Player entity, boolean cooldown, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause.
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleport(Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific location
*
* @param otherUser - Which user will be teleported
* @param loc - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport a player to a specific player
*
* @param otherUser - Which user will be teleported
* @param entity - Where should the player end up
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle tp fallback on /jail and /home
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void respawn(final Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /warp teleports
*
* @param otherUser - Which user will be teleported
* @param warp - The name of the warp the user will be teleported too.
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param cause - The reported teleportPlayer cause
* @param future - Future which is completed with the success status of the execution
*/
void warp(IUser otherUser, String warp, Trade chargeFor, PlayerTeleportEvent.TeleportCause cause, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports
*
* @param chargeFor - What the user will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle /back teleports that
* are executed by a different player with this
* instance of teleport as a target.
*
* @param teleporter - The user performing the /back command.
* This value may be {@code null} to indicate console.
* @param chargeFor - What the {@code teleporter} will be charged if teleportPlayer is successful
* @param future - Future which is completed with the success status of the execution
*/
void back(IUser teleporter, Trade chargeFor, CompletableFuture<Boolean> future);
/**
* Teleport wrapper used to handle throwing user home after a jail sentence
*
* @param future - Future which is completed with the success status of the execution
*/
void back(CompletableFuture<Boolean> future);
}
@@ -1,13 +0,0 @@
package com.earth2me.essentials.api;
import java.util.Locale;
public interface II18n {
/**
* Gets the current locale setting
*
* @return the current locale, if not set it will return the default locale
*/
Locale getCurrentLocale();
}
@@ -1,6 +0,0 @@
package com.earth2me.essentials.api;
public interface IReload {
void onReload();
}
@@ -1,173 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static com.earth2me.essentials.I18n.tl;
public class Commandbalancetop extends EssentialsCommand {
public Commandbalancetop() {
super("balancetop");
}
private static final int CACHETIME = 2 * 60 * 1000;
public static final int MINUSERS = 50;
private static final SimpleTextInput cache = new SimpleTextInput();
private static long cacheage = 0;
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
int page = 0;
boolean force = false;
if (args.length > 0) {
try {
page = Integer.parseInt(args[0]);
} catch (NumberFormatException ex) {
if (args[0].equalsIgnoreCase("force") && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force"))) {
force = true;
}
}
}
if (!force && lock.readLock().tryLock()) {
try {
if (cacheage > System.currentTimeMillis() - CACHETIME) {
outputCache(sender, page);
return;
}
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
}
} finally {
lock.readLock().unlock();
}
} else {
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
}
}
ess.runTaskAsynchronously(new Viewer(sender, commandLabel, page, force));
}
private static void outputCache(final CommandSource sender, int page) {
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(cacheage);
final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
sender.sendMessage(tl("balanceTop", format.format(cal.getTime())));
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
}
private class Calculator implements Runnable {
private final transient Viewer viewer;
private final boolean force;
public Calculator(final Viewer viewer, final boolean force) {
this.viewer = viewer;
this.force = force;
}
@Override
public void run() {
lock.writeLock().lock();
try {
if (force || cacheage <= System.currentTimeMillis() - CACHETIME) {
cache.getLines().clear();
final Map<String, BigDecimal> balances = new HashMap<>();
BigDecimal totalMoney = BigDecimal.ZERO;
if (ess.getSettings().isEcoDisabled()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Internal economy functions disabled, aborting baltop.");
}
} else {
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user != null) {
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
// Don't list NPCs in output
continue;
}
if (!user.isAuthorized("essentials.balancetop.exclude")) {
final BigDecimal userMoney = user.getMoney();
user.updateMoneyCache(userMoney);
totalMoney = totalMoney.add(userMoney);
final String name = user.isHidden() ? user.getName() : user.getDisplayName();
balances.put(name, userMoney);
}
}
}
}
final List<Map.Entry<String, BigDecimal>> sortedEntries = new ArrayList<>(balances.entrySet());
sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
cache.getLines().add(tl("serverTotal", NumberUtil.displayCurrency(totalMoney, ess)));
int pos = 1;
for (Map.Entry<String, BigDecimal> entry : sortedEntries) {
cache.getLines().add(pos + ". " + entry.getKey() + ", " + NumberUtil.displayCurrency(entry.getValue(), ess));
pos++;
}
cacheage = System.currentTimeMillis();
}
} finally {
lock.writeLock().unlock();
}
ess.runTaskAsynchronously(viewer);
}
}
private class Viewer implements Runnable {
private final transient CommandSource sender;
private final transient int page;
private final transient boolean force;
private final transient String commandLabel;
public Viewer(final CommandSource sender, final String commandLabel, final int page, final boolean force) {
this.sender = sender;
this.page = page;
this.force = force;
this.commandLabel = commandLabel;
}
@Override
public void run() {
lock.readLock().lock();
try {
if (!force && cacheage > System.currentTimeMillis() - CACHETIME) {
outputCache(sender, page);
return;
}
} finally {
lock.readLock().unlock();
}
ess.runTaskAsynchronously(new Calculator(new Viewer(sender, commandLabel, page, false), force));
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList("1");
if (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force")) {
options.add("force");
}
return options;
} else {
return Collections.emptyList();
}
}
}
@@ -1,128 +0,0 @@
package com.earth2me.essentials.commands;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.DateUtil;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.bukkit.inventory.ItemStack;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static com.earth2me.essentials.I18n.tl;
public class Commandcreatekit extends EssentialsCommand {
private static final String PASTE_URL = "https://hastebin.com/";
private static final String PASTE_UPLOAD_URL = PASTE_URL + "documents";
private static final Gson GSON = new Gson();
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final DumperOptions yamlOptions = new DumperOptions();
private final Representer yamlRepresenter = new YamlRepresenter();
private final YamlConstructor yamlConstructor = new YamlConstructor();
public Commandcreatekit() {
super("createkit");
yamlOptions.setDefaultFlowStyle(FlowStyle.BLOCK);
yamlRepresenter.setDefaultFlowStyle(FlowStyle.BLOCK);
}
// /createkit <name> <delay>
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length != 2) {
throw new NotEnoughArgumentsException();
}
// Command handler will auto fail if this fails.
long delay = Long.parseLong(args[1]);
String kitname = args[0];
ItemStack[] items = user.getBase().getInventory().getContents();
List<String> list = new ArrayList<>();
for (ItemStack is : items) {
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
String serialized = ess.getItemDb().serialize(is);
list.add(serialized);
}
}
// Some users might want to directly write to config knowing the consequences. *shrug*
if (!ess.getSettings().isPastebinCreateKit()) {
ess.getKits().addKit(kitname, list, delay);
user.sendMessage(tl("createdKit", kitname, list.size(), delay));
} else {
ConfigurationSection config = new MemoryConfiguration();
config.set("kits." + kitname + ".delay", delay);
config.set("kits." + kitname + ".items", list);
final Yaml yaml = new Yaml(yamlConstructor, yamlRepresenter, yamlOptions);
String fileContents = "# Copy the kit code below into the kits section in your config.yml file\n";
fileContents += yaml.dump(config.getValues(false));
uploadPaste(user.getSource(), kitname, delay, fileContents);
}
}
private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final String contents) {
executorService.submit(() -> {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(PASTE_UPLOAD_URL).openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("User-Agent", "EssentialsX plugin");
try (OutputStream os = connection.getOutputStream()) {
os.write(contents.getBytes(Charsets.UTF_8));
}
// Error
if (connection.getResponseCode() >= 400) {
sender.sendMessage(tl("createKitFailed", kitName));
String message = CharStreams.toString(new InputStreamReader(connection.getErrorStream(), Charsets.UTF_8));
ess.getLogger().severe("Error creating kit: " + message);
return;
}
// Read URL
JsonObject object = GSON.fromJson(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8), JsonObject.class);
String pasteUrl = PASTE_URL + object.get("key").getAsString();
connection.disconnect();
String separator = tl("createKitSeparator");
String delayFormat = "0";
if (delay > 0) {
delayFormat = DateUtil.formatDateDiff(System.currentTimeMillis() + (delay * 1000));
}
sender.sendMessage(separator);
sender.sendMessage(tl("createKitSuccess", kitName, delayFormat, pasteUrl));
sender.sendMessage(separator);
if (ess.getSettings().isDebug()) {
ess.getLogger().info(sender.getSender().getName() + " created a kit: " + pasteUrl);
}
} catch (Exception e) {
sender.sendMessage(tl("createKitFailed", kitName));
e.printStackTrace();
}
});
}
}
@@ -1,468 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.EssentialsUpgrade;
import com.earth2me.essentials.User;
import com.earth2me.essentials.UserMap;
import com.earth2me.essentials.utils.*;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
import java.util.function.Supplier;
import static com.earth2me.essentials.I18n.tl;
// This command has 4 undocumented behaviours #EasterEgg
public class Commandessentials extends EssentialsCommand {
private static final Sound NOTE_HARP = EnumUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO");
private static final Sound MOO_SOUND = EnumUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK");
private static final String NYAN_TUNE = "1D#,1E,2F#,,2A#,1E,1D#,1E,2F#,2B,2D#,2E,2D#,2A#,2B,,2F#,,1D#,1E,2F#,2B,2C#,2A#,2B,2C#,2E,2D#,2E,2C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1B,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1B,,";
private static final String[] CONSOLE_MOO = new String[]{" (__)", " (oo)", " /------\\/", " / | ||", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
private static final String[] PLAYER_MOO = new String[]{" (__)", " (oo)", " /------\\/", " / | | |", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
public Commandessentials() {
super("essentials");
}
private transient TuneRunnable currentTune = null;
private static final List<String> versionPlugins = Arrays.asList(
"Vault", // API
"Reserve", // API
"PlaceholderAPI", // API
"CMI", // potential for issues
"Towny", // past issues; admins should ensure latest
"ChestShop", // past issues; admins should ensure latest
"Citizens", // fires player events
"LuckPerms", // permissions (recommended)
"UltraPermissions",
"PermissionsEx", // permissions (unsupported)
"GroupManager", // permissions (unsupported)
"bPermissions" // permissions (unsupported)
);
private static final List<String> officialPlugins = Arrays.asList(
"EssentialsAntiBuild",
"EssentialsChat",
"EssentialsGeoIP",
"EssentialsProtect",
"EssentialsSpawn",
"EssentialsXMPP"
);
private static final List<String> warnPlugins = Arrays.asList(
"PermissionsEx",
"GroupManager",
"bPremissions"
);
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length == 0) {
showUsage(sender);
}
switch(args[0]) {
// Info commands
case "debug":
case "verbose":
runDebug(server, sender, commandLabel, args);
break;
case "ver":
case "version":
runVersion(server, sender, commandLabel, args);
break;
case "cmd":
case "commands":
runCommands(server, sender, commandLabel, args);
break;
// Data commands
case "reload":
runReload(server, sender, commandLabel, args);
break;
case "reset":
runReset(server, sender, commandLabel, args);
break;
case "cleanup":
runCleanup(server, sender, commandLabel, args);
break;
case "uuidconvert":
runUUIDConvert(server, sender, commandLabel, args);
break;
case "uuidtest":
runUUIDTest(server, sender, commandLabel, args);
break;
// "#EasterEgg"
case "nya":
case "nyan":
runNya(server, sender, commandLabel, args);
break;
case "moo":
runMoo(server, sender, commandLabel, args);
break;
default:
showUsage(sender);
}
}
// Displays the command's usage.
private void showUsage(final CommandSource sender) throws Exception {
throw new NotEnoughArgumentsException("/<command> <reload/debug/commands>");
}
// Lists commands that are being handed over to other plugins.
private void runCommands(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
final StringBuilder disabledCommands = new StringBuilder();
for (Map.Entry<String, String> entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) {
if (disabledCommands.length() > 0) {
disabledCommands.append("\n");
}
disabledCommands.append(entry.getKey()).append(" => ").append(entry.getValue());
}
if (disabledCommands.length() > 0) {
sender.sendMessage(tl("blockList"));
sender.sendMessage(disabledCommands.toString());
} else {
sender.sendMessage(tl("blockListEmpty"));
}
}
// Resets the given player's user data.
private void runReset(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new Exception("/<command> reset <player>");
}
final User user = getPlayer(server, args, 1, true, true);
user.reset();
sender.sendMessage("Reset Essentials userdata for player: " + user.getDisplayName());
}
// Toggles debug mode.
private void runDebug(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
ess.getSettings().setDebug(!ess.getSettings().isDebug());
sender.sendMessage("Essentials " + ess.getDescription().getVersion() + " debug mode " + (ess.getSettings().isDebug() ? "enabled" : "disabled"));
}
// Reloads all reloadable configs.
private void runReload(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
ess.reload();
sender.sendMessage(tl("essentialsReload", ess.getDescription().getVersion()));
}
// Pop tarts.
private void runNya(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (currentTune != null) {
currentTune.cancel();
}
currentTune = new TuneRunnable(NYAN_TUNE, NOTE_HARP, ess::getOnlinePlayers);
currentTune.runTaskTimer(ess, 20, 2);
}
// Cow farts.
private void runMoo(final Server server, final CommandSource sender, final String command, final String[] args) {
if (args.length == 2 && args[1].equals("moo")) {
for (String s : CONSOLE_MOO) {
logger.info(s);
}
for (Player player : ess.getOnlinePlayers()) {
player.sendMessage(PLAYER_MOO);
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
}
} else {
if (sender.isPlayer()) {
sender.getSender().sendMessage(PLAYER_MOO);
final Player player = sender.getPlayer();
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
} else {
sender.getSender().sendMessage(CONSOLE_MOO);
}
}
}
// Cleans up inactive users.
private void runCleanup(final Server server, final CommandSource sender, final String command, final String[] args) throws Exception {
if (args.length < 2 || !NumberUtil.isInt(args[1])) {
sender.sendMessage("This sub-command will delete users who haven't logged in in the last <days> days.");
sender.sendMessage("Optional parameters define the minimum amount required to prevent deletion.");
sender.sendMessage("Unless you define larger default values, this command will ignore people who have more than 0 money/homes.");
throw new Exception("/<command> cleanup <days> [money] [homes]");
}
sender.sendMessage(tl("cleaning"));
final long daysArg = Long.parseLong(args[1]);
final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9\\.]", "")) : 0;
final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0;
final UserMap userMap = ess.getUserMap();
ess.runTaskAsynchronously(() -> {
long currTime = System.currentTimeMillis();
for (UUID u : userMap.getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user == null) {
continue;
}
long lastLog = user.getLastLogout();
if (lastLog == 0) {
lastLog = user.getLastLogin();
}
if (lastLog == 0) {
user.setLastLogin(currTime);
}
if (user.isNPC()) {
continue;
}
long timeDiff = currTime - lastLog;
long milliDays = daysArg * 24L * 60L * 60L * 1000L;
int homeCount = user.getHomes().size();
double moneyCount = user.getMoney().doubleValue();
if ((lastLog == 0) || (timeDiff < milliDays) || (homeCount > homesArg) || (moneyCount > moneyArg)) {
continue;
}
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Deleting user: " + user.getName() + " Money: " + moneyCount + " Homes: " + homeCount + " Last seen: " + DateUtil.formatDateDiff(lastLog));
}
user.reset();
}
sender.sendMessage(tl("cleaned"));
});
}
// Forces a rerun of userdata UUID conversion.
private void runUUIDConvert(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sender.sendMessage("Starting Essentials UUID userdata conversion; this may lag the server.");
Boolean ignoreUFCache = (args.length > 2 && args[1].toLowerCase(Locale.ENGLISH).contains("ignore"));
EssentialsUpgrade.uuidFileConvert(ess, ignoreUFCache);
sender.sendMessage("UUID conversion complete. Check your server log for more information.");
}
// Looks up various UUIDs for a user.
private void runUUIDTest(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (args.length < 2) {
throw new Exception("/<command> uuidtest <name>");
}
String name = args[1];
sender.sendMessage("Looking up UUID for " + name);
UUID onlineUUID = null;
for (Player player : ess.getOnlinePlayers()) {
if (player.getName().equalsIgnoreCase(name)) {
onlineUUID = player.getUniqueId();
break;
}
}
UUID essUUID = ess.getUserMap().getUser(name).getConfigUUID();
org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
UUID bukkituuid = player.getUniqueId();
sender.sendMessage("Bukkit Lookup: " + bukkituuid.toString());
if (onlineUUID != null && onlineUUID != bukkituuid) {
sender.sendMessage("Online player: " + onlineUUID.toString());
}
if (essUUID != null && essUUID != bukkituuid) {
sender.sendMessage("Essentials config: " + essUUID.toString());
}
UUID npcuuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
sender.sendMessage("NPC UUID: " + npcuuid.toString());
UUID offlineuuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
sender.sendMessage("Offline Mode UUID: " + offlineuuid.toString());
}
// Displays versions of EssentialsX and related plugins.
private void runVersion(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.version")) return;
boolean isMismatched = false;
boolean isVaultInstalled = false;
boolean isUnsupported = false;
final boolean isServerSupported = VersionUtil.isServerSupported();
final PluginManager pm = server.getPluginManager();
final String essVer = pm.getPlugin("Essentials").getDescription().getVersion();
sender.sendMessage(tl(isServerSupported ? "versionOutputFine" : "versionOutputWarn", "Server", server.getBukkitVersion() + " " + server.getVersion()));
sender.sendMessage(tl("versionOutputFine", "EssentialsX", essVer));
for (Plugin plugin : pm.getPlugins()) {
final PluginDescriptionFile desc = plugin.getDescription();
String name = desc.getName();
String version = desc.getVersion();
if (name.startsWith("Essentials") && !name.equalsIgnoreCase("Essentials")) {
if (officialPlugins.contains(name)) {
name = name.replace("Essentials", "EssentialsX");
if (!version.equalsIgnoreCase(essVer)) {
isMismatched = true;
sender.sendMessage(tl("versionOutputWarn", name, version));
} else {
sender.sendMessage(tl("versionOutputFine", name, version));
}
} else {
sender.sendMessage(tl("versionOutputUnsupported", name, version));
isUnsupported = true;
}
}
if (versionPlugins.contains(name)) {
if (warnPlugins.contains(name)) {
sender.sendMessage(tl("versionOutputUnsupported", name, version));
isUnsupported = true;
} else {
sender.sendMessage(tl("versionOutputFine", name, version));
}
}
if (name.equals("Vault")) isVaultInstalled = true;
}
if (isMismatched) {
sender.sendMessage(tl("versionMismatchAll"));
}
if (!isVaultInstalled) {
sender.sendMessage(tl("versionOutputVaultMissing"));
}
if (isUnsupported) {
sender.sendMessage(tl("versionOutputUnsupportedPlugins"));
}
if (!VersionUtil.isServerSupported()) {
sender.sendMessage(tl("serverUnsupported"));
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList();
options.add("debug");
options.add("commands");
options.add("version");
options.add("reload");
options.add("reset");
options.add("cleanup");
//options.add("uuidconvert");
//options.add("uuidtest");
//options.add("nya");
//options.add("moo");
return options;
}
switch (args[0]) {
case "moo":
if (args.length == 2) {
return Lists.newArrayList("moo");
}
break;
case "reset":
case "uuidtest":
if (args.length == 2) {
return getPlayers(server, sender);
}
break;
case "cleanup":
if (args.length == 2) {
return COMMON_DURATIONS;
} else if (args.length == 3 || args.length == 4) {
return Lists.newArrayList("-1", "0");
}
break;
case "uuidconvert":
if (args.length == 2) {
return Lists.newArrayList("ignoreUFCache");
}
break;
}
return Collections.emptyList();
}
private static class TuneRunnable extends BukkitRunnable {
private static final Map<String, Float> noteMap = ImmutableMap.<String, Float>builder()
.put("1F#", 0.5f)
.put("1G", 0.53f)
.put("1G#", 0.56f)
.put("1A", 0.6f)
.put("1A#", 0.63f)
.put("1B", 0.67f)
.put("1C", 0.7f)
.put("1C#", 0.76f)
.put("1D", 0.8f)
.put("1D#", 0.84f)
.put("1E", 0.9f)
.put("1F", 0.94f)
.put("2F#", 1.0f)
.put("2G", 1.06f)
.put("2G#", 1.12f)
.put("2A", 1.18f)
.put("2A#", 1.26f)
.put("2B", 1.34f)
.put("2C", 1.42f)
.put("2C#", 1.5f)
.put("2D", 1.6f)
.put("2D#", 1.68f)
.put("2E", 1.78f)
.put("2F", 1.88f)
.build();
private final String[] tune;
private final Sound sound;
private final Supplier<Collection<Player>> players;
private int i = 0;
TuneRunnable(final String tuneStr, final Sound sound, final Supplier<Collection<Player>> players) {
this.tune = tuneStr.split(",");
this.sound = sound;
this.players = players;
}
@Override
public void run() {
final String note = tune[i];
i++;
if (i >= tune.length) {
cancel();
}
if (note == null || note.isEmpty()) {
return;
}
for (Player onlinePlayer : players.get()) {
onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note));
}
}
}
}
@@ -1,62 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.*;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commandhelp extends EssentialsCommand {
public Commandhelp() {
super("help");
}
@Override
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
IText output;
String pageStr = args.length > 0 ? args[0] : null;
String chapterPageStr = args.length > 1 ? args[1] : null;
String command = commandLabel;
final IText input = new TextInput(user.getSource(), "help", false, ess);
if (input.getLines().isEmpty()) {
if (NumberUtil.isInt(pageStr) || pageStr == null) {
output = new HelpInput(user, "", ess);
} else {
if (pageStr.length() > 26) {
pageStr = pageStr.substring(0, 25);
}
output = new HelpInput(user, pageStr.toLowerCase(Locale.ENGLISH), ess);
command = command.concat(" ").concat(pageStr);
pageStr = chapterPageStr;
}
chapterPageStr = null;
} else {
user.setDisplayNick();
output = new KeywordReplacer(input, user.getSource(), ess);
}
final TextPager pager = new TextPager(output);
pager.showPage(pageStr, chapterPageStr, command, user.getSource());
}
@Override
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
sender.sendMessage(tl("helpConsole"));
}
@Override
protected List<String> getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) {
if (args.length == 1) {
return getCommands(server);
} else {
return Collections.emptyList();
}
}
}
@@ -1,75 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class Commanditemlore extends EssentialsCommand {
public Commanditemlore() {
super("itemlore");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
ItemStack item = user.getBase().getItemInHand();
if (item.getType().name().contains("AIR")) {
throw new Exception(tl("itemloreInvalidItem"));
}
if (args.length == 0) {
throw new NotEnoughArgumentsException();
}
ItemMeta im = item.getItemMeta();
if (args[0].equalsIgnoreCase("add") && args.length > 1) {
String line = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 1)).trim();
List<String> lore = im.hasLore() ? im.getLore() : new ArrayList<>();
lore.add(line);
im.setLore(lore);
item.setItemMeta(im);
user.sendMessage(tl("itemloreSuccess", line));
} else if (args[0].equalsIgnoreCase("clear")) {
im.setLore(new ArrayList<>());
item.setItemMeta(im);
user.sendMessage(tl("itemloreClear"));
} else if (args[0].equalsIgnoreCase("set") && args.length > 2 && NumberUtil.isInt(args[1])) {
if (!im.hasLore()) {
throw new Exception(tl("itemloreNoLore"));
}
int line = Integer.parseInt(args[1]);
String newLine = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 2)).trim();
List<String> lore = im.getLore();
try {
lore.set(line - 1, newLine);
} catch (Exception e) {
throw new Exception(tl("itemloreNoLine", line), e);
}
im.setLore(lore);
item.setItemMeta(im);
user.sendMessage(tl("itemloreSuccessLore", line, newLine));
} else {
throw new NotEnoughArgumentsException();
}
}
@Override
protected List<String> getTabCompleteOptions(Server server, User user, String commandLabel, String[] args) {
if (args.length == 1) {
return Lists.newArrayList("add", "set", "clear");
} else {
return Collections.emptyList();
}
}
}
@@ -1,38 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Locale;
import static com.earth2me.essentials.I18n.tl;
public class Commanditemname extends EssentialsCommand {
public Commanditemname() {
super("itemname");
}
@Override
protected void run(Server server, User user, String commandLabel, String[] args) throws Exception {
ItemStack item = user.getBase().getItemInHand();
if (item.getType() == Material.AIR) {
user.sendMessage(tl("itemnameInvalidItem", item.getType().toString().toLowerCase(Locale.ENGLISH).replace('_', ' ')));
return;
}
String name = FormatUtil.formatString(user, "essentials.itemname", getFinalArg(args, 0)).trim();
if (name.isEmpty()) {
name = null;
}
ItemMeta im = item.getItemMeta();
im.setDisplayName(name);
item.setItemMeta(im);
user.sendMessage(name == null ? tl("itemnameClear") : tl("itemnameSuccess", name));
}
}
@@ -1,128 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.PlayerList;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Server;
import java.util.*;
import static com.earth2me.essentials.I18n.tl;
public class Commandlist extends EssentialsCommand {
public Commandlist() {
super("list");
}
@Override
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
boolean showHidden = true;
User user = null;
if (sender.isPlayer()) {
user = ess.getUser(sender.getPlayer());
showHidden = user.isAuthorized("essentials.list.hidden") || user.canInteractVanished();
}
sender.sendMessage(PlayerList.listSummary(ess, user, showHidden));
final Map<String, List<User>> playerList = PlayerList.getPlayerLists(ess, user, showHidden);
if (args.length > 0) {
sender.sendMessage(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase()));
} else {
sendGroupedList(sender, commandLabel, playerList);
}
}
// Output the standard /list output, when no group is specified
private void sendGroupedList(CommandSource sender, String commandLabel, Map<String, List<User>> playerList) {
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
final List<String> asterisk = new ArrayList<>();
// Loop through the custom defined groups and display them
for (String oConfigGroup : configGroups) {
String groupValue = ess.getSettings().getListGroupConfig().get(oConfigGroup).toString().trim();
String configGroup = oConfigGroup.toLowerCase();
// If the group value is an asterisk, then skip it, and handle it later
if (groupValue.equals("*")) {
asterisk.add(oConfigGroup);
continue;
}
// If the group value is hidden, we don't need to display it
if (groupValue.equalsIgnoreCase("hidden")) {
playerList.remove(configGroup);
continue;
}
List<User> outputUserList;
final List<User> matchedList = playerList.get(configGroup);
// If the group value is an int, then we might need to truncate it
if (NumberUtil.isInt(groupValue)) {
if (matchedList != null && !matchedList.isEmpty()) {
playerList.remove(configGroup);
outputUserList = new ArrayList<>(matchedList);
int limit = Integer.parseInt(groupValue);
if (matchedList.size() > limit) {
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, tl("groupNumber", matchedList.size(), commandLabel, FormatUtil.stripFormat(configGroup))));
} else {
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
}
continue;
}
}
outputUserList = PlayerList.getMergedList(ess, playerList, configGroup);
// If we have no users, than we don't need to continue parsing this group
if (outputUserList.isEmpty()) {
continue;
}
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
}
Set<String> var = playerList.keySet();
String[] onlineGroups = var.toArray(new String[0]);
Arrays.sort(onlineGroups, String.CASE_INSENSITIVE_ORDER);
// If we have an asterisk group, then merge all remaining groups
if (!asterisk.isEmpty()) {
List<User> asteriskUsers = new ArrayList<>();
for (String onlineGroup : onlineGroups) {
asteriskUsers.addAll(playerList.get(onlineGroup));
}
for (String key : asterisk) {
playerList.put(key, asteriskUsers);
}
onlineGroups = asterisk.toArray(new String[0]);
}
// If we have any groups remaining after the custom groups loop through and display them
for (String onlineGroup : onlineGroups) {
List<User> users = playerList.get(onlineGroup);
String groupName = asterisk.isEmpty() ? users.get(0).getGroup() : onlineGroup;
if (ess.getPermissionsHandler().getName().equals("ConfigPermissions")) {
groupName = tl("connectedPlayers");
}
if (users == null || users.isEmpty()) {
continue;
}
sender.sendMessage(PlayerList.outputFormat(groupName, PlayerList.listUsers(ess, users, ", ")));
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return new ArrayList<>(PlayerList.getPlayerLists(ess, sender.getUser(ess), true).keySet());
} else {
return Collections.emptyList();
}
}
}
@@ -1,206 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.CommandSource;
import com.earth2me.essentials.User;
import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.collect.Lists;
import org.bukkit.Server;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
public class Commandmail extends EssentialsCommand {
private static int mailsPerMinute = 0;
private static long timestamp = 0;
public Commandmail() {
super("mail");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
final List<String> mail = user.getMails();
if (mail.isEmpty()) {
user.sendMessage(tl("noMail"));
throw new NoChargeException();
}
IText input = new SimpleTextInput(mail);
final TextPager pager = new TextPager(input);
pager.showPage(args.length > 1 ? args[1] : null, null, commandLabel + " " + args[0], user.getSource());
user.sendMessage(tl("mailClear"));
return;
}
if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
if (!user.isAuthorized("essentials.mail.send")) {
throw new Exception(tl("noPerm", "essentials.mail.send"));
}
if (user.isMuted()) {
String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
if (dateDiff == null) {
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
}
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
}
User u;
try {
u = getPlayer(server, args[1], true, true);
} catch (PlayerNotFoundException e) {
throw new Exception(tl("playerNeverOnServer", args[1]));
}
String mail = tl("mailFormat", user.getName(), FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 2)))));
if (mail.length() > 1000) {
throw new Exception(tl("mailTooLong"));
}
if (!u.isIgnoredPlayer(user)) {
if (Math.abs(System.currentTimeMillis() - timestamp) > 60000) {
timestamp = System.currentTimeMillis();
mailsPerMinute = 0;
}
mailsPerMinute++;
if (mailsPerMinute > ess.getSettings().getMailsPerMinute()) {
throw new Exception(tl("mailDelay", ess.getSettings().getMailsPerMinute()));
}
u.addMail(tl("mailMessage", mail));
}
user.sendMessage(tl("mailSentTo", u.getDisplayName(), u.getName()));
user.sendMessage(mail);
return;
}
if (args.length > 1 && "sendall".equalsIgnoreCase(args[0])) {
if (!user.isAuthorized("essentials.mail.sendall")) {
throw new Exception(tl("noPerm", "essentials.mail.sendall"));
}
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", user.getName(),
FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 1)))))));
user.sendMessage(tl("mailSent"));
return;
}
if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
if (user.getMails() == null || user.getMails().isEmpty()) {
user.sendMessage(tl("noMail"));
throw new NoChargeException();
}
user.setMails(null);
user.sendMessage(tl("mailCleared"));
return;
}
throw new NotEnoughArgumentsException();
}
@Override
protected void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception {
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
throw new Exception(tl("onlyPlayers", commandLabel + " read"));
} else if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
throw new Exception(tl("onlyPlayers", commandLabel + " clear"));
} else if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
User u;
try {
u = getPlayer(server, args[1], true, true);
} catch (PlayerNotFoundException e) {
throw new Exception(tl("playerNeverOnServer", args[1]));
}
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 2))));
sender.sendMessage(tl("mailSent"));
return;
} else if (args.length >= 2 && "sendall".equalsIgnoreCase(args[0])) {
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1)))));
sender.sendMessage(tl("mailSent"));
return;
} else if (args.length >= 2) {
//allow sending from console without "send" argument, since it's the only thing the console can do
User u;
try {
u = getPlayer(server, args[0], true, true);
} catch (PlayerNotFoundException e) {
throw new Exception(tl("playerNeverOnServer", args[0]));
}
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1))));
sender.sendMessage(tl("mailSent"));
return;
}
throw new NotEnoughArgumentsException();
}
private class SendAll implements Runnable {
String message;
public SendAll(String message) {
this.message = message;
}
@Override
public void run() {
for (UUID userid : ess.getUserMap().getAllUniqueUsers()) {
User user = ess.getUserMap().getUser(userid);
if (user != null) {
user.addMail(message);
}
}
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
if (args.length == 1) {
List<String> options = Lists.newArrayList("read", "clear");
if (user.isAuthorized("essentials.mail.send")) {
options.add("send");
}
if (user.isAuthorized("essentials.mail.sendall")) {
options.add("sendall");
}
return options;
} else if (args.length == 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) {
return getPlayers(server, user);
} else if (args.length == 2 && args[0].equalsIgnoreCase("read")) {
final List<String> mail = user.getMails();
int pages = mail.size() / 9 + (mail.size() % 9 > 0 ? 1 : 0);
if (pages == 0) {
return Lists.newArrayList("0");
} else {
List<String> options = Lists.newArrayList("1");
if (pages > 1) {
options.add(String.valueOf(pages));
}
return options;
}
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall") && user.isAuthorized("essentials.mail.sendall"))) {
return null; // Use vanilla handler
} else {
return Collections.emptyList();
}
}
@Override
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
if (args.length == 1) {
return Lists.newArrayList("send", "sendall");
} else if (args.length == 2 && args[0].equalsIgnoreCase("send")) {
return getPlayers(server, sender);
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall"))) {
return null; // Use vanilla handler
} else {
return Collections.emptyList();
}
}
}
@@ -1,78 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.AsyncTeleport;
import com.earth2me.essentials.Trade;
import com.earth2me.essentials.User;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpaccept extends EssentialsCommand {
public Commandtpaccept() {
super("tpaccept");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
final User requester;
try {
requester = ess.getUser(user.getTeleportRequest());
} catch (Exception ex) {
throw new Exception(tl("noPendingRequest"));
}
if (!requester.getBase().isOnline()) {
throw new Exception(tl("noPendingRequest"));
}
if (user.isTpRequestHere() && ((!requester.isAuthorized("essentials.tpahere") && !requester.isAuthorized("essentials.tpaall")) || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + user.getWorld().getName())))) {
throw new Exception(tl("noPendingRequest"));
}
if (!user.isTpRequestHere() && (!requester.isAuthorized("essentials.tpa") || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + requester.getWorld().getName())))) {
throw new Exception(tl("noPendingRequest"));
}
if (args.length > 0 && !requester.getName().contains(args[0])) {
throw new Exception(tl("noPendingRequest"));
}
if (!user.hasOutstandingTeleportRequest()) {
user.requestTeleport(null, false);
throw new Exception(tl("requestTimedOut"));
}
final Trade charge = new Trade(this.getName(), ess);
user.sendMessage(tl("requestAccepted"));
requester.sendMessage(tl("requestAcceptedFrom", user.getDisplayName()));
CompletableFuture<Boolean> future = getNewExceptionFuture(requester.getSource(), commandLabel);
future.exceptionally(e -> {
user.sendMessage(tl("pendingTeleportCancelled"));
return false;
});
if (user.isTpRequestHere()) {
final Location loc = user.getTpRequestLocation();
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
future.thenAccept(success -> {
if (success) {
requester.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
});
teleport.teleportPlayer(user, user.getTpRequestLocation(), charge, TeleportCause.COMMAND, future);
} else {
AsyncTeleport teleport = (AsyncTeleport) requester.getAsyncTeleport();
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
teleport.teleport(user.getBase(), charge, TeleportCause.COMMAND, future);
}
user.requestTeleport(null, false);
throw new NoChargeException();
}
}
@@ -1,28 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Server;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpdeny extends EssentialsCommand {
public Commandtpdeny() {
super("tpdeny");
}
@Override
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
if (user.getTeleportRequest() == null) {
throw new Exception(tl("noPendingRequest"));
}
final User player = ess.getUser(user.getTeleportRequest());
if (player == null) {
throw new Exception(tl("noPendingRequest"));
}
user.sendMessage(tl("requestDenied"));
player.sendMessage(tl("requestDeniedFrom", user.getDisplayName()));
user.requestTeleport(null, false);
}
}
@@ -1,35 +0,0 @@
package com.earth2me.essentials.commands;
import com.earth2me.essentials.User;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.event.player.PlayerTeleportEvent;
import static com.earth2me.essentials.I18n.tl;
public class Commandtpoffline extends EssentialsCommand {
public Commandtpoffline() {
super("tpoffline");
}
@Override
public void run(final Server server, final User user, final String label, final String[] args) throws Exception {
switch (args.length) {
case 0:
throw new NotEnoughArgumentsException();
default:
final User target = getPlayer(server, args, 0, true, true);
final Location logout = target.getLogoutLocation();
if (user.getWorld() != logout.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + logout.getWorld().getName())) {
throw new Exception(tl("noPerm", "essentials.worlds." + logout.getWorld().getName()));
}
user.sendMessage(tl("teleporting", logout.getWorld().getName(), logout.getBlockX(), logout.getBlockY(), logout.getBlockZ()));
user.getAsyncTeleport().now(logout, false, PlayerTeleportEvent.TeleportCause.COMMAND, getNewExceptionFuture(user.getSource(), label));
}
}
}
@@ -1,30 +0,0 @@
package com.earth2me.essentials.craftbukkit;
import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import java.util.Set;
public class BanLookup {
public static Boolean isBanned(IEssentials ess, User user) {
return isBanned(ess, user.getName());
}
public static Boolean isBanned(IEssentials ess, String name) {
return getBanEntry(ess, name) != null;
}
public static BanEntry getBanEntry(IEssentials ess, String name) {
Set<BanEntry> benteries = ess.getServer().getBanList(BanList.Type.NAME).getBanEntries();
for (BanEntry banEnt : benteries) {
if (banEnt.getTarget().equals(name)) {
return banEnt;
}
}
return null;
}
}
@@ -1,349 +0,0 @@
package com.earth2me.essentials.items;
import com.earth2me.essentials.IConf;
import com.earth2me.essentials.User;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.api.IEssentials;
import net.ess3.api.PluginKey;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
import org.bukkit.Material;
import org.bukkit.block.Banner;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import java.util.*;
import java.util.function.Function;
import static com.earth2me.essentials.I18n.tl;
public abstract class AbstractItemDb implements IConf, net.ess3.api.IItemDb {
protected final IEssentials ess;
protected boolean ready = false;
private final Map<PluginKey, ItemResolver> resolverMap = new HashMap<>();
AbstractItemDb(IEssentials ess) {
this.ess = ess;
}
@Override
public void registerResolver(Plugin plugin, String name, ItemResolver resolver) throws Exception {
PluginKey key = PluginKey.fromKey(plugin, name);
if (resolverMap.containsKey(key)) {
throw new Exception("Tried to add a duplicate resolver with name " + key.toString());
}
resolverMap.put(key, resolver);
}
@Override
public void unregisterResolver(Plugin plugin, String name) throws Exception {
PluginKey key = PluginKey.fromKey(plugin, name);
if (!resolverMap.containsKey(key)) {
throw new Exception("Tried to remove nonexistent resolver with name " + key.toString());
}
resolverMap.remove(key);
}
@Override
public boolean isResolverPresent(Plugin plugin, String name) {
return resolverMap.containsKey(PluginKey.fromKey(plugin, name));
}
@Override
public Map<PluginKey, ItemResolver> getResolvers() {
return new HashMap<>(resolverMap);
}
@Override
public Map<PluginKey, ItemResolver> getResolvers(Plugin plugin) {
Map<PluginKey, ItemResolver> matchingResolvers = new HashMap<>();
for (PluginKey key : resolverMap.keySet()) {
if (key.getPlugin().equals(plugin)) {
matchingResolvers.put(key, resolverMap.get(key));
}
}
return matchingResolvers;
}
@Override
public ItemResolver getResolver(Plugin plugin, String name) {
return resolverMap.get(PluginKey.fromKey(plugin, name));
}
@Override
public ItemStack get(String id) throws Exception {
return get(id, true);
}
ItemStack tryResolverDeserialize(String id) {
for (PluginKey key : resolverMap.keySet()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info(String.format("Trying to deserialize item '%s' with resolver '%s'...", id, key));
}
Function<String, ItemStack> resolver = resolverMap.get(key);
ItemStack stack = resolver.apply(id);
if (stack != null) {
return stack;
}
}
return null;
}
String tryResolverSerialize(ItemStack stack) {
for (PluginKey key : resolverMap.keySet()) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info(String.format("Trying to serialize '%s' with resolver '%s'...", stack.toString(), key));
}
ItemResolver resolver = resolverMap.get(key);
String serialized = resolver.serialize(stack);
if (serialized != null) {
return serialized;
}
}
return null;
}
Collection<String> getResolverNames() {
List<String> result = new ArrayList<>();
for (ItemResolver resolver : resolverMap.values()) {
Collection<String> resolverNames = resolver.getNames();
if (resolverNames != null) {
result.addAll(resolverNames);
}
}
return result;
}
@Override
public List<ItemStack> getMatching(User user, String[] args) throws Exception {
List<ItemStack> is = new ArrayList<>();
if (args.length < 1) {
is.add(user.getItemInHand().clone());
} else if (args[0].equalsIgnoreCase("hand")) {
is.add(user.getItemInHand().clone());
} else if (args[0].equalsIgnoreCase("inventory") || args[0].equalsIgnoreCase("invent") || args[0].equalsIgnoreCase("all")) {
for (ItemStack stack : user.getBase().getInventory().getContents()) {
if (stack == null || stack.getType() == Material.AIR) {
continue;
}
is.add(stack.clone());
}
} else if (args[0].equalsIgnoreCase("blocks")) {
for (ItemStack stack : user.getBase().getInventory().getContents()) {
if (stack == null || stack.getType() == Material.AIR) {
continue;
}
is.add(stack.clone());
}
} else {
is.add(get(args[0]));
}
if (is.isEmpty() || is.get(0).getType() == Material.AIR) {
throw new Exception(tl("itemSellAir"));
}
return is;
}
@Override
public String serialize(ItemStack is) {
return serialize(is, true);
}
@Override
public String serialize(ItemStack is, boolean useResolvers) {
if (useResolvers) {
String serialized = tryResolverSerialize(is);
if (serialized != null) {
return serialized;
}
}
String mat = name(is);
if (VersionUtil.getServerBukkitVersion().isLowerThanOrEqualTo(VersionUtil.v1_12_2_R01) && is.getData().getData() != 0) {
mat = mat + ":" + is.getData().getData();
}
int quantity = is.getAmount();
StringBuilder sb = new StringBuilder(); // Add space AFTER you add something. We can trim at end.
sb.append(mat).append(" ").append(quantity).append(" ");
// ItemMeta applies to anything.
if (is.hasItemMeta()) {
ItemMeta meta = is.getItemMeta();
if (meta.hasDisplayName()) {
sb.append("name:").append(meta.getDisplayName().replaceAll(" ", "_")).append(" ");
}
if (meta.hasLore()) {
sb.append("lore:");
boolean first = true;
for (String s : meta.getLore()) {
// Add | before the line if it's not the first one. Easy but weird way
// to do this since we need each line separated by |
if (!first) {
sb.append("|");
}
first = false;
sb.append(s.replaceAll(" ", "_"));
}
sb.append(" ");
}
if (meta.hasEnchants()) {
for (Enchantment e : meta.getEnchants().keySet()) {
sb.append(e.getName().toLowerCase()).append(":").append(meta.getEnchantLevel(e)).append(" ");
}
}
Set<ItemFlag> flags = meta.getItemFlags();
if (flags != null && !flags.isEmpty()) {
sb.append("itemflags:");
boolean first = true;
for (ItemFlag flag : flags) {
if (!first) {
sb.append(",");
}
sb.append(flag.name());
first = false;
}
}
}
final Material material = is.getType();
switch (material) {
case WRITTEN_BOOK:
// Everything from http://wiki.ess3.net/wiki/Item_Meta#Books in that order.
// Interesting as I didn't see a way to do pages or chapters.
BookMeta bookMeta = (BookMeta) is.getItemMeta();
if (bookMeta.hasTitle()) {
sb.append("title:").append(bookMeta.getTitle()).append(" ");
}
if (bookMeta.hasAuthor()) {
sb.append("author:").append(bookMeta.getAuthor()).append(" ");
}
// Only other thing it could have is lore but that's done up there ^^^
break;
case ENCHANTED_BOOK:
EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) is.getItemMeta();
for (Enchantment e : enchantmentStorageMeta.getStoredEnchants().keySet()) {
sb.append(e.getName().toLowerCase()).append(":").append(enchantmentStorageMeta.getStoredEnchantLevel(e)).append(" ");
}
break;
}
if (MaterialUtil.isFirework(material)) {
// Everything from http://wiki.ess3.net/wiki/Item_Meta#Fireworks in that order.
FireworkMeta fireworkMeta = (FireworkMeta) is.getItemMeta();
if (fireworkMeta.hasEffects()) {
for (FireworkEffect effect : fireworkMeta.getEffects()) {
if (effect.getColors() != null && !effect.getColors().isEmpty()) {
sb.append("color:");
boolean first = true;
for (Color c : effect.getColors()) {
if (!first) {
sb.append(","); // same thing as above.
}
sb.append(c.toString());
first = false;
}
sb.append(" ");
}
sb.append("shape:").append(effect.getType().name()).append(" ");
if (effect.getFadeColors() != null && !effect.getFadeColors().isEmpty()) {
sb.append("fade:");
boolean first = true;
for (Color c : effect.getFadeColors()) {
if (!first) {
sb.append(","); // same thing as above.
}
sb.append(c.toString());
first = false;
}
sb.append(" ");
}
}
sb.append("power:").append(fireworkMeta.getPower()).append(" ");
}
} else if (MaterialUtil.isPotion(material)) {
Potion potion = Potion.fromItemStack(is);
for (PotionEffect e : potion.getEffects()) {
// long but needs to be effect:speed power:2 duration:120 in that order.
sb.append("splash:").append(potion.isSplash()).append(" ").append("effect:").append(e.getType().getName().toLowerCase()).append(" ").append("power:").append(e.getAmplifier()).append(" ").append("duration:").append(e.getDuration() / 20).append(" ");
}
} else if (MaterialUtil.isPlayerHead(material, is.getData().getData())) {
// item stack with meta
SkullMeta skullMeta = (SkullMeta) is.getItemMeta();
if (skullMeta != null && skullMeta.hasOwner()) {
sb.append("player:").append(skullMeta.getOwner()).append(" ");
}
} else if (MaterialUtil.isBanner(material)) {
if (material.toString().contains("SHIELD")) {
// Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795
BlockStateMeta shieldMeta = (BlockStateMeta) is.getItemMeta();
Banner shieldBannerMeta = (Banner) shieldMeta.getBlockState();
DyeColor baseDyeColor = shieldBannerMeta.getBaseColor();
if (baseDyeColor != null) {
int basecolor = baseDyeColor.getColor().asRGB();
sb.append("basecolor:").append(basecolor).append(" ");
}
for (org.bukkit.block.banner.Pattern p : shieldBannerMeta.getPatterns()) {
String type = p.getPattern().getIdentifier();
int color = p.getColor().getColor().asRGB();
sb.append(type).append(",").append(color).append(" ");
}
} else {
BannerMeta bannerMeta = (BannerMeta) is.getItemMeta();
if (bannerMeta != null) {
DyeColor baseDyeColor = bannerMeta.getBaseColor();
if (baseDyeColor == null) {
baseDyeColor = MaterialUtil.getColorOf(material);
}
if (baseDyeColor != null) {
int basecolor = baseDyeColor
.getColor()
.asRGB();
sb.append("basecolor:").append(basecolor).append(" ");
}
for (org.bukkit.block.banner.Pattern p : bannerMeta.getPatterns()) {
String type = p.getPattern().getIdentifier();
int color = p.getColor().getColor().asRGB();
sb.append(type).append(",").append(color).append(" ");
}
}
}
} else if (MaterialUtil.isLeatherArmor(material)) {
LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) is.getItemMeta();
int rgb = leatherArmorMeta.getColor().asRGB();
sb.append("color:").append(rgb).append(" ");
}
return sb.toString().trim().replaceAll("§", "&");
}
@Override
public boolean isReady() {
return ready;
}
}
@@ -1,89 +0,0 @@
package com.earth2me.essentials.items;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.EssentialsConf;
import com.earth2me.essentials.IConf;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import net.ess3.api.IItemDb;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
public class CustomItemResolver implements IItemDb.ItemResolver, IConf {
private final EssentialsConf config;
private final Essentials ess;
private final HashMap<String, String> map = new HashMap<>();
public CustomItemResolver(Essentials ess) {
config = new EssentialsConf(new File(ess.getDataFolder(), "custom_items.yml"));
this.ess = ess;
config.setTemplateName("/custom_items.yml");
}
@Override
public ItemStack apply(String item) {
if (map.containsKey(item)) {
try {
return ess.getItemDb().get(map.get(item));
} catch (Exception ignored) {}
}
return null;
}
@Override
public Collection<String> getNames() {
return map.keySet();
}
@Override
public void reloadConfig() {
map.clear();
config.load();
ConfigurationSection section = config.getConfigurationSection("aliases");
if (section == null || section.getKeys(false).isEmpty()) {
ess.getLogger().warning("No aliases found in custom_items.yml.");
return;
}
for (String alias : section.getKeys(false)) {
if (!section.isString(alias)) continue;
String target = section.getString(alias);
if (target != null && !section.contains(target) && existsInItemDb(target)) {
map.put(alias, target);
}
}
}
public void setAlias(String alias, String target) {
if (map.containsKey(alias) && map.get(alias).equalsIgnoreCase(target)) {
return;
}
map.put(alias, target);
save();
}
public void removeAlias(String alias) {
if (map.remove(alias) != null) {
save();
}
}
private void save() {
config.setProperty("aliases", map);
config.save();
}
private boolean existsInItemDb(String item) {
try {
ess.getItemDb().get(item);
return true;
} catch (Exception e) {
return false;
}
}
}
@@ -1,720 +0,0 @@
package com.earth2me.essentials.metrics;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class Metrics {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
final String defaultPackage = new String(
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
// The plugin id
private final int pluginId;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
* @param pluginId The id of the plugin.
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(Plugin plugin, int pluginId) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
this.pluginId = pluginId;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
"To honor their work, you should not disable it.\n" +
"This has nearly no effect on the server performance!\n" +
"Check out https://bStats.org/ to learn more :)"
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) { }
}
// Load the data
enabled = config.getBoolean("enabled", true);
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) { }
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
if (chart == null) {
throw new IllegalArgumentException("Chart cannot be null!");
}
charts.add(chart);
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName().replace("Essentials", "EssentialsX");
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("id", pluginId); // Append the id of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
JsonArray customCharts = new JsonArray();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
JsonObject chart = customChart.getRequestJsonObject();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.add("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
}
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
}
} catch (NoSuchFieldException ignored) { }
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(() -> {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data);
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
private JsonObject getRequestJsonObject() {
JsonObject chart = new JsonObject();
chart.addProperty("chartId", chartId);
try {
JsonObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
chart.add("data", data);
} catch (Throwable t) {
if (logFailedRequests) {
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract JsonObject getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom advanced pie.
*/
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom drilldown pie.
*/
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObject value = new JsonObject();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
value.addProperty(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
values.add(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom single line chart.
*/
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom multi line chart.
*/
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom simple bar chart.
*/
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
JsonArray categoryValues = new JsonArray();
categoryValues.add(new JsonPrimitive(entry.getValue()));
values.add(entry.getKey(), categoryValues);
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom advanced bar chart.
*/
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
continue; // Skip this invalid
}
allSkipped = false;
JsonArray categoryValues = new JsonArray();
for (int categoryValue : entry.getValue()) {
categoryValues.add(new JsonPrimitive(categoryValue));
}
values.add(entry.getKey(), categoryValues);
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
}
@@ -1,142 +0,0 @@
package com.earth2me.essentials.metrics;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.register.payment.Methods;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MetricsWrapper {
private final Essentials ess;
private final Metrics metrics;
private final Map<String, Boolean> commands = new HashMap<>();
private final Plugin plugin;
private static boolean hasWarned = false;
private static final List<String> KNOWN_FORCED_METRICS = ImmutableList.of("ChatControl");
public MetricsWrapper(Plugin plugin, int pluginId, boolean includeCommands) {
this.plugin = plugin;
this.ess = (Essentials) Bukkit.getPluginManager().getPlugin("Essentials");
this.metrics = new Metrics(plugin, pluginId);
if (metrics.isEnabled()) {
plugin.getLogger().info("Starting Metrics. Opt-out using the global bStats config.");
} else {
plugin.getLogger().info("Metrics disabled per bStats config.");
}
checkForcedMetrics();
addPermsChart();
addEconomyChart();
// bStats' backend currently doesn't support multi-line charts or advanced bar charts
// These are included for when bStats is ready to accept this data
addVersionHistoryChart();
if (includeCommands) addCommandsChart();
}
public void markCommand(String command, boolean state) {
commands.put(command, state);
}
public void addCustomChart(Metrics.CustomChart chart) {
metrics.addCustomChart(chart);
}
private void addPermsChart() {
metrics.addCustomChart(new Metrics.DrilldownPie("permsPlugin", () -> {
Map<String, Map<String, Integer>> result = new HashMap<>();
String handler = ess.getPermissionsHandler().getName();
Map<String, Integer> backend = new HashMap<>();
backend.put(ess.getPermissionsHandler().getBackendName(), 1);
result.put(handler, backend);
return result;
}));
}
private void addEconomyChart() {
metrics.addCustomChart(new Metrics.DrilldownPie("econPlugin", () -> {
Map<String, Map<String, Integer>> result = new HashMap<>();
Map<String, Integer> backend = new HashMap<>();
if (Methods.hasMethod()) {
backend.put(Methods.getMethod().getBackend(), 1);
result.put(Methods.getMethod().getName(), backend);
} else {
backend.put("Essentials", 1);
result.put("Essentials", backend);
}
return result;
}));
}
private void addVersionHistoryChart() {
metrics.addCustomChart(new Metrics.MultiLineChart("versionHistory", () -> {
HashMap<String, Integer> result = new HashMap<>();
result.put(plugin.getDescription().getVersion(), 1);
return result;
}));
}
private void addCommandsChart() {
for (String command : plugin.getDescription().getCommands().keySet()) {
markCommand(command, false);
}
metrics.addCustomChart(new Metrics.AdvancedBarChart("commands", () -> {
Map<String, int[]> result = new HashMap<>();
for (Map.Entry<String, Boolean> entry : commands.entrySet()) {
if (entry.getValue()) {
result.put(entry.getKey(), new int[]{1,0});
} else {
result.put(entry.getKey(), new int[]{0,1});
}
}
return result;
}));
}
private void checkForcedMetrics() {
if (hasWarned) return;
hasWarned = true;
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Identifies bStats classes
if (KNOWN_FORCED_METRICS.contains(JavaPlugin.getProvidingPlugin(service).getName())) {
warnForcedMetrics(service);
} else {
try {
service.getDeclaredField("pluginId"); // Only present in recent bStats classes, which should also have the enabled field unless modified
} catch (NoSuchFieldException e) {
// Old bStats class found so "enabled" field detection is unreliable.
break;
}
try {
service.getDeclaredField("enabled"); // In modified forced metrics classes, this will fail
} catch (NoSuchFieldException e) {
warnForcedMetrics(service);
}
}
} catch (NoSuchFieldException ignored) {}
}
});
}
private void warnForcedMetrics(Class<?> service) {
Plugin servicePlugin = JavaPlugin.getProvidingPlugin(service);
plugin.getLogger().severe("WARNING: Potential forced metrics collection by plugin '" + servicePlugin.getName() + "' v" + servicePlugin.getDescription().getVersion());
plugin.getLogger().severe("Your server is running a plugin that may not respect bStats' opt-out settings.");
plugin.getLogger().severe("This may cause data to be uploaded to bStats.org for plugins that use bStats, even if you've opted out in the bStats config.");
plugin.getLogger().severe("Please report this to bStats and to the authors of '" + servicePlugin.getName() + "'. (Offending class: " + service.getName() + ")");
}
}
@@ -1,229 +0,0 @@
package com.earth2me.essentials.register.payment;
import org.bukkit.plugin.Plugin;
/**
* Interface to be implemented by a payment method.
* Copyright (C) 2011
* AOL license <http://aol.nexua.org>
*
* @author Nijikokun <nijikokun@shortmail.com> (@nijikokun)
*/
public interface Method {
/**
* Encodes the Plugin into an Object disguised as the Plugin. If you want the original Plugin Class you must cast it
* to the correct Plugin, to do so you have to verify the name and or version then cast.
* <p/>
* <pre>
* if(method.getName().equalsIgnoreCase("iConomy"))
* iConomy plugin = ((iConomy)method.getPlugin());</pre>
*
* @return <code>Object</code>
*
* @see #getName()
* @see #getVersion()
*/
Plugin getPlugin();
/**
* Returns the actual name of this method.
*
* @return <code>String</code> Plugin name.
*/
String getName();
/**
* Returns the backend plugin of this economy method, if applicable.
*
* @return <code>String</code> Plugin name.
*/
String getBackend();
/**
* Returns the reported name of this method.
*
* @return <code>String</code> Plugin name.
*/
String getLongName();
/**
* Returns the actual version of this method.
*
* @return <code>String</code> Plugin version.
*/
String getVersion();
/**
* Returns the amount of decimal places that get stored NOTE: it will return -1 if there is no rounding
*
* @return <code>int</code> for each decimal place
*/
int fractionalDigits();
/**
* Formats amounts into this payment methods style of currency display.
*
* @param amount Double
*
* @return <code>String</code> - Formatted Currency Display.
*/
String format(double amount);
/**
* Allows the verification of bank API existence in this payment method.
*
* @return <code>boolean</code>
*/
boolean hasBanks();
/**
* Determines the existence of a bank via name.
*
* @param bank Bank name
*
* @return <code>boolean</code>
*
* @see #hasBanks
*/
boolean hasBank(String bank);
/**
* Determines the existence of an account via name.
*
* @param name Account name
*
* @return <code>boolean</code>
*/
boolean hasAccount(String name);
/**
* Check to see if an account <code>name</code> is tied to a <code>bank</code>.
*
* @param bank Bank name
* @param name Account name
*
* @return <code>boolean</code>
*/
boolean hasBankAccount(String bank, String name);
/**
* Forces an account creation
*
* @param name Account name
*
* @return <code>boolean</code>
*/
boolean createAccount(String name);
/**
* Forces an account creation
*
* @param name Account name
* @param balance Initial account balance
*
* @return <code>boolean</code>
*/
boolean createAccount(String name, Double balance);
/**
* Returns a <code>MethodAccount</code> class for an account <code>name</code>.
*
* @param name Account name
*
* @return <code>MethodAccount</code> <em>or</em> <code>Null</code>
*/
MethodAccount getAccount(String name);
/**
* Returns a <code>MethodBankAccount</code> class for an account <code>name</code>.
*
* @param bank Bank name
* @param name Account name
*
* @return <code>MethodBankAccount</code> <em>or</em> <code>Null</code>
*/
MethodBankAccount getBankAccount(String bank, String name);
/**
* Checks to verify the compatibility between this Method and a plugin. Internal usage only, for the most part.
*
* @param plugin Plugin
*
* @return <code>boolean</code>
*/
boolean isCompatible(Plugin plugin);
/**
* Set Plugin data.
*
* @param plugin Plugin
*/
void setPlugin(Plugin plugin);
/**
* Contains Calculator and Balance functions for Accounts.
*/
interface MethodAccount {
double balance();
boolean set(double amount);
boolean add(double amount);
boolean subtract(double amount);
boolean multiply(double amount);
boolean divide(double amount);
boolean hasEnough(double amount);
boolean hasOver(double amount);
boolean hasUnder(double amount);
boolean isNegative();
boolean remove();
@Override
String toString();
}
/**
* Contains Calculator and Balance functions for Bank Accounts.
*/
interface MethodBankAccount {
double balance();
String getBankName();
int getBankId();
boolean set(double amount);
boolean add(double amount);
boolean subtract(double amount);
boolean multiply(double amount);
boolean divide(double amount);
boolean hasEnough(double amount);
boolean hasOver(double amount);
boolean hasUnder(double amount);
boolean isNegative();
boolean remove();
@Override
String toString();
}
}
@@ -1,245 +0,0 @@
package com.earth2me.essentials.register.payment;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import java.util.HashSet;
import java.util.Set;
/**
* The <code>Methods</code> initializes Methods that utilize the Method interface based on a "first come, first served"
* basis.
* <p/>
* Allowing you to check whether a payment method exists or not.
* <p/>
* Methods also allows you to set a preferred method of payment before it captures payment plugins in the initialization
* process.
* <p/>
* in
* <code>bukkit.yml</code>: <blockquote><pre>
* economy:
* preferred: "iConomy"
* </pre></blockquote>
*
* @author Nijikokun <nijikokun@shortmail.com> (@nijikokun) @copyright: Copyright (C) 2011 @license: AOL license
* <http://aol.nexua.org>
*/
public class Methods {
private static String version = null;
private static boolean self = false;
private static Method Method = null;
private static String preferred = "";
private static final Set<Method> Methods = new HashSet<>();
private static final Set<String> Dependencies = new HashSet<>();
private static final Set<Method> Attachables = new HashSet<>();
static {
_init();
}
/**
* Implement all methods along with their respective name & class.
*/
private static void _init() {
addMethod("Vault", new com.earth2me.essentials.register.payment.methods.VaultEco());
}
/**
* Used by the plugin to setup version
*
* @param v version
*/
public static void setVersion(String v) {
version = v;
}
/**
* Use to reset methods during disable
*/
public static void reset() {
version = null;
self = false;
Method = null;
preferred = "";
Attachables.clear();
}
/**
* Use to get version of Register plugin
*
* @return version
*/
public static String getVersion() {
return version;
}
/**
* Returns an array of payment method names that have been loaded through the <code>_init</code> method.
*
* @return <code>Set<String></code> - Array of payment methods that are loaded.
*
* @see #setMethod(PluginManager)
*/
public static Set<String> getDependencies() {
return Dependencies;
}
/**
* Interprets Plugin class data to verify whether it is compatible with an existing payment method to use for
* payments and other various economic activity.
*
* @param plugin Plugin data from bukkit, Internal Class file.
*
* @return Method <em>or</em> Null
*/
public static Method createMethod(Plugin plugin) {
for (Method method : Methods) {
if (method.isCompatible(plugin)) {
method.setPlugin(plugin);
return method;
}
}
return null;
}
private static void addMethod(String name, Method method) {
Dependencies.add(name);
Methods.add(method);
}
/**
* Verifies if Register has set a payment method for usage yet.
*
* @return <code>boolean</code>
*
* @see #setMethod(PluginManager)
* @see #checkDisabled(Plugin)
*/
public static boolean hasMethod() {
return (Method != null);
}
/**
* Checks Plugin Class against a multitude of checks to verify it's usability as a payment method.
*
* @param manager the plugin manager for the server
*
* @return <code>boolean</code> True on success, False on failure.
*/
public static boolean setMethod(PluginManager manager) {
if (hasMethod()) {
return true;
}
if (self) {
self = false;
return false;
}
int count = 0;
boolean match = false;
Plugin plugin;
for (String name : getDependencies()) {
if (hasMethod()) {
break;
}
plugin = manager.getPlugin(name);
if (plugin == null || !plugin.isEnabled()) {
continue;
}
Method current = createMethod(plugin);
if (current == null) {
continue;
}
if (preferred.isEmpty()) {
Method = current;
} else {
Attachables.add(current);
}
}
if (!preferred.isEmpty()) {
do {
if (hasMethod()) {
match = true;
} else {
for (Method attached : Attachables) {
if (attached == null) {
continue;
}
if (hasMethod()) {
match = true;
break;
}
if (preferred.isEmpty()) {
Method = attached;
}
if (count == 0) {
if (preferred.equalsIgnoreCase(attached.getName())) {
Method = attached;
} else {
Method = attached;
}
}
}
count++;
}
} while (!match);
}
return hasMethod();
}
/**
* Sets the preferred economy
*
* @return <code>boolean</code>
*/
public static boolean setPreferred(String check) {
if (getDependencies().contains(check)) {
preferred = check;
return true;
}
return false;
}
/**
* Grab the existing and initialized (hopefully) Method Class.
*
* @return <code>Method</code> <em>or</em> <code>Null</code>
*/
public static Method getMethod() {
return Method;
}
/**
* Verify is a plugin is disabled, only does this if we there is an existing payment method initialized in
* Register.
*
* @param method Plugin data from bukkit, Internal Class file.
*
* @return <code>boolean</code>
*/
public static boolean checkDisabled(Plugin method) {
if (!hasMethod()) {
return true;
}
if (Method.isCompatible(method)) {
Method = null;
}
return (Method == null);
}
}
@@ -1,283 +0,0 @@
package com.earth2me.essentials.register.payment.methods;
import com.earth2me.essentials.register.payment.Method;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
public class VaultEco implements Method {
private Plugin vault;
private Economy economy;
@Override
public Plugin getPlugin() {
return this.vault;
}
@Override
public boolean createAccount(String name, Double amount) {
if (hasAccount(name)) {
return false;
}
return false;
}
@Override
public String getName() {
return this.vault.getDescription().getName();
}
@Override
public String getBackend() {
return economy == null ? "NoEco" : economy.getName();
}
@Override
public String getLongName() {
return getName().concat(" - Economy: ").concat(getBackend());
}
@Override
public String getVersion() {
return this.vault.getDescription().getVersion();
}
@Override
public int fractionalDigits() {
return 0;
}
@Override
public String format(double amount) {
return this.economy.format(amount);
}
@Override
public boolean hasBanks() {
return this.economy.hasBankSupport();
}
@Override
public boolean hasBank(String bank) {
return this.economy.getBanks().contains(bank);
}
@Override
public boolean hasAccount(String name) {
return this.economy.hasAccount(name);
}
@Override
public boolean hasBankAccount(String bank, String name) {
return this.economy.isBankOwner(bank, name).transactionSuccess() || this.economy.isBankMember(bank, name).transactionSuccess();
}
@Override
public boolean createAccount(String name) {
return this.economy.createBank(name, "").transactionSuccess();
}
public boolean createAccount(String name, double balance) {
if (!this.economy.createBank(name, "").transactionSuccess()) {
return false;
}
return this.economy.bankDeposit(name, balance).transactionSuccess();
}
@Override
public MethodAccount getAccount(String name) {
if (!hasAccount(name)) {
return null;
}
return new VaultAccount(name, this.economy);
}
@Override
public MethodBankAccount getBankAccount(String bank, String name) {
if (!hasBankAccount(bank, name)) {
return null;
}
return new VaultBankAccount(bank, economy);
}
@Override
public boolean isCompatible(Plugin plugin) {
try {
RegisteredServiceProvider<Economy> ecoPlugin = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
return plugin.getName().equals("Vault") && ecoPlugin != null && !ecoPlugin.getProvider().getName().equals("Essentials Economy");
} catch (LinkageError | Exception e) {
return false;
}
}
@Override
public void setPlugin(Plugin plugin) {
this.vault = plugin;
RegisteredServiceProvider<Economy> economyProvider = this.vault.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (economyProvider != null) {
this.economy = economyProvider.getProvider();
}
}
public static class VaultAccount implements MethodAccount {
private final String name;
private final Economy economy;
VaultAccount(String name, Economy economy) {
this.name = name;
this.economy = economy;
}
@Override
public double balance() {
return this.economy.getBalance(this.name);
}
@Override
public boolean set(double amount) {
if (!this.economy.withdrawPlayer(this.name, this.balance()).transactionSuccess()) {
return false;
}
if (amount == 0) {
return true;
}
return this.economy.depositPlayer(this.name, amount).transactionSuccess();
}
@Override
public boolean add(double amount) {
return this.economy.depositPlayer(this.name, amount).transactionSuccess();
}
@Override
public boolean subtract(double amount) {
return this.economy.withdrawPlayer(this.name, amount).transactionSuccess();
}
@Override
public boolean multiply(double amount) {
double balance = this.balance();
return this.set(balance * amount);
}
@Override
public boolean divide(double amount) {
double balance = this.balance();
return this.set(balance / amount);
}
@Override
public boolean hasEnough(double amount) {
return (this.balance() >= amount);
}
@Override
public boolean hasOver(double amount) {
return (this.balance() > amount);
}
@Override
public boolean hasUnder(double amount) {
return (this.balance() < amount);
}
@Override
public boolean isNegative() {
return (this.balance() < 0);
}
@Override
public boolean remove() {
return this.set(0.0);
}
}
public static class VaultBankAccount implements MethodBankAccount {
private final String bank;
private final Economy economy;
public VaultBankAccount(String bank, Economy economy) {
this.bank = bank;
this.economy = economy;
}
@Override
public String getBankName() {
return this.bank;
}
@Override
public int getBankId() {
return -1;
}
@Override
public double balance() {
return this.economy.bankBalance(this.bank).balance;
}
@Override
public boolean set(double amount) {
if (!this.economy.bankWithdraw(this.bank, this.balance()).transactionSuccess()) {
return false;
}
if (amount == 0) {
return true;
}
return this.economy.bankDeposit(this.bank, amount).transactionSuccess();
}
@Override
public boolean add(double amount) {
return this.economy.bankDeposit(this.bank, amount).transactionSuccess();
}
@Override
public boolean subtract(double amount) {
return this.economy.bankWithdraw(this.bank, amount).transactionSuccess();
}
@Override
public boolean multiply(double amount) {
double balance = this.balance();
return this.set(balance * amount);
}
@Override
public boolean divide(double amount) {
double balance = this.balance();
return this.set(balance / amount);
}
@Override
public boolean hasEnough(double amount) {
return (this.balance() >= amount);
}
@Override
public boolean hasOver(double amount) {
return (this.balance() > amount);
}
@Override
public boolean hasUnder(double amount) {
return (this.balance() < amount);
}
@Override
public boolean isNegative() {
return (this.balance() < 0);
}
@Override
public boolean remove() {
return this.set(0.0);
}
}
}
@@ -1,40 +0,0 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.MapValueType;
import com.earth2me.essentials.storage.StorageObject;
import org.bukkit.Location;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Jails implements StorageObject {
@MapValueType(Location.class)
private Map<String, Location> jails = new HashMap<>();
public Map<String, Location> getJails() {
return jails;
}
public void setJails(Map<String, Location> jails) {
this.jails = jails;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Jails jails1 = (Jails) o;
return Objects.equals(jails, jails1.jails);
}
@Override
public int hashCode() {
return Objects.hash(jails);
}
}
@@ -1,39 +0,0 @@
package com.earth2me.essentials.settings;
import com.earth2me.essentials.storage.MapValueType;
import com.earth2me.essentials.storage.StorageObject;
import org.bukkit.Location;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Spawns implements StorageObject {
@MapValueType(Location.class)
private Map<String, Location> spawns = new HashMap<>();
public Map<String, Location> getSpawns() {
return spawns;
}
public void setSpawns(Map<String, Location> spawns) {
this.spawns = spawns;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Spawns spawns1 = (Spawns) o;
return Objects.equals(spawns, spawns1.spawns);
}
@Override
public int hashCode() {
return Objects.hash(spawns);
}
}
@@ -1,29 +0,0 @@
package com.earth2me.essentials.signs;
import com.earth2me.essentials.User;
import net.ess3.api.IEssentials;
import java.util.List;
import static com.earth2me.essentials.I18n.tl;
public class SignMail extends EssentialsSign {
public SignMail() {
super("Mail");
}
@Override
protected boolean onSignInteract(final ISign sign, final User player, final String username, final IEssentials ess) throws SignException {
final List<String> mail = player.getMails();
if (mail.isEmpty()) {
player.sendMessage(tl("noNewMail"));
return false;
}
for (String s : mail) {
player.sendMessage(s);
}
player.sendMessage(tl("markMailAsRead"));
return true;
}
}
@@ -1,57 +0,0 @@
package com.earth2me.essentials.storage;
import net.ess3.api.IEssentials;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
public abstract class AbstractDelayedYamlFileReader<T extends StorageObject> implements Runnable {
private final transient File file;
private final transient Class<T> clazz;
protected final transient IEssentials plugin;
public AbstractDelayedYamlFileReader(final IEssentials ess, final File file, final Class<T> clazz) {
this.file = file;
this.clazz = clazz;
this.plugin = ess;
ess.runTaskAsynchronously(this);
}
public abstract void onStart();
@Override
public void run() {
onStart();
try {
final FileReader reader = new FileReader(file);
try {
final T object = new YamlStorageReader(reader, plugin).load(clazz);
onSuccess(object);
} finally {
try {
reader.close();
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "File can't be closed: " + file.toString(), ex);
}
}
} catch (FileNotFoundException ex) {
onException();
if (plugin.getSettings() == null || plugin.getSettings().isDebug()) {
Bukkit.getLogger().log(Level.INFO, "File not found: " + file.toString());
}
} catch (ObjectLoadException ex) {
onException();
Bukkit.getLogger().log(Level.SEVERE, "File broken: " + file.toString(), ex.getCause());
}
}
public abstract void onSuccess(T object);
public abstract void onException();
}
@@ -1,45 +0,0 @@
package com.earth2me.essentials.storage;
import net.ess3.api.IEssentials;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.logging.Level;
public abstract class AbstractDelayedYamlFileWriter implements Runnable {
private final transient File file;
public AbstractDelayedYamlFileWriter(IEssentials ess, File file) {
this.file = file;
ess.runTaskAsynchronously(this);
}
public abstract StorageObject getObject();
@Override
public void run() {
PrintWriter pw = null;
try {
final StorageObject object = getObject();
final File folder = file.getParentFile();
if (!folder.exists()) {
folder.mkdirs();
}
pw = new PrintWriter(file);
new YamlStorageWriter(pw).save(object);
} catch (FileNotFoundException ex) {
Bukkit.getLogger().log(Level.SEVERE, file.toString(), ex);
} finally {
onFinish();
if (pw != null) {
pw.close();
}
}
}
public abstract void onFinish();
}
@@ -1,131 +0,0 @@
package com.earth2me.essentials.storage;
import com.earth2me.essentials.IConf;
import net.ess3.api.IEssentials;
import net.ess3.api.IReload;
import org.bukkit.Bukkit;
import java.io.File;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
public abstract class AsyncStorageObjectHolder<T extends StorageObject> implements IConf, IStorageObjectHolder<T>, IReload {
private transient T data;
private final transient ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final transient Class<T> clazz;
protected final transient IEssentials ess;
public AsyncStorageObjectHolder(final IEssentials ess, final Class<T> clazz) {
this.ess = ess;
this.clazz = clazz;
try {
this.data = clazz.newInstance();
} catch (IllegalAccessException | InstantiationException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
@Override
public T getData() {
return data;
}
@Override
public void acquireReadLock() {
rwl.readLock().lock();
}
@Override
public void acquireWriteLock() {
while (rwl.getReadHoldCount() > 0) {
rwl.readLock().unlock();
}
rwl.writeLock().lock();
rwl.readLock().lock();
}
@Override
public void close() {
unlock();
}
@Override
public void unlock() {
if (rwl.isWriteLockedByCurrentThread()) {
rwl.writeLock().unlock();
new StorageObjectDataWriter();
}
while (rwl.getReadHoldCount() > 0) {
rwl.readLock().unlock();
}
}
@Override
public void reloadConfig() {
new StorageObjectDataReader();
}
@Override
public void onReload() {
new StorageObjectDataReader();
}
public abstract void finishRead();
public abstract void finishWrite();
public abstract File getStorageFile();
private class StorageObjectDataWriter extends AbstractDelayedYamlFileWriter {
StorageObjectDataWriter() {
super(ess, getStorageFile());
}
@Override
public StorageObject getObject() {
acquireReadLock();
return getData();
}
@Override
public void onFinish() {
unlock();
finishWrite();
}
}
private class StorageObjectDataReader extends AbstractDelayedYamlFileReader<T> {
StorageObjectDataReader() {
super(ess, getStorageFile(), clazz);
}
@Override
public void onStart() {
rwl.writeLock().lock();
}
@Override
public void onSuccess(final T object) {
if (object != null) {
data = object;
}
rwl.writeLock().unlock();
finishRead();
}
@Override
public void onException() {
if (data == null) {
try {
data = clazz.newInstance();
} catch (IllegalAccessException | InstantiationException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
rwl.writeLock().unlock();
}
}
}
@@ -1,212 +0,0 @@
package com.earth2me.essentials.storage;
import com.earth2me.essentials.Enchantments;
import com.earth2me.essentials.utils.NumberUtil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.plugin.Plugin;
import org.yaml.snakeyaml.constructor.BaseConstructor;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.nodes.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BukkitConstructor extends CustomClassLoaderConstructor {
public BukkitConstructor(final Class<?> clazz, final Plugin plugin) {
super(clazz, plugin.getClass().getClassLoader());
yamlClassConstructors.put(NodeId.scalar, new ConstructBukkitScalar());
yamlClassConstructors.put(NodeId.mapping, new ConstructBukkitMapping());
PropertyUtils propertyUtils = getPropertyUtils();
propertyUtils.setSkipMissingProperties(true);
setPropertyUtils(propertyUtils);
}
private Method constructScalarMethod = null;
protected String constructScalarRefl(ScalarNode scalarNode) {
try {
if (constructScalarMethod == null) {
constructScalarMethod = BaseConstructor.class.getDeclaredMethod("constructScalar", ScalarNode.class);
}
return (String) constructScalarMethod.invoke(this, scalarNode);
} catch (NoSuchMethodException
| SecurityException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private class ConstructBukkitScalar extends ConstructScalar {
@Override
public Object construct(final Node node) {
if (node.getType().equals(Material.class)) {
final String val = constructScalarRefl((ScalarNode) node);
return Material.matchMaterial(val);
}
if (node.getType().equals(MaterialData.class)) {
final String val = constructScalarRefl((ScalarNode) node);
if (val.isEmpty()) {
return null;
}
final String[] split = val.split("[:+',;.]", 2);
if (split.length == 0) {
return null;
}
Material mat = Material.matchMaterial(split[0]);
if (mat == null) {
return null;
}
byte data = 0;
if (split.length == 2 && NumberUtil.isInt(split[1])) {
data = Byte.parseByte(split[1]);
}
return new MaterialData(mat, data);
}
if (node.getType().equals(ItemStack.class)) {
final String val = constructScalarRefl((ScalarNode) node);
if (val.isEmpty()) {
return null;
}
final String[] split1 = val.split("\\W");
if (split1.length == 0) {
return null;
}
final String[] split2 = split1[0].split("[:+',;.]", 2);
if (split2.length == 0) {
return null;
}
Material mat = Material.matchMaterial(split2[0]);
if (mat == null) {
return null;
}
short data = 0;
if (split2.length == 2 && NumberUtil.isInt(split2[1])) {
data = Short.parseShort(split2[1]);
}
int size = mat.getMaxStackSize();
if (split1.length > 1 && NumberUtil.isInt(split1[1])) {
size = Integer.parseInt(split1[1]);
}
final ItemStack stack = new ItemStack(mat, size, data);
if (split1.length > 2) {
for (int i = 2; i < split1.length; i++) {
final String[] split3 = split1[0].split("[:+',;.]", 2);
if (split3.length < 1) {
continue;
}
Enchantment enchantment = Enchantments.getByName(split3[0]);
if (enchantment == null) {
continue;
}
int level = enchantment.getStartLevel();
if (split3.length == 2 && NumberUtil.isInt(split3[1])) {
level = Integer.parseInt(split3[1]);
}
if (level < enchantment.getStartLevel()) {
level = enchantment.getStartLevel();
}
if (level > enchantment.getMaxLevel()) {
level = enchantment.getMaxLevel();
}
stack.addUnsafeEnchantment(enchantment, level);
}
}
return stack;
}
if (node.getType().equals(EnchantmentLevel.class)) {
final String val = constructScalarRefl((ScalarNode) node);
if (val.isEmpty()) {
return null;
}
final String[] split = val.split("[:+',;.]", 2);
if (split.length == 0) {
return null;
}
Enchantment enchant = Enchantments.getByName(split[0]);
if (enchant == null) {
return null;
}
int level = enchant.getStartLevel();
if (split.length == 2 && NumberUtil.isInt(split[1])) {
level = Integer.parseInt(split[1]);
}
if (level < enchant.getStartLevel()) {
level = enchant.getStartLevel();
}
if (level > enchant.getMaxLevel()) {
level = enchant.getMaxLevel();
}
return new EnchantmentLevel(enchant, level);
}
return super.construct(node);
}
}
private class ConstructBukkitMapping extends ConstructMapping {
@Override
public Object construct(final Node node) {
if (node.getType().equals(Location.class)) {
//TODO: NPE checks
final MappingNode mnode = (MappingNode) node;
String worldName = "";
double x = 0, y = 0, z = 0;
float yaw = 0, pitch = 0;
if (mnode.getValue().size() < 4) {
return null;
}
for (NodeTuple nodeTuple : mnode.getValue()) {
final String key = constructScalarRefl((ScalarNode) nodeTuple.getKeyNode());
final ScalarNode snode = (ScalarNode) nodeTuple.getValueNode();
if (key.equalsIgnoreCase("world")) {
worldName = constructScalarRefl(snode);
}
if (key.equalsIgnoreCase("x")) {
x = Double.parseDouble(constructScalarRefl(snode));
}
if (key.equalsIgnoreCase("y")) {
y = Double.parseDouble(constructScalarRefl(snode));
}
if (key.equalsIgnoreCase("z")) {
z = Double.parseDouble(constructScalarRefl(snode));
}
if (key.equalsIgnoreCase("yaw")) {
yaw = Float.parseFloat(constructScalarRefl(snode));
}
if (key.equalsIgnoreCase("pitch")) {
pitch = Float.parseFloat(constructScalarRefl(snode));
}
}
if (worldName == null || worldName.isEmpty()) {
return null;
}
final World world = Bukkit.getWorld(worldName);
if (world == null) {
return null;
}
return new Location(world, x, y, z, yaw, pitch);
}
return super.construct(node);
}
}
}
@@ -1,8 +0,0 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.*;
@Target(ElementType.FIELD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Comment {
String[] value() default "";
}
@@ -1,67 +0,0 @@
package com.earth2me.essentials.storage;
import org.bukkit.enchantments.Enchantment;
import java.util.Map.Entry;
public class EnchantmentLevel implements Entry<Enchantment, Integer> {
private Enchantment enchantment;
private int level;
public EnchantmentLevel(final Enchantment enchantment, final int level) {
this.enchantment = enchantment;
this.level = level;
}
public Enchantment getEnchantment() {
return enchantment;
}
public void setEnchantment(final Enchantment enchantment) {
this.enchantment = enchantment;
}
public int getLevel() {
return level;
}
public void setLevel(final int level) {
this.level = level;
}
@Override
public Enchantment getKey() {
return enchantment;
}
@Override
public Integer getValue() {
return level;
}
@Override
public Integer setValue(final Integer v) {
int t = level;
level = v;
return t;
}
@Override
public int hashCode() {
return enchantment.hashCode() ^ level;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof Entry) {
final Entry entry = (Entry) obj;
if (entry.getKey() instanceof Enchantment && entry.getValue() instanceof Integer) {
final Enchantment enchant = (Enchantment) entry.getKey();
final Integer lvl = (Integer) entry.getValue();
return this.enchantment.equals(enchant) && this.level == lvl;
}
}
return false;
}
}
@@ -1,14 +0,0 @@
package com.earth2me.essentials.storage;
public interface IStorageObjectHolder<T extends StorageObject> {
T getData();
void acquireReadLock();
void acquireWriteLock();
void close();
void unlock();
}
@@ -1,6 +0,0 @@
package com.earth2me.essentials.storage;
public interface IStorageReader {
<T extends StorageObject> T load(final Class<? extends T> clazz) throws ObjectLoadException;
}
@@ -1,6 +0,0 @@
package com.earth2me.essentials.storage;
public interface IStorageWriter {
void save(final StorageObject object);
}
@@ -1,11 +0,0 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ListType {
Class value() default String.class;
}
@@ -1,11 +0,0 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MapKeyType {
Class value() default String.class;
}
@@ -1,11 +0,0 @@
package com.earth2me.essentials.storage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MapValueType {
Class value() default String.class;
}
@@ -1,8 +0,0 @@
package com.earth2me.essentials.storage;
public class ObjectLoadException extends Exception {
public ObjectLoadException(Throwable thrwbl) {
super(thrwbl);
}
}
@@ -1,5 +0,0 @@
package com.earth2me.essentials.storage;
public interface StorageObject {
}
@@ -1,94 +0,0 @@
package com.earth2me.essentials.storage;
import org.bukkit.plugin.Plugin;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import java.io.Reader;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class YamlStorageReader implements IStorageReader {
private transient static final Map<Class, Yaml> PREPARED_YAMLS = Collections.synchronizedMap(new HashMap<>());
private transient static final Map<Class, ReentrantLock> LOCKS = new HashMap<>();
private transient final Reader reader;
private transient final Plugin plugin;
public YamlStorageReader(final Reader reader, final Plugin plugin) {
this.reader = reader;
this.plugin = plugin;
}
@Override
public <T extends StorageObject> T load(final Class<? extends T> clazz) throws ObjectLoadException {
Yaml yaml = PREPARED_YAMLS.get(clazz);
if (yaml == null) {
yaml = new Yaml(prepareConstructor(clazz));
PREPARED_YAMLS.put(clazz, yaml);
}
ReentrantLock lock;
synchronized (LOCKS) {
lock = LOCKS.get(clazz);
if (lock == null) {
lock = new ReentrantLock();
}
}
lock.lock();
try {
T object = yaml.load(reader);
if (object == null) {
object = clazz.newInstance();
}
return object;
} catch (IllegalAccessException | InstantiationException ex) {
throw new ObjectLoadException(ex);
} finally {
lock.unlock();
}
}
private Constructor prepareConstructor(final Class<?> clazz) {
final Constructor constructor = new BukkitConstructor(clazz, plugin);
final Set<Class> classes = new HashSet<>();
prepareConstructor(constructor, classes, clazz);
return constructor;
}
private void prepareConstructor(final Constructor constructor, final Set<Class> classes, final Class clazz) {
classes.add(clazz);
final TypeDescription description = new TypeDescription(clazz);
for (Field field : clazz.getDeclaredFields()) {
prepareList(field, description, classes, constructor);
prepareMap(field, description, classes, constructor);
if (StorageObject.class.isAssignableFrom(field.getType()) && !classes.contains(field.getType())) {
prepareConstructor(constructor, classes, field.getType());
}
}
constructor.addTypeDescription(description);
}
private void prepareList(final Field field, final TypeDescription description, final Set<Class> classes, final Constructor constructor) {
final ListType listType = field.getAnnotation(ListType.class);
if (listType != null) {
description.putListPropertyType(field.getName(), listType.value());
if (StorageObject.class.isAssignableFrom(listType.value()) && !classes.contains(listType.value())) {
prepareConstructor(constructor, classes, listType.value());
}
}
}
private void prepareMap(final Field field, final TypeDescription description, final Set<Class> classes, final Constructor constructor) {
final MapValueType mapType = field.getAnnotation(MapValueType.class);
if (mapType != null) {
final MapKeyType mapKeyType = field.getAnnotation(MapKeyType.class);
description.putMapPropertyType(field.getName(), mapKeyType == null ? String.class : mapKeyType.value(), mapType.value());
if (StorageObject.class.isAssignableFrom(mapType.value()) && !classes.contains(mapType.value())) {
prepareConstructor(constructor, classes, mapType.value());
}
}
}
}
@@ -1,255 +0,0 @@
package com.earth2me.essentials.storage;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.yaml.snakeyaml.Yaml;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class YamlStorageWriter implements IStorageWriter {
private transient static final Pattern NON_WORD_PATTERN = Pattern.compile("\\W");
private transient static final Yaml YAML = new Yaml();
private transient final PrintWriter writer;
public YamlStorageWriter(final PrintWriter writer) {
this.writer = writer;
}
@Override
public void save(final StorageObject object) {
try {
writeToFile(object, 0, object.getClass());
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(YamlStorageWriter.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void writeToFile(final Object object, final int depth, final Class clazz) throws IllegalAccessException {
for (Field field : clazz.getDeclaredFields()) {
final int modifier = field.getModifiers();
if (Modifier.isPrivate(modifier) && !Modifier.isTransient(modifier) && !Modifier.isStatic(modifier)) {
field.setAccessible(true);
final Object data = field.get(object);
if (writeKey(field, depth, data)) {
continue;
}
if (data instanceof StorageObject) {
writer.println();
writeToFile(data, depth + 1, data.getClass());
} else if (data instanceof Map) {
writeMap((Map<Object, Object>) data, depth + 1);
} else if (data instanceof Collection) {
writeCollection((Collection<Object>) data, depth + 1);
} else if (data instanceof Location) {
writeLocation((Location) data, depth + 1);
} else {
writeScalar(data);
writer.println();
}
}
}
}
private boolean writeKey(final Field field, final int depth, final Object data) {
final boolean commentPresent = writeComment(field, depth);
if (data == null && !commentPresent) {
return true;
}
writeIndention(depth);
if (data == null && commentPresent) {
writer.print('#');
}
final String name = field.getName();
writer.print(name);
writer.print(": ");
if (data == null && commentPresent) {
writer.println();
writer.println();
return true;
}
return false;
}
private boolean writeComment(final Field field, final int depth) {
final boolean commentPresent = field.isAnnotationPresent(Comment.class);
if (commentPresent) {
final Comment comments = field.getAnnotation(Comment.class);
for (String comment : comments.value()) {
final String trimmed = comment.trim();
if (trimmed.isEmpty()) {
continue;
}
writeIndention(depth);
writer.print("# ");
writer.print(trimmed);
writer.println();
}
}
return commentPresent;
}
private void writeCollection(final Collection<Object> data, final int depth) throws IllegalAccessException {
writer.println();
if (data.isEmpty()) {
writer.println();
}
for (Object entry : data) {
if (entry != null) {
writeIndention(depth);
writer.print("- ");
if (entry instanceof StorageObject) {
writer.println();
writeToFile(entry, depth + 1, entry.getClass());
} else if (entry instanceof Location) {
writeLocation((Location) entry, depth + 1);
} else {
writeScalar(entry);
}
}
}
writer.println();
}
private void writeMap(final Map<Object, Object> data, final int depth) throws IllegalArgumentException, IllegalAccessException {
writer.println();
if (data.isEmpty()) {
writer.println();
}
for (Entry<Object, Object> entry : data.entrySet()) {
final Object value = entry.getValue();
if (value != null) {
writeIndention(depth);
writeKey(entry.getKey());
writer.print(": ");
if (value instanceof StorageObject) {
writer.println();
writeToFile(value, depth + 1, value.getClass());
} else if (value instanceof Collection) {
writeCollection((Collection<Object>) value, depth + 1);
} else if (value instanceof Location) {
writeLocation((Location) value, depth + 1);
} else {
writeScalar(value);
writer.println();
}
}
}
}
private void writeIndention(final int depth) {
for (int i = 0; i < depth; i++) {
writer.print(" ");
}
}
private void writeScalar(final Object data) {
if (data instanceof String || data instanceof Boolean || data instanceof Number) {
synchronized (YAML) {
YAML.dumpAll(Collections.singletonList(data).iterator(), writer);
}
} else if (data instanceof Material) {
writeMaterial(data);
writer.println();
} else if (data instanceof MaterialData) {
writeMaterialData(data);
writer.println();
} else if (data instanceof ItemStack) {
writeItemStack(data);
writer.println();
} else if (data instanceof EnchantmentLevel) {
writeEnchantmentLevel(data);
writer.println();
} else {
throw new UnsupportedOperationException();
}
}
private void writeKey(final Object data) {
if (data instanceof String || data instanceof Boolean || data instanceof Number) {
String output = data.toString();
if (NON_WORD_PATTERN.matcher(output).find()) {
writer.print('"');
writer.print(output.replace("\"", "\\\""));
writer.print('"');
} else {
writer.print(output);
}
} else if (data instanceof Material) {
writeMaterial(data);
} else if (data instanceof MaterialData) {
writeMaterialData(data);
} else if (data instanceof EnchantmentLevel) {
writeEnchantmentLevel(data);
} else {
throw new UnsupportedOperationException();
}
}
private void writeMaterial(final Object data) {
writer.print(data.toString().toLowerCase(Locale.ENGLISH));
}
private void writeMaterialData(final Object data) {
final MaterialData matData = (MaterialData) data;
writeMaterial(matData.getItemType());
if (matData.getData() > 0) {
writer.print(':');
writer.print(matData.getData());
}
}
private void writeItemStack(final Object data) {
final ItemStack itemStack = (ItemStack) data;
writeMaterialData(itemStack.getData());
writer.print(' ');
writer.print(itemStack.getAmount());
for (Entry<Enchantment, Integer> entry : itemStack.getEnchantments().entrySet()) {
writer.print(' ');
writeEnchantmentLevel(entry);
}
}
private void writeEnchantmentLevel(Object data) {
final Entry<Enchantment, Integer> enchLevel = (Entry<Enchantment, Integer>) data;
writer.print(enchLevel.getKey().getName().toLowerCase(Locale.ENGLISH));
writer.print(':');
writer.print(enchLevel.getValue());
}
private void writeLocation(final Location entry, final int depth) {
writer.println();
writeIndention(depth);
writer.print("world: ");
writeScalar(entry.getWorld().getName());
writeIndention(depth);
writer.print("x: ");
writeScalar(entry.getX());
writeIndention(depth);
writer.print("y: ");
writeScalar(entry.getY());
writeIndention(depth);
writer.print("z: ");
writeScalar(entry.getZ());
writeIndention(depth);
writer.print("yaw: ");
writeScalar(entry.getYaw());
writeIndention(depth);
writer.print("pitch: ");
writeScalar(entry.getPitch());
}
}
@@ -1,153 +0,0 @@
package com.earth2me.essentials.utils;
import org.bukkit.Bukkit;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.material.MaterialData;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.Set;
public class MaterialUtil {
private static final Set<Material> BEDS;
private static final Set<Material> BANNERS;
private static final Set<Material> FIREWORKS;
private static final Set<Material> LEGACY_SKULLS;
private static final Set<Material> LEATHER_ARMOR;
private static final Set<Material> MOB_HEADS;
// includes TIPPED_ARROW which also has potion effects
private static final Set<Material> PLAYER_HEADS;
private static final Set<Material> POTIONS;
private static final Set<Material> SIGN_POSTS;
private static final Set<Material> WALL_SIGNS;
public static final Material SPAWNER = EnumUtil.getMaterial("MOB_SPAWNER", "SPAWNER");
static {
BEDS = EnumUtil.getAllMatching(Material.class, "BED", "BED_BLOCK", "WHITE_BED", "ORANGE_BED",
"MAGENTA_BED", "LIGHT_BLUE_BED", "YELLOW_BED", "LIME_BED", "PINK_BED", "GRAY_BED",
"LIGHT_GRAY_BED", "CYAN_BED", "PURPLE_BED", "BLUE_BED", "BROWN_BED", "GREEN_BED",
"RED_BED", "BLACK_BED");
BANNERS = EnumUtil.getAllMatching(Material.class, "BANNER", "WHITE_BANNER",
"ORANGE_BANNER", "MAGENTA_BANNER", "LIGHT_BLUE_BANNER", "YELLOW_BANNER", "LIME_BANNER",
"PINK_BANNER","GRAY_BANNER","LIGHT_GRAY_BANNER", "CYAN_BANNER", "PURPLE_BANNER",
"BLUE_BANNER", "BROWN_BANNER", "GREEN_BANNER", "RED_BANNER", "BLACK_BANNER", "SHIELD");
FIREWORKS = EnumUtil.getAllMatching(Material.class, "FIREWORK", "FIREWORK_ROCKET",
"FIREWORK_CHARGE", "FIREWORK_STAR");
LEATHER_ARMOR = EnumUtil.getAllMatching(Material.class, "LEATHER_HELMET",
"LEATHER_CHESTPLATE", "LEATHER_LEGGINGS", "LEATHER_BOOTS");
LEGACY_SKULLS = EnumUtil.getAllMatching(Material.class,"SKULL", "SKULL_ITEM");
MOB_HEADS = EnumUtil.getAllMatching(Material.class, "SKELETON_SKULL",
"SKELETON_WALL_SKULL", "WITHER_SKELETON_SKULL", "WITHER_SKELETON_WALL_SKULL",
"CREEPER_HEAD", "CREEPER_WALL_HEAD", "ZOMBIE_HEAD", "ZOMBIE_WALL_HEAD", "DRAGON_HEAD"
, "DRAGON_WALL_HEAD");
PLAYER_HEADS = EnumUtil.getAllMatching(Material.class, "PLAYER_HEAD", "PLAYER_WALL_HEAD");
POTIONS = EnumUtil.getAllMatching(Material.class, "POTION", "SPLASH_POTION",
"LINGERING_POTION", "TIPPED_ARROW");
SIGN_POSTS = EnumUtil.getAllMatching(Material.class, "SIGN", "SIGN_POST",
"ACACIA_SIGN", "BIRCH_SIGN",
"DARK_OAK_SIGN", "JUNGLE_SIGN",
"OAK_SIGN", "SPRUCE_SIGN",
"CRIMSON_SIGN", "WARPED_SIGN");
WALL_SIGNS = EnumUtil.getAllMatching(Material.class, "WALL_SIGN",
"ACACIA_WALL_SIGN", "BIRCH_WALL_SIGN",
"DARK_OAK_WALL_SIGN", "JUNGLE_WALL_SIGN",
"OAK_WALL_SIGN", "SPRUCE_WALL_SIGN",
"CRIMSON_WALL_SIGN", "WARPED_WALL_SIGN");
}
public static boolean isBed(Material material) {
return BEDS.contains(material);
}
public static boolean isBanner(Material material) {
return BANNERS.contains(material);
}
public static boolean isFirework(Material material) {
return FIREWORKS.contains(material);
}
public static boolean isLeatherArmor(Material material) {
return LEATHER_ARMOR.contains(material);
}
public static boolean isMobHead(Material material, int durability) {
if (MOB_HEADS.contains(material)) {
return true;
}
return LEGACY_SKULLS.contains(material) && (durability != 3);
}
public static boolean isPlayerHead(Material material, int durability) {
if (PLAYER_HEADS.contains(material)) {
return true;
}
return LEGACY_SKULLS.contains(material) && durability == 3;
}
public static boolean isPotion(Material material) {
return POTIONS.contains(material);
}
public static boolean isSignPost(Material material) {
return SIGN_POSTS.contains(material);
}
public static boolean isWallSign(Material material) {
return WALL_SIGNS.contains(material);
}
public static boolean isSign(Material material) {
return isSignPost(material) || isWallSign(material);
}
public static boolean isSkull(Material material) {
return isPlayerHead(material, -1) || isMobHead(material, -1);
}
public static Material convertFromLegacy(int id, byte damage) {
for (Material material : EnumSet.allOf(Material.class)) {
if (material.getId() == id) {
try {
return Bukkit.getUnsafe().fromLegacy(new MaterialData(material, damage));
} catch (NoSuchMethodError error) {
break;
}
}
}
try {
Method getMaterialFromId = Material.class.getDeclaredMethod("getMaterial", int.class);
return (Material) getMaterialFromId.invoke(null, id);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {}
return null;
}
public static DyeColor getColorOf(Material material) {
for (DyeColor color : DyeColor.values()) {
if (material.toString().contains(color.name())) {
return color;
}
}
return DyeColor.WHITE;
}
}
@@ -1,187 +0,0 @@
package com.earth2me.essentials.utils;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import org.bukkit.Bukkit;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VersionUtil {
public static final BukkitVersion v1_8_8_R01 = BukkitVersion.fromString("1.8.8-R0.1-SNAPSHOT");
public static final BukkitVersion v1_9_R01 = BukkitVersion.fromString("1.9-R0.1-SNAPSHOT");
public static final BukkitVersion v1_9_4_R01 = BukkitVersion.fromString("1.9.4-R0.1-SNAPSHOT");
public static final BukkitVersion v1_10_R01 = BukkitVersion.fromString("1.10-R0.1-SNAPSHOT");
public static final BukkitVersion v1_10_2_R01 = BukkitVersion.fromString("1.10.2-R0.1-SNAPSHOT");
public static final BukkitVersion v1_11_R01 = BukkitVersion.fromString("1.11-R0.1-SNAPSHOT");
public static final BukkitVersion v1_11_2_R01 = BukkitVersion.fromString("1.11.2-R0.1-SNAPSHOT");
public static final BukkitVersion v1_12_0_R01 = BukkitVersion.fromString("1.12.0-R0.1-SNAPSHOT");
public static final BukkitVersion v1_12_2_R01 = BukkitVersion.fromString("1.12.2-R0.1-SNAPSHOT");
public static final BukkitVersion v1_13_0_R01 = BukkitVersion.fromString("1.13.0-R0.1-SNAPSHOT");
public static final BukkitVersion v1_13_2_R01 = BukkitVersion.fromString("1.13.2-R0.1-SNAPSHOT");
public static final BukkitVersion v1_14_R01 = BukkitVersion.fromString("1.14-R0.1-SNAPSHOT");
public static final BukkitVersion v1_14_4_R01 = BukkitVersion.fromString("1.14.4-R0.1-SNAPSHOT");
public static final BukkitVersion v1_15_R01 = BukkitVersion.fromString("1.15-R0.1-SNAPSHOT");
public static final BukkitVersion v1_15_2_R01 = BukkitVersion.fromString("1.15.2-R0.1-SNAPSHOT");
public static final BukkitVersion v1_16_1_R01 = BukkitVersion.fromString("1.16.1-R0.1-SNAPSHOT");
public static final BukkitVersion v1_16_2_R01 = BukkitVersion.fromString("1.16.2-R0.1-SNAPSHOT");
private static final Set<BukkitVersion> supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_2_R01);
private static BukkitVersion serverVersion = null;
public static BukkitVersion getServerBukkitVersion() {
if (serverVersion == null) {
serverVersion = BukkitVersion.fromString(Bukkit.getServer().getBukkitVersion());
}
return serverVersion;
}
public static boolean isServerSupported() {
return supportedVersions.contains(getServerBukkitVersion());
}
public static class BukkitVersion implements Comparable<BukkitVersion> {
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-pre(\\d))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?");
private final int major;
private final int minor;
private final int prerelease;
private final int patch;
private final double revision;
public static BukkitVersion fromString(String string) {
Preconditions.checkNotNull(string, "string cannot be null.");
Matcher matcher = VERSION_PATTERN.matcher(string);
if (!matcher.matches()) {
if (!Bukkit.getName().equals("Essentials Fake Server")) {
throw new IllegalArgumentException(string + " is not in valid version format. e.g. 1.8.8-R0.1");
}
matcher = VERSION_PATTERN.matcher(v1_14_R01.toString());
Preconditions.checkArgument(matcher.matches(), string + " is not in valid version format. e.g. 1.8.8-R0.1");
}
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.groupCount() < 5 ? "" : matcher.group(5), matcher.group(4));
}
private static BukkitVersion from(String major, String minor, String patch, String revision, String prerelease) {
if (patch == null || patch.isEmpty()) patch = "0";
if (revision == null || revision.isEmpty()) revision = "0";
if (prerelease == null || prerelease.isEmpty()) prerelease = "-1";
return new BukkitVersion(Integer.parseInt(major),
Integer.parseInt(minor),
Integer.parseInt(patch),
Double.parseDouble(revision),
Integer.parseInt(prerelease));
}
private BukkitVersion(int major, int minor, int patch, double revision, int prerelease) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.revision = revision;
this.prerelease = prerelease;
}
public boolean isHigherThan(BukkitVersion o) {
return compareTo(o) > 0;
}
public boolean isHigherThanOrEqualTo(BukkitVersion o) {
return compareTo(o) >= 0;
}
public boolean isLowerThan(BukkitVersion o) {
return compareTo(o) < 0;
}
public boolean isLowerThanOrEqualTo(BukkitVersion o) {
return compareTo(o) <= 0;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
public int getPatch() {
return patch;
}
public double getRevision() {
return revision;
}
public int getPrerelease() {
return prerelease;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BukkitVersion that = (BukkitVersion) o;
return major == that.major &&
minor == that.minor &&
patch == that.patch &&
revision == that.revision &&
prerelease == that.prerelease;
}
@Override
public int hashCode() {
return Objects.hashCode(major, minor, patch, revision, prerelease);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(major + "." + minor);
if (patch != 0) {
sb.append(".").append(patch);
}
if (prerelease != -1) {
sb.append("-pre").append(prerelease);
}
return sb.append("-R").append(revision).toString();
}
@Override
public int compareTo(BukkitVersion o) {
if (major < o.major) {
return -1;
} else if (major > o.major) {
return 1;
} else { // equal major
if (minor < o.minor) {
return -1;
} else if (minor > o.minor) {
return 1;
} else { // equal minor
if (patch < o.patch) {
return -1;
} else if (patch > o.patch) {
return 1;
} else { // equal patch
if (prerelease < o.prerelease) {
return -1;
} else if (prerelease > o.prerelease) {
return 1;
} else { // equal prerelease
return Double.compare(revision, o.revision);
}
}
}
}
}
}
}
@@ -0,0 +1,107 @@
package com.earth2me.essentials;
import org.bukkit.command.Command;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AlternativeCommandsHandler {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private final transient Map<String, List<Command>> altcommands = new HashMap<>();
private final transient Map<String, String> disabledList = new HashMap<>();
private final transient IEssentials ess;
public AlternativeCommandsHandler(final IEssentials ess) {
this.ess = ess;
for (final Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
if (plugin.isEnabled()) {
addPlugin(plugin);
}
}
}
public final void addPlugin(final Plugin plugin) {
if (plugin.getDescription().getMain().contains("com.earth2me.essentials") || plugin.getDescription().getMain().contains("net.essentialsx")) {
return;
}
for (final Map.Entry<String, Command> entry : getPluginCommands(plugin).entrySet()) {
final String[] commandSplit = entry.getKey().split(":", 2);
final String commandName = commandSplit.length > 1 ? commandSplit[1] : entry.getKey();
final Command command = entry.getValue();
final List<Command> pluginCommands = altcommands.computeIfAbsent(commandName.toLowerCase(Locale.ENGLISH), k -> new ArrayList<>());
boolean found = false;
for (final Command pc2 : pluginCommands) {
// Safe cast, everything that's added comes from getPluginCommands which already performs the cast check.
if (((PluginIdentifiableCommand) pc2).getPlugin().equals(plugin)) {
found = true;
break;
}
}
if (!found) {
pluginCommands.add(command);
}
}
}
private Map<String, Command> getPluginCommands(Plugin plugin) {
final Map<String, Command> commands = new HashMap<>();
for (final Map.Entry<String, Command> entry : ess.getKnownCommandsProvider().getKnownCommands().entrySet()) {
if (entry.getValue() instanceof PluginIdentifiableCommand && ((PluginIdentifiableCommand) entry.getValue()).getPlugin().equals(plugin)) {
commands.put(entry.getKey(), entry.getValue());
}
}
return commands;
}
public void removePlugin(final Plugin plugin) {
final Iterator<Map.Entry<String, List<Command>>> iterator = altcommands.entrySet().iterator();
while (iterator.hasNext()) {
final Map.Entry<String, List<Command>> entry = iterator.next();
entry.getValue().removeIf(pc -> !(pc instanceof PluginIdentifiableCommand) || ((PluginIdentifiableCommand) pc).getPlugin().equals(plugin));
if (entry.getValue().isEmpty()) {
iterator.remove();
}
}
}
public Command getAlternative(final String label) {
final List<Command> commands = altcommands.get(label);
if (commands == null || commands.isEmpty()) {
return null;
}
if (commands.size() == 1) {
return commands.get(0);
}
// return the first command that is not an alias
for (final Command command : commands) {
if (command.getName().equalsIgnoreCase(label)) {
return command;
}
}
// return the first alias
return commands.get(0);
}
public void executed(final String label, final Command pc) {
if (pc instanceof PluginIdentifiableCommand) {
final String altString = ((PluginIdentifiableCommand) pc).getPlugin().getName() + ":" + pc.getName();
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, "Essentials: Alternative command " + label + " found, using " + altString);
}
disabledList.put(label, altString);
}
}
public Map<String, String> disabledCommands() {
return disabledList;
}
}
@@ -25,7 +25,6 @@ import java.util.concurrent.ExecutionException;
import static com.earth2me.essentials.I18n.tl;
public class AsyncTeleport implements IAsyncTeleport {
private final IUser teleportOwner;
private final IEssentials ess;
@@ -33,30 +32,24 @@ public class AsyncTeleport implements IAsyncTeleport {
private TeleportType tpType;
public AsyncTeleport(IUser user, IEssentials ess) {
public AsyncTeleport(final IUser user, final IEssentials ess) {
this.teleportOwner = user;
this.ess = ess;
tpType = TeleportType.NORMAL;
}
public enum TeleportType {
TPA,
BACK,
NORMAL
}
public void cooldown(boolean check) throws Throwable {
CompletableFuture<Boolean> exceptionFuture = new CompletableFuture<>();
public void cooldown(final boolean check) throws Throwable {
final CompletableFuture<Boolean> exceptionFuture = new CompletableFuture<>();
if (cooldown(check, exceptionFuture)) {
try {
exceptionFuture.get();
} catch (ExecutionException e) {
} catch (final ExecutionException e) {
throw e.getCause();
}
}
}
public boolean cooldown(boolean check, CompletableFuture<Boolean> future) {
public boolean cooldown(final boolean check, final CompletableFuture<Boolean> future) {
final Calendar time = new GregorianCalendar();
if (teleportOwner.getLastTeleportTimestamp() > 0) {
// Take the current time, and remove the delay from it.
@@ -93,7 +86,7 @@ public class AsyncTeleport implements IAsyncTeleport {
private boolean cooldownApplies() {
boolean applies = true;
String globalBypassPerm = "essentials.teleport.cooldown.bypass";
final String globalBypassPerm = "essentials.teleport.cooldown.bypass";
switch (tpType) {
case NORMAL:
applies = !teleportOwner.isAuthorized(globalBypassPerm);
@@ -111,15 +104,14 @@ public class AsyncTeleport implements IAsyncTeleport {
}
private void warnUser(final IUser user, final double delay) {
Calendar c = new GregorianCalendar();
final Calendar c = new GregorianCalendar();
c.add(Calendar.SECOND, (int) delay);
c.add(Calendar.MILLISECOND, (int) ((delay * 1000.0) % 1000.0));
user.sendMessage(tl("dontMoveMessage", DateUtil.formatDateDiff(c.getTimeInMillis())));
}
@Override
public void now(Location loc, boolean cooldown, TeleportCause cause, CompletableFuture<Boolean> future) {
public void now(final Location loc, final boolean cooldown, final TeleportCause cause, final CompletableFuture<Boolean> future) {
if (cooldown && cooldown(false, future)) {
return;
}
@@ -128,7 +120,7 @@ public class AsyncTeleport implements IAsyncTeleport {
}
@Override
public void now(Player entity, boolean cooldown, TeleportCause cause, CompletableFuture<Boolean> future) {
public void now(final Player entity, final boolean cooldown, final TeleportCause cause, final CompletableFuture<Boolean> future) {
if (cooldown && cooldown(false, future)) {
future.complete(false);
return;
@@ -142,12 +134,19 @@ public class AsyncTeleport implements IAsyncTeleport {
});
}
private void runOnMain(Runnable runnable) throws ExecutionException, InterruptedException {
@Override
public void nowUnsafe(Location loc, TeleportCause cause, CompletableFuture<Boolean> future) {
final CompletableFuture<Boolean> paperFuture = PaperLib.teleportAsync(teleportOwner.getBase(), loc, cause);
paperFuture.thenAccept(future::complete);
paperFuture.exceptionally(future::completeExceptionally);
}
private void runOnMain(final Runnable runnable) throws ExecutionException, InterruptedException {
if (Bukkit.isPrimaryThread()) {
runnable.run();
return;
}
CompletableFuture<Object> taskLock = new CompletableFuture<>();
final CompletableFuture<Object> taskLock = new CompletableFuture<>();
Bukkit.getScheduler().runTask(ess, () -> {
runnable.run();
taskLock.complete(new Object());
@@ -155,15 +154,15 @@ public class AsyncTeleport implements IAsyncTeleport {
taskLock.get();
}
protected void nowAsync(IUser teleportee, ITarget target, TeleportCause cause, CompletableFuture<Boolean> future) {
protected void nowAsync(final IUser teleportee, final ITarget target, final TeleportCause cause, final CompletableFuture<Boolean> future) {
cancel(false);
PreTeleportEvent event = new PreTeleportEvent(teleportee, cause, target);
final PreTeleportEvent event = new PreTeleportEvent(teleportee, cause, target);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
future.complete(false);
return;
}
teleportee.setLastLocation();
if (!ess.getSettings().isForcePassengerTeleport() && !teleportee.getBase().isEmpty()) {
if (!ess.getSettings().isTeleportPassengerDismount()) {
@@ -173,20 +172,24 @@ public class AsyncTeleport implements IAsyncTeleport {
try {
runOnMain(() -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context.
} catch (ExecutionException | InterruptedException e) {
} catch (final ExecutionException | InterruptedException e) {
future.completeExceptionally(e);
return;
}
}
teleportee.setLastLocation();
if (teleportee.isAuthorized("essentials.back.onteleport")) {
teleportee.setLastLocation();
}
final Location targetLoc = target.getLocation();
if (ess.getSettings().isTeleportSafetyEnabled() && LocationUtil.isBlockOutsideWorldBorder(targetLoc.getWorld(), targetLoc.getBlockX(), targetLoc.getBlockZ())) {
if (ess.getSettings().isTeleportSafetyEnabled() && !ess.getSettings().isForceDisableTeleportSafety() && LocationUtil.isBlockOutsideWorldBorder(targetLoc.getWorld(), targetLoc.getBlockX(), targetLoc.getBlockZ())) {
targetLoc.setX(LocationUtil.getXInsideWorldBorder(targetLoc.getWorld(), targetLoc.getBlockX()));
targetLoc.setZ(LocationUtil.getZInsideWorldBorder(targetLoc.getWorld(), targetLoc.getBlockZ()));
}
PaperLib.getChunkAtAsync(targetLoc).thenAccept(chunk -> {
PaperLib.getChunkAtAsync(targetLoc.getWorld(), targetLoc.getBlockX() >> 4, targetLoc.getBlockZ() >> 4, true, true).thenAccept(chunk -> {
Location loc = targetLoc;
if (LocationUtil.isBlockUnsafeForUser(teleportee, chunk.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
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.
@@ -195,7 +198,7 @@ public class AsyncTeleport implements IAsyncTeleport {
try {
//There's a chance the safer location is outside the loaded chunk so still teleport async here.
PaperLib.teleportAsync(teleportee.getBase(), LocationUtil.getSafeDestination(ess, teleportee, loc), cause);
} catch (Exception e) {
} catch (final Exception e) {
future.completeExceptionally(e);
return;
}
@@ -217,28 +220,31 @@ public class AsyncTeleport implements IAsyncTeleport {
}
}
future.complete(true);
}).exceptionally(th -> {
future.completeExceptionally(th);
return null;
});
}
@Override
public void teleport(Location loc, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
public void teleport(final Location loc, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
teleport(teleportOwner, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void teleport(Player entity, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
public void teleport(final Player entity, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
teleportOwner.sendMessage(tl("teleportToPlayer", entity.getDisplayName()));
teleport(teleportOwner, new PlayerTarget(entity), chargeFor, cause, future);
}
@Override
public void teleportPlayer(IUser otherUser, Location loc, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
public void teleportPlayer(final IUser otherUser, final Location loc, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
teleport(otherUser, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void teleportPlayer(IUser otherUser, Player entity, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
ITarget target = new PlayerTarget(entity);
public void teleportPlayer(final IUser otherUser, final Player entity, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
final ITarget target = new PlayerTarget(entity);
teleport(otherUser, target, chargeFor, cause, future);
future.thenAccept(success -> {
if (success) {
@@ -248,12 +254,13 @@ public class AsyncTeleport implements IAsyncTeleport {
});
}
private void teleport(IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
private void teleport(final IUser teleportee, final ITarget target, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
future.complete(false);
return;
}
delay = event.getDelay();
@@ -274,10 +281,12 @@ public class AsyncTeleport implements IAsyncTeleport {
}
if (cooldown(true, future)) {
future.complete(false);
return;
}
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass") || teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
if (cooldown(false, future)) {
future.complete(false);
return;
}
nowAsync(teleportee, target, cause, future);
@@ -287,18 +296,19 @@ public class AsyncTeleport implements IAsyncTeleport {
return;
}
}
future.complete(true);
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false, future);
}
private void teleportOther(IUser teleporter, IUser teleportee, ITarget target, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
private void teleportOther(final IUser teleporter, final IUser teleportee, final ITarget target, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
@@ -338,19 +348,20 @@ public class AsyncTeleport implements IAsyncTeleport {
return;
}
}
future.complete(true);
return;
}
cancel(false);
warnUser(teleportee, delay);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false, future);
}
@Override
public void respawn(Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
public void respawn(final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
double delay = ess.getSettings().getTeleportDelay();
TeleportWarmupEvent event = new TeleportWarmupEvent(teleportOwner, cause, null, delay);
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleportOwner, cause, null, delay);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
@@ -374,15 +385,16 @@ public class AsyncTeleport implements IAsyncTeleport {
if (chargeFor != null) {
chargeFor.charge(teleportOwner, future);
}
future.complete(true);
return;
}
cancel(false);
warnUser(teleportOwner, delay);
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true);
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true, future);
}
void respawnNow(IUser teleportee, TeleportCause cause, CompletableFuture<Boolean> future) {
void respawnNow(final IUser teleportee, final TeleportCause cause, final CompletableFuture<Boolean> future) {
final Player player = teleportee.getBase();
PaperLib.getBedSpawnLocationAsync(player, true).thenAccept(location -> {
if (location != null) {
@@ -395,39 +407,47 @@ public class AsyncTeleport implements IAsyncTeleport {
ess.getServer().getPluginManager().callEvent(pre);
nowAsync(teleportee, new LocationTarget(pre.getRespawnLocation()), cause, future);
}
}).exceptionally(th -> {
future.completeExceptionally(th);
return null;
});
}
@Override
public void warp(IUser otherUser, String warp, Trade chargeFor, TeleportCause cause, CompletableFuture<Boolean> future) {
UserWarpEvent event = new UserWarpEvent(otherUser, warp, chargeFor);
public void warp(final IUser otherUser, String warp, final Trade chargeFor, final TeleportCause cause, final CompletableFuture<Boolean> future) {
final UserWarpEvent event = new UserWarpEvent(otherUser, warp, chargeFor);
Bukkit.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
warp = event.getWarp();
Location loc;
final Location loc;
try {
loc = ess.getWarps().getWarp(warp);
} catch (WarpNotFoundException | InvalidWorldException e) {
} catch (final WarpNotFoundException | InvalidWorldException e) {
future.completeExceptionally(e);
return;
}
otherUser.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
if (!otherUser.equals(teleportOwner)) {
teleportOwner.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
final String finalWarp = warp;
future.thenAccept(success -> {
if (success) {
otherUser.sendMessage(tl("warpingTo", finalWarp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
if (!otherUser.equals(teleportOwner)) {
teleportOwner.sendMessage(tl("warpingTo", finalWarp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}
}
});
teleport(otherUser, new LocationTarget(loc), chargeFor, cause, future);
}
@Override
public void back(Trade chargeFor, CompletableFuture<Boolean> future) {
public void back(final Trade chargeFor, final CompletableFuture<Boolean> future) {
back(teleportOwner, chargeFor, future);
}
@Override
public void back(IUser teleporter, Trade chargeFor, CompletableFuture<Boolean> future) {
public void back(final IUser teleporter, final Trade chargeFor, final CompletableFuture<Boolean> future) {
tpType = TeleportType.BACK;
final Location loc = teleportOwner.getLastLocation();
teleportOwner.sendMessage(tl("backUsageMsg", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
@@ -435,23 +455,29 @@ public class AsyncTeleport implements IAsyncTeleport {
}
@Override
public void back(CompletableFuture<Boolean> future) {
public void back(final CompletableFuture<Boolean> future) {
nowAsync(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND, future);
}
public void setTpType(TeleportType tpType) {
public void setTpType(final TeleportType tpType) {
this.tpType = tpType;
}
//If we need to cancelTimer a pending teleportPlayer call this method
private void cancel(boolean notifyUser) {
private void cancel(final boolean notifyUser) {
if (timedTeleport != null) {
timedTeleport.cancelTimer(notifyUser);
timedTeleport = null;
}
}
private void initTimer(long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
timedTeleport = new AsyncTimedTeleport(teleportOwner, ess, this, delay, teleportUser, target, chargeFor, cause, respawn);
private void initTimer(final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn, CompletableFuture<Boolean> future) {
timedTeleport = new AsyncTimedTeleport(teleportOwner, ess, this, delay, future, teleportUser, target, chargeFor, cause, respawn);
}
public enum TeleportType {
TPA,
BACK,
NORMAL
}
}
@@ -10,17 +10,15 @@ import java.util.concurrent.CompletableFuture;
import static com.earth2me.essentials.I18n.tl;
public class AsyncTimedTeleport implements Runnable {
private static final double MOVE_CONSTANT = 0.3;
private final IUser teleportOwner;
private final IEssentials ess;
private final AsyncTeleport teleport;
private final UUID timer_teleportee;
private int timer_task;
private final long timer_started; // time this task was initiated
private final long timer_delay; // how long to delay the teleportPlayer
private double timer_health;
private final CompletableFuture<Boolean> parentFuture;
// note that I initially stored a clone of the location for reference, but...
// when comparing locations, I got incorrect mismatches (rounding errors, looked like)
// so, the X/Y/Z values are stored instead and rounded off
@@ -32,8 +30,14 @@ 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 double timer_health;
AsyncTimedTeleport(IUser user, IEssentials ess, AsyncTeleport teleport, long delay, IUser teleportUser, ITarget target, Trade chargeFor, TeleportCause cause, boolean respawn) {
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) {
this(user, ess, teleport, delay, null, teleportUser, target, chargeFor, cause, respawn);
}
AsyncTimedTeleport(final IUser user, final IEssentials ess, final AsyncTeleport teleport, final long delay, final CompletableFuture<Boolean> future, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) {
this.teleportOwner = user;
this.ess = ess;
this.teleport = teleport;
@@ -51,6 +55,18 @@ public class AsyncTimedTeleport implements Runnable {
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
if (future != null) {
this.parentFuture = future;
return;
}
final CompletableFuture<Boolean> cFuture = new CompletableFuture<>();
cFuture.exceptionally(e -> {
ess.showError(teleportOwner.getSource(), e, "\\ teleport");
return false;
});
this.parentFuture = cFuture;
}
@Override
@@ -84,12 +100,12 @@ public class AsyncTimedTeleport implements Runnable {
@Override
public void run() {
timer_health = teleportUser.getBase().getHealth(); // in case user healed, then later gets injured
timer_health = teleportUser.getBase().getHealth(); // in case user healed, then later gets injured
final long now = System.currentTimeMillis();
if (now > timer_started + timer_delay) {
try {
teleport.cooldown(false);
} catch (Throwable ex) {
} catch (final Throwable ex) {
teleportOwner.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
if (teleportOwner != teleportUser) {
teleportUser.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
@@ -99,40 +115,37 @@ public class AsyncTimedTeleport implements Runnable {
cancelTimer(false);
teleportUser.sendMessage(tl("teleportationCommencing"));
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(e -> {
ess.showError(teleportOwner.getSource(), e, "\\ teleport");
return false;
});
if (timer_chargeFor != null) {
timer_chargeFor.isAffordableFor(teleportOwner);
}
if (timer_respawn) {
teleport.respawnNow(teleportUser, timer_cause, future);
teleport.respawnNow(teleportUser, timer_cause, parentFuture);
} else {
teleport.nowAsync(teleportUser, timer_teleportTarget, timer_cause, future);
teleport.nowAsync(teleportUser, timer_teleportTarget, timer_cause, parentFuture);
}
future.thenAccept(success -> {
parentFuture.thenAccept(success -> {
if (timer_chargeFor != null) {
try {
timer_chargeFor.charge(teleportOwner);
} catch (ChargeException ex) {
} catch (final ChargeException ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
}
}
});
} catch (Exception ex) {
} catch (final Exception ex) {
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
}
}
}
}
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
}
//If we need to cancelTimer a pending teleportPlayer call this method
void cancelTimer(boolean notifyUser) {
void cancelTimer(final boolean notifyUser) {
if (timer_task == -1) {
return;
}
@@ -14,15 +14,14 @@ import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class Backup implements Runnable {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private transient final Server server;
private transient final IEssentials ess;
private final AtomicBoolean pendingShutdown = new AtomicBoolean(false);
private transient boolean running = false;
private transient int taskId = -1;
private transient boolean active = false;
private final AtomicBoolean pendingShutdown = new AtomicBoolean(false);
private transient CompletableFuture<Object> taskLock = null;
public Backup(final IEssentials ess) {
@@ -60,7 +59,7 @@ public class Backup implements Runnable {
return taskLock;
}
public void setPendingShutdown(boolean shutdown) {
public void setPendingShutdown(final boolean shutdown) {
pendingShutdown.set(shutdown);
}
@@ -89,13 +88,13 @@ public class Backup implements Runnable {
ess.runTaskAsynchronously(() -> {
try {
final ProcessBuilder childBuilder = new ProcessBuilder(command);
final ProcessBuilder childBuilder = new ProcessBuilder(command.split(" "));
childBuilder.redirectErrorStream(true);
childBuilder.directory(ess.getDataFolder().getParentFile().getParentFile());
final Process child = childBuilder.start();
ess.runTaskAsynchronously(() -> {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream()))) {
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream()))) {
String line;
do {
line = reader.readLine();
@@ -104,12 +103,12 @@ public class Backup implements Runnable {
}
} while (line != null);
}
} catch (IOException ex) {
} catch (final IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
});
child.waitFor();
} catch (InterruptedException | IOException ex) {
} catch (final InterruptedException | IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
} finally {
class BackupEnableSaveTask implements Runnable {
@@ -124,6 +123,7 @@ public class Backup implements Runnable {
LOGGER.log(Level.INFO, tl("backupFinished"));
}
}
if (!pendingShutdown.get()) {
ess.scheduleSyncDelayedTask(new BackupEnableSaveTask());
}
@@ -0,0 +1,94 @@
package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import net.essentialsx.api.v2.services.BalanceTop;
import org.bukkit.plugin.ServicePriority;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BalanceTopImpl implements BalanceTop {
private final IEssentials ess;
private LinkedHashMap<UUID, BalanceTop.Entry> topCache = new LinkedHashMap<>();
private BigDecimal balanceTopTotal = BigDecimal.ZERO;
private long cacheAge = 0;
private CompletableFuture<Void> cacheLock;
public BalanceTopImpl(IEssentials ess) {
this.ess = ess;
ess.getServer().getServicesManager().register(BalanceTop.class, this, ess, ServicePriority.Normal);
}
private void calculateBalanceTopMap() {
final List<Entry> entries = new LinkedList<>();
BigDecimal newTotal = BigDecimal.ZERO;
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
final User user = ess.getUserMap().getUser(u);
if (user != null) {
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
// Don't list NPCs in output
continue;
}
if (!user.isBaltopExempt()) {
final BigDecimal userMoney = user.getMoney();
user.updateMoneyCache(userMoney);
newTotal = newTotal.add(userMoney);
final String name;
if (user.getBase() instanceof OfflinePlayer) {
name = user.getLastAccountName();
} else if (user.isHidden()) {
name = user.getName();
} else {
name = user.getDisplayName();
}
entries.add(new BalanceTop.Entry(user.getUUID(), name, userMoney));
}
}
}
final LinkedHashMap<UUID, Entry> sortedMap = new LinkedHashMap<>();
entries.sort((entry1, entry2) -> entry2.getBalance().compareTo(entry1.getBalance()));
for (Entry entry : entries) {
sortedMap.put(entry.getUuid(), entry);
}
topCache = sortedMap;
balanceTopTotal = newTotal;
cacheAge = System.currentTimeMillis();
cacheLock.complete(null);
cacheLock = null;
}
@Override
public CompletableFuture<Void> calculateBalanceTopMapAsync() {
if (cacheLock != null) {
return cacheLock;
}
cacheLock = new CompletableFuture<>();
ess.runTaskAsynchronously(this::calculateBalanceTopMap);
return cacheLock;
}
@Override
public Map<UUID, Entry> getBalanceTopCache() {
return Collections.unmodifiableMap(topCache);
}
@Override
public long getCacheAge() {
return cacheAge;
}
@Override
public BigDecimal getBalanceTopTotal() {
return balanceTopTotal;
}
public boolean isCacheLocked() {
return cacheLock != null;
}
}
@@ -1,6 +1,5 @@
package com.earth2me.essentials;
public class ChargeException extends Exception {
public ChargeException(final String message) {
super(message);
@@ -3,7 +3,6 @@ package com.earth2me.essentials;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CommandSource {
protected CommandSender sender;
@@ -22,7 +21,7 @@ public class CommandSource {
return null;
}
public final net.ess3.api.IUser getUser(IEssentials ess) {
public final net.ess3.api.IUser getUser(final IEssentials ess) {
if (sender instanceof Player) {
return ess.getUser((Player) sender);
}
@@ -37,18 +36,21 @@ public class CommandSource {
return this.sender = base;
}
public void sendMessage(String message) {
public void sendMessage(final String message) {
if (!message.isEmpty()) {
sender.sendMessage(message);
}
}
public boolean isAuthorized(String permission, IEssentials ess) {
public boolean isAuthorized(final String permission, final IEssentials ess) {
return !(sender instanceof Player) || getUser(ess).isAuthorized(permission);
}
public String getSelfSelector() {
return sender instanceof Player ? getPlayer().getName() : "*";
}
public String getDisplayName() {
return sender instanceof Player ? getPlayer().getDisplayName() : getSender().getName();
}
}
@@ -2,76 +2,98 @@ package com.earth2me.essentials;
import com.earth2me.essentials.messaging.IMessageRecipient;
import com.earth2me.essentials.messaging.SimpleMessageRecipient;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.UUID;
import static com.earth2me.essentials.I18n.tl;
public final class Console implements IMessageRecipient {
public static final String NAME = "Console";
public static final String DISPLAY_NAME = tl("consoleName");
private static Console instance; // Set in essentials
private final IEssentials ess;
private final IMessageRecipient messageRecipient;
private Console(final IEssentials ess) {
this.ess = ess;
this.messageRecipient = new SimpleMessageRecipient(ess, this);
}
public static Console getInstance() {
return instance;
}
static void setInstance(IEssentials ess) { // Called in Essentials#onEnable()
static void setInstance(final IEssentials ess) { // Called in Essentials#onEnable()
instance = new Console(ess);
}
/**
* @deprecated Use {@link Console#getCommandSender()}
*/
@Deprecated
public static CommandSender getCommandSender(Server server) throws Exception {
public static CommandSender getCommandSender(final Server server) throws Exception {
return server.getConsoleSender();
}
private Console(IEssentials ess) {
this.ess = ess;
this.messageRecipient = new SimpleMessageRecipient(ess, this);
}
public CommandSender getCommandSender() {
return ess.getServer().getConsoleSender();
}
@Override public String getName() {
@Override
public String getName() {
return Console.NAME;
}
@Override public String getDisplayName() {
return Console.NAME;
@Override
public UUID getUUID() {
return null;
}
@Override public void sendMessage(String message) {
@Override
public String getDisplayName() {
return Console.DISPLAY_NAME;
}
@Override
public void sendMessage(final String message) {
getCommandSender().sendMessage(message);
}
@Override public boolean isReachable() {
@Override
public boolean isReachable() {
return true;
}
/* ================================
* >> DELEGATE METHODS
* ================================ */
@Override public MessageResponse sendMessage(IMessageRecipient recipient, String message) {
@Override
public MessageResponse sendMessage(final IMessageRecipient recipient, final String message) {
return this.messageRecipient.sendMessage(recipient, message);
}
@Override public MessageResponse onReceiveMessage(IMessageRecipient sender, String message) {
@Override
public MessageResponse onReceiveMessage(final IMessageRecipient sender, final String message) {
return this.messageRecipient.onReceiveMessage(sender, message);
}
@Override public IMessageRecipient getReplyRecipient() {
@Override
public IMessageRecipient getReplyRecipient() {
return this.messageRecipient.getReplyRecipient();
}
@Override public void setReplyRecipient(IMessageRecipient recipient) {
@Override
public void setReplyRecipient(final IMessageRecipient recipient) {
this.messageRecipient.setReplyRecipient(recipient);
}
@Override
public boolean isHiddenFrom(Player player) {
return false;
}
}
@@ -9,8 +9,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Enchantments {
public final class Enchantments {
private static final Map<String, Enchantment> ENCHANTMENTS = new HashMap<>();
private static final Map<String, Enchantment> ALIASENCHANTMENTS = new HashMap<>();
private static boolean isFlat;
@@ -152,76 +151,78 @@ public class Enchantments {
// 1.8
try {
Enchantment depthStrider = Enchantment.getByName("DEPTH_STRIDER");
final Enchantment depthStrider = Enchantment.getByName("DEPTH_STRIDER");
if (depthStrider != null) {
ENCHANTMENTS.put("depthstrider", depthStrider);
ALIASENCHANTMENTS.put("depth", depthStrider);
ALIASENCHANTMENTS.put("strider", depthStrider);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
// 1.9
try {
Enchantment frostWalker = Enchantment.getByName("FROST_WALKER");
final Enchantment frostWalker = Enchantment.getByName("FROST_WALKER");
if (frostWalker != null) {
ENCHANTMENTS.put("frostwalker", frostWalker);
ALIASENCHANTMENTS.put("frost", frostWalker);
ALIASENCHANTMENTS.put("walker", frostWalker);
}
Enchantment mending = Enchantment.getByName("MENDING");
final Enchantment mending = Enchantment.getByName("MENDING");
if (mending != null) {
ENCHANTMENTS.put("mending", mending);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
// 1.11
try {
Enchantment bindingCurse = Enchantment.getByName("BINDING_CURSE");
final Enchantment bindingCurse = Enchantment.getByName("BINDING_CURSE");
if (bindingCurse != null) {
ENCHANTMENTS.put("bindingcurse", bindingCurse);
ALIASENCHANTMENTS.put("bindcurse", bindingCurse);
ALIASENCHANTMENTS.put("binding", bindingCurse);
ALIASENCHANTMENTS.put("bind", bindingCurse);
}
Enchantment vanishingCurse = Enchantment.getByName("VANISHING_CURSE");
final Enchantment vanishingCurse = Enchantment.getByName("VANISHING_CURSE");
if (vanishingCurse != null) {
ENCHANTMENTS.put("vanishingcurse", vanishingCurse);
ALIASENCHANTMENTS.put("vanishcurse", vanishingCurse);
ALIASENCHANTMENTS.put("vanishing", vanishingCurse);
ALIASENCHANTMENTS.put("vanish", vanishingCurse);
}
Enchantment sweeping = Enchantment.getByName("SWEEPING_EDGE");
final Enchantment sweeping = Enchantment.getByName("SWEEPING_EDGE");
if (sweeping != null) {
ENCHANTMENTS.put("sweepingedge", sweeping);
ALIASENCHANTMENTS.put("sweepedge", sweeping);
ALIASENCHANTMENTS.put("sweeping", sweeping);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
try { // 1.13
Enchantment loyalty = Enchantment.getByName("LOYALTY");
final Enchantment loyalty = Enchantment.getByName("LOYALTY");
if (loyalty != null) {
ENCHANTMENTS.put("loyalty", loyalty);
ALIASENCHANTMENTS.put("loyal", loyalty);
ALIASENCHANTMENTS.put("return", loyalty);
}
Enchantment impaling = Enchantment.getByName("IMPALING");
final Enchantment impaling = Enchantment.getByName("IMPALING");
if (impaling != null) {
ENCHANTMENTS.put("impaling", impaling);
ALIASENCHANTMENTS.put("impale", impaling);
ALIASENCHANTMENTS.put("oceandamage", impaling);
ALIASENCHANTMENTS.put("oceandmg", impaling);
}
Enchantment riptide = Enchantment.getByName("RIPTIDE");
final Enchantment riptide = Enchantment.getByName("RIPTIDE");
if (riptide != null) {
ENCHANTMENTS.put("riptide", riptide);
ALIASENCHANTMENTS.put("rip", riptide);
ALIASENCHANTMENTS.put("tide", riptide);
ALIASENCHANTMENTS.put("launch", riptide);
}
Enchantment channelling = Enchantment.getByName("CHANNELING");
final Enchantment channelling = Enchantment.getByName("CHANNELING");
if (channelling != null) {
ENCHANTMENTS.put("channelling", channelling);
ALIASENCHANTMENTS.put("chanelling", channelling);
@@ -229,56 +230,74 @@ public class Enchantments {
ALIASENCHANTMENTS.put("chaneling", channelling);
ALIASENCHANTMENTS.put("channel", channelling);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
try { // 1.14
Enchantment multishot = Enchantment.getByName("MULTISHOT");
final Enchantment multishot = Enchantment.getByName("MULTISHOT");
if (multishot != null) {
ENCHANTMENTS.put("multishot", multishot);
ALIASENCHANTMENTS.put("tripleshot", multishot);
}
Enchantment quickCharge = Enchantment.getByName("QUICK_CHARGE");
final Enchantment quickCharge = Enchantment.getByName("QUICK_CHARGE");
if (quickCharge != null) {
ENCHANTMENTS.put("quickcharge", quickCharge);
ALIASENCHANTMENTS.put("quickdraw", quickCharge);
ALIASENCHANTMENTS.put("fastcharge", quickCharge);
ALIASENCHANTMENTS.put("fastdraw", quickCharge);
}
Enchantment piercing = Enchantment.getByName("PIERCING");
final Enchantment piercing = Enchantment.getByName("PIERCING");
if (piercing != null) {
ENCHANTMENTS.put("piercing", piercing);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
try { // 1.16
Enchantment soulspeed = Enchantment.getByName("SOUL_SPEED");
final Enchantment soulspeed = Enchantment.getByName("SOUL_SPEED");
if (soulspeed != null) {
ENCHANTMENTS.put("soulspeed", soulspeed);
ALIASENCHANTMENTS.put("soilspeed", soulspeed);
ALIASENCHANTMENTS.put("sandspeed", soulspeed);
}
} catch (IllegalArgumentException ignored) {}
} catch (final IllegalArgumentException ignored) {
}
try {
Class<?> namespacedKeyClass = Class.forName("org.bukkit.NamespacedKey");
Class<?> enchantmentClass = Class.forName("org.bukkit.enchantments.Enchantment");
final Class<?> namespacedKeyClass = Class.forName("org.bukkit.NamespacedKey");
final Class<?> enchantmentClass = Class.forName("org.bukkit.enchantments.Enchantment");
enchantmentClass.getDeclaredMethod("getByKey", namespacedKeyClass);
isFlat = true;
} catch (ClassNotFoundException | NoSuchMethodException e) {
} catch (final ClassNotFoundException | NoSuchMethodException e) {
isFlat = false;
}
}
public static Enchantment getByName(String name) {
private Enchantments() {
}
public static Enchantment getByName(final String name) {
if (name == null || name.isEmpty()) {
return null;
}
Enchantment enchantment = null;
if (isFlat) { // 1.13+ only
enchantment = Enchantment.getByKey(NamespacedKey.minecraft(name.toLowerCase()));
try {
enchantment = Enchantment.getByKey(NamespacedKey.minecraft(name.toLowerCase()));
} catch (IllegalArgumentException ignored) {
// NamespacedKey throws IAE if key does not match regex
}
}
if (enchantment == null) {
enchantment = Enchantment.getByName(name.toUpperCase());
}
if (enchantment == null) {
enchantment = Enchantment.getByName(name.toLowerCase());
}
if (enchantment == null) {
enchantment = Enchantment.getByName(name);
}
if (enchantment == null) {
enchantment = ENCHANTMENTS.get(name.toLowerCase(Locale.ENGLISH));
}
@@ -295,4 +314,20 @@ public class Enchantments {
public static Set<String> keySet() {
return ENCHANTMENTS.keySet();
}
public static void registerEnchantment(String name, Enchantment enchantment) {
if (ENCHANTMENTS.containsKey(name) || ALIASENCHANTMENTS.containsKey(name)) {
return;
}
ENCHANTMENTS.put(name, enchantment);
}
public static void registerAlias(String name, Enchantment enchantment) {
if (ENCHANTMENTS.containsKey(name) || ALIASENCHANTMENTS.containsKey(name) || !ENCHANTMENTS.containsValue(enchantment)) {
return;
}
ALIASENCHANTMENTS.put(name, enchantment);
}
}
@@ -1,6 +1,5 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.LocationUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import net.ess3.api.IEssentials;
import org.bukkit.GameMode;
@@ -15,7 +14,6 @@ import org.bukkit.inventory.ItemStack;
import java.util.Locale;
public class EssentialsBlockListener implements Listener {
private final transient IEssentials ess;
@@ -25,15 +23,9 @@ public class EssentialsBlockListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onBlockPlace(final BlockPlaceEvent event) {
// Do not rely on getItemInHand();
// http://leaky.bukkit.org/issues/663
final ItemStack is = LocationUtil.convertBlockToItem(event.getBlockPlaced());
final ItemStack is = event.getItemInHand();
if (is == null) {
return;
}
if (is.getType() == MaterialUtil.SPAWNER && event.getItemInHand() != null && event.getPlayer() != null && event.getItemInHand().getType() == MaterialUtil.SPAWNER) {
if (is.getType() == MaterialUtil.SPAWNER && ess.getPersistentDataProvider().getString(is, "convert") != null) {
final BlockState blockState = event.getBlockPlaced().getState();
if (blockState instanceof CreatureSpawner) {
final CreatureSpawner spawner = (CreatureSpawner) blockState;
@@ -49,14 +41,10 @@ public class EssentialsBlockListener implements Listener {
final User user = ess.getUser(event.getPlayer());
if (user.hasUnlimited(is) && user.getBase().getGameMode() == GameMode.SURVIVAL) {
class UnlimitedItemSpawnTask implements Runnable {
@Override
public void run() {
user.getBase().getInventory().addItem(is);
user.getBase().updateInventory();
}
}
ess.scheduleSyncDelayedTask(new UnlimitedItemSpawnTask());
ess.scheduleSyncDelayedTask(() -> {
user.getBase().getInventory().addItem(is);
user.getBase().updateInventory();
});
}
}
}
@@ -1,16 +1,31 @@
package com.earth2me.essentials;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import net.ess3.api.IEssentials;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.*;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.*;
import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.entity.EntityCombustEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.entity.PotionSplashEvent;
import org.bukkit.inventory.ItemStack;
import java.util.List;
@@ -20,13 +35,12 @@ import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsEntityListener implements Listener {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private static final transient Pattern powertoolPlayer = Pattern.compile("\\{player\\}");
private final IEssentials ess;
public EssentialsEntityListener(IEssentials ess) {
public EssentialsEntityListener(final IEssentials ess) {
this.ess = ess;
}
@@ -42,7 +56,7 @@ public class EssentialsEntityListener implements Listener {
} else if (eDefend instanceof Ageable) {
final ItemStack hand = attacker.getBase().getItemInHand();
if (ess.getSettings().isMilkBucketEasterEggEnabled()
&& hand != null && hand.getType() == Material.MILK_BUCKET) {
&& hand != null && hand.getType() == Material.MILK_BUCKET) {
((Ageable) eDefend).setBaby();
hand.setType(Material.BUCKET);
attacker.getBase().setItemInHand(hand);
@@ -99,6 +113,7 @@ public class EssentialsEntityListener implements Listener {
LOGGER.log(Level.INFO, String.format("[PT] %s issued server command: /%s", attacker.getName(), command));
}
}
ess.scheduleSyncDelayedTask(new PowerToolInteractTask());
event.setCancelled(true);
@@ -128,9 +143,9 @@ public class EssentialsEntityListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onEntityCombustByEntity(final EntityCombustByEntityEvent event) {
if (event.getCombuster() instanceof Arrow && event.getEntity() instanceof Player) {
Arrow combuster = (Arrow) event.getCombuster();
final Arrow combuster = (Arrow) event.getCombuster();
if (combuster.getShooter() instanceof Player) {
Player shooter = (Player) combuster.getShooter();
final Player shooter = (Player) combuster.getShooter();
if (shooter.hasMetadata("NPC")) {
return;
}
@@ -147,7 +162,7 @@ public class EssentialsEntityListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerDeathEvent(final PlayerDeathEvent event) {
Entity entity = event.getEntity();
final Entity entity = event.getEntity();
if (entity.hasMetadata("NPC")) {
return;
}
@@ -180,11 +195,11 @@ public class EssentialsEntityListener implements Listener {
if (user.isAuthorized("essentials.keepinv")) {
event.setKeepInventory(true);
event.getDrops().clear();
ISettings.KeepInvPolicy vanish = ess.getSettings().getVanishingItemsPolicy();
ISettings.KeepInvPolicy bind = ess.getSettings().getBindingItemsPolicy();
final ISettings.KeepInvPolicy vanish = ess.getSettings().getVanishingItemsPolicy();
final ISettings.KeepInvPolicy bind = ess.getSettings().getBindingItemsPolicy();
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_11_2_R01) && (vanish != ISettings.KeepInvPolicy.KEEP || bind != ISettings.KeepInvPolicy.KEEP)) {
for (ItemStack stack : event.getEntity().getInventory()) {
if (stack != null) {
for (final ItemStack stack : event.getEntity().getInventory()) {
if (stack != null && !MaterialUtil.isAir(stack.getType())) {
if (stack.getEnchantments().containsKey(Enchantment.VANISHING_CURSE)) {
if (vanish == ISettings.KeepInvPolicy.DELETE) {
event.getEntity().getInventory().remove(stack);
@@ -203,10 +218,12 @@ public class EssentialsEntityListener implements Listener {
}
}
}
ItemStack[] armor = event.getEntity().getInventory().getArmorContents();
// Now check armor
final ItemStack[] armor = event.getEntity().getInventory().getArmorContents();
for (int i = 0; i < armor.length; i++) {
ItemStack stack = armor[i];
if (stack != null) {
final ItemStack stack = armor[i];
if (stack != null && !MaterialUtil.isAir(stack.getType())) {
if (stack.getEnchantments().containsKey(Enchantment.VANISHING_CURSE)) {
if (vanish == ISettings.KeepInvPolicy.DELETE) {
armor[i] = null;
@@ -230,6 +247,24 @@ public class EssentialsEntityListener implements Listener {
}
}
event.getEntity().getInventory().setArmorContents(armor);
// Now check offhand
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_9_R01)) {
final ItemStack stack = event.getEntity().getInventory().getItemInOffHand();
//noinspection ConstantConditions
if (stack != null && !MaterialUtil.isAir(stack.getType())) {
final boolean isVanish = stack.getEnchantments().containsKey(Enchantment.VANISHING_CURSE);
final boolean isBind = stack.getEnchantments().containsKey(Enchantment.BINDING_CURSE);
if (isVanish || isBind) {
event.getEntity().getInventory().setItemInOffHand(null);
if ((isVanish && vanish == ISettings.KeepInvPolicy.DROP) || (isBind && bind == ISettings.KeepInvPolicy.DROP)) {
if (!event.getDrops().contains(stack)) {
event.getDrops().add(stack);
}
}
}
}
}
}
}
}
@@ -257,7 +292,7 @@ public class EssentialsEntityListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPotionSplashEvent(final PotionSplashEvent event) {
for (LivingEntity entity : event.getAffectedEntities()) {
for (final LivingEntity entity : event.getAffectedEntities()) {
if (entity instanceof Player && ess.getUser((Player) entity).isGodModeEnabled()) {
event.setIntensity(entity, 0d);
}
@@ -265,7 +300,7 @@ public class EssentialsEntityListener implements Listener {
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityShootBow(EntityShootBowEvent event) {
public void onEntityShootBow(final EntityShootBowEvent event) {
if (event.getEntity() instanceof Player) {
final User user = ess.getUser((Player) event.getEntity());
if (user.isAfk()) {
@@ -275,7 +310,7 @@ public class EssentialsEntityListener implements Listener {
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onEntityTarget(EntityTargetEvent event) {
public void onEntityTarget(final EntityTargetEvent event) {
if (event.getTarget() instanceof Player) {
final User user = ess.getUser((Player) event.getTarget());
if (user.isVanished()) {
@@ -6,14 +6,27 @@ import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.TextInput;
import com.earth2me.essentials.textreader.TextPager;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.LocationUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import io.papermc.lib.PaperLib;
import net.ess3.api.IEssentials;
import net.ess3.api.events.AfkStatusChangeEvent;
import org.bukkit.*;
import net.ess3.provider.CommandSendListenerProvider;
import net.ess3.provider.providers.BukkitCommandSendListenerProvider;
import net.ess3.provider.providers.PaperCommandSendListenerProvider;
import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.FormattedCommandAlias;
import org.bukkit.command.PluginCommand;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -23,8 +36,20 @@ import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.*;
import org.bukkit.event.player.AsyncPlayerChatEvent;
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.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;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
@@ -34,18 +59,28 @@ import org.bukkit.inventory.PlayerInventory;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.text.NumberFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsPlayerListener implements Listener {
public class EssentialsPlayerListener implements Listener, FakeAccessor {
private static final Logger LOGGER = Logger.getLogger("Essentials");
private final transient IEssentials ess;
private final ConcurrentHashMap<UUID, Integer> pendingMotdTasks = new ConcurrentHashMap<>();
public EssentialsPlayerListener(final IEssentials parent) {
this.ess = parent;
@@ -55,7 +90,7 @@ public class EssentialsPlayerListener implements Listener {
try {
Class.forName("org.bukkit.event.entity.EntityPickupItemEvent");
return true;
} catch (ClassNotFoundException ignored) {
} catch (final ClassNotFoundException ignored) {
return false;
}
}
@@ -64,7 +99,16 @@ public class EssentialsPlayerListener implements Listener {
try {
Class.forName("org.bukkit.event.player.PlayerCommandSendEvent");
return true;
} catch (ClassNotFoundException ignored) {
} catch (final ClassNotFoundException ignored) {
return false;
}
}
private static boolean isPaperCommandSendEvent() {
try {
Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent");
return true;
} catch (final ClassNotFoundException ignored) {
return false;
}
}
@@ -73,7 +117,7 @@ public class EssentialsPlayerListener implements Listener {
try {
Class.forName("org.bukkit.event.player.PlayerPickupArrowEvent");
return true;
} catch (ClassNotFoundException ignored) {
} catch (final ClassNotFoundException ignored) {
return false;
}
}
@@ -91,8 +135,10 @@ public class EssentialsPlayerListener implements Listener {
ess.getServer().getPluginManager().registerEvents(new PickupListenerPre1_12(), ess);
}
if (isCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new CommandSendListener(), ess);
if (isPaperCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new PaperCommandSendListenerProvider(new CommandSendFilter()), ess);
} else if (isCommandSendEvent()) {
ess.getServer().getPluginManager().registerEvents(new BukkitCommandSendListenerProvider(new CommandSendFilter()), ess);
}
}
@@ -113,7 +159,7 @@ public class EssentialsPlayerListener implements Listener {
if (user.isMuted()) {
event.setCancelled(true);
String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
if (dateDiff == null) {
user.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
} else {
@@ -130,7 +176,7 @@ public class EssentialsPlayerListener implements Listener {
it.remove();
}
}
} catch (UnsupportedOperationException ex) {
} catch (final UnsupportedOperationException ex) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Ignore could not block chat due to custom chat plugin event.", ex);
} else {
@@ -138,7 +184,7 @@ public class EssentialsPlayerListener implements Listener {
}
}
user.updateActivityOnInteract(true);
user.updateActivityOnChat(true);
user.setDisplayNick();
}
@@ -171,8 +217,8 @@ public class EssentialsPlayerListener implements Listener {
to.setY(from.getY());
to.setZ(from.getZ());
try {
event.setTo(LocationUtil.getSafeDestination(to));
} catch (Exception ex) {
event.setTo(LocationUtil.getSafeDestination(ess, to));
} catch (final Exception ex) {
event.setTo(to);
}
return;
@@ -187,15 +233,22 @@ public class EssentialsPlayerListener implements Listener {
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);
}
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();
final String msg = ess.getSettings().getCustomQuitMessage()
.replace("{PLAYER}", player.getDisplayName())
.replace("{USERNAME}", player.getName())
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()));
.replace("{PLAYER}", player.getDisplayName())
.replace("{USERNAME}", player.getName())
.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)));
event.setQuitMessage(msg.isEmpty() ? null : msg);
}
@@ -212,10 +265,10 @@ public class EssentialsPlayerListener implements Listener {
user.getBase().getOpenInventory().getTopInventory().clear();
}
ArrayList<HumanEntity> viewers = new ArrayList<>(user.getBase().getInventory().getViewers());
for (HumanEntity viewer : viewers) {
final ArrayList<HumanEntity> viewers = new ArrayList<>(user.getBase().getInventory().getViewers());
for (final HumanEntity viewer : viewers) {
if (viewer instanceof Player) {
User uviewer = ess.getUser((Player) viewer);
final User uviewer = ess.getUser((Player) viewer);
if (uviewer.isInvSee()) {
uviewer.getBase().closeInventory();
}
@@ -274,14 +327,18 @@ public class EssentialsPlayerListener implements Listener {
user.startTransaction();
final String lastAccountName = user.getLastAccountName(); // For comparison
user.setLastAccountName(user.getBase().getName());
user.setLastLogin(currentTime);
user.setDisplayNick();
updateCompass(user);
// 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 (String p : ess.getVanishedPlayersNew()) {
Player toVanish = ess.getServer().getPlayerExact(p);
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()) {
@@ -295,36 +352,48 @@ public class EssentialsPlayerListener implements Listener {
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()) {
//NOOP
effectiveMessage = null;
} else if (ess.getSettings().isCustomJoinMessage()) {
String msg = ess.getSettings().getCustomJoinMessage()
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers()))
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()));
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers()))
.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;
}
int motdDelay = ess.getSettings().getMotdDelay() / 50;
DelayMotdTask motdTask = new DelayMotdTask(user);
if (motdDelay > 0) {
ess.scheduleSyncDelayedTask(motdTask, motdDelay);
} else {
motdTask.run();
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")) {
final List<String> mail = user.getMails();
if (mail.isEmpty()) {
if (user.getUnreadMailAmount() == 0) {
if (ess.getSettings().isNotifyNoNewMail()) {
user.sendMessage(tl("noNewMail")); // Only notify if they want us to.
}
@@ -333,9 +402,17 @@ public class EssentialsPlayerListener implements Listener {
}
}
if (user.isAuthorized("essentials.updatecheck")) {
ess.runTaskAsynchronously(() -> {
for (String str : ess.getUpdateChecker().getVersionMessages(false, false)) {
user.sendMessage(str);
}
});
}
if (user.isAuthorized("essentials.fly.safelogin")) {
user.getBase().setFallDistance(0);
if (LocationUtil.shouldFly(user.getLocation())) {
if (LocationUtil.shouldFly(ess, user.getLocation())) {
user.getBase().setAllowFlight(true);
user.getBase().setFlying(true);
if (ess.getSettings().isSendFlyEnableOnJoin()) {
@@ -368,18 +445,20 @@ public class EssentialsPlayerListener implements Listener {
class DelayMotdTask implements Runnable {
private final User user;
public DelayMotdTask(User user) {
DelayMotdTask(final User user) {
this.user = user;
}
@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 (IOException ex) {
} catch (final IOException ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
} else {
@@ -407,7 +486,7 @@ public class EssentialsPlayerListener implements Listener {
private void updateCompass(final User user) {
if (ess.getSettings().isCompassTowardsHomePerm() && !user.isAuthorized("essentials.home.compass")) return;
Location loc = user.getHome(user.getLocation());
final Location loc = user.getHome(user.getLocation());
if (loc == null) {
PaperLib.getBedSpawnLocationAsync(user.getBase(), false).thenAccept(location -> {
if (location != null) {
@@ -421,62 +500,51 @@ public class EssentialsPlayerListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLoginBanned(final PlayerLoginEvent event) {
switch (event.getResult()) {
case KICK_BANNED:
BanEntry banEntry = ess.getServer().getBanList(BanList.Type.NAME).getBanEntry(event.getPlayer().getName());
if (banEntry != null) {
Date banExpiry = banEntry.getExpiration();
if (banExpiry != null) {
String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
event.setKickMessage(tl("tempbanJoin", expiry, banEntry.getReason()));
} else {
event.setKickMessage(tl("banJoin", banEntry.getReason()));
}
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(tl("tempbanJoin", expiry, banEntry.getReason()));
} else {
banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress());
if (banEntry != null) {
event.setKickMessage(tl("banIpJoin", banEntry.getReason()));
}
event.setKickMessage(tl("banJoin", banEntry.getReason()));
}
break;
default:
break;
} else {
banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress());
if (banEntry != null) {
event.setKickMessage(tl("banIpJoin", banEntry.getReason()));
}
}
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerLogin(final PlayerLoginEvent event) {
switch (event.getResult()) {
case KICK_FULL:
final User kfuser = ess.getUser(event.getPlayer());
if (kfuser.isAuthorized("essentials.joinfullserver")) {
event.allow();
return;
}
if (event.getResult() == Result.KICK_FULL) {
final User kfuser = ess.getUser(event.getPlayer());
if (kfuser.isAuthorized("essentials.joinfullserver")) {
event.allow();
return;
}
if (ess.getSettings().isCustomServerFullMessage()) {
event.disallow(Result.KICK_FULL, tl("serverFull"));
break;
default:
break;
}
}
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerTeleport(final PlayerTeleportEvent event) {
final boolean backListener = ess.getSettings().registerBackInListener();
final boolean teleportInvulnerability = ess.getSettings().isTeleportInvulnerability();
if (backListener || teleportInvulnerability) {
Player player = event.getPlayer();
if (player.hasMetadata("NPC")) {
return;
}
final User user = ess.getUser(player);
//There is TeleportCause.COMMMAND but plugins have to actively pass the cause in on their teleports.
if (user.isAuthorized("essentials.back.onteleport") && backListener && (event.getCause() == TeleportCause.PLUGIN || event.getCause() == TeleportCause.COMMAND)) {
user.setLastLocation();
}
if (teleportInvulnerability && (event.getCause() == TeleportCause.PLUGIN || event.getCause() == TeleportCause.COMMAND)) {
user.enableInvulnerabilityAfterTeleport();
}
final Player player = event.getPlayer();
if (player.hasMetadata("NPC") || !(event.getCause() == TeleportCause.PLUGIN || event.getCause() == TeleportCause.COMMAND)) {
return;
}
final User user = ess.getUser(player);
if (ess.getSettings().registerBackInListener() && user.isAuthorized("essentials.back.onteleport")) {
user.setLastLocation();
}
if (ess.getSettings().isTeleportInvulnerability()) {
user.enableInvulnerabilityAfterTeleport();
}
}
@@ -501,17 +569,38 @@ public class EssentialsPlayerListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
final Player player = event.getPlayer();
final String cmd = event.getMessage().toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH);
final int argStartIndex = event.getMessage().indexOf(" ");
final String args = argStartIndex == -1 ? "" // No arguments present
: event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
PluginCommand pluginCommand = ess.getServer().getPluginCommand(cmd);
// If the plugin command does not exist, check if it is an alias from commands.yml
if (ess.getServer().getPluginCommand(cmd) == null) {
final Command knownCommand = ess.getKnownCommandsProvider().getKnownCommands().get(cmd);
if (knownCommand instanceof FormattedCommandAlias) {
final FormattedCommandAlias command = (FormattedCommandAlias) knownCommand;
for (String fullCommand : ess.getFormattedCommandAliasProvider().createCommands(command, event.getPlayer(), args.split(" "))) {
handlePlayerCommandPreprocess(event, fullCommand);
}
return;
}
}
// Handle the command given from the event.
handlePlayerCommandPreprocess(event, cmd + args);
}
public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent event, final String effectiveCommand) {
final Player player = event.getPlayer();
final String cmd = effectiveCommand.toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH);
final PluginCommand pluginCommand = ess.getServer().getPluginCommand(cmd);
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
User user = ess.getUser(player);
|| (!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")) {
for (User spyer : ess.getOnlineUsers()) {
for (final User spyer : ess.getOnlineUsers()) {
if (spyer.isSocialSpyEnabled() && !player.equals(spyer.getBase())) {
if (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) {
spyer.sendMessage(tl("socialSpyMutedPrefix") + player.getDisplayName() + ": " + event.getMessage());
@@ -527,7 +616,7 @@ public class EssentialsPlayerListener implements Listener {
final User user = ess.getUser(player);
if (user.isMuted() && (ess.getSettings().getMuteCommands().contains(cmd) || ess.getSettings().getMuteCommands().contains("*"))) {
event.setCancelled(true);
String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
if (dateDiff == null) {
player.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
} else {
@@ -545,8 +634,10 @@ public class EssentialsPlayerListener implements Listener {
switch (pluginCommand.getName()) {
case "afk":
update = false;
// fall through
case "vanish":
broadcast = false;
break;
}
}
@@ -554,19 +645,19 @@ public class EssentialsPlayerListener implements Listener {
user.updateActivityOnInteract(broadcast);
}
if (ess.getSettings().isCommandCooldownsEnabled() && pluginCommand != null
&& !user.isAuthorized("essentials.commandcooldowns.bypass")) {
int argStartIndex = event.getMessage().indexOf(" ");
String args = argStartIndex == -1 ? "" // No arguments present
: " " + event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
String fullCommand = pluginCommand.getName() + args;
if (ess.getSettings().isCommandCooldownsEnabled()
&& !user.isAuthorized("essentials.commandcooldowns.bypass")) {
final int argStartIndex = effectiveCommand.indexOf(" ");
final String args = argStartIndex == -1 ? "" // No arguments present
: " " + effectiveCommand.substring(argStartIndex); // arguments start at argStartIndex; substring from there.
final String fullCommand = pluginCommand == null ? effectiveCommand : pluginCommand.getName() + args;
// Used to determine whether a user already has an existing cooldown
// If so, no need to check for (and write) new ones.
boolean cooldownFound = false;
// Iterate over a copy of getCommandCooldowns in case of concurrent modifications
for (Entry<Pattern, Long> entry : new HashMap<>(user.getCommandCooldowns()).entrySet()) {
for (final Entry<Pattern, Long> entry : new HashMap<>(user.getCommandCooldowns()).entrySet()) {
// Remove any expired cooldowns
if (entry.getValue() <= System.currentTimeMillis()) {
user.clearCommandCooldown(entry.getKey());
@@ -574,7 +665,7 @@ public class EssentialsPlayerListener implements Listener {
} else if (entry.getKey().matcher(fullCommand).matches()) {
// User's current cooldown hasn't expired, inform and terminate cooldown code.
if (entry.getValue() > System.currentTimeMillis()) {
String commandCooldownTime = DateUtil.formatDateDiff(entry.getValue());
final String commandCooldownTime = DateUtil.formatDateDiff(entry.getValue());
user.sendMessage(tl("commandCooldown", commandCooldownTime));
cooldownFound = true;
event.setCancelled(true);
@@ -584,13 +675,13 @@ public class EssentialsPlayerListener implements Listener {
}
if (!cooldownFound) {
Entry<Pattern, Long> cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand);
final Entry<Pattern, Long> cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand);
if (cooldownEntry != null) {
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Applying " + cooldownEntry.getValue() + "ms cooldown on /" + fullCommand + " for" + user.getName() + ".");
}
Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue());
final Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue());
user.addCommandCooldown(cooldownEntry.getKey(), expiry, ess.getSettings().isCommandCooldownPersistent(fullCommand));
}
}
@@ -603,9 +694,9 @@ public class EssentialsPlayerListener implements Listener {
if (ess.getSettings().isWorldChangeFlyResetEnabled()) {
if (user.getBase().getGameMode() != GameMode.CREATIVE
// COMPAT: String compare for 1.7.10
&& !user.getBase().getGameMode().name().equals("SPECTATOR")
&& !user.isAuthorized("essentials.fly")) {
// COMPAT: String compare for 1.7.10
&& !user.getBase().getGameMode().name().equals("SPECTATOR")
&& !user.isAuthorized("essentials.fly")) {
user.getBase().setFallDistance(0f);
user.getBase().setAllowFlight(false);
}
@@ -656,7 +747,10 @@ public class EssentialsPlayerListener implements Listener {
switch (event.getAction()) {
case RIGHT_CLICK_BLOCK:
if (!event.isCancelled() && MaterialUtil.isBed(event.getClickedBlock().getType()) && ess.getSettings().getUpdateBedAtDaytime()) {
User player = ess.getUser(event.getPlayer());
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_13_2_R01) && ((org.bukkit.block.data.type.Bed) event.getClickedBlock().getBlockData()).isOccupied()) {
break;
}
final User player = ess.getUser(event.getPlayer());
if (player.isAuthorized("essentials.sethome.bed") && player.getWorld().getEnvironment().equals(World.Environment.NORMAL)) {
player.getBase().setBedSpawnLocation(event.getClickedBlock().getLocation());
// In 1.15 and above, vanilla sends its own bed spawn message.
@@ -674,6 +768,7 @@ public class EssentialsPlayerListener implements Listener {
break;
}
}
// fall through
case LEFT_CLICK_BLOCK:
if (event.getItem() != null && event.getItem().getType() != Material.AIR) {
final User user = ess.getUser(event.getPlayer());
@@ -702,7 +797,7 @@ public class EssentialsPlayerListener implements Listener {
class DelayedClickJumpTask implements Runnable {
@Override
public void run() {
Location loc = user.getLocation();
final Location loc = user.getLocation();
loc.setX(otarget.getX());
loc.setZ(otarget.getZ());
while (LocationUtil.isBlockDamaging(loc.getWorld(), loc.getBlockX(), loc.getBlockY() - 1, loc.getBlockZ())) {
@@ -711,8 +806,9 @@ public class EssentialsPlayerListener implements Listener {
PaperLib.teleportAsync(user.getBase(), loc, TeleportCause.PLUGIN);
}
}
ess.scheduleSyncDelayedTask(new DelayedClickJumpTask());
} catch (Exception ex) {
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
@@ -741,6 +837,7 @@ public class EssentialsPlayerListener implements Listener {
LOGGER.log(Level.INFO, String.format("[PT] %s issued server command: /%s", user.getName(), command));
}
}
ess.scheduleSyncDelayedTask(new PowerToolUseTask());
}
@@ -761,8 +858,8 @@ public class EssentialsPlayerListener implements Listener {
clickedInventory = event.getRawSlot() < top.getSize() ? top : event.getView().getBottomInventory();
}
final User user = ess.getUser((Player) event.getWhoClicked());
if (type == InventoryType.PLAYER) {
final User user = ess.getUser((Player) event.getWhoClicked());
final InventoryHolder invHolder = top.getHolder();
if (invHolder instanceof HumanEntity) {
final User invOwner = ess.getUser((Player) invHolder);
@@ -772,29 +869,27 @@ public class EssentialsPlayerListener implements Listener {
}
}
} else if (type == InventoryType.ENDER_CHEST) {
final User user = ess.getUser((Player) event.getWhoClicked());
if (user.isEnderSee() && (!user.isAuthorized("essentials.enderchest.modify"))) {
if (user.isEnderSee() && !user.isAuthorized("essentials.enderchest.modify")) {
event.setCancelled(true);
refreshPlayer = user.getBase();
}
} else if (type == InventoryType.WORKBENCH) {
User user = ess.getUser((Player) event.getWhoClicked());
if (user.isRecipeSee()) {
event.setCancelled(true);
refreshPlayer = user.getBase();
}
} else if (type == InventoryType.CHEST && top.getSize() == 9) {
final User user = ess.getUser((Player) event.getWhoClicked());
final InventoryHolder invHolder = top.getHolder();
if (invHolder instanceof HumanEntity && user.isInvSee()) {
if (invHolder instanceof HumanEntity && user.isInvSee() && event.getClick() != ClickType.MIDDLE) {
event.setCancelled(true);
refreshPlayer = user.getBase();
}
} else if (clickedInventory != null && clickedInventory.getType() == InventoryType.PLAYER) {
if (ess.getSettings().isDirectHatAllowed() && event.getClick() == ClickType.LEFT && event.getSlot() == 39
&& event.getCursor().getType() != Material.AIR && event.getCursor().getType().getMaxDurability() == 0
&& !MaterialUtil.isSkull(event.getCursor().getType())
&& ess.getUser(event.getWhoClicked()).isAuthorized("essentials.hat") && !ess.getUser(event.getWhoClicked()).isAuthorized("essentials.hat.prevent-type." + event.getCursor().getType().name().toLowerCase())) {
&& event.getCursor().getType() != Material.AIR && event.getCursor().getType().getMaxDurability() == 0
&& !MaterialUtil.isSkull(event.getCursor().getType())
&& user.isAuthorized("essentials.hat") && !user.isAuthorized("essentials.hat.prevent-type." + event.getCursor().getType().name().toLowerCase())
&& !isPreventBindingHat(user, (PlayerInventory) clickedInventory)) {
event.setCancelled(true);
final PlayerInventory inv = (PlayerInventory) clickedInventory;
final ItemStack head = inv.getHelmet();
@@ -808,6 +903,14 @@ public class EssentialsPlayerListener implements Listener {
}
}
private boolean isPreventBindingHat(User user, PlayerInventory inventory) {
if (VersionUtil.getServerBukkitVersion().isHigherThan(VersionUtil.v1_9_4_R01)) {
final ItemStack head = inventory.getHelmet();
return head != null && head.getEnchantments().containsKey(Enchantment.BINDING_CURSE) && !user.isAuthorized("essentials.hat.ignore-binding");
}
return false;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onInventoryCloseEvent(final InventoryCloseEvent event) {
Player refreshPlayer = null;
@@ -848,7 +951,7 @@ public class EssentialsPlayerListener implements Listener {
user.updateActivityOnInteract(true);
}
private final class ArrowPickupListener implements Listener {
private static final class ArrowPickupListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onArrowPickup(final org.bukkit.event.player.PlayerPickupArrowEvent event) {
if (event.getItem().hasMetadata(Commandfireball.FIREBALL_META_KEY)) {
@@ -881,17 +984,16 @@ public class EssentialsPlayerListener implements Listener {
}
}
private final class CommandSendListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void onCommandSend(final PlayerCommandSendEvent event) {
User user = ess.getUser(event.getPlayer());
private final class CommandSendFilter implements CommandSendListenerProvider.Filter {
@Override
public Predicate<String> apply(Player player) {
final User user = ess.getUser(player);
final Set<PluginCommand> checked = new HashSet<>();
final Set<PluginCommand> toRemove = new HashSet<>();
Set<PluginCommand> checked = new HashSet<>();
Set<PluginCommand> toRemove = new HashSet<>();
event.getCommands().removeIf(label -> {
return label -> {
if (isEssentialsCommand(label)) {
PluginCommand command = ess.getServer().getPluginCommand(label);
final PluginCommand command = ess.getServer().getPluginCommand(label);
if (!checked.contains(command)) {
checked.add(command);
if (!user.isAuthorized(command.getName().equals("r") ? "essentials.msg" : "essentials." + command.getName())) {
@@ -901,25 +1003,26 @@ public class EssentialsPlayerListener implements Listener {
return toRemove.contains(command);
}
return false;
});
if (ess.getSettings().isDebug()) {
ess.getLogger().info("Removed commands: " + toRemove.toString());
}
};
}
/**
* Returns true if all of the following are true:
* - The command is a plugin command
* - The plugin command is from a plugin in an essentials-controlled package
* - The plugin command is from an official EssentialsX plugin or addon
* - There is no known alternative OR the alternative is overridden by Essentials
*/
private boolean isEssentialsCommand(String label) {
PluginCommand command = ess.getServer().getPluginCommand(label);
private boolean isEssentialsCommand(final String label) {
final PluginCommand command = ess.getServer().getPluginCommand(label);
return command != null
&& (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials"))
&& (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));
}
}
@Override
public void getUser(Player player) {
ess.getUser(player);
}
}
@@ -1,6 +1,7 @@
package com.earth2me.essentials;
import com.earth2me.essentials.register.payment.Methods;
import com.earth2me.essentials.economy.EconomyLayer;
import com.earth2me.essentials.economy.EconomyLayers;
import net.ess3.api.IEssentials;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -10,7 +11,6 @@ import org.bukkit.event.server.PluginEnableEvent;
import java.util.logging.Level;
public class EssentialsPluginListener implements Listener, IConf {
private final transient IEssentials ess;
@@ -26,8 +26,11 @@ public class EssentialsPluginListener implements Listener, IConf {
ess.getPermissionsHandler().setUseSuperperms(ess.getSettings().useBukkitPermissions());
ess.getPermissionsHandler().checkPermissions();
ess.getAlternativeCommandsHandler().addPlugin(event.getPlugin());
if (!Methods.hasMethod() && Methods.setMethod(ess.getServer().getPluginManager())) {
ess.getLogger().log(Level.INFO, "Payment method found (" + Methods.getMethod().getLongName() + " version: " + ess.getPaymentMethod().getMethod().getVersion() + ")");
if (EconomyLayers.isServerStarted()) {
final EconomyLayer layer = EconomyLayers.onPluginEnable(event.getPlugin());
if (layer != null) {
ess.getLogger().log(Level.INFO, "Essentials found a compatible payment resolution method: " + layer.getName() + " (v" + layer.getPluginVersion() + ")!");
}
}
}
@@ -38,10 +41,13 @@ public class EssentialsPluginListener implements Listener, IConf {
}
ess.getPermissionsHandler().checkPermissions();
ess.getAlternativeCommandsHandler().removePlugin(event.getPlugin());
// Check to see if the plugin thats being disabled is the one we are using
if (ess.getPaymentMethod() != null && Methods.hasMethod() && Methods.checkDisabled(event.getPlugin())) {
Methods.reset();
ess.getLogger().log(Level.INFO, "Payment method was disabled. No longer accepting payments.");
if (EconomyLayers.onPluginDisable(event.getPlugin())) {
final EconomyLayer layer = EconomyLayers.getSelectedLayer();
if (layer != null) {
ess.getLogger().log(Level.INFO, "Essentials found a new compatible payment resolution method: " + layer.getName() + " (v" + layer.getPluginVersion() + ")!");
} else {
ess.getLogger().log(Level.INFO, "Active payment resolution method has been disabled! Falling back to Essentials' default payment resolution system!");
}
}
}
@@ -15,7 +15,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
public class EssentialsServerListener implements Listener {
private static final List<String> ignoredSLPECallers = Arrays.asList(
".LegacyPingHandler.channelRead(", // CB responding to pings from pre-Netty clients
@@ -24,9 +23,9 @@ public class EssentialsServerListener implements Listener {
);
private final transient IEssentials ess;
private final boolean isPaperSample;
private boolean unsupportedLogged = false;
private boolean npeWarned = false;
private final boolean isPaperSample;
private Method setSampleText;
private Method getSampleText;
@@ -52,34 +51,34 @@ public class EssentialsServerListener implements Listener {
public void onServerListPing(final ServerListPingEvent event) throws Exception {
if (isPaperSample) {
try {
List<String> playerNames = (List<String>) getSampleText.invoke(event, null);
final List<String> playerNames = (List<String>) getSampleText.invoke(event, null);
playerNames.removeIf(player -> ess.getUser(player).isVanished());
setSampleText.invoke(event, playerNames);
} catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
} catch (final IllegalAccessException | InvocationTargetException | ClassCastException e) {
if (!unsupportedLogged && shouldWarnSLPECaller(e)) {
ess.getLogger().log(Level.WARNING, "Unable to hide players from server list ping "
+ "using Paper 1.12 method!", e);
+ "using Paper 1.12 method!", e);
unsupportedLogged = true;
}
} catch (NullPointerException e) {
} catch (final NullPointerException e) {
if (!npeWarned && shouldWarnSLPECaller(e)) {
npeWarned = true;
Exception ex = new Exception("A plugin has fired a ServerListPingEvent "
+ "without implementing Paper's methods. Point the author to https://git.io/v7Xzl.");
final Exception ex = new Exception("A plugin has fired a ServerListPingEvent "
+ "without implementing Paper's methods. Point the author to https://git.io/v7Xzl.");
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
} else {
try {
Iterator<Player> iterator = event.iterator();
final Iterator<Player> iterator = event.iterator();
while (iterator.hasNext()) {
Player player = iterator.next();
final Player player = iterator.next();
if (ess.getUser(player).isVanished()) {
iterator.remove();
}
}
} catch (UnsupportedOperationException e) {
} catch (final UnsupportedOperationException e) {
if (!unsupportedLogged && shouldWarnSLPECaller(e)) {
ess.getLogger().log(Level.WARNING, "Could not hide vanished players while handling " + event.getClass().getName(), e);
unsupportedLogged = true;
@@ -96,16 +95,16 @@ public class EssentialsServerListener implements Listener {
* @param throwable A throwable caught by a catch block
* @return Whether or not to send a warning about this particular caller
*/
private boolean shouldWarnSLPECaller(Throwable throwable) {
private boolean shouldWarnSLPECaller(final Throwable throwable) {
final int maxStackDepth = 20; // Limit the depth when searching through the stack trace
int depth = 0;
for (StackTraceElement element : throwable.getStackTrace()) {
for (final StackTraceElement element : throwable.getStackTrace()) {
depth++;
if (depth > maxStackDepth) {
break;
}
for (String ignoredString : ignoredSLPECallers) {
for (final String ignoredString : ignoredSLPECallers) {
if (element.toString().contains(ignoredString)) {
return false; // We know about this error and should ignore it, so don't warn
}
@@ -3,19 +3,24 @@ package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import org.bukkit.entity.Player;
import java.util.*;
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;
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 transient long lastPoll = System.nanoTime();
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 int skip1 = 0;
private int skip2 = 0;
private final long maxTime = 10 * 1000000;
private final long tickInterval = 50;
EssentialsTimer(final IEssentials ess) {
this.ess = ess;
@@ -33,13 +38,14 @@ public class EssentialsTimer implements Runnable {
if (history.size() > 10) {
history.remove();
}
double tps = tickInterval * 1000000.0 / timeSpent;
final double tps = tickInterval * 1000000.0 / timeSpent;
if (tps <= 21) {
history.add(tps);
}
lastPoll = startTime;
int count = 0;
for (Player player : ess.getOnlinePlayers()) {
onlineUsers.clear();
for (final Player player : ess.getOnlinePlayers()) {
count++;
if (skip1 > 0) {
skip1--;
@@ -56,7 +62,7 @@ public class EssentialsTimer implements Runnable {
onlineUsers.add(user.getBase().getUniqueId());
user.setLastOnlineActivity(currentTime);
user.checkActivity();
} catch (Exception e) {
} catch (final Exception e) {
ess.getLogger().log(Level.WARNING, "EssentialsTimer Error:", e);
}
}
@@ -96,7 +102,7 @@ public class EssentialsTimer implements Runnable {
public double getAverageTPS() {
double avg = 0;
for (Double f : history) {
for (final Double f : history) {
if (f != null) {
avg += f;
}
@@ -1,25 +1,51 @@
package com.earth2me.essentials;
import com.earth2me.essentials.config.ConfigurateUtil;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.config.EssentialsUserConfiguration;
import com.earth2me.essentials.craftbukkit.BanLookup;
import com.earth2me.essentials.craftbukkit.FakeWorld;
import com.earth2me.essentials.settings.Spawns;
import com.earth2me.essentials.storage.YamlStorageWriter;
import com.earth2me.essentials.utils.StringUtil;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.gson.reflect.TypeToken;
import net.ess3.api.IEssentials;
import net.essentialsx.api.v2.services.mail.MailMessage;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.spongepowered.configurate.CommentedConfigurationNode;
import java.io.*;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
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.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -27,23 +53,341 @@ import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class EssentialsUpgrade {
private final static Logger LOGGER = Logger.getLogger("Essentials");
private static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
private static final Pattern PATTERN_CONFIG_NAME = Pattern.compile(PATTERN_CONFIG_NAME_REGEX);
private final transient IEssentials ess;
private final transient EssentialsConf doneFile;
private final transient EssentialsConfiguration doneFile;
EssentialsUpgrade(final IEssentials essentials) {
ess = essentials;
if (!ess.getDataFolder().exists()) {
ess.getDataFolder().mkdirs();
}
doneFile = new EssentialsConf(new File(ess.getDataFolder(), "upgrades-done.yml"));
doneFile = new EssentialsConfiguration(new File(ess.getDataFolder(), "upgrades-done.yml"));
doneFile.load();
}
public 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");
if (!userdir.exists()) {
return;
}
int countFiles = 0;
int countFails = 0;
int countEssCache = 0;
int countBukkit = 0;
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
for (final String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
final int showProgress = countFiles % 250;
if (showProgress == 0) {
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
}
countFiles++;
final String name = string.substring(0, string.length() - 4);
final EssentialsUserConfiguration config;
UUID uuid = null;
try {
uuid = UUID.fromString(name);
} catch (final IllegalArgumentException ex) {
final File file = new File(userdir, string);
final EssentialsConfiguration conf = new EssentialsConfiguration(file);
conf.load();
conf.setProperty("lastAccountName", name);
conf.save();
final String uuidConf = ignoreUFCache ? "force-uuid" : "uuid";
final String uuidString = conf.getString(uuidConf, null);
for (int i = 0; i < 4; i++) {
try {
uuid = UUID.fromString(uuidString);
countEssCache++;
break;
} catch (final Exception ex2) {
if (conf.getBoolean("npc", false)) {
uuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
break;
}
final org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
uuid = player.getUniqueId();
}
if (uuid != null) {
countBukkit++;
break;
}
}
if (uuid != null) {
conf.blockingSave();
config = new EssentialsUserConfiguration(name, uuid, new File(userdir, uuid + ".yml"));
config.convertLegacyFile();
ess.getUserMap().trackUUID(uuid, name, false);
continue;
}
countFails++;
}
}
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
ess.getLogger().info("To rerun the conversion type /essentials uuidconvert");
}
public void convertMailList() {
if (doneFile.getBoolean("updateUsersMailList", false)) {
return;
}
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
config.load();
if (config.hasProperty("mail") && config.isList("mail")) {
final ArrayList<MailMessage> messages = new ArrayList<>();
for (String mailStr : Collections.synchronizedList(config.getList("mail", String.class))) {
if (mailStr == null) {
continue;
}
messages.add(new MailMessage(false, true, null, null, 0L, 0L, mailStr));
}
config.removeProperty("mail");
config.setExplicitList("mail", messages, new TypeToken<List<MailMessage>>() {}.getType());
config.blockingSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
throw ex;
}
}
doneFile.setProperty("updateUsersMailList", true);
doneFile.save();
LOGGER.info("Done converting mail list.");
}
public void convertStupidCamelCaseUserdataKeys() {
if (doneFile.getBoolean("updateUsersStupidLegacyPathNames", false)) {
return;
}
LOGGER.info("Attempting to migrate legacy userdata keys to Configurate");
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
for (final File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
config.load();
if (config.hasProperty("muteReason")) {
final String reason = config.getString("muteReason", null);
config.removeProperty("muteReason");
config.setProperty("mute-reason", reason);
}
if (config.hasProperty("ipAddress")) {
final String ip = config.getString("ipAddress", null);
config.removeProperty("ipAddress");
config.setProperty("ip-address", ip);
}
if (config.hasProperty("lastAccountName")) {
final String name = config.getString("lastAccountName", null);
config.removeProperty("lastAccountName");
config.setProperty("last-account-name", name);
}
if (config.hasProperty("acceptingPay")) {
final boolean isPay = config.getBoolean("acceptingPay", true);
config.removeProperty("acceptingPay");
config.setProperty("accepting-pay", isPay);
}
config.blockingSave();
} catch (final RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
throw ex;
}
}
doneFile.setProperty("updateUsersStupidLegacyPathNames", true);
doneFile.save();
LOGGER.info("Done converting legacy userdata keys to Configurate.");
}
/**
* This migration cleans up unused files left behind by the chaos resulting from Vault's questionable economy
* integration, and upstream Essentials' rushed and untested 1.7.10 UUID support.
* Both of these have been fixed in EssentialsX as of 2.18.x and 2.19.x respectively, but the leftover userdata
* files can reach into the tens of thousands and can cause excessive memory and storage usage, so this migration
* relocates these files to a backup folder to be removed by the server owner at a later date.
* <p>
* To quote JRoy, who suffered immensely while trying to debug and fix various related issues:
* <p>
* "Essentials decided when adding its initial support for UUIDs, it wanted an implementation which would cause
* eternal pain and suffering for any person who dared touch any of the code in the future. This code that was made
* was so bad, it managed to somehow not maintain any actual UUID support for any external integrations/plugins.
* Up until 2.19.0 and 2.18.0 respectively, our Vault integration and entire Economy system was entirely based off
* username strings, and thanks to Vault being a flawed standard, for some reason exposes account create to third
* party plugins rather than letting the implementation handle it. That doesn't seem like a huge problem at the
* surface, but there was one small problem: whoever made the Vault integration for Essentials suffered a stroke in
* the process of creating it. The implementation for the createAccount method, regardless of whether it was an
* actual player or an NPC account (which the Vault spec NEVER accounted for but plugins just have to guess when
* to support them), it would always create an NPC account. This caused any plugin integrating with Vault, creating
* NPC accounts for pretty much every single player on the server. It still, to this day, amazes me how nobody saw
* this code and didn't die without rewriting it; Or how everybody simply didn't stop using this plugin because how
* awful that godforsaken code was. Anyways, this upgrade does its best to delete NPC accounts created by the
* 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() {
if (doneFile.getBoolean("updatePurgeBrokenNpcAccounts", false)) {
return;
}
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
return;
}
final File[] userFiles = userdataFolder.listFiles();
if (userFiles.length == 0) {
return;
}
final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup");
if (backupFolder.exists()) {
LOGGER.info("NPC backup folder already exists; skipping NPC purge.");
LOGGER.info("To finish purging broken NPC accounts, rename the \"plugins/Essentials/userdata-npc-backup\" folder and restart your server.");
return;
} else if (!backupFolder.mkdir()) {
LOGGER.info("Skipping NPC purge due to error creating backup folder.");
return;
}
LOGGER.info("#===========================================================================#");
LOGGER.info(" EssentialsX will now purge any NPC accounts which were incorrectly created.");
LOGGER.info(" Only NPC accounts with the default starting balance will be deleted. If");
LOGGER.info(" they turn out to be valid NPC accounts, they will be re-created as needed.");
LOGGER.info(" Any files deleted here will be backed up to the ");
LOGGER.info(" \"plugins/Essentials/userdata-npc-backup\" folder. If you notice any files");
LOGGER.info(" have been purged incorrectly, you should restore it from the backup and");
LOGGER.info(" report it to us on GitHub:");
LOGGER.info(" https://github.com/EssentialsX/Essentials/issues/new/choose");
LOGGER.info("");
LOGGER.info(" NOTE: This is a one-time process and will take several minutes if you have");
LOGGER.info(" a lot of userdata files! If you interrupt this process, EssentialsX will");
LOGGER.info(" skip the process until you rename or remove the backup folder.");
LOGGER.info("#===========================================================================#");
final int totalUserFiles = userFiles.length;
LOGGER.info("Found ~" + totalUserFiles + " files under \"plugins/Essentials/userdata\"...");
final AtomicInteger movedAccounts = new AtomicInteger(0);
final AtomicInteger totalAccounts = new AtomicInteger(0);
// Less spammy feedback for greater userdata counts, eg 100 files -> 5 seconds, 1k -> 7s, 10k -> 9s, 100k -> 11s, 1m -> 14s
final long feedbackInterval = Math.min(15, 1 + Math.round(2.1 * Math.log10(userFiles.length)));
final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
final ScheduledFuture<?> feedbackTask = executor.scheduleWithFixedDelay(
() -> LOGGER.info("Scanned " + totalAccounts.get() + "/" + totalUserFiles + " accounts; moved " + movedAccounts.get() + " accounts"),
5, feedbackInterval, TimeUnit.SECONDS);
for (final File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
totalAccounts.incrementAndGet();
config.load();
if (config.getKeys().size() > 4) {
continue;
}
if (!config.getBoolean("npc", false)) {
continue;
}
final BigDecimal money = config.getBigDecimal("money", null);
if (money == null || money.compareTo(ess.getSettings().getStartingBalance()) != 0) {
continue;
}
if (config.getKeys().size() == 4 && !config.hasProperty("last-account-name") && config.hasProperty("mail")) {
continue;
}
try {
//noinspection UnstableApiUsage
Files.move(file, new File(backupFolder, file.getName()));
movedAccounts.incrementAndGet();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Error while moving NPC file", e);
}
} catch (final RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
feedbackTask.cancel(false);
executor.shutdown();
throw ex;
}
}
feedbackTask.cancel(false);
executor.shutdown();
doneFile.setProperty("updatePurgeBrokenNpcAccounts", true);
doneFile.save();
LOGGER.info("#===========================================================================#");
LOGGER.info(" EssentialsX has finished purging NPC accounts.");
LOGGER.info("");
LOGGER.info(" Deleted accounts: " + movedAccounts);
LOGGER.info(" Total accounts processed: " + totalAccounts);
LOGGER.info("");
LOGGER.info(" Purged accounts have been backed up to");
LOGGER.info(" \"plugins/Essentials/userdata-npc-backup\", and can be restored from there");
LOGGER.info(" if needed. Please report any files which have been incorrectly deleted");
LOGGER.info(" to us on GitHub:");
LOGGER.info(" https://github.com/EssentialsX/Essentials/issues/new/choose");
LOGGER.info("#===========================================================================#");
}
public void convertIgnoreList() {
Pattern pattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
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;
}
@@ -56,16 +400,16 @@ public class EssentialsUpgrade {
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
for (final File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
config.load();
if (config.hasProperty("ignore")) {
List<String> migratedIgnores = new ArrayList<>();
for (String name : Collections.synchronizedList(config.getStringList("ignore"))) {
final List<String> migratedIgnores = new ArrayList<>();
for (final String name : Collections.synchronizedList(config.getList("ignore", String.class))) {
if (name == null) {
continue;
}
@@ -73,17 +417,17 @@ public class EssentialsUpgrade {
LOGGER.info("Detected already migrated ignore list!");
return;
}
User user = ess.getOfflineUser(name);
final User user = ess.getOfflineUser(name);
if (user != null && user.getBase() != null) {
migratedIgnores.add(user.getBase().getUniqueId().toString());
}
}
config.removeProperty("ignore");
config.setProperty("ignore", migratedIgnores);
config.forceSave();
config.blockingSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
} catch (final RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
throw ex;
}
}
@@ -93,25 +437,25 @@ public class EssentialsUpgrade {
}
public void convertKits() {
Kits kits = ess.getKits();
EssentialsConf config = kits.getConfig();
final Kits kits = ess.getKits();
final EssentialsConfiguration config = kits.getRootConfig();
if (doneFile.getBoolean("kitsyml", false)) {
return;
}
LOGGER.info("Attempting to convert old kits in config.yml to new kits.yml");
ConfigurationSection section = ess.getSettings().getKitSection();
final CommentedConfigurationNode section = ess.getSettings().getKitSection();
if (section == null) {
LOGGER.info("No kits found to migrate.");
return;
}
Map<String, Object> legacyKits = ess.getSettings().getKitSection().getValues(true);
final Map<String, Object> legacyKits = ConfigurateUtil.getRawMap(section);
for (Map.Entry<String, Object> entry : legacyKits.entrySet()) {
for (final Map.Entry<String, Object> entry : legacyKits.entrySet()) {
LOGGER.info("Converting " + entry.getKey());
config.set("kits." + entry.getKey(), entry.getValue());
config.setRaw("kits." + entry.getKey(), entry.getValue());
}
config.save();
@@ -120,7 +464,7 @@ public class EssentialsUpgrade {
LOGGER.info("Done converting kits.");
}
private void moveMotdRulesToFile(String name) {
private void moveMotdRulesToFile(final String name) {
if (doneFile.getBoolean("move" + name + "ToFile", false)) {
return;
}
@@ -133,28 +477,28 @@ public class EssentialsUpgrade {
if (!configFile.exists()) {
return;
}
final EssentialsConf conf = new EssentialsConf(configFile);
final EssentialsConfiguration conf = new EssentialsConfiguration(configFile);
conf.load();
List<String> lines = conf.getStringList(name);
final List<String> lines = conf.getList(name, String.class);
if (lines != null && !lines.isEmpty()) {
if (!file.createNewFile()) {
throw new IOException("Failed to create file " + file);
}
PrintWriter writer = new PrintWriter(file);
final PrintWriter writer = new PrintWriter(file);
for (String line : lines) {
for (final String line : lines) {
writer.println(line);
}
writer.close();
}
doneFile.setProperty("move" + name + "ToFile", true);
doneFile.save();
} catch (IOException e) {
} catch (final IOException e) {
LOGGER.log(Level.SEVERE, tl("upgradingFilesError"), e);
}
}
private void removeLinesFromConfig(File file, String regex, String info) throws Exception {
private void removeLinesFromConfig(final File file, final String regex, final String info) throws Exception {
boolean needUpdate = false;
final BufferedReader bReader = new BufferedReader(new FileReader(file));
final File tempFile = File.createTempFile("essentialsupgrade", ".tmp.yml", ess.getDataFolder());
@@ -205,29 +549,30 @@ public class EssentialsUpgrade {
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
for (final File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
config.load();
if (config.hasProperty("powertools")) {
final Map<String, Object> powertools = config.getConfigurationSection("powertools").getValues(false);
if (powertools == null) {
final Map<String, Object> powertools = ConfigurateUtil.getRawMap(config.getSection("powertools"));
if (powertools.isEmpty()) {
continue;
}
for (Map.Entry<String, Object> entry : powertools.entrySet()) {
for (final Map.Entry<String, Object> entry : powertools.entrySet()) {
if (entry.getValue() instanceof String) {
List<String> temp = new ArrayList<>();
final List<String> temp = new ArrayList<>();
temp.add((String) entry.getValue());
powertools.put(entry.getKey(), temp);
}
}
config.forceSave();
config.setRaw("powertools", powertools);
config.blockingSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
} catch (final RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
throw ex;
}
}
@@ -245,47 +590,45 @@ public class EssentialsUpgrade {
}
final File[] userFiles = userdataFolder.listFiles();
for (File file : userFiles) {
for (final File file : userFiles) {
if (!file.isFile() || !file.getName().endsWith(".yml")) {
continue;
}
final EssentialsConf config = new EssentialsConf(file);
final EssentialsConfiguration config = new EssentialsConfiguration(file);
try {
config.load();
if (config.hasProperty("home") && config.hasProperty("home.default")) {
final String defworld = (String) config.getProperty("home.default");
final Location defloc = getFakeLocation(config, "home.worlds." + defworld);
final String defworld = config.getString("home.default", null);
final Location defloc = getFakeLocation(config.getRootNode(), "home.worlds." + defworld);
if (defloc != null) {
config.setProperty("homes.home", defloc);
}
Set<String> worlds = config.getConfigurationSection("home.worlds").getKeys(false);
final Set<String> worlds = ConfigurateUtil.getKeys(config.getSection("home.worlds"));
Location loc;
String worldName;
if (worlds == null) {
if (worlds.isEmpty()) {
continue;
}
for (String world : worlds) {
for (final String world : worlds) {
if (defworld.equalsIgnoreCase(world)) {
continue;
}
loc = getFakeLocation(config, "home.worlds." + world);
loc = getFakeLocation(config.getRootNode(), "home.worlds." + world);
if (loc == null) {
continue;
}
worldName = loc.getWorld().getName().toLowerCase(Locale.ENGLISH);
if (worldName != null && !worldName.isEmpty()) {
config.setProperty("homes." + worldName, loc);
}
config.setProperty("homes." + worldName, loc);
}
config.removeProperty("home");
config.forceSave();
config.blockingSave();
}
} catch (RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file.toString());
} catch (final RuntimeException ex) {
LOGGER.log(Level.INFO, "File: " + file);
throw ex;
}
}
@@ -302,7 +645,7 @@ public class EssentialsUpgrade {
return;
}
final File[] listOfFiles = usersFolder.listFiles();
for (File listOfFile : listOfFiles) {
for (final File listOfFile : listOfFiles) {
final String filename = listOfFile.getName();
if (!listOfFile.isFile() || !filename.endsWith(".yml")) {
continue;
@@ -338,16 +681,17 @@ public class EssentialsUpgrade {
return null;
}
public Location getFakeLocation(EssentialsConf config, String path) {
String worldName = config.getString((path != null ? path + "." : "") + "world");
public Location getFakeLocation(final CommentedConfigurationNode config, final String path) {
final String worldName = config.getString((path != null ? path + "." : "") + "world");
if (worldName == null || worldName.isEmpty()) {
return null;
}
World world = getFakeWorld(worldName);
final World world = getFakeWorld(worldName);
if (world == null) {
return null;
}
return new Location(world, config.getDouble((path != null ? path + "." : "") + "x", 0), config.getDouble((path != null ? path + "." : "") + "y", 0), config.getDouble((path != null ? path + "." : "") + "z", 0), (float) config.getDouble((path != null ? path + "." : "") + "yaw", 0), (float) config.getDouble((path != null ? path + "." : "") + "pitch", 0));
return new Location(world, config.node("x").getDouble(0), config.node("y").getDouble(0),
config.node("z").getDouble(0), config.node("yaw").getFloat(0), config.node("pitch").getFloat(0));
}
private void deleteOldItemsCsv() {
@@ -363,21 +707,21 @@ public class EssentialsUpgrade {
oldconfigs.add(new BigInteger("c33bc9b8ee003861611bbc2f48eb6f4f", 16)); // jul 24
oldconfigs.add(new BigInteger("6ff17925430735129fc2a02f830c1daa", 16)); // crlf
MessageDigest digest = ManagedFile.getDigest();
final MessageDigest digest = ManagedFile.getDigest();
final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
final byte[] buffer = new byte[1024];
try (DigestInputStream dis = new DigestInputStream(bis, digest)) {
try (final DigestInputStream dis = new DigestInputStream(bis, digest)) {
while (dis.read(buffer) != -1) {
}
}
BigInteger hash = new BigInteger(1, digest.digest());
final BigInteger hash = new BigInteger(1, digest.digest());
if (oldconfigs.contains(hash) && !file.delete()) {
throw new IOException("Could not delete file " + file.toString());
throw new IOException("Could not delete file " + file);
}
doneFile.setProperty("deleteOldItemsCsv", true);
doneFile.save();
} catch (IOException ex) {
} catch (final IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
@@ -390,24 +734,20 @@ public class EssentialsUpgrade {
final File configFile = new File(ess.getDataFolder(), "spawn.yml");
if (configFile.exists()) {
final EssentialsConf config = new EssentialsConf(configFile);
final EssentialsConfiguration config = new EssentialsConfiguration(configFile);
try {
config.load();
if (!config.hasProperty("spawns")) {
final Spawns spawns = new Spawns();
Set<String> keys = config.getKeys(false);
for (String group : keys) {
Location loc = getFakeLocation(config, group);
spawns.getSpawns().put(group.toLowerCase(Locale.ENGLISH), loc);
for (final Map.Entry<String, CommentedConfigurationNode> entry : config.getMap().entrySet()) {
final Location loc = getFakeLocation(entry.getValue(), entry.getKey());
config.setProperty(entry.getKey(), loc);
}
if (!configFile.renameTo(new File(ess.getDataFolder(), "spawn.yml.old"))) {
throw new Exception(tl("fileRenameError", "spawn.yml"));
}
try (PrintWriter writer = new PrintWriter(configFile)) {
new YamlStorageWriter(writer).save(spawns);
}
config.blockingSave();
}
} catch (Exception ex) {
} catch (final Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
@@ -422,24 +762,20 @@ public class EssentialsUpgrade {
final File configFile = new File(ess.getDataFolder(), "jail.yml");
if (configFile.exists()) {
final EssentialsConf config = new EssentialsConf(configFile);
final EssentialsConfiguration config = new EssentialsConfiguration(configFile);
try {
config.load();
if (!config.hasProperty("jails")) {
final com.earth2me.essentials.settings.Jails jails = new com.earth2me.essentials.settings.Jails();
Set<String> keys = config.getKeys(false);
for (String jailName : keys) {
Location loc = getFakeLocation(config, jailName);
jails.getJails().put(jailName.toLowerCase(Locale.ENGLISH), loc);
for (final Map.Entry<String, CommentedConfigurationNode> entry : config.getMap().entrySet()) {
final Location loc = getFakeLocation(entry.getValue(), entry.getKey());
config.setProperty(entry.getKey(), loc);
}
if (!configFile.renameTo(new File(ess.getDataFolder(), "jail.yml.old"))) {
throw new Exception(tl("fileRenameError", "jail.yml"));
}
try (PrintWriter writer = new PrintWriter(configFile)) {
new YamlStorageWriter(writer).save(jails);
}
config.blockingSave();
}
} catch (Exception ex) {
} catch (final Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
@@ -460,7 +796,7 @@ public class EssentialsUpgrade {
return;
}
Boolean ignoreUFCache = doneFile.getBoolean("ignore-userfiles-cache", false);
final Boolean ignoreUFCache = doneFile.getBoolean("ignore-userfiles-cache", false);
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
@@ -469,7 +805,7 @@ public class EssentialsUpgrade {
int countFiles = 0;
int countReqFiles = 0;
for (String string : userdir.list()) {
for (final String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
@@ -481,7 +817,7 @@ public class EssentialsUpgrade {
try {
uuid = UUID.fromString(name);
} catch (IllegalArgumentException ex) {
} catch (final IllegalArgumentException ex) {
countReqFiles++;
}
@@ -499,7 +835,7 @@ public class EssentialsUpgrade {
try {
Thread.sleep(15000);
} catch (InterruptedException ex) {
} catch (final InterruptedException ex) {
// NOOP
}
@@ -509,89 +845,6 @@ public class EssentialsUpgrade {
doneFile.save();
}
public static void uuidFileConvert(IEssentials ess, Boolean ignoreUFCache) {
ess.getLogger().info("Starting Essentials UUID userdata conversion");
final File userdir = new File(ess.getDataFolder(), "userdata");
if (!userdir.exists()) {
return;
}
int countFiles = 0;
int countFails = 0;
int countEssCache = 0;
int countBukkit = 0;
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
for (String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
final int showProgress = countFiles % 250;
if (showProgress == 0) {
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
}
countFiles++;
String name = string.substring(0, string.length() - 4);
EssentialsUserConf config;
UUID uuid = null;
try {
uuid = UUID.fromString(name);
} catch (IllegalArgumentException ex) {
File file = new File(userdir, string);
EssentialsConf conf = new EssentialsConf(file);
conf.load();
conf.setProperty("lastAccountName", name);
conf.save();
String uuidConf = ignoreUFCache ? "force-uuid" : "uuid";
String uuidString = conf.getString(uuidConf, null);
for (int i = 0; i < 4; i++) {
try {
uuid = UUID.fromString(uuidString);
countEssCache++;
break;
} catch (Exception ex2) {
if (conf.getBoolean("npc", false)) {
uuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
break;
}
org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
uuid = player.getUniqueId();
}
if (uuid != null) {
countBukkit++;
break;
}
}
if (uuid != null) {
conf.forceSave();
config = new EssentialsUserConf(name, uuid, new File(userdir, uuid + ".yml"));
config.convertLegacyFile();
ess.getUserMap().trackUUID(uuid, name, false);
continue;
}
countFails++;
}
}
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
ess.getLogger().info("To rerun the conversion type /essentials uuidconvert");
}
public void banFormatChange() {
if (doneFile.getBoolean("banFormatChange", false)) {
return;
@@ -608,7 +861,7 @@ public class EssentialsUpgrade {
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
for (String string : userdir.list()) {
for (final String string : userdir.list()) {
if (!string.endsWith(".yml") || string.length() < 5) {
continue;
}
@@ -621,27 +874,27 @@ public class EssentialsUpgrade {
countFiles++;
final File pFile = new File(userdir, string);
final EssentialsConf conf = new EssentialsConf(pFile);
final EssentialsConfiguration conf = new EssentialsConfiguration(pFile);
conf.load();
String banReason;
final String banReason;
long banTimeout;
try {
banReason = conf.getConfigurationSection("ban").getString("reason");
} catch (NullPointerException n) {
if (conf.hasProperty("ban.reason")) {
banReason = conf.getString("ban.reason", null);
} else {
banReason = null;
}
final String playerName = conf.getString("lastAccountName");
final String playerName = conf.getString("lastAccountName", null);
if (playerName != null && playerName.length() > 1 && banReason != null && banReason.length() > 1) {
try {
if (conf.getConfigurationSection("ban").contains("timeout")) {
banTimeout = Long.parseLong(conf.getConfigurationSection("ban").getString("timeout"));
if (conf.hasProperty("ban.timeout")) {
banTimeout = Long.parseLong(conf.getString("ban.timeout", null));
} else {
banTimeout = 0L;
}
} catch (NumberFormatException n) {
} catch (final NumberFormatException n) {
banTimeout = 0L;
}
@@ -658,34 +911,22 @@ public class EssentialsUpgrade {
ess.getLogger().info("Ban format update complete.");
}
private void updateBan(String playerName, String banReason, Long banTimeout) {
if (banTimeout == 0) {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, null, Console.NAME);
} else {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, new Date(banTimeout), Console.NAME);
}
private void updateBan(final String playerName, final String banReason, final Long banTimeout) {
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, banTimeout == 0 ? null : new Date(banTimeout), Console.NAME);
}
private static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
private static final Pattern PATTERN_CONFIG_NAME = Pattern.compile(PATTERN_CONFIG_NAME_REGEX);
private void repairUserMap() {
if (doneFile.getBoolean("userMapRepaired", false)) {
return;
}
ess.getLogger().info("Starting usermap repair");
File userdataFolder = new File(ess.getDataFolder(), "userdata");
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
if (!userdataFolder.isDirectory()) {
ess.getLogger().warning("Missing userdata folder, aborting");
return;
}
File[] files = userdataFolder.listFiles(YML_FILTER);
final File[] files = userdataFolder.listFiles(YML_FILTER);
final DecimalFormat format = new DecimalFormat("#0.00");
final Map<String, UUID> names = Maps.newHashMap();
@@ -701,7 +942,7 @@ public class EssentialsUpgrade {
try {
// ".yml" ending has 4 chars...
uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
} catch (IllegalArgumentException ignored) {
} catch (final IllegalArgumentException ignored) {
}
}
@@ -709,7 +950,7 @@ public class EssentialsUpgrade {
if (uuidMatcher.find()) {
try {
uuid = UUID.fromString(uuidMatcher.group(1));
} catch (IllegalArgumentException ignored) {
} catch (final IllegalArgumentException ignored) {
}
}
@@ -728,7 +969,7 @@ public class EssentialsUpgrade {
if (index % 1000 == 0) {
ess.getLogger().info("Reading: " + format.format((100d * (double) index) / files.length)
+ "%");
+ "%");
}
} catch (final IOException e) {
ess.getLogger().log(Level.SEVERE, "Error while reading file: ", e);
@@ -764,5 +1005,8 @@ public class EssentialsUpgrade {
warnMetrics();
repairUserMap();
convertIgnoreList();
convertStupidCamelCaseUserdataKeys();
convertMailList();
purgeBrokenNpcAccounts();
}
}
@@ -6,12 +6,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class ExecuteTimer {
private final transient List<ExecuteRecord> times;
private final transient DecimalFormat decimalFormat = new DecimalFormat("#0.000", DecimalFormatSymbols.getInstance(Locale.US));
public ExecuteTimer() {
times = new ArrayList<>();
}
@@ -37,7 +35,7 @@ public class ExecuteTimer {
long time2 = 0;
double duration;
for (ExecuteRecord pair : times) {
for (final ExecuteRecord pair : times) {
mark = pair.getMark();
time2 = pair.getTime();
if (time1 > 0) {
@@ -54,12 +52,11 @@ public class ExecuteTimer {
return output.toString();
}
private static class ExecuteRecord {
private final String mark;
private final long time;
public ExecuteRecord(final String mark, final long time) {
ExecuteRecord(final String mark, final long time) {
this.mark = mark;
this.time = time;
}
@@ -72,4 +69,4 @@ public class ExecuteTimer {
return time;
}
}
}
}
@@ -2,38 +2,48 @@ package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class I18n implements net.ess3.api.II18n {
private static I18n instance;
private static final String MESSAGES = "messages";
private final transient Locale defaultLocale = Locale.getDefault();
private transient Locale currentLocale = defaultLocale;
private transient ResourceBundle customBundle;
private transient ResourceBundle localeBundle;
private final transient ResourceBundle defaultBundle;
private transient Map<String, MessageFormat> messageFormatCache = new HashMap<>();
private final transient IEssentials ess;
private static final Pattern NODOUBLEMARK = Pattern.compile("''");
private static final ResourceBundle NULL_BUNDLE = new ResourceBundle() {
public Enumeration<String> getKeys() {
return null;
}
protected Object handleGetObject(String key) {
protected Object handleGetObject(final String key) {
return null;
}
};
private static I18n instance;
private final transient Locale defaultLocale = Locale.getDefault();
private final transient ResourceBundle defaultBundle;
private final transient IEssentials ess;
private transient Locale currentLocale = defaultLocale;
private transient ResourceBundle customBundle;
private transient ResourceBundle localeBundle;
private transient Map<String, MessageFormat> messageFormatCache = new HashMap<>();
public I18n(final IEssentials ess) {
this.ess = ess;
@@ -42,6 +52,21 @@ public class I18n implements net.ess3.api.II18n {
customBundle = NULL_BUNDLE;
}
public static String tl(final String string, final Object... objects) {
if (instance == null) {
return "";
}
if (objects.length == 0) {
return NODOUBLEMARK.matcher(instance.translate(string)).replaceAll("'");
} else {
return instance.format(string, objects);
}
}
public static String capitalCase(final String input) {
return input == null || input.length() == 0 ? input : input.toUpperCase(Locale.ENGLISH).charAt(0) + input.toLowerCase(Locale.ENGLISH).substring(1);
}
public void onEnable() {
instance = this;
}
@@ -59,33 +84,24 @@ public class I18n implements net.ess3.api.II18n {
try {
try {
return customBundle.getString(string);
} catch (MissingResourceException ex) {
} catch (final MissingResourceException ex) {
return localeBundle.getString(string);
}
} catch (MissingResourceException ex) {
Logger.getLogger("Essentials").log(Level.WARNING, String.format("Missing translation key \"%s\" in translation file %s", ex.getKey(), localeBundle.getLocale().toString()), ex);
} catch (final MissingResourceException ex) {
if (ess == null || ess.getSettings().isDebug()) {
Logger.getLogger("Essentials").log(Level.WARNING, String.format("Missing translation key \"%s\" in translation file %s", ex.getKey(), localeBundle.getLocale().toString()), ex);
}
return defaultBundle.getString(string);
}
}
public static String tl(final String string, final Object... objects) {
if (instance == null) {
return "";
}
if (objects.length == 0) {
return NODOUBLEMARK.matcher(instance.translate(string)).replaceAll("'");
} else {
return instance.format(string, objects);
}
}
public String format(final String string, final Object... objects) {
String format = translate(string);
MessageFormat messageFormat = messageFormatCache.get(format);
if (messageFormat == null) {
try {
messageFormat = new MessageFormat(format);
} catch (IllegalArgumentException e) {
} catch (final IllegalArgumentException e) {
ess.getLogger().log(Level.SEVERE, "Invalid Translation key for '" + string + "': " + e.getMessage());
format = format.replaceAll("\\{(\\D*?)\\}", "\\[$1\\]");
messageFormat = new MessageFormat(format);
@@ -114,21 +130,17 @@ public class I18n implements net.ess3.api.II18n {
try {
localeBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
} catch (final MissingResourceException ex) {
localeBundle = NULL_BUNDLE;
}
try {
customBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new FileResClassLoader(I18n.class.getClassLoader(), ess), new UTF8PropertiesControl());
} catch (MissingResourceException ex) {
} catch (final MissingResourceException ex) {
customBundle = NULL_BUNDLE;
}
}
public static String capitalCase(final String input) {
return input == null || input.length() == 0 ? input : input.toUpperCase(Locale.ENGLISH).charAt(0) + input.toLowerCase(Locale.ENGLISH).substring(1);
}
/**
* Attempts to load properties files from the plugin directory before falling back to the jar.
*/
@@ -146,7 +158,8 @@ public class I18n implements net.ess3.api.II18n {
if (file.exists()) {
try {
return file.toURI().toURL();
} catch (MalformedURLException ignored) {}
} catch (final MalformedURLException ignored) {
}
}
return null;
}
@@ -157,7 +170,8 @@ public class I18n implements net.ess3.api.II18n {
if (file.exists()) {
try {
return new FileInputStream(file);
} catch (FileNotFoundException ignored) {}
} catch (final FileNotFoundException ignored) {
}
}
return null;
}
@@ -168,14 +182,14 @@ public class I18n implements net.ess3.api.II18n {
* Java 9 fixes this by defaulting to UTF-8 for .properties files.
*/
private static class UTF8PropertiesControl extends ResourceBundle.Control {
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IOException {
String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
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;
InputStream stream = null;
if (reload) {
URL url = loader.getResource(resourceName);
final URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
final URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
@@ -3,14 +3,31 @@ package com.earth2me.essentials;
import com.earth2me.essentials.api.IItemDb;
import com.earth2me.essentials.api.IJails;
import com.earth2me.essentials.api.IWarps;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.commands.PlayerNotFoundException;
import com.earth2me.essentials.perm.PermissionsHandler;
import com.earth2me.essentials.register.payment.Methods;
import com.earth2me.essentials.updatecheck.UpdateChecker;
import net.ess3.nms.refl.providers.ReflOnlineModeProvider;
import net.ess3.provider.ContainerProvider;
import net.ess3.provider.FormattedCommandAliasProvider;
import net.ess3.provider.ItemUnbreakableProvider;
import net.ess3.provider.KnownCommandsProvider;
import net.ess3.provider.MaterialTagProvider;
import net.ess3.provider.PersistentDataProvider;
import net.ess3.provider.SerializationProvider;
import net.ess3.provider.ServerStateProvider;
import net.ess3.provider.SignDataProvider;
import net.ess3.provider.SpawnerBlockProvider;
import net.ess3.provider.SpawnerItemProvider;
import net.ess3.provider.SyncCommandsProvider;
import net.ess3.provider.WorldInfoProvider;
import net.essentialsx.api.v2.services.BalanceTop;
import net.essentialsx.api.v2.services.mail.MailService;
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.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
@@ -18,6 +35,7 @@ import org.bukkit.scheduler.BukkitTask;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
@@ -26,6 +44,8 @@ public interface IEssentials extends Plugin {
void reload();
Map<String, IEssentialsCommand> getCommandMap();
List<String> onTabCompleteEssentials(CommandSender sender, Command command, String commandLabel, String[] args, ClassLoader classLoader, String commandPath, String permissionPrefix, IEssentialsModule module);
boolean onCommandEssentials(CommandSender sender, Command command, String commandLabel, String[] args, ClassLoader classLoader, String commandPath, String permissionPrefix, IEssentialsModule module);
@@ -39,6 +59,12 @@ public interface IEssentials extends Plugin {
User getUser(Player base);
User matchUser(Server server, User sourceUser, String searchTerm, Boolean getHidden, boolean getOffline) throws PlayerNotFoundException;
boolean canInteractWith(CommandSource interactor, User interactee);
boolean canInteractWith(User interactor, User interactee);
I18n getI18n();
User getOfflineUser(String name);
@@ -69,7 +95,7 @@ public interface IEssentials extends Plugin {
RandomTeleport getRandomTeleport();
Methods getPaymentMethod();
UpdateChecker getUpdateChecker();
BukkitTask runTaskAsynchronously(Runnable run);
@@ -95,18 +121,52 @@ public interface IEssentials extends Plugin {
UserMap getUserMap();
BalanceTop getBalanceTop();
EssentialsTimer getTimer();
MailService getMail();
/**
* Get a list of players who are vanished.
*
* @return A list of players who are vanished
* @deprecated Use {@link net.ess3.api.IEssentials#getVanishedPlayersNew()} where possible.
*/
@Deprecated
List<String> getVanishedPlayers();
Collection<Player> getOnlinePlayers();
Collection<User> getOnlineUsers();
Iterable<User> getOnlineUsers();
SpawnerItemProvider getSpawnerItemProvider();
SpawnerBlockProvider getSpawnerBlockProvider();
ServerStateProvider getServerStateProvider();
MaterialTagProvider getMaterialTagProvider();
ContainerProvider getContainerProvider();
KnownCommandsProvider getKnownCommandsProvider();
SerializationProvider getSerializationProvider();
FormattedCommandAliasProvider getFormattedCommandAliasProvider();
SyncCommandsProvider getSyncCommandsProvider();
PersistentDataProvider getPersistentDataProvider();
ReflOnlineModeProvider getOnlineModeProvider();
ItemUnbreakableProvider getItemUnbreakableProvider();
WorldInfoProvider getWorldInfoProvider();
SignDataProvider getSignDataProvider();
PluginCommand getPluginCommand(String cmd);
}
@@ -1,5 +1,4 @@
package com.earth2me.essentials;
public interface IEssentialsModule {
}
@@ -4,9 +4,10 @@ import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.signs.EssentialsSign;
import com.earth2me.essentials.textreader.IText;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventPriority;
import org.spongepowered.configurate.CommentedConfigurationNode;
import java.io.File;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.List;
@@ -16,8 +17,9 @@ import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public interface ISettings extends IConf {
File getConfigFile();
boolean areSignsDisabled();
IText getAnnounceNewPlayerFormat();
@@ -34,6 +36,8 @@ public interface ISettings extends IConf {
String getChatFormat(String group);
String getWorldAlias(String world);
int getChatRadius();
int getNearRadius();
@@ -42,6 +46,12 @@ public interface ISettings extends IConf {
char getChatQuestion();
boolean isShoutDefault();
boolean isPersistShout();
boolean isChatQuestionEnabled();
BigDecimal getCommandCost(IEssentialsCommand cmd);
BigDecimal getCommandCost(String label);
@@ -60,28 +70,12 @@ public interface ISettings extends IConf {
boolean getSocialSpyListenMutedPlayers();
boolean isSocialSpyMessages();
Set<String> getMuteCommands();
/**
* @Deprecated in favor of {@link Kits#getKits()}
*/
@Deprecated
ConfigurationSection getKits();
/**
* @Deprecated in favor of {@link Kits#getKit(String)}
*/
@Deprecated
Map<String, Object> getKit(String kit);
/**
* @Deprecated in favor of {@link Kits#addKit(String, List, long)}}
*/
@Deprecated
void addKit(String name, List<String> lines, long delay);
@Deprecated
ConfigurationSection getKitSection();
CommentedConfigurationNode getKitSection();
boolean isSkippingUsedOneTimeKitsFromKitList();
@@ -139,10 +133,16 @@ public interface ISettings extends IConf {
boolean isCommandDisabled(String label);
Set<String> getDisabledCommands();
boolean isVerboseCommandUsages();
boolean isCommandOverridden(String name);
boolean isDebug();
void setDebug(boolean debug);
boolean isEcoDisabled();
@Deprecated
@@ -178,6 +178,8 @@ public interface ISettings extends IConf {
boolean changePlayerListName();
boolean changeTabCompleteName();
boolean isPlayerCommand(String string);
boolean useBukkitPermissions();
@@ -198,8 +200,12 @@ public interface ISettings extends IConf {
boolean cancelAfkOnInteract();
boolean cancelAfkOnChat();
boolean sleepIgnoresAfkPlayers();
boolean sleepIgnoresVanishedPlayers();
boolean isAfkListName();
String getAfkListName();
@@ -216,14 +222,6 @@ public interface ISettings extends IConf {
boolean hasJoinQuitMessagePlayerCount();
enum KeepInvPolicy {
KEEP,
DELETE,
DROP
}
void setDebug(boolean debug);
Set<String> getNoGodWorlds();
boolean getUpdateBedAtDaytime();
@@ -236,6 +234,8 @@ public interface ISettings extends IConf {
boolean isWorldHomePermissions();
int getMaxTreeCommandRange();
boolean registerBackInListener();
boolean getDisableItemPickupWhileAfk();
@@ -246,6 +246,8 @@ public interface ISettings extends IConf {
long getTpaAcceptCancellation();
int getTpaMaxRequests();
long getTeleportInvulnerability();
boolean isTeleportInvulnerability();
@@ -290,12 +292,20 @@ public interface ISettings extends IConf {
String getCustomQuitMessage();
String getCustomNewUsernameMessage();
boolean isCustomNewUsernameMessage();
boolean isCustomServerFullMessage();
boolean isNotifyNoNewMail();
boolean isDropItemsIfFull();
boolean isLastMessageReplyRecipient();
boolean isReplyToVanished();
BigDecimal getMinimumPayAmount();
boolean isPayExcludesIgnoreList();
@@ -334,8 +344,12 @@ public interface ISettings extends IConf {
List<EssentialsSign> getUnprotectedSignNames();
boolean isKitAutoEquip();
boolean isPastebinCreateKit();
boolean isUseBetterKits();
boolean isAllowBulkBuySell();
boolean isAllowSellNamedItems();
@@ -354,7 +368,9 @@ public interface ISettings extends IConf {
boolean isConfirmCommandEnabledByDefault(String commandName);
boolean isTeleportBackWhenFreedFromJail();
TeleportWhenFreePolicy getTeleportWhenFreePolicy();
boolean isJailOnlineTime();
boolean isCompassTowardsHomePerm();
@@ -365,7 +381,7 @@ public interface ISettings extends IConf {
boolean allowOldIdSigns();
boolean isWaterSafe();
boolean isSafeUsermap();
boolean logCommandBlockCommands();
@@ -382,4 +398,22 @@ public interface ISettings extends IConf {
boolean infoAfterDeath();
boolean isRespawnAtBed();
boolean isUpdateCheckEnabled();
boolean showZeroBaltop();
enum KeepInvPolicy {
KEEP,
DELETE,
DROP
}
enum TeleportWhenFreePolicy {
SPAWN,
BACK,
OFF
}
}
@@ -2,7 +2,6 @@ package com.earth2me.essentials;
import org.bukkit.Location;
public interface ITarget {
Location getLocation();
}
}
@@ -2,21 +2,34 @@ package com.earth2me.essentials;
import com.earth2me.essentials.api.IAsyncTeleport;
import com.earth2me.essentials.commands.IEssentialsCommand;
import com.earth2me.essentials.config.entities.CommandCooldown;
import net.ess3.api.ITeleport;
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 org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* Provides access to the user abstraction and stored data. Maintainers should add methods to <i>this interface</i>.
*
* @deprecated External plugins should use {@link net.ess3.api.IUser} instead of this interface, in case future APIs are added.
*/
@Deprecated
public interface IUser {
boolean isAuthorized(String node);
@@ -51,8 +64,11 @@ public interface IUser {
/**
* Returns whether this user has an outstanding teleport request to deal with.
*
* @deprecated The teleport request system has been moved into a multi-user teleport request queue.
* @see IUser#hasPendingTpaRequests(boolean, boolean)
* @return whether there is a teleport request
*/
@Deprecated
boolean hasOutstandingTeleportRequest();
/**
@@ -67,9 +83,6 @@ public interface IUser {
void setMoney(final BigDecimal value) throws MaxMoneyException;
@Deprecated
void setAfk(final boolean set);
void setAfk(final boolean set, final AfkStatusChangeEvent.Cause cause);
/**
@@ -77,7 +90,6 @@ public interface IUser {
* supported plugins. Use isVanished() if you want to check if a user is vanished by Essentials.
*
* @return If the user is hidden or not
*
* @see IUser#isVanished()
*/
boolean isHidden();
@@ -92,6 +104,11 @@ public interface IUser {
boolean canBuild();
/**
* @deprecated The teleport request system has been moved into a multi-user teleport request queue.
* @see IUser#getNextTpaRequest(boolean, boolean, boolean)
*/
@Deprecated
long getTeleportRequestTime();
void enableInvulnerabilityAfterTeleport();
@@ -106,7 +123,6 @@ public interface IUser {
* plugin.
*
* @return If the user is vanished or not
*
* @see IUser#isHidden()
*/
boolean isVanished();
@@ -144,16 +160,34 @@ public interface IUser {
void setJail(String jail);
String getFormattedJailTime();
@Deprecated
List<String> getMails();
@Deprecated
void addMail(String mail);
void sendMail(MailSender sender, String message);
void sendMail(MailSender sender, String message, long expireAt);
ArrayList<MailMessage> getMailMessages();
void setMailList(ArrayList<MailMessage> messages);
int getMailAmount();
boolean isAfk();
void setIgnoreMsg(boolean ignoreMsg);
@Deprecated
void setAfk(final boolean set);
boolean isIgnoreMsg();
void setIgnoreMsg(boolean ignoreMsg);
@Deprecated
void setConfigProperty(String node, Object object);
Set<String> getConfigKeys();
@@ -161,13 +195,16 @@ public interface IUser {
Map<String, Object> getConfigMap();
Map<String, Object> getConfigMap(String node);
@Deprecated
Map<Pattern, Long> getCommandCooldowns();
List<CommandCooldown> getCooldownsList();
Date getCommandCooldownExpiry(String label);
void addCommandCooldown(Pattern pattern, Date expiresAt, boolean save);
boolean clearCommandCooldown(Pattern pattern);
/*
@@ -179,24 +216,28 @@ public interface IUser {
String getName();
UUID getUUID();
String getDisplayName();
String getFormattedNickname();
String getAfkMessage();
void setAfkMessage(final String message);
long getAfkSince();
boolean isAcceptingPay();
void setAcceptingPay(boolean acceptingPay);
boolean isPromptingPayConfirm();
void setPromptingPayConfirm(boolean prompt);
boolean isPromptingClearConfirm();
void setPromptingClearConfirm(boolean prompt);
boolean isLastMessageReplyRecipient();
@@ -204,4 +245,80 @@ public interface IUser {
void setLastMessageReplyRecipient(boolean enabled);
Map<User, BigDecimal> getConfirmingPayments();
Block getTargetBlock(int maxDistance);
void setToggleShout(boolean toggleShout);
boolean isToggleShout();
/**
* Gets information about the most-recently-made, non-expired TPA request in the tpa queue of this {@link IUser}.
* <p>
* The TPA Queue is Last-In-First-Out queue which stores all the active pending teleport
* requests of this {@link IUser}. Timeout calculations are also done during the
* iteration process of this method, ensuring that teleport requests made past the timeout
* period are removed from queue and therefore not returned here. The maximum size of this
* queue is determined by {@link ISettings#getTpaMaxRequests()}.
*
* @param inform true if the underlying {@link IUser} should be informed if a request expires during iteration.
* @param ignoreExpirations true if this method should not process expirations for the entire queue and stop execution on the first unexpired request.
* @param excludeHere true if /tphere requests should be ignored in fetching the next tpa request.
* @return A {@link TpaRequest} corresponding to the next available request or null if no valid request is present.
*/
@Nullable TpaRequest getNextTpaRequest(boolean inform, boolean ignoreExpirations, boolean excludeHere);
/**
* Whether or not this {@link IUser} has any valid TPA requests in queue.
*
* @param inform true if the user should be informed if a request expires during iteration.
* @param excludeHere true if /tpahere requests should be ignored in checking if a tpa request is available.
* @return true if the user has an available pending request in queue.
*/
boolean hasPendingTpaRequests(boolean inform, boolean excludeHere);
class TpaRequest {
private final String name;
private final UUID requesterUuid;
private boolean here;
private Location location;
private long time;
public TpaRequest(String name, UUID requesterUuid) {
this.name = name;
this.requesterUuid = requesterUuid;
}
public String getName() {
return name;
}
public UUID getRequesterUuid() {
return requesterUuid;
}
public boolean isHere() {
return here;
}
public void setHere(boolean here) {
this.here = here;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
}
@@ -1,9 +1,10 @@
package com.earth2me.essentials;
import com.earth2me.essentials.storage.AsyncStorageObjectHolder;
import com.earth2me.essentials.config.ConfigurateUtil;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.config.entities.LazyLocation;
import net.ess3.api.IEssentials;
import net.ess3.api.IUser;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@@ -16,28 +17,59 @@ import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.*;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.plugin.PluginManager;
import org.spongepowered.configurate.CommentedConfigurationNode;
import java.io.File;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.earth2me.essentials.I18n.tl;
public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.settings.Jails> implements net.ess3.api.IJails {
private static final transient Logger LOGGER = Bukkit.getLogger();
public class Jails implements net.ess3.api.IJails {
private static final transient Logger LOGGER = Logger.getLogger("Essentials");
private static transient boolean enabled = false;
private final IEssentials ess;
private final EssentialsConfiguration config;
private final Map<String, LazyLocation> jails = new HashMap<>();
public Jails(final IEssentials ess) {
super(ess, com.earth2me.essentials.settings.Jails.class);
this.ess = ess;
this.config = new EssentialsConfiguration(new File(ess.getDataFolder(), "jail.yml"));
reloadConfig();
}
@Override
public void reloadConfig() {
synchronized (jails) {
config.load();
jails.clear();
final CommentedConfigurationNode jailsNode = config.getSection("jails");
for (final Map.Entry<String, CommentedConfigurationNode> entry : ConfigurateUtil.getMap(jailsNode).entrySet()) {
final CommentedConfigurationNode jailNode = entry.getValue();
final String worldId = jailNode.node("world").getString();
if (worldId == null || worldId.isEmpty()) {
continue;
}
jails.put(entry.getKey().toLowerCase(Locale.ENGLISH), new LazyLocation(worldId, jailNode.node("world-name").getString(""), jailNode.node("x").getDouble(), jailNode.node("y").getDouble(),
jailNode.node("z").getDouble(), jailNode.node("yaw").getFloat(), jailNode.node("pitch").getFloat()));
}
checkRegister();
}
}
private void registerListeners() {
enabled = true;
final PluginManager pluginManager = ess.getServer().getPluginManager();
@@ -48,21 +80,6 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
}
}
@Override
public File getStorageFile() {
return new File(ess.getDataFolder(), "jail.yml");
}
@Override
public void finishRead() {
checkRegister();
}
@Override
public void finishWrite() {
checkRegister();
}
public void resetListener() {
enabled = false;
checkRegister();
@@ -75,94 +92,104 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
}
@Override
public Location getJail(final String jailName) throws Exception {
acquireReadLock();
try {
if (getData().getJails() == null || jailName == null || !getData().getJails().containsKey(jailName.toLowerCase(Locale.ENGLISH))) {
public void startTransaction() {
config.startTransaction();
}
@Override
public void stopTransaction(final boolean blocking) {
config.stopTransaction(blocking);
}
@Override
public Location getJail(String jailName) throws Exception {
if (jailName == null) {
throw new Exception(tl("jailNotExist"));
}
jailName = jailName.toLowerCase(Locale.ENGLISH);
synchronized (jails) {
if (!jails.containsKey(jailName)) {
throw new Exception(tl("jailNotExist"));
}
Location loc = getData().getJails().get(jailName.toLowerCase(Locale.ENGLISH));
if (loc == null || loc.getWorld() == null) {
throw new Exception(tl("jailNotExist"));
final Location location = jails.get(jailName).location();
if (location == null) {
throw new Exception(tl("jailWorldNotExist"));
}
return loc;
} finally {
unlock();
return location;
}
}
@Override
public Collection<String> getList() throws Exception {
acquireReadLock();
try {
if (getData().getJails() == null) {
return Collections.emptyList();
}
return new ArrayList<>(getData().getJails().keySet());
} finally {
unlock();
synchronized (jails) {
return new ArrayList<>(jails.keySet());
}
}
@Override
public void removeJail(final String jail) throws Exception {
acquireWriteLock();
try {
if (getData().getJails() == null) {
return;
public void removeJail(String jail) throws Exception {
if (jail == null) {
return;
}
jail = jail.toLowerCase(Locale.ENGLISH);
synchronized (jails) {
if (jails.remove(jail) != null) {
config.getSection("jails").node(jail).set(null);
config.save();
}
getData().getJails().remove(jail.toLowerCase(Locale.ENGLISH));
} finally {
unlock();
}
}
/**
* @deprecated This method does not use asynchronous teleportation. Use {@link Jails#sendToJail(IUser, String, CompletableFuture)}
*/
@SuppressWarnings("deprecation")
@Override
@Deprecated
public void sendToJail(final IUser user, final String jail) throws Exception {
acquireReadLock();
try {
if (user.getBase().isOnline()) {
Location loc = getJail(jail);
user.getTeleport().now(loc, false, TeleportCause.COMMAND);
public void sendToJail(final IUser user, String jail) throws Exception {
if (jail == null || jail.isEmpty()) {
return;
}
jail = jail.toLowerCase(Locale.ENGLISH);
synchronized (jails) {
if (jails.containsKey(jail)) {
if (user.getBase().isOnline()) {
user.getTeleport().now(getJail(jail), false, TeleportCause.COMMAND);
}
user.setJail(jail);
}
user.setJail(jail);
} finally {
unlock();
}
}
@Override
public void sendToJail(IUser user, String jail, CompletableFuture<Boolean> future) throws Exception {
acquireReadLock();
try {
if (user.getBase().isOnline()) {
Location loc = getJail(jail);
user.getAsyncTeleport().now(loc, false, TeleportCause.COMMAND, future);
future.thenAccept(success -> {
user.setJail(jail);
});
return;
public void sendToJail(final IUser user, final String jailName, final CompletableFuture<Boolean> future) throws Exception {
if (jailName == null || jailName.isEmpty()) {
return;
}
final String jail = jailName.toLowerCase(Locale.ENGLISH);
synchronized (jails) {
if (jails.containsKey(jail)) {
if (user.getBase().isOnline()) {
user.getAsyncTeleport().now(getJail(jail), false, TeleportCause.COMMAND, future);
future.thenAccept(success -> user.setJail(jail));
return;
}
user.setJail(jail);
}
user.setJail(jail);
} finally {
unlock();
}
}
@Override
public void setJail(final String jailName, final Location loc) throws Exception {
acquireWriteLock();
try {
if (getData().getJails() == null) {
getData().setJails(new HashMap<>());
}
getData().getJails().put(jailName.toLowerCase(Locale.ENGLISH), loc);
} finally {
unlock();
public void setJail(String jailName, final Location loc) throws Exception {
jailName = jailName.toLowerCase(Locale.ENGLISH);
synchronized (jails) {
jails.put(jailName, LazyLocation.fromLocation(loc));
config.setProperty("jails." + jailName, loc);
config.save();
}
}
@@ -170,12 +197,11 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
public int getCount() {
try {
return getList().size();
} catch (Exception ex) {
} catch (final Exception ex) {
return 0;
}
}
private class JailListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailBlockBreak(final BlockBreakEvent event) {
@@ -224,7 +250,7 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onJailPlayerGameModeChange(PlayerGameModeChangeEvent event) {
public void onJailPlayerGameModeChange(final PlayerGameModeChangeEvent event) {
final User user = ess.getUser(event.getPlayer());
if (user.isJailed()) {
event.setCancelled(true);
@@ -240,7 +266,7 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
try {
event.setRespawnLocation(getJail(user.getJail()));
} catch (Exception ex) {
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
@@ -258,7 +284,7 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
try {
event.setTo(getJail(user.getJail()));
} catch (Exception ex) {
} catch (final Exception ex) {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
} else {
@@ -277,7 +303,7 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
return;
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
final CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally(ex -> {
if (ess.getSettings().isDebug()) {
LOGGER.log(Level.INFO, tl("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()), ex);
@@ -290,7 +316,7 @@ public class Jails extends AsyncStorageObjectHolder<com.earth2me.essentials.sett
try {
sendToJail(user, user.getJail(), future);
} catch (Exception ex) {
} catch (final Exception ex) {
future.completeExceptionally(ex);
}
}
@@ -7,21 +7,26 @@ import com.earth2me.essentials.textreader.IText;
import com.earth2me.essentials.textreader.KeywordReplacer;
import com.earth2me.essentials.textreader.SimpleTextInput;
import com.earth2me.essentials.utils.DateUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.NumberUtil;
import net.ess3.api.IEssentials;
import net.ess3.api.events.KitClaimEvent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import java.math.BigDecimal;
import java.util.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import static com.earth2me.essentials.I18n.tl;
public class Kit {
final IEssentials ess;
final String kitName;
@@ -50,7 +55,7 @@ public class Kit {
}
public void checkDelay(final User user) throws Exception {
long nextUse = getNextUse(user);
final long nextUse = getNextUse(user);
if (nextUse == 0L) {
} else if (nextUse < 0L) {
@@ -71,6 +76,10 @@ public class Kit {
user.setKitTimestamp(kitName, time.getTimeInMillis());
}
public void resetTime(final User user) {
user.setKitTimestamp(kitName, 0);
}
public void chargeUser(final User user) throws Exception {
charge.charge(user);
}
@@ -86,7 +95,7 @@ public class Kit {
try {
// Make sure delay is valid
delay = kit.containsKey("delay") ? ((Number) kit.get("delay")).doubleValue() : 0.0d;
} catch (Exception e) {
} catch (final Exception e) {
throw new Exception(tl("kitError2"));
}
@@ -127,7 +136,7 @@ public class Kit {
final List<String> itemList = new ArrayList<>();
final Object kitItems = kit.get("items");
if (kitItems instanceof List) {
for (Object item : (List) kitItems) {
for (final Object item : (List) kitItems) {
if (item instanceof String) {
itemList.add(item.toString());
continue;
@@ -137,7 +146,7 @@ public class Kit {
return itemList;
}
throw new Exception("Invalid item list");
} catch (Exception e) {
} catch (final Exception e) {
ess.getLogger().log(Level.WARNING, "Error parsing kit " + kitName + ": " + e.getMessage());
throw new Exception(tl("kitError2"), e);
}
@@ -149,10 +158,10 @@ public class Kit {
public boolean expandItems(final User user, final List<String> items) throws Exception {
try {
IText input = new SimpleTextInput(items);
IText output = new KeywordReplacer(input, user.getSource(), ess, true, true);
final IText input = new SimpleTextInput(items);
final IText output = new KeywordReplacer(input, user.getSource(), ess, true, true);
KitClaimEvent event = new KitClaimEvent(user, this);
final KitClaimEvent event = new KitClaimEvent(user, this);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
@@ -160,42 +169,72 @@ public class Kit {
boolean spew = false;
final boolean allowUnsafe = ess.getSettings().allowUnsafeEnchantments();
final boolean currencyIsSuffix = ess.getSettings().isCurrencySymbolSuffixed();
List<ItemStack> itemList = new ArrayList<>();
List<String> commandQueue = new ArrayList<>();
List<String> moneyQueue = new ArrayList<>();
for (String kitItem : output.getLines()) {
if (!currencyIsSuffix ? kitItem.startsWith(ess.getSettings().getCurrencySymbol()) : kitItem.endsWith(ess.getSettings().getCurrencySymbol())) {
final boolean autoEquip = ess.getSettings().isKitAutoEquip();
final List<ItemStack> itemList = new ArrayList<>();
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()) {
if (kitItem.startsWith("$") || kitItem.startsWith(currencySymbol)) {
moneyQueue.add(NumberUtil.sanitizeCurrencyString(kitItem, ess));
continue;
}
if (kitItem.startsWith("/")) {
String command = kitItem.substring(1);
String name = user.getName();
final String name = user.getName();
command = command.replace("{player}", name);
commandQueue.add(command);
continue;
}
final String[] parts = kitItem.split(" +");
final ItemStack parseStack = ess.getItemDb().get(parts[0], parts.length > 1 ? Integer.parseInt(parts[1]) : 1);
final ItemStack stack;
if (parseStack.getType() == Material.AIR) {
continue;
if (kitItem.startsWith("@")) {
if (ess.getSerializationProvider() == null) {
ess.getLogger().log(Level.WARNING, tl("kitError3", kitName, user.getName()));
continue;
}
stack = ess.getSerializationProvider().deserializeItem(Base64Coder.decodeLines(kitItem.substring(1)));
} else {
final String[] parts = kitItem.split(" +");
final ItemStack parseStack = ess.getItemDb().get(parts[0], parts.length > 1 ? Integer.parseInt(parts[1]) : 1);
if (parseStack.getType() == Material.AIR) {
continue;
}
final MetaItemStack metaStack = new MetaItemStack(parseStack);
if (parts.length > 2) {
// We pass a null sender here because kits should not do perm checks
metaStack.parseStringMeta(null, allowUnsafe, parts, 2, ess);
}
stack = metaStack.getItemStack();
}
final MetaItemStack metaStack = new MetaItemStack(parseStack);
if (parts.length > 2) {
// We pass a null sender here because kits should not do perm checks
metaStack.parseStringMeta(null, allowUnsafe, parts, 2, ess);
if (autoEquip) {
final Material material = stack.getType();
final PlayerInventory inventory = user.getBase().getInventory();
if (MaterialUtil.isHelmet(material) && isEmptyStack(inventory.getHelmet())) {
inventory.setHelmet(stack);
continue;
} else if (MaterialUtil.isChestplate(material) && isEmptyStack(inventory.getChestplate())) {
inventory.setChestplate(stack);
continue;
} else if (MaterialUtil.isLeggings(material) && isEmptyStack(inventory.getLeggings())) {
inventory.setLeggings(stack);
continue;
} else if (MaterialUtil.isBoots(material) && isEmptyStack(inventory.getBoots())) {
inventory.setBoots(stack);
continue;
}
}
itemList.add(metaStack.getItemStack());
itemList.add(stack);
}
final Map<Integer, ItemStack> overfilled;
final boolean allowOversizedStacks = user.isAuthorized("essentials.oversizedstacks");
final boolean isDropItemsIfFull = ess.getSettings().isDropItemsIfFull();
@@ -205,7 +244,7 @@ public class Kit {
} else {
overfilled = InventoryWorkaround.addItems(user.getBase().getInventory(), itemList.toArray(new ItemStack[0]));
}
for (ItemStack itemStack : overfilled.values()) {
for (final ItemStack itemStack : overfilled.values()) {
int spillAmount = itemStack.getAmount();
if (!allowOversizedStacks) {
itemStack.setAmount(Math.min(spillAmount, itemStack.getMaxStackSize()));
@@ -232,24 +271,28 @@ public class Kit {
// Process money & command queues
// Done after all items have been processed so commands are not run and money is not given if
// an error occurs during the item giving process
for (String valueString : moneyQueue) {
BigDecimal value = new BigDecimal(valueString.trim());
Trade t = new Trade(value, ess);
for (final String valueString : moneyQueue) {
final BigDecimal value = new BigDecimal(valueString.trim());
final Trade t = new Trade(value, ess);
t.pay(user, OverflowType.DROP);
}
for (String cmd : commandQueue) {
for (final String cmd : commandQueue) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd);
}
if (spew) {
user.sendMessage(tl("kitInvFull"));
}
} catch (Exception e) {
} catch (final Exception e) {
user.getBase().updateInventory();
ess.getLogger().log(Level.WARNING, e.getMessage());
throw new Exception(tl("kitError2"), e);
}
return true;
}
private boolean isEmptyStack(ItemStack stack) {
return stack == null || MaterialUtil.isAir(stack.getType());
}
}
@@ -0,0 +1,168 @@
package com.earth2me.essentials;
import com.earth2me.essentials.config.ConfigurateUtil;
import com.earth2me.essentials.config.EssentialsConfiguration;
import com.earth2me.essentials.utils.NumberUtil;
import org.spongepowered.configurate.CommentedConfigurationNode;
import java.io.File;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static com.earth2me.essentials.I18n.capitalCase;
import static com.earth2me.essentials.I18n.tl;
public class Kits implements IConf {
private final IEssentials ess;
private final EssentialsConfiguration rootConfig;
private final Map<String, EssentialsConfiguration> kitToConfigMap = new HashMap<>();
private final Map<String, Map<String, Object>> kitMap = new HashMap<>();
public Kits(final IEssentials essentials) {
this.ess = essentials;
this.rootConfig = new EssentialsConfiguration(new File(essentials.getDataFolder(), "kits.yml"), "/kits.yml");
reloadConfig();
}
@Override
public void reloadConfig() {
rootConfig.load();
parseKits();
}
public File getFile() {
return rootConfig.getFile();
}
private void parseKit(final String kitName, final CommentedConfigurationNode kitSection, final EssentialsConfiguration parentConfig) {
if (kitSection.isMap()) {
final String effectiveKitName = kitName.toLowerCase(Locale.ENGLISH);
kitToConfigMap.put(effectiveKitName, parentConfig);
kitMap.put(effectiveKitName, ConfigurateUtil.getRawMap(kitSection));
}
}
private void parseKits() {
kitToConfigMap.clear();
kitMap.clear();
// Kits from kits.yml file
final CommentedConfigurationNode fileKits = rootConfig.getSection("kits");
if (fileKits != null) {
for (final Map.Entry<String, CommentedConfigurationNode> kitEntry : ConfigurateUtil.getMap(fileKits).entrySet()) {
parseKit(kitEntry.getKey(), kitEntry.getValue(), rootConfig);
}
}
// Kits from kits subdirectory
final File kitsFolder = new File(this.ess.getDataFolder(), "kits");
if (!kitsFolder.exists() || !kitsFolder.isDirectory()) {
return;
}
final File[] kitsFiles = kitsFolder.listFiles();
//noinspection ConstantConditions - will not be null, conditions checked above.
for (final File kitFile : kitsFiles) {
if (kitFile.getName().endsWith(".yml")) {
final EssentialsConfiguration kitConfig = new EssentialsConfiguration(kitFile);
kitConfig.load();
final CommentedConfigurationNode kits = kitConfig.getSection("kits");
if (kits != null) {
for (final Map.Entry<String, CommentedConfigurationNode> kitEntry : ConfigurateUtil.getMap(kits).entrySet()) {
parseKit(kitEntry.getKey(), kitEntry.getValue(), kitConfig);
}
}
}
}
}
/**
* Should be used for EssentialsUpgrade conversions <b>only</b>.
*/
public EssentialsConfiguration getRootConfig() {
return rootConfig;
}
public Set<String> getKitKeys() {
return kitMap.keySet();
}
public Map<String, Object> getKit(final String name) {
if (name != null) {
return kitMap.get(name.replace('.', '_').replace('/', '_'));
}
return null;
}
// Tries to find an existing kit name that matches the given name, ignoring case. Returns null if no match.
public String matchKit(final String name) {
if (name != null) {
for (final String kitName : kitMap.keySet()) {
if (kitName.equalsIgnoreCase(name)) {
return kitName;
}
}
}
return null;
}
public void addKit(String name, final List<String> lines, final long delay) {
name = name.replace('.', '_').replace('/', '_').toLowerCase(Locale.ENGLISH);
// Will overwrite but w/e
rootConfig.setProperty("kits." + name + ".delay", delay);
rootConfig.setProperty("kits." + name + ".items", lines);
parseKits();
rootConfig.save();
}
public void removeKit(String name) {
name = name.replace('.', '_').replace('/', '_').toLowerCase(Locale.ENGLISH);
if (!kitToConfigMap.containsKey(name) || !kitMap.containsKey(name)) {
return;
}
final EssentialsConfiguration config = kitToConfigMap.get(name);
config.removeProperty("kits." + name);
config.blockingSave();
parseKits();
}
public String listKits(final net.ess3.api.IEssentials ess, final User user) throws Exception {
try {
final StringBuilder list = new StringBuilder();
for (final String kitItem : kitMap.keySet()) {
if (user == null) {
list.append(" ").append(capitalCase(kitItem));
} else if (user.isAuthorized("essentials.kits." + kitItem.toLowerCase(Locale.ENGLISH))) {
String cost = "";
String name = capitalCase(kitItem);
final BigDecimal costPrice = new Trade("kit-" + kitItem.toLowerCase(Locale.ENGLISH), ess).getCommandCost(user);
if (costPrice.signum() > 0) {
cost = tl("kitCost", NumberUtil.displayCurrency(costPrice, ess));
}
final Kit kit = new Kit(kitItem, ess);
final double nextUse = kit.getNextUse(user);
if (nextUse == -1 && ess.getSettings().isSkippingUsedOneTimeKitsFromKitList()) {
continue;
} else if (nextUse != 0) {
name = tl("kitDelay", name);
}
list.append(" ").append(name).append(cost);
}
}
return list.toString().trim();
} catch (final Exception ex) {
throw new Exception(tl("kitError"), ex);
}
}
}
@@ -2,11 +2,10 @@ package com.earth2me.essentials;
import org.bukkit.Location;
public class LocationTarget implements ITarget {
private final Location location;
LocationTarget(Location location) {
LocationTarget(final Location location) {
this.location = location;
}
@@ -14,4 +13,4 @@ public class LocationTarget implements ITarget {
public Location getLocation() {
return location;
}
}
}
@@ -0,0 +1,53 @@
package com.earth2me.essentials;
import net.ess3.api.IUser;
import net.essentialsx.api.v2.services.mail.MailService;
import net.essentialsx.api.v2.services.mail.MailMessage;
import net.essentialsx.api.v2.services.mail.MailSender;
import org.bukkit.plugin.ServicePriority;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import static com.earth2me.essentials.I18n.tl;
public class MailServiceImpl implements MailService {
private final transient ThreadLocal<SimpleDateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy/MM/dd HH:mm"));
public MailServiceImpl(IEssentials ess) {
ess.getServer().getServicesManager().register(MailService.class, this, ess, ServicePriority.Normal);
}
@Override
public void sendMail(IUser recipient, MailSender sender, String message) {
sendMail(recipient, sender, message, 0L);
}
@Override
public void sendMail(IUser recipient, MailSender sender, String message, long expireAt) {
sendMail(recipient, new MailMessage(false, false, sender.getName(), sender.getUUID(), System.currentTimeMillis(), expireAt, message));
}
@Override
public void sendLegacyMail(IUser recipient, String message) {
sendMail(recipient, new MailMessage(false, true, null, null, 0L, 0L, message));
}
private void sendMail(IUser recipient, MailMessage message) {
final ArrayList<MailMessage> messages = recipient.getMailMessages();
messages.add(0, message);
recipient.setMailList(messages);
}
@Override
public String getMailLine(MailMessage mail) {
final String message = mail.getMessage();
if (mail.isLegacy()) {
return tl("mailMessage", message);
}
final String expire = mail.getTimeExpire() != 0 ? "Timed" : "";
return tl((mail.isRead() ? "mailFormatNewRead" : "mailFormatNew") + expire, df.get().format(new Date(mail.getTimeSent())), mail.getSenderUsername(), message);
}
}
@@ -3,7 +3,16 @@ package com.earth2me.essentials;
import net.ess3.api.IEssentials;
import org.bukkit.Bukkit;
import java.io.*;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
@@ -16,7 +25,6 @@ import java.util.logging.Level;
import static com.earth2me.essentials.I18n.tl;
public class ManagedFile {
private static final int BUFFERSIZE = 1024 * 8;
private final transient File file;
@@ -29,7 +37,7 @@ public class ManagedFile {
if (checkForVersion(file, ess.getDescription().getVersion()) && !file.delete()) {
throw new IOException("Could not delete file " + file.toString());
}
} catch (IOException ex) {
} catch (final IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
}
@@ -37,17 +45,17 @@ public class ManagedFile {
if (!file.exists()) {
try {
copyResourceAscii("/" + filename, file);
} catch (IOException ex) {
} catch (final IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, tl("itemsCsvNotLoaded", filename), ex);
}
}
}
public static void copyResourceAscii(final String resourceName, final File file) throws IOException {
try (InputStreamReader reader = new InputStreamReader(ManagedFile.class.getResourceAsStream(resourceName))) {
try (final InputStreamReader reader = new InputStreamReader(ManagedFile.class.getResourceAsStream(resourceName))) {
final MessageDigest digest = getDigest();
try (DigestOutputStream digestStream = new DigestOutputStream(new FileOutputStream(file), digest)) {
try (OutputStreamWriter writer = new OutputStreamWriter(digestStream)) {
try (final DigestOutputStream digestStream = new DigestOutputStream(new FileOutputStream(file), digest)) {
try (final OutputStreamWriter writer = new OutputStreamWriter(digestStream)) {
final char[] buffer = new char[BUFFERSIZE];
do {
final int length = reader.read(buffer);
@@ -72,7 +80,7 @@ public class ManagedFile {
if (file.length() < 33) {
return false;
}
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
final byte[] buffer = new byte[(int) file.length()];
int position = 0;
do {
@@ -86,7 +94,7 @@ public class ManagedFile {
if (bais.skip(file.length() - 33) != file.length() - 33) {
return false;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(bais))) {
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(bais))) {
String hash = reader.readLine();
if (hash != null && hash.matches("#[a-f0-9]{32}")) {
hash = hash.substring(1);
@@ -97,7 +105,7 @@ public class ManagedFile {
if (!versioncheck.equalsIgnoreCase(version)) {
bais.reset();
final MessageDigest digest = getDigest();
try (DigestInputStream digestStream = new DigestInputStream(bais, digest)) {
try (final DigestInputStream digestStream = new DigestInputStream(bais, digest)) {
final byte[] bytes = new byte[(int) file.length() - 33];
digestStream.read(bytes);
final BigInteger correct = new BigInteger(hash, 16);
@@ -119,14 +127,14 @@ public class ManagedFile {
public static MessageDigest getDigest() throws IOException {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
} catch (final NoSuchAlgorithmException ex) {
throw new IOException(ex);
}
}
public List<String> getLines() {
try {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
try (final BufferedReader reader = new BufferedReader(new FileReader(file))) {
final List<String> lines = new ArrayList<>();
do {
final String line = reader.readLine();
@@ -138,7 +146,7 @@ public class ManagedFile {
} while (true);
return lines;
}
} catch (IOException ex) {
} catch (final IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
return Collections.emptyList();
}
@@ -7,9 +7,9 @@ import com.earth2me.essentials.utils.EnumUtil;
import com.earth2me.essentials.utils.FormatUtil;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.NumberUtil;
import com.earth2me.essentials.utils.VersionUtil;
import com.google.common.base.Joiner;
import net.ess3.api.IEssentials;
import net.ess3.nms.refl.ReflUtil;
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.bukkit.FireworkEffect;
@@ -19,38 +19,53 @@ import org.bukkit.block.banner.PatternType;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
import org.bukkit.inventory.meta.BannerMeta;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.FireworkEffectMeta;
import org.bukkit.inventory.meta.FireworkMeta;
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.potion.Potion;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.lang.reflect.Method;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.regex.Pattern;
import static com.earth2me.essentials.I18n.tl;
public class MetaItemStack {
private static final Map<String, DyeColor> colorMap = new HashMap<>();
private static final Map<String, FireworkEffect.Type> fireworkShape = new HashMap<>();
private static boolean useNewSkullMethod = true;
static {
for (DyeColor color : DyeColor.values()) {
for (final DyeColor color : DyeColor.values()) {
colorMap.put(color.name(), color);
}
for (FireworkEffect.Type type : FireworkEffect.Type.values()) {
for (final FireworkEffect.Type type : FireworkEffect.Type.values()) {
fireworkShape.put(type.name(), type);
}
}
private final transient Pattern splitPattern = Pattern.compile("[:+',;.]");
private static final transient Pattern splitPattern = Pattern.compile("[:+',;.]");
private static final transient Pattern hexPattern = Pattern.compile("#([0-9a-fA-F]{6})");
private ItemStack stack;
private FireworkEffect.Builder builder = FireworkEffect.builder();
private PotionEffectType pEffectType;
private PotionEffect pEffect;
private boolean validFirework = false;
private boolean validFireworkCharge = false;
private boolean validPotionEffect = false;
private boolean validPotionDuration = false;
private boolean validPotionPower = false;
@@ -63,6 +78,24 @@ public class MetaItemStack {
this.stack = stack.clone();
}
private static void setSkullOwner(final IEssentials ess, final ItemStack stack, final String owner) {
if (!(stack.getItemMeta() instanceof SkullMeta)) return;
final SkullMeta meta = (SkullMeta) stack.getItemMeta();
if (useNewSkullMethod) {
try {
meta.setOwningPlayer(ess.getServer().getOfflinePlayer(owner));
stack.setItemMeta(meta);
return;
} catch (final NoSuchMethodError e) {
useNewSkullMethod = false;
}
}
meta.setOwner(owner);
stack.setItemMeta(meta);
}
public ItemStack getItemStack() {
return stack;
}
@@ -101,9 +134,9 @@ public class MetaItemStack {
try {
ess.getServer().getUnsafe().modifyItemStack(stack.clone(), "{}");
return true;
} catch (NoSuchMethodError nsme) {
} catch (final NoSuchMethodError nsme) {
return true;
} catch (Throwable npe) {
} catch (final Throwable npe) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Itemstack is invalid", npe);
}
@@ -111,17 +144,17 @@ public class MetaItemStack {
}
}
public void parseStringMeta(final CommandSource sender, final boolean allowUnsafe, String[] string, int fromArg, final IEssentials ess) throws Exception {
public void parseStringMeta(final CommandSource sender, final boolean allowUnsafe, final String[] string, final int fromArg, final IEssentials ess) throws Exception {
if (string[fromArg].startsWith("{") && hasMetaPermission(sender, "vanilla", false, true, ess)) {
try {
stack = ess.getServer().getUnsafe().modifyItemStack(stack, Joiner.on(' ').join(Arrays.asList(string).subList(fromArg, string.length)));
} catch (NullPointerException npe) {
} catch (final NullPointerException npe) {
if (ess.getSettings().isDebug()) {
ess.getLogger().log(Level.INFO, "Itemstack is invalid", npe);
}
} catch (NoSuchMethodError nsme) {
} catch (final NoSuchMethodError nsme) {
throw new Exception(tl("noMetaJson"), nsme);
} catch (Throwable throwable) {
} catch (final Throwable throwable) {
throw new Exception(throwable.getMessage(), throwable);
}
} else {
@@ -132,14 +165,23 @@ public class MetaItemStack {
if (!hasMetaPermission(sender, "firework", true, true, ess)) {
throw new Exception(tl("noMetaFirework"));
}
FireworkEffect effect = builder.build();
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
final FireworkEffect effect = builder.build();
final FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
fmeta.addEffect(effect);
if (fmeta.getEffects().size() > 1 && !hasMetaPermission(sender, "firework-multiple", true, true, ess)) {
throw new Exception(tl("multipleCharges"));
}
stack.setItemMeta(fmeta);
}
if (validFireworkCharge) {
if (!hasMetaPermission(sender, "firework", true, true, ess)) {
throw new Exception(tl("noMetaFirework"));
}
final FireworkEffect effect = builder.build();
final FireworkEffectMeta meta = (FireworkEffectMeta) stack.getItemMeta();
meta.setEffect(effect);
stack.setItemMeta(meta);
}
}
}
@@ -149,7 +191,7 @@ public class MetaItemStack {
return;
}
Material WRITTEN_BOOK = EnumUtil.getMaterial("WRITTEN_BOOK");
final Material WRITTEN_BOOK = EnumUtil.getMaterial("WRITTEN_BOOK");
if (split.length > 1 && split[0].equalsIgnoreCase("name") && hasMetaPermission(sender, "name", false, true, ess)) {
final String displayName = FormatUtil.replaceFormat(split[1].replace('_', ' '));
@@ -158,28 +200,28 @@ public class MetaItemStack {
stack.setItemMeta(meta);
} else if (split.length > 1 && (split[0].equalsIgnoreCase("lore") || split[0].equalsIgnoreCase("desc")) && hasMetaPermission(sender, "lore", false, true, ess)) {
final List<String> lore = new ArrayList<>();
for (String line : split[1].split("(?<!\\\\)\\|")) {
for (final String line : split[1].split("(?<!\\\\)\\|")) {
lore.add(FormatUtil.replaceFormat(line.replace('_', ' ').replace("\\|", "|")));
}
final ItemMeta meta = stack.getItemMeta();
meta.setLore(lore);
stack.setItemMeta(meta);
} else if (split[0].equalsIgnoreCase("unbreakable") && hasMetaPermission(sender, "unbreakable", false, true, ess)) {
boolean value = split.length <= 1 || Boolean.parseBoolean(split[1]);
setUnbreakable(stack, value);
final boolean value = split.length <= 1 || Boolean.parseBoolean(split[1]);
setUnbreakable(ess, stack, value);
} else if (split.length > 1 && (split[0].equalsIgnoreCase("player") || split[0].equalsIgnoreCase("owner")) && hasMetaPermission(sender, "head", false, true, ess)) {
if (MaterialUtil.isPlayerHead(stack.getType(), stack.getDurability())) {
if (MaterialUtil.isPlayerHead(stack)) {
final String owner = split[1];
setSkullOwner(ess, stack, owner);
} else {
throw new Exception(tl("onlyPlayerSkulls"));
}
} else if (split.length > 1 && split[0].equalsIgnoreCase("book") && stack.getType() == WRITTEN_BOOK && (hasMetaPermission(sender, "book", true, true, ess) || hasMetaPermission(sender, "chapter-" + split[1].toLowerCase(Locale.ENGLISH), true, true, ess))) {
} else if (split.length > 1 && split[0].equalsIgnoreCase("book") && MaterialUtil.isEditableBook(stack.getType()) && (hasMetaPermission(sender, "book",true, true, ess) || hasMetaPermission(sender, "chapter-" + split[1].toLowerCase(Locale.ENGLISH), true, true, ess))) {
final BookMeta meta = (BookMeta) stack.getItemMeta();
final IText input = new BookInput("book", true, ess);
final BookPager pager = new BookPager(input);
List<String> pages = pager.getPages(split[1]);
final List<String> pages = pager.getPages(split[1]);
meta.setPages(pages);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("author") && stack.getType() == WRITTEN_BOOK && hasMetaPermission(sender, "author", false, true, ess)) {
@@ -192,7 +234,24 @@ public class MetaItemStack {
final BookMeta meta = (BookMeta) stack.getItemMeta();
meta.setTitle(title);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("power") && (MaterialUtil.isFirework(stack.getType()))&& hasMetaPermission(sender, "firework-power", false, true, ess)) {
} else if (split.length > 1 && split[0].startsWith("page") && split[0].length() > 4 && MaterialUtil.isEditableBook(stack.getType()) && hasMetaPermission(sender, "page", false, true, ess)) {
final int page = NumberUtil.isInt(split[0].substring(4)) ? (Integer.parseInt(split[0].substring(4)) - 1) : 0;
final BookMeta meta = (BookMeta) stack.getItemMeta();
final List<String> pages = meta.hasPages() ? new ArrayList<>(meta.getPages()) : new ArrayList<>();
final List<String> lines = new ArrayList<>();
for (final String line : split[1].split("(?<!\\\\)\\|")) {
lines.add(FormatUtil.replaceFormat(line.replace('_', ' ').replace("\\|", "|")));
}
final String content = String.join("\n", lines);
if (page >= pages.size()) {
for (int i = 0; i <= page - pages.size(); i++) {
pages.add("");
}
}
pages.set(page, content);
meta.setPages(pages);
stack.setItemMeta(meta);
} else if (split.length > 1 && split[0].equalsIgnoreCase("power") && MaterialUtil.isFirework(stack.getType()) && hasMetaPermission(sender, "firework-power", false, true, ess)) {
final int power = NumberUtil.isInt(split[1]) ? Integer.parseInt(split[1]) : 0;
final FireworkMeta meta = (FireworkMeta) stack.getItemMeta();
meta.setPower(power > 3 ? 4 : power);
@@ -200,24 +259,31 @@ public class MetaItemStack {
} else if (split.length > 1 && split[0].equalsIgnoreCase("itemflags") && hasMetaPermission(sender, "itemflags", false, true, ess)) {
addItemFlags(string);
} else if (MaterialUtil.isFirework(stack.getType())) {
//WARNING - Meta for fireworks will be ignored after this point.
addFireworkMeta(sender, false, string, ess);
if (!parseEnchantmentStrings(sender, allowUnsafe, split, ess)) {
//WARNING - Meta for fireworks will be ignored after this point.
addFireworkMeta(sender, false, string, ess);
}
} else if (MaterialUtil.isFireworkCharge(stack.getType())) {
if (!parseEnchantmentStrings(sender, allowUnsafe, split, ess)) {
//WARNING - Meta for fireworks will be ignored after this point.
addChargeMeta(sender, false, string, ess);
}
} else if (MaterialUtil.isPotion(stack.getType())) {
//WARNING - Meta for potions will be ignored after this point.
addPotionMeta(sender, false, string, ess);
if (split[0].equalsIgnoreCase("power") || !parseEnchantmentStrings(sender, allowUnsafe, split, ess)) {
//WARNING - Meta for potions will be ignored after this point.
addPotionMeta(sender, false, string, ess);
}
} else if (MaterialUtil.isBanner(stack.getType())) {
if (stack.getType().toString().equals("SHIELD") && Enchantments.getByName(split[0]) != null) {
parseEnchantmentStrings(sender, allowUnsafe, split, ess);
} else {
if (!parseEnchantmentStrings(sender, allowUnsafe, split, ess)) {
//WARNING - Meta for banners will be ignored after this point.
addBannerMeta(sender, false, string, ess);
}
} else if (split.length > 1 && (split[0].equalsIgnoreCase("color") || split[0].equalsIgnoreCase("colour")) && MaterialUtil.isLeatherArmor(stack.getType())) {
final String[] color = split[1].split("(\\||,)");
final String[] color = split[1].split("[|,]");
if (color.length == 1 && (NumberUtil.isInt(color[0]) || color[0].startsWith("#"))) {
// Either integer or hexadecimal
final LeatherArmorMeta meta = (LeatherArmorMeta) stack.getItemMeta();
String input = color[0];
final String input = color[0];
if (input.startsWith("#")) { // Hex
meta.setColor(Color.fromRGB(
Integer.valueOf(input.substring(1, 3), 16),
@@ -243,16 +309,16 @@ public class MetaItemStack {
}
public void addItemFlags(final String string) throws Exception {
String[] separate = splitPattern.split(string, 2);
final String[] separate = splitPattern.split(string, 2);
if (separate.length != 2) {
throw new Exception(tl("invalidItemFlagMeta", string));
}
String[] split = separate[1].split(",");
ItemMeta meta = stack.getItemMeta();
final String[] split = separate[1].split(",");
final ItemMeta meta = stack.getItemMeta();
for (String s : split) {
for (ItemFlag flag : ItemFlag.values()) {
for (final String s : split) {
for (final ItemFlag flag : ItemFlag.values()) {
if (s.equalsIgnoreCase(flag.name())) {
meta.addItemFlags(flag);
}
@@ -266,8 +332,69 @@ public class MetaItemStack {
stack.setItemMeta(meta);
}
private void addChargeMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
return;
}
if (split[0].equalsIgnoreCase("color") || split[0].equalsIgnoreCase("colour") || (allowShortName && split[0].equalsIgnoreCase("c"))) {
final List<Color> primaryColors = new ArrayList<>();
final String[] colors = split[1].split(",");
for (final String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
validFireworkCharge = true;
primaryColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else if (hexPattern.matcher(color).matches()) {
validFireworkCharge = true;
primaryColors.add(Color.fromRGB(Integer.decode(color)));
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
builder.withColor(primaryColors);
} else if (split[0].equalsIgnoreCase("shape") || split[0].equalsIgnoreCase("type") || (allowShortName && (split[0].equalsIgnoreCase("s") || split[0].equalsIgnoreCase("t")))) {
FireworkEffect.Type finalEffect = null;
split[1] = split[1].equalsIgnoreCase("large") ? "BALL_LARGE" : split[1];
if (fireworkShape.containsKey(split[1].toUpperCase())) {
finalEffect = fireworkShape.get(split[1].toUpperCase());
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
if (finalEffect != null) {
builder.with(finalEffect);
}
} else if (split[0].equalsIgnoreCase("fade") || (allowShortName && split[0].equalsIgnoreCase("f"))) {
final List<Color> fadeColors = new ArrayList<>();
final String[] colors = split[1].split(",");
for (final String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
fadeColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else if (hexPattern.matcher(color).matches()) {
fadeColors.add(Color.fromRGB(Integer.decode(color)));
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
if (!fadeColors.isEmpty()) {
builder.withFade(fadeColors);
}
} else if (split[0].equalsIgnoreCase("effect") || (allowShortName && split[0].equalsIgnoreCase("e"))) {
final String[] effects = split[1].split(",");
for (final String effect : effects) {
if (effect.equalsIgnoreCase("twinkle")) {
builder.flicker(true);
} else if (effect.equalsIgnoreCase("trail")) {
builder.trail(true);
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
}
}
}
public void addFireworkMeta(final CommandSource sender, final boolean allowShortName, final String string, final IEssentials ess) throws Exception {
if (MaterialUtil.isFirework(stack.getType())) {
if (MaterialUtil.isFirework(stack.getType())) {
final String[] split = splitPattern.split(string, 2);
if (split.length < 2) {
return;
@@ -278,8 +405,8 @@ public class MetaItemStack {
if (!hasMetaPermission(sender, "firework", true, true, ess)) {
throw new Exception(tl("noMetaFirework"));
}
FireworkEffect effect = builder.build();
FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
final FireworkEffect effect = builder.build();
final FireworkMeta fmeta = (FireworkMeta) stack.getItemMeta();
fmeta.addEffect(effect);
if (fmeta.getEffects().size() > 1 && !hasMetaPermission(sender, "firework-multiple", true, true, ess)) {
throw new Exception(tl("multipleCharges"));
@@ -288,12 +415,15 @@ public class MetaItemStack {
builder = FireworkEffect.builder();
}
List<Color> primaryColors = new ArrayList<>();
String[] colors = split[1].split(",");
for (String color : colors) {
final List<Color> primaryColors = new ArrayList<>();
final String[] colors = split[1].split(",");
for (final String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
validFirework = true;
primaryColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else if (hexPattern.matcher(color).matches()) {
validFirework = true;
primaryColors.add(Color.fromRGB(Integer.decode(color)));
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
@@ -301,7 +431,7 @@ public class MetaItemStack {
builder.withColor(primaryColors);
} else if (split[0].equalsIgnoreCase("shape") || split[0].equalsIgnoreCase("type") || (allowShortName && (split[0].equalsIgnoreCase("s") || split[0].equalsIgnoreCase("t")))) {
FireworkEffect.Type finalEffect = null;
split[1] = (split[1].equalsIgnoreCase("large") ? "BALL_LARGE" : split[1]);
split[1] = split[1].equalsIgnoreCase("large") ? "BALL_LARGE" : split[1];
if (fireworkShape.containsKey(split[1].toUpperCase())) {
finalEffect = fireworkShape.get(split[1].toUpperCase());
} else {
@@ -311,11 +441,13 @@ public class MetaItemStack {
builder.with(finalEffect);
}
} else if (split[0].equalsIgnoreCase("fade") || (allowShortName && split[0].equalsIgnoreCase("f"))) {
List<Color> fadeColors = new ArrayList<>();
String[] colors = split[1].split(",");
for (String color : colors) {
final List<Color> fadeColors = new ArrayList<>();
final String[] colors = split[1].split(",");
for (final String color : colors) {
if (colorMap.containsKey(color.toUpperCase())) {
fadeColors.add(colorMap.get(color.toUpperCase()).getFireworkColor());
} else if (hexPattern.matcher(color).matches()) {
fadeColors.add(Color.fromRGB(Integer.decode(color)));
} else {
throw new Exception(tl("invalidFireworkFormat", split[1], split[0]));
}
@@ -324,8 +456,8 @@ public class MetaItemStack {
builder.withFade(fadeColors);
}
} else if (split[0].equalsIgnoreCase("effect") || (allowShortName && split[0].equalsIgnoreCase("e"))) {
String[] effects = split[1].split(",");
for (String effect : effects) {
final String[] effects = split[1].split(",");
for (final String effect : effects) {
if (effect.equalsIgnoreCase("twinkle")) {
builder.flicker(true);
} else if (effect.equalsIgnoreCase("trail")) {
@@ -386,21 +518,21 @@ public class MetaItemStack {
}
if (isValidPotion()) {
PotionMeta pmeta = (PotionMeta) stack.getItemMeta();
final PotionMeta pmeta = (PotionMeta) stack.getItemMeta();
pEffect = pEffectType.createEffect(duration, power);
if (pmeta.getCustomEffects().size() > 1 && !hasMetaPermission(sender, "potions.multiple", true, false, ess)) {
throw new Exception(tl("multiplePotionEffects"));
}
pmeta.addCustomEffect(pEffect, true);
stack.setItemMeta(pmeta);
if (ReflUtil.getNmsVersionObject().isHigherThanOrEqualTo(ReflUtil.V1_9_R1)) {
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_9_R01)) {
if (isSplashPotion && stack.getType() != Material.SPLASH_POTION) {
stack.setType(Material.SPLASH_POTION);
} else if (!isSplashPotion && stack.getType() != Material.POTION) {
stack.setType(Material.POTION);
}
} else {
Potion potion = Potion.fromItemStack(stack);
final Potion potion = Potion.fromDamage(stack.getDurability());
potion.setSplash(isSplashPotion);
potion.apply(stack);
}
@@ -409,25 +541,27 @@ public class MetaItemStack {
}
}
private void parseEnchantmentStrings(final CommandSource sender, final boolean allowUnsafe, final String[] split, final IEssentials ess) throws Exception {
private boolean parseEnchantmentStrings(final CommandSource sender, final boolean allowUnsafe, final String[] split, final IEssentials ess) throws Exception {
final Enchantment enchantment = Enchantments.getByName(split[0]);
if (enchantment == null || !hasMetaPermission(sender, "enchantments." + enchantment.getName().toLowerCase(Locale.ENGLISH), false, false, ess)) {
return;
if (enchantment == null) {
return false;
}
int level = -1;
if (split.length > 1) {
try {
level = Integer.parseInt(split[1]);
} catch (NumberFormatException ex) {
level = -1;
if (hasMetaPermission(sender, "enchantments." + enchantment.getName().toLowerCase(Locale.ENGLISH), false, false, ess)) {
int level = -1;
if (split.length > 1) {
try {
level = Integer.parseInt(split[1]);
} catch (final NumberFormatException ex) {
level = -1;
}
}
}
if (level < 0 || (!allowUnsafe && level > enchantment.getMaxLevel())) {
level = enchantment.getMaxLevel();
if (level < 0 || (!allowUnsafe && level > enchantment.getMaxLevel())) {
level = enchantment.getMaxLevel();
}
addEnchantment(sender, allowUnsafe, enchantment, level);
}
addEnchantment(sender, allowUnsafe, enchantment, level);
return true;
}
public void addEnchantment(final CommandSource sender, final boolean allowUnsafe, final Enchantment enchantment, final int level) throws Exception {
@@ -436,15 +570,14 @@ public class MetaItemStack {
}
try {
if (stack.getType().equals(Material.ENCHANTED_BOOK)) {
EnchantmentStorageMeta meta = (EnchantmentStorageMeta) stack.getItemMeta();
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) stack.getItemMeta();
if (level == 0) {
meta.removeStoredEnchant(enchantment);
} else {
meta.addStoredEnchant(enchantment, level, allowUnsafe);
}
stack.setItemMeta(meta);
} else // all other material types besides ENCHANTED_BOOK
{
} else { // all other material types besides ENCHANTED_BOOK
if (level == 0) {
stack.removeEnchantment(enchantment);
} else {
@@ -455,7 +588,7 @@ public class MetaItemStack {
}
}
}
} catch (Exception ex) {
} catch (final Exception ex) {
throw new Exception("Enchantment " + enchantment.getName() + ": " + ex.getMessage(), ex);
}
}
@@ -485,16 +618,17 @@ public class MetaItemStack {
PatternType patternType = null;
try {
patternType = PatternType.valueOf(split[0]);
} catch (Exception ignored ) {}
} catch (final Exception ignored) {
}
final BannerMeta meta = (BannerMeta) stack.getItemMeta();
if (split[0].equalsIgnoreCase("basecolor")) {
Color color = Color.fromRGB(Integer.parseInt(split[1]));
final Color color = Color.fromRGB(Integer.parseInt(split[1]));
meta.setBaseColor(DyeColor.getByColor(color));
} else if (patternType != null) {
PatternType type = PatternType.valueOf(split[0]);
DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
final PatternType type = PatternType.valueOf(split[0]);
final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
meta.addPattern(pattern);
}
@@ -509,18 +643,19 @@ public class MetaItemStack {
PatternType patternType = null;
try {
patternType = PatternType.valueOf(split[0]);
} catch (Exception ignored ) {}
} catch (final Exception ignored) {
}
// Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795
BlockStateMeta meta = (BlockStateMeta) stack.getItemMeta();
Banner banner = (Banner) meta.getBlockState();
final BlockStateMeta meta = (BlockStateMeta) stack.getItemMeta();
final Banner banner = (Banner) meta.getBlockState();
if (split[0].equalsIgnoreCase("basecolor")) {
Color color = Color.fromRGB(Integer.parseInt(split[1]));
final Color color = Color.fromRGB(Integer.parseInt(split[1]));
banner.setBaseColor(DyeColor.getByColor(color));
} else if (patternType != null) {
PatternType type = PatternType.valueOf(split[0]);
DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
final PatternType type = PatternType.valueOf(split[0]);
final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1])));
final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type);
banner.addPattern(pattern);
}
banner.update();
@@ -547,59 +682,9 @@ public class MetaItemStack {
}
}
private static int bukkitUnbreakableSupport = -1;
private static Method spigotMethod;
private static Method setUnbreakableMethod;
private void setUnbreakable(ItemStack is, boolean unbreakable) {
ItemMeta meta = is.getItemMeta();
try {
if (bukkitUnbreakableSupport == -1) {
try {
ItemMeta.class.getDeclaredMethod("setUnbreakable", boolean.class);
bukkitUnbreakableSupport = 1;
} catch (NoSuchMethodException | SecurityException ex) {
bukkitUnbreakableSupport = 0;
}
}
if (bukkitUnbreakableSupport == 1) {
meta.setUnbreakable(unbreakable);
} else {
if (spigotMethod == null) {
spigotMethod = meta.getClass().getDeclaredMethod("spigot");
spigotMethod.setAccessible(true);
}
Object itemStackSpigot = spigotMethod.invoke(meta);
if (setUnbreakableMethod == null) {
setUnbreakableMethod = itemStackSpigot.getClass().getDeclaredMethod("setUnbreakable", Boolean.TYPE);
setUnbreakableMethod.setAccessible(true);
}
setUnbreakableMethod.invoke(itemStackSpigot, unbreakable);
}
is.setItemMeta(meta);
} catch (Throwable t) {
t.printStackTrace();
}
}
private static boolean useNewSkullMethod = true;
private static void setSkullOwner(final IEssentials ess, final ItemStack stack, final String owner) {
if (!(stack.getItemMeta() instanceof SkullMeta)) return;
SkullMeta meta = (SkullMeta) stack.getItemMeta();
if (useNewSkullMethod) {
try {
meta.setOwningPlayer(ess.getServer().getOfflinePlayer(owner));
stack.setItemMeta(meta);
return;
} catch (NoSuchMethodError e) {
useNewSkullMethod = false;
}
}
meta.setOwner(owner);
stack.setItemMeta(meta);
private void setUnbreakable(final IEssentials ess, final ItemStack is, final boolean unbreakable) {
final ItemMeta meta = is.getItemMeta();
ess.getItemUnbreakableProvider().setUnbreakable(meta, unbreakable);
is.setItemMeta(meta);
}
}
Loaded 100 of 728 files, more files were not shown because too many files have changed in this diff. Show more