Compare commits

..

265 Commits

Author SHA1 Message Date
Auxilor
ac10fa46dc Fixed javadoc 2023-01-23 11:27:44 +00:00
Auxilor
7c616e64ae Fixed XP / XPL 2023-01-18 20:03:28 +00:00
Auxilor
708f9130c6 Removed old docs URL 2023-01-18 12:53:13 +00:00
Auxilor
9118d49c67 Updated README.md 2023-01-18 12:52:29 +00:00
Auxilor
a1ce72476f Fixed README.md 2023-01-18 12:38:39 +00:00
Auxilor
2cfab99644 Fixed a plethora of codestyle issues 2023-01-17 17:57:40 +00:00
Auxilor
cc9b3f7710 Small blips 2023-01-17 17:50:07 +00:00
Auxilor
5bfe48c8d9 Updated CONTRIBUTING.md 2023-01-17 17:48:45 +00:00
Auxilor
22ff157ffc Merge branch 'MisterFrans_master' into develop 2023-01-17 17:44:35 +00:00
Auxilor
720dbe789c Merge branch 'patch-1' into develop 2023-01-17 17:42:49 +00:00
Auxilor
b51dd51941 Deprecated Prerequisite#HAS_BUNGEECORD and Prerequisite#HAS_VELOCITY 2023-01-17 16:05:06 +00:00
Auxilor
f3ffaa4cf6 Javadoc improvements 2023-01-17 16:02:07 +00:00
Auxilor
085032e315 Further command cleanup, added lang.yml validation and removed magic strings 2023-01-17 15:59:15 +00:00
Auxilor
3d920ee2b4 Updated to 6.49.0 2023-01-17 12:20:51 +00:00
Auxilor
06a04e4375 Fixed spelling inconsistency 2023-01-17 12:19:59 +00:00
Auxilor
7349f15784 Cleaned up command rework 2023-01-17 12:18:10 +00:00
Auxilor
a4c77857d5 Merge branch 'master' into Samkist_master 2023-01-17 12:00:10 +00:00
Auxilor
999fafc8df Javadoc cleanjp 2023-01-17 12:00:04 +00:00
Auxilor
0d533850f6 Updated to 6.48.3 2023-01-13 17:53:44 +00:00
Auxilor
569f9cfcb4 Merge remote-tracking branch 'origin/master' 2023-01-13 17:52:53 +00:00
Auxilor
f0619f2374 Updated to 6.48.2 2023-01-13 17:52:48 +00:00
Auxilor
7e8d97e11d Updated MythicMobs and ExecutableItems 2023-01-13 17:52:20 +00:00
Auxilor
d3414f25ad Cleanup 2023-01-13 17:39:29 +00:00
MCCasper
f0cf118448 Update McmmoManager.java
Change to not quit after first iteration of loop
2022-12-29 10:07:05 -05:00
François
297bb10b85 Removal of an unnecessary semicolon 2022-12-27 13:06:22 +01:00
François
751624bc8d Update of Lands api to the latest version (6.26.7). 2022-12-26 15:34:11 +01:00
Auxilor
8b1b15a3e4 Updated to 6.48.2 2022-12-20 15:05:30 +00:00
Auxilor
7fe330bafb Fixed 1.19.3 support 2022-12-20 15:05:21 +00:00
Auxilor
20584b2a9b Updated to 6.48.1 2022-12-12 12:13:32 +00:00
Auxilor
bd7594a117 Fixed 1.19.3 build 2022-12-12 12:13:22 +00:00
Samuel Pizette
f1bfa21270 restructured some methods 2022-12-11 20:32:23 -05:00
Samuel Pizette
01aa1e708a more missing documentations 2022-12-11 14:17:16 -05:00
Samuel Pizette
8424baa285 wrote missing documentations 2022-12-11 14:14:37 -05:00
Auxilor
d9a8d26990 Fixed 1.19.3 build 2022-12-11 17:07:33 +00:00
Auxilor
4d3eeaaefc Added 1.19.3 support 2022-12-11 16:58:58 +00:00
Samuel Pizette
d54a2b9516 fixed method signature of extensions 2022-12-10 17:18:06 -05:00
Samuel Pizette
f7f12b6255 finished working refactor 2022-12-10 16:50:52 -05:00
Samuel Pizette
42eb1344a6 removed unneeded debug statement 2022-12-10 16:08:33 -05:00
Samuel Pizette
4c2a8585cc got command functionality working 2022-12-10 16:08:01 -05:00
Samuel Pizette
8cccc67b0d wrote documentation for Eco Plugin and Eco Sub Command 2022-12-09 14:42:12 -05:00
Samuel Pizette
396d74497c wrote EcoHandledCommand.kt documentation 2022-12-09 14:38:13 -05:00
Samuel Pizette
49602dce04 rewrote notify documentation 2022-12-09 14:24:13 -05:00
Samuel Pizette
5da811ba74 added notifyPermissionRequired and updated some comments 2022-12-09 13:52:03 -05:00
Samuel Pizette
a4d57e21fe cleaned up eco handled command 2022-12-09 13:29:33 -05:00
Samuel Pizette
4b8efdc79f whoops 2022-12-09 12:10:39 -05:00
Auxilor
610110efde Improved CombinedDisplayPrice 2022-12-07 16:38:32 +00:00
Samuel Pizette
a87f675269 updated method signature annotations to accurately reflect behavior 2022-12-06 03:42:34 -05:00
Samuel Pizette
a371d314b8 shortened logic of notifyNull extension 2022-12-06 03:38:28 -05:00
Samuel Pizette
9a9097adc5 updated some annotations 2022-12-05 20:27:31 -05:00
Samuel Pizette
0669a57e4b updated some annotations 2022-12-05 20:08:35 -05:00
Samuel Pizette
692eaf6836 changed onTabComplete return signature in EcoDelegatedBukkitCommand.kt from nullable to not null 2022-12-05 19:22:09 -05:00
Auxilor
7f9052c64d Updated to 6.48.0 2022-12-05 12:34:53 +00:00
Auxilor
55a841b3f5 Added CombinedDisplayPrice 2022-12-05 12:34:45 +00:00
Samuel Pizette
85f02c5ca2 wrote notification kotlin extensions 2022-12-04 19:35:54 -05:00
Samuel Pizette
74c428b90d rearranged notify methods 2022-12-04 19:17:36 -05:00
Samuel Pizette
fd8c67fa66 wrote handle tab complete for ecohandled command 2022-12-04 19:13:23 -05:00
Samuel Pizette
a396754e2e commands rework progress
- Wrote EcoDelegatedBukkitCommand.kt
- Wrote EcoHandledCommand.kt
- Wrote Eco#createSubCommand
- Renamed RegistrableCommandBase.java to PluginCommandBase
- Moved most of EcoPluginCommand.kt to EcoHandledCommand.kt
- Changed Delegate type in PluginCommand from CommandBase to PluginCommandBase
2022-12-04 18:52:19 -05:00
Samuel Pizette
6f97f47712 removed getAliases and getDescription from commandbase 2022-12-04 18:50:03 -05:00
Samuel Pizette
d1109e485a more fixing auto reformat 2022-12-04 17:56:07 -05:00
Samuel Pizette
476e5c7cae fuck auto reformat 2022-12-04 17:54:50 -05:00
Samuel Pizette
dc2b7a6fda returned instance get to original location 2022-12-04 17:53:49 -05:00
Samuel Pizette
00f18519b0 progress commit 2022-12-04 17:52:05 -05:00
Samuel Pizette
f7ea5fd182 fix eco format 2022-11-30 16:50:02 -05:00
Samuel Pizette
70d29c872a removed HandledCommand 2022-11-29 16:29:23 -05:00
Samuel Pizette
f79f4a84c3 deprecated DelegatedBukkitCommand 2022-11-29 16:29:16 -05:00
Samuel Pizette
9af63907ef Merge branch 'master' into master 2022-11-29 16:28:37 -05:00
Samuel Pizette
c9aa92895b work in progress commit 2022-11-29 16:13:01 -05:00
Auxilor
c57c824027 Cleaned up UltraEconomy integration 2022-11-29 15:56:30 +00:00
Auxilor
7cb905e65a Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-api/src/main/java/com/willfp/eco/core/gui/slot/ConfigSlot.java
2022-11-29 15:47:52 +00:00
Auxilor
31a2c7e338 Added lore to ConfigSlot 2022-11-29 15:47:41 +00:00
Auxilor
1759b52f82 Added lore to ConfigSlot 2022-11-29 15:37:36 +00:00
Auxilor
ccf93e3a4d Fixed 2x2 crafting bug 2022-11-28 23:55:45 +00:00
Auxilor
abd07389ab Fixed dynamic command registration 2022-11-28 17:28:29 +00:00
Auxilor
80ad738bb2 Removed AbstractItemStackBuilder correctifying things 2022-11-28 15:10:28 +00:00
Auxilor
b01105819a Updated EconomyShopGUI and ShopGUI+ integrations 2022-11-28 14:46:38 +00:00
Auxilor
a7c08b0731 Updated shop API (again) 2022-11-28 14:40:42 +00:00
Auxilor
7e4c071698 Fixed javadoc 2022-11-28 14:28:38 +00:00
Auxilor
f94f7ead08 Reworked price/shop API to have call-site multipliers 2022-11-28 14:26:46 +00:00
Auxilor
b21c5bf3a9 Fixed captive filter and GUI drag bug 2022-11-28 13:57:41 +00:00
Auxilor
7a9e8c5c10 Fixed bug with multiple stacked paginated menus 2022-11-25 16:51:17 +00:00
Auxilor
a6ddbc46ab Fixes 2022-11-25 16:28:24 +00:00
Auxilor
ffaee137d8 Overhauled ShopSellEvent API 2022-11-24 23:19:03 +00:00
Auxilor
18d882dac6 Reworked Shop API 2022-11-24 23:12:25 +00:00
Auxilor
52841f7f04 Fixed bug with price copying 2022-11-23 22:31:37 +00:00
Auxilor
47b72e9243 Javadoc fix 2022-11-23 22:03:44 +00:00
Auxilor
854a10e8fd Added Price#withMultiplier 2022-11-23 22:00:07 +00:00
Auxilor
823ef6477b Refactoring for naming, updated to 6.47.0 2022-11-23 21:56:07 +00:00
Auxilor
eccb146852 Reworked slot changes into canCaptivateItem 2022-11-18 18:08:21 +00:00
Auxilor
d877b707d6 Improved lang.yml/config.yml warning messages 2022-11-17 15:12:28 +00:00
Auxilor
bcb7401c74 Fixed captive slot changes PR 2022-11-17 15:10:05 +00:00
Auxilor
f05c5f3cd6 Fixed UltraEconomy integration 2022-11-17 15:02:40 +00:00
_OfTeN_
3bd8bccb81 Added UltraEconomy support to Price Lookup 2022-11-17 02:58:53 +03:00
_OfTeN_
6f55787c84 Added ability to filter items for captive slots 2022-11-13 02:50:23 +03:00
_OfTeN_
eb4dc168fc Added proper error displaying when missing config or lang ymls 2022-11-11 02:08:19 +03:00
Auxilor
c5085de5d1 Updated to 6.46.1 2022-11-09 12:59:44 +00:00
Auxilor
019cdbf9c8 Fixed tab completion bug 2022-11-09 12:59:37 +00:00
Auxilor
d5ddcaea4b Cleaned up Menu API 2022-11-08 18:11:27 +00:00
Auxilor
d3a7ef72e8 Thread naming 2022-11-08 16:39:28 +00:00
Auxilor
a311ce1227 Added save-interval option 2022-11-08 16:01:46 +00:00
Auxilor
5c0d4540a8 Merge branch 'pvpmanager-support' into develop 2022-11-08 15:55:03 +00:00
Auxilor
7e66ee8071 Merge branch 'fix/exposed' into develop 2022-11-08 15:54:06 +00:00
Auxilor
fd233df736 Added relevant kt extension for onBuild 2022-11-07 16:43:32 +00:00
Auxilor
6baf636e6a Added MenuBuilder#onBuild 2022-11-07 16:42:40 +00:00
Auxilor
9ee579f2c4 Updated to 6.46.0 2022-11-07 16:40:14 +00:00
Auxilor
3b5ea87353 Fixes to ReactiveSlot 2022-11-07 15:42:46 +00:00
Auxilor
00d32ed218 Added CaptiveItemChangeEvent 2022-11-07 15:32:01 +00:00
Auxilor
5ce9a1c04e Added ItemStack#modify and TestableItem#modify 2022-11-07 15:13:19 +00:00
Auxilor
966549065d Added PlayableSound 2022-11-07 15:08:26 +00:00
Auxilor
ee2911b57c Revert "Added "undyable" arg parser"
This reverts commit a9c32000d8.
2022-11-07 15:00:47 +00:00
_OfTeN_
f15ec5eec0 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/customitems/CustomItemsScyther.kt
2022-11-07 15:54:25 +03:00
_OfTeN_
a9c32000d8 Added "undyable" arg parser 2022-11-07 15:54:02 +03:00
_OfTeN_
b68459951f Fixed scyther integration 2022-11-07 14:51:03 +03:00
Will FP
b520c76169 Update README.md 2022-11-06 20:03:25 +00:00
Auxilor
d081afbd8e Improved command sync 2022-11-06 19:23:11 +00:00
Auxilor
46fd0439a5 Fixed dynamic command reg 2022-11-06 19:17:21 +00:00
Auxilor
ad58ce4a74 GUI Fixes + Improvements 2022-11-06 14:21:49 +00:00
Auxilor
b5cd8f42e0 Fixed illusioner goals 2022-11-05 21:25:52 +00:00
Auxilor
d0baf50709 Fixed illusioner goals 2022-11-04 15:07:34 +00:00
Auxilor
2496f318fa Removed health-fixer 2022-11-04 13:57:16 +00:00
Auxilor
048c200c95 Changed ConfiguredPrice to be a delegate 2022-11-01 22:05:25 +00:00
Auxilor
17446acb2e Fixed javadoc 2022-11-01 21:55:56 +00:00
Auxilor
7929113c91 Improved PriceItem 2022-11-01 21:53:27 +00:00
Auxilor
6acc5864bd Reworked Price API (again) 2022-11-01 21:36:09 +00:00
Kees Monshouwer
fa64950d28 add support for PvPManager 2022-10-31 09:39:17 +01:00
Auxilor
730c20dbc0 Added Testable Extensions 2022-10-30 16:53:29 +00:00
Auxilor
0c5ae54c3a Codestyle 2022-10-30 16:34:08 +00:00
Auxilor
b48e80837d Fixed javadoc 2022-10-30 16:32:30 +00:00
Auxilor
17eb4cf5f8 Added alias and description support to PluginCommand 2022-10-30 16:02:44 +00:00
Auxilor
890f85fa56 Added Player versions of onExecute and tabComplete 2022-10-30 15:58:14 +00:00
Auxilor
86b427c95e Improved commands 2022-10-30 15:21:21 +00:00
Auxilor
8c1fde57b0 Improved DelegatedBukkitCommand 2022-10-30 15:03:17 +00:00
Auxilor
2efc040a8e Added dynamic command registration 2022-10-29 19:06:15 +01:00
Auxilor
54b2b42512 Added price multipliers 2022-10-29 16:53:24 +01:00
Auxilor
e67d9d634c Fixed javadoc 2022-10-29 16:34:18 +01:00
Auxilor
39fb676b9a Updated to 6.45.0 2022-10-29 16:28:57 +01:00
Auxilor
ec8936b765 Updated price API 2022-10-29 16:28:48 +01:00
Auxilor
cf347de4b8 Updated to 6.44.1 2022-10-27 16:20:51 +01:00
Auxilor
2b7122c5c2 Fixed particles 2022-10-27 16:20:42 +01:00
Auxilor
062b5c9b92 Fixed ProtocolLib bug with TemporaryPlayer 2022-10-27 15:13:20 +01:00
DaRacci
052cd74756 fix: explicit database for transactions 2022-10-28 01:06:35 +11:00
Auxilor
082b39a2e4 Fixed config bug with list getters 2022-10-25 16:27:35 +01:00
Auxilor
616fa032d9 Removed Prices#lookup due to poor usage 2022-10-24 15:59:41 +01:00
Auxilor
ea997239fc Fix 2022-10-24 15:59:06 +01:00
Auxilor
ad04abab73 Reworked prices 2022-10-24 15:29:03 +01:00
Auxilor
f440ef922b Updated prices, removed display text 2022-10-24 14:57:27 +01:00
Auxilor
933271fb4a Imrpoved Economy Price 2022-10-24 13:41:41 +01:00
Auxilor
9679d3100f Imrpoved Economy Price 2022-10-24 13:38:39 +01:00
Auxilor
6e20763522 Refactor 2022-10-24 13:20:30 +01:00
Auxilor
18dea2c20c Fixed javadoc 2022-10-24 13:19:38 +01:00
Auxilor
62b666559c Added MathContext, added value to price 2022-10-24 13:17:09 +01:00
Auxilor
465563523b Fixed javadco 2022-10-24 12:40:14 +01:00
Auxilor
f0f014ed89 Added particle lookup system 2022-10-24 12:23:41 +01:00
Auxilor
58811c5d77 Updated PriceFree 2022-10-24 11:53:01 +01:00
Auxilor
81d495e76e Added display text to prices 2022-10-23 21:43:43 +01:00
Auxilor
c78e397959 Renamed addState to setState 2022-10-23 17:32:28 +01:00
Auxilor
95740f155e Imports 2022-10-23 17:08:46 +01:00
Auxilor
5638d5e152 Added GenericConfig to cover last remaining use of TransientConfig 2022-10-23 16:56:10 +01:00
Auxilor
4d3712057c Added plurals to vanilla item names 2022-10-23 16:48:06 +01:00
Auxilor
458fcd78b3 Updated to 6.44.0 2022-10-23 16:41:49 +01:00
Auxilor
ee13de31f4 Added price system 2022-10-23 16:40:52 +01:00
Auxilor
9588d49788 Added functional pattern to plugin lifecycle hooks 2022-10-23 15:55:23 +01:00
Auxilor
8e7ce298b0 Improved Config API, minor cleanup 2022-10-23 15:48:50 +01:00
Auxilor
32a11ce5b8 Added friendly material names 2022-10-23 15:08:33 +01:00
Auxilor
960f62cc8b Updated to 6.43.7 2022-10-16 21:48:14 +01:00
Auxilor
28ceb83eb5 Fixed ExtendedPersistentDataContainerFactory.kt 2022-10-16 21:47:46 +01:00
Auxilor
6f748b6b8a Updated to 6.43.6 2022-10-07 17:28:39 +01:00
Auxilor
190ea5d49f Refactor + Fix 2022-10-07 17:28:28 +01:00
Auxilor
c0ed083a5c Fixed PAPI 2022-10-04 11:58:15 +01:00
Auxilor
04f04bb7a6 Fixed custom model data parser 2022-10-04 11:48:06 +01:00
Auxilor
b8a3806ff9 Updated to 6.43.5 2022-10-04 11:44:17 +01:00
Auxilor
ae49d41542 Fixes to placeholders and integrations 2022-10-04 11:43:10 +01:00
Auxilor
5f2255a3bc Fixed initial render 2022-10-03 23:33:39 +01:00
Auxilor
065ccfbe67 Updated to 6.43.4 2022-10-03 22:30:27 +01:00
Auxilor
17727c9015 Optimized second render for captive changes 2022-10-03 22:30:20 +01:00
Auxilor
ea64e69b4d Fixed GUI bug 2022-10-03 22:25:03 +01:00
Auxilor
07ca6c2359 Fixed 2 more GUI bugs 2022-10-03 21:58:53 +01:00
Auxilor
162558b1c2 Updated to 6.43.3 2022-10-03 13:44:28 +01:00
Auxilor
10f9e8dce0 Fixed Skull 2022-10-03 13:44:17 +01:00
Will FP
b02943d7ff Merge pull request #204
Fix: Skull texture out of bounds error
2022-10-03 13:42:39 +01:00
Auxilor
40ad970ffa Updated to 6.43.2 2022-10-03 13:00:02 +01:00
Auxilor
aefdfa786d Fixed menu rendering bugs 2022-10-03 12:59:54 +01:00
MillionthOdin16
1cf08955a0 Additional check for bounds error
explanation in comment
2022-10-02 19:33:37 -04:00
Auxilor
4077a4c28b Updated to 6.43.1 2022-10-02 20:01:30 +01:00
Auxilor
6c375ef297 Fixed changing held item edge case 2022-10-02 20:01:23 +01:00
Auxilor
f77fc5d182 Fixed compile bug 2022-10-02 14:24:30 +01:00
Auxilor
6d1cc4c05d Fixed buildscript comments 2022-10-02 14:19:16 +01:00
Auxilor
0775f0992f Internal refactoring, added Model Engine support as an arg parser 2022-10-02 14:12:57 +01:00
Auxilor
debb39105d Moved #getPage #getMaxPage to Menu 2022-10-02 12:20:57 +01:00
Auxilor
3b4aa59bfb Fixed startup bug 2022-10-02 12:03:29 +01:00
Auxilor
4e902c3964 Added warning to legacy MySQL 2022-10-02 02:20:58 +01:00
Auxilor
3e325697e7 Added PersistentDataKeyType#CONFIG 2022-10-02 02:17:42 +01:00
Auxilor
13b3e1e440 Cleaning up after Handler 2022-10-02 02:08:22 +01:00
Auxilor
2b2406a4d0 Deprecated PluginDependent 2022-10-02 01:57:13 +01:00
Auxilor
048982c74a Deprecated PluginDependent 2022-10-02 01:56:45 +01:00
Auxilor
29b63a2ebb Removed Handler, HandlerComponent, and all internal-only API components except Eco; huge backend rework 2022-10-02 01:52:35 +01:00
Auxilor
2a9cbdacfd Cleanup 2022-10-02 00:33:36 +01:00
Auxilor
35cfcdf4a9 Improved ConfigSlot 2022-10-02 00:30:30 +01:00
Auxilor
06c1dc9afb Fixed excludes 2022-10-01 22:34:06 +01:00
Auxilor
18b58d584f Fixes to GUI 2022-10-01 22:31:37 +01:00
Auxilor
a774791e65 Updated excludes 2022-10-01 22:26:43 +01:00
Auxilor
bc22adae84 Fixed changing item bugs with Menu, added Menu#allowsChangingHeldItem 2022-10-01 22:24:24 +01:00
Auxilor
0eba82b221 Added .toml support to config 2022-10-01 21:59:16 +01:00
Auxilor
4822ec3955 Prevented changing the held item with a menu open 2022-10-01 20:55:56 +01:00
Auxilor
b845001467 Many improvements to captive items and menus 2022-10-01 14:36:53 +01:00
Auxilor
25b6a15c9c Fixed getStringsOrNull 2022-10-01 13:11:42 +01:00
Auxilor
1a3d035f78 Revert "Fixed null lore bug with AbstractItemStackBuilder"
This reverts commit a69f9f6e11.
2022-10-01 13:02:21 +01:00
Auxilor
a69f9f6e11 Fixed null lore bug with AbstractItemStackBuilder 2022-10-01 12:55:28 +01:00
Auxilor
6efd9e4fcd Fixed MySQLDataHandler 2022-10-01 12:47:41 +01:00
Auxilor
3e948beb7a Hotfix for SnakeYaml 2022-09-30 20:05:16 +01:00
Auxilor
a8a491a0b7 Brushing over some deprecated behaviour 2022-09-30 19:59:34 +01:00
Auxilor
2c0330898c Javadoc 2022-09-30 19:57:23 +01:00
Auxilor
e9794cfbf4 Fixed OfflinePlayer#exactBalance 2022-09-30 19:54:10 +01:00
Auxilor
1694d858bc Fixed EconomyIntegration javadoc 2022-09-30 19:51:23 +01:00
Auxilor
b79955ae9e Switched EconomyIntegration to be based around BigDecimal 2022-09-30 19:49:20 +01:00
Auxilor
803a8c5d17 Fixed yaml size limit 2022-09-30 19:39:47 +01:00
Auxilor
c5cd15ad92 Merge remote-tracking branch 'origin/master' into develop 2022-09-30 15:24:43 +01:00
Auxilor
489b170888 Added lambda parameter names to GUI DSL 2022-09-30 15:17:39 +01:00
Auxilor
9f57a322e8 Added ItemStack#toSNBT extension function 2022-09-30 15:15:34 +01:00
Auxilor
86e113214d Internal refactoring 2022-09-30 15:10:10 +01:00
Auxilor
e791bb8893 Renamed signals to menu events 2022-09-30 15:04:44 +01:00
Auxilor
f1220bd186 Added dispenserMenu() to GUI DSL 2022-09-30 14:07:54 +01:00
Auxilor
21d933cb11 Added support for 3x3 menus, improved Java Page API. 2022-09-30 14:06:39 +01:00
Auxilor
e1c063d5f4 Added dynamic-size components 2022-09-30 13:29:50 +01:00
Auxilor
27d2b5c8a4 Added menu signals 2022-09-30 12:44:06 +01:00
Auxilor
3b34d6ef27 Made captivity reactive 2022-09-30 12:20:46 +01:00
Auxilor
bc1c8b8f46 Re-added MythicMobs (repo back up) 2022-09-30 12:09:20 +01:00
Auxilor
2fa926ec02 Fix 2022-09-30 12:05:09 +01:00
Auxilor
d7891e1218 Improved Page API 2022-09-29 20:17:57 +01:00
Auxilor
11685cd352 Fixed PageChanger 2022-09-29 20:04:43 +01:00
Auxilor
785127fe16 Optimized AbstractItemStackBuilder via FIS 2022-09-29 19:56:44 +01:00
Auxilor
4a13bc5fea Cleanup 2022-09-29 19:52:43 +01:00
Auxilor
c7ad122050 Minor fix 2022-09-29 19:52:25 +01:00
Auxilor
28d63fc2e3 Improved performance of menu renders 2022-09-29 19:51:33 +01:00
Auxilor
bb62cc0bcd Fixed default layer 2022-09-29 19:12:29 +01:00
Auxilor
740c79f087 Added layers to menus and fixed several bugs (and temporarily removed MythicCraft repos) 2022-09-29 19:10:02 +01:00
Auxilor
33ab8e04a0 Fixed javadoc 2022-09-29 17:35:53 +01:00
Auxilor
fcd3aac363 Updated to 6.43.0 2022-09-29 17:33:56 +01:00
Auxilor
753d148d1b Refactor 2022-09-29 17:33:45 +01:00
Auxilor
1bb47a9f13 Improved MergedStateMenu 2022-09-29 17:21:31 +01:00
Auxilor
40b4c26e0f Added menu pagination 2022-09-29 17:17:13 +01:00
Auxilor
97adae7b32 More GUI improvements, improving Slot API 2022-09-29 16:15:19 +01:00
Auxilor
3d35a91314 Major improvements to menus 2022-09-29 15:51:26 +01:00
Auxilor
0caa328b1e Overhauled GUI component-based backend to support reactive and static slots 2022-09-29 15:08:34 +01:00
Auxilor
ab8c946914 Reworked GUI backend to be component-based 2022-09-29 14:10:41 +01:00
Auxilor
e667537404 Fixed CustomSlot 2022-09-29 13:41:29 +01:00
Auxilor
6bd2f2b823 Codestyle 2022-09-29 12:54:54 +01:00
Auxilor
8d8a8045c0 Moved frontend -> backend communication to be purely via the handler 2022-09-29 09:51:43 +01:00
Auxilor
d5c669c72c Added shorthand NamespacedKey and FixedMetadataValue creation 2022-09-29 08:49:17 +01:00
Auxilor
28b268e175 Added %player% to configslot 2022-09-28 12:38:38 +01:00
Auxilor
33937d1ce7 Switched Crunch to fork 2022-09-28 11:04:59 +01:00
Auxilor
e971778cc3 Updated to 6.42.0 2022-09-28 10:47:51 +01:00
Auxilor
f99612ded3 Added CustomSlot, improved various GUI elements, added GUI components. 2022-09-28 10:47:42 +01:00
Auxilor
50272bbbcf Improved skull texture API backend 2022-09-28 08:44:16 +01:00
Auxilor
9a703f6190 One more held item slot fix 2022-09-26 14:02:20 +01:00
Auxilor
f8fec7eec4 Updated to 6.41.3 2022-09-26 13:57:50 +01:00
Auxilor
f6aadda4ed Fixed PacketHeldItemSlot 2022-09-26 13:57:39 +01:00
Auxilor
d8fca0f348 Switched KingdomsX to local jar 2022-09-26 12:52:46 +01:00
Auxilor
65ff4c4a31 PR Codestyle 2022-09-26 12:36:34 +01:00
Auxilor
90702bc7aa Merge remote-tracking branch 'origin/develop' into develop 2022-09-26 12:34:53 +01:00
Will FP
e81b788a1b Merge pull request #199 from mani1232/master
Update KingdomsX dependence
2022-09-26 12:34:46 +01:00
Auxilor
ebc0ee7940 Updated to 6.41.2 2022-09-26 12:33:25 +01:00
Auxilor
82d269daf1 Improved PacketWindowItems and DisplayFrame 2022-09-26 12:32:07 +01:00
Auxilor
9d5300d6ae Began display system fixes 2022-09-26 12:10:22 +01:00
mani1232
8870e4d6fb Update KingdomsX dependence 2022-09-25 21:26:21 +02:00
_OfTeN_
7767e48e51 Fixed scyther integration 2022-09-14 23:03:20 +03:00
284 changed files with 8366 additions and 3397 deletions

View File

@@ -1,38 +1,7 @@
# How to contribute to eco # How to contribute to eco
## Codestyle Please open any Pull Requests into the `develop` branch or ideally into a new branch for your changes. PRs that go into `master` won't be ignored, but I have to checkout and merge manually, which makes your PR show as being closed.
1. The eco checkstyle is in /config/checkstyle.xml Do not write any Kotlin-only APIs; all API components should be written in Java, Kotlin extensions should not have functionality that isn't available in java. The same applies the other way round, do not write any backend code in Java, it should be Kotlin-exclusive.
- The pull request must not have any checkstyle issues. If you have any questions about contributing, feel free to ask in the [Discord](https://discord.gg/ZcwpSsE)!
- Every method and field must have a javadoc attached.
2. Use JetBrains annotations
- Every parameter should be annotated with @NotNull or @Nullable
3. Imports
- No group (*) imports.
- No static imports.
4. Kotlin
- Kotlin should be the only language used in the backend, java should be the only language used in the frontend.
- Kotlin API extensions should only be for creating extension functions and extra niceties that aren't possible in java.
Do not write API components in kotlin.
- Kotlin code should never be called directly from the frontend Java API. Kotlin API extensions should always rely on
java, not the other way round.
## Dependency Injection
- eco uses Dependency Injection
- Any calls to Eco#getHandler#getEcoPlugin are code smells and should never be used unless **absolutely necessary**.
- NamespacedKeys, FixedMetadataValues, Runnables, and Schedules should be managed using AbstractEcoPlugin through DI.
- Any DI class should extend PluginDependent where possible. If the class extends another, then you **must** store the
plugin instance in a private final variable called **plugin** with a private or protected getter.
## Other
- All drops **must** be sent through a DropQueue - calls to World#dropItem will get your PR rejected.
- eco is built with java 17.

View File

@@ -1,5 +1,5 @@
# eco # eco
eco is a powerful Spigot development library that simplifies the process of plugin creation and supercharges eco is a powerful Spigot plugin framework that simplifies the process of plugin creation and supercharges
your plugins. your plugins.
It's the engine behind [EcoEnchants](https://polymart.org/resource/490), [Reforges](https://polymart.org/resource/1330), It's the engine behind [EcoEnchants](https://polymart.org/resource/490), [Reforges](https://polymart.org/resource/1330),
[EcoItems](https://polymart.org/resource/1247), [EcoSkills](https://polymart.org/resource/1351), [EcoItems](https://polymart.org/resource/1247), [EcoSkills](https://polymart.org/resource/1351),
@@ -16,30 +16,54 @@ and many more.
<a href="https://bstats.org/plugin/bukkit/EcoEnchants" alt="bstats players"> <a href="https://bstats.org/plugin/bukkit/EcoEnchants" alt="bstats players">
<img src="https://img.shields.io/bstats/players/7666?color=informational"/> <img src="https://img.shields.io/bstats/players/7666?color=informational"/>
</a> </a>
<a href="https://plugins.auxilor.io/" alt="Docs (gitbook)">
<img src="https://img.shields.io/badge/docs-gitbook-informational"/>
</a>
<a href="https://discord.gg/ZcwpSsE/" alt="Discord"> <a href="https://discord.gg/ZcwpSsE/" alt="Discord">
<img src="https://img.shields.io/discord/452518336627081236?label=discord&color=informational"/> <img src="https://img.shields.io/discord/452518336627081236?label=discord&color=informational"/>
</a> </a>
<a href="https://github.com/Auxilor/eco/actions/workflows/java-ci.yml" alt="Latest Dev Build"> <a href="https://github.com/Auxilor/eco/actions/workflows/java-ci.yml" alt="Latest Dev Build">
<img src="https://img.shields.io/github/workflow/status/Auxilor/eco/Java%20CI/develop?color=informational"/> <img src="https://img.shields.io/github/actions/workflow/status/Auxilor/eco/java-ci.yml?branch=develop&color=informational"/>
</a> </a>
</p> </p>
eco comes packed with all the tools you need in your plugins:
- Modern command API
- Native color parsing with full hex/RGB/MiniMessage support
- Yaml/JSON/TOML config system
- Persistent data storage API with Yaml/MySQL/MongoDB support
- Packet item display system
- Entity AI API with near-1:1 NMS mappings
- More events
- Extension API, essentially plugins for plugins
- Fluent dependency injection for NamespacedKey, Metadata values, etc.
- Ultra-fast ItemStack reimplementation bypassing ItemMeta
- Complete GUI API with pre-made components available from [ecomponent](https://github.com/Auxilor/ecomponent)
- Over 30 native integrations for other plugins
- First-class custom item support with lookup strings
- Math expression parsing via [Crunch](https://github.com/Redempt/Crunch)
- Particle lookups
- Complete Placeholder API
- Price system, supporting economy plugins, XP, Items, etc.
- NMS/Version-specific tooling
- Custom crafting recipe API with support for stacks and custom items
- Native plugin update checking
- Native bStats support
- Full Kotlin support and native extensions
- Tooling to make meta-frameworks, like [libreforge](https://github.com/Auxilor/libreforge)
- And much more
# For server owners # For server owners
- Requires ProtocolLib to be installed: get the latest version [here](https://www.spigotmc.org/resources/protocollib.1997/) - Requires ProtocolLib to be installed: get the latest version [here](https://www.spigotmc.org/resources/protocollib.1997/)
- Supports 1.17+ - Supports 1.17+
## Downloads ## Downloads
- Stable (Recommended): [GitHub](https://github.com/Auxilor/eco/releases), [Polymart](https://polymart.org/resource/eco.773) - Stable: [GitHub](https://github.com/Auxilor/eco/releases), [Polymart](https://polymart.org/resource/eco.773)
- Dev (Not Recommended): [GitHub](https://github.com/Auxilor/eco/actions/workflows/java-ci.yml) (Open latest run and download) - Dev: [GitHub](https://github.com/Auxilor/eco/actions/workflows/java-ci.yml) (Open latest run and download)
# For developers # For developers
## Javadoc ## Javadoc
The 6.38.2 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.38.2/javadoc/) The 6.49.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.49.0/javadoc/)
## Plugin Information ## Plugin Information
@@ -68,7 +92,7 @@ dependencies {
} }
``` ```
Replace `Tag` with a release tag for eco, eg `6.27.2`. Replace `Tag` with a release tag for eco, eg `6.49.0`.
Maven: Maven:
@@ -88,7 +112,7 @@ Maven:
</dependency> </dependency>
``` ```
Replace `Tag` with a release tag for eco, eg `6.27.2`. Replace `Tag` with a release tag for eco, eg `6.49.0`.
## Build locally: ## Build locally:
@@ -103,7 +127,7 @@ cd eco
## License ## License
*Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).* eco is licensed under GNU GPL3. *Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).*
<h1 align="center"> <h1 align="center">
Check out our partners! Check out our partners!

View File

@@ -25,6 +25,7 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_18_R1", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_18_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R2", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_18_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R1", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_19_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R2", configuration = "reobf"))
} }
allprojects { allprojects {
@@ -77,6 +78,8 @@ allprojects {
// LibsDisguises // LibsDisguises
maven("https://repo.md-5.net/content/groups/public/") maven("https://repo.md-5.net/content/groups/public/")
maven("https://repo.techscode.com/repository/maven-releases/")
} }
dependencies { dependencies {
@@ -158,6 +161,7 @@ allprojects {
relocate("org.litote", "com.willfp.eco.libs.litote") relocate("org.litote", "com.willfp.eco.libs.litote")
relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams") relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams")
relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe
relocate("com.moandjiezana.toml", "com.willfp.eco.libs.toml")
/* /*
Kotlin and caffeine are not shaded so that they can be accessed directly by eco plugins. Kotlin and caffeine are not shaded so that they can be accessed directly by eco plugins.

View File

@@ -1,82 +1,611 @@
package com.willfp.eco.core; package com.willfp.eco.core;
import com.willfp.eco.core.command.CommandBase;
import com.willfp.eco.core.command.PluginCommandBase;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.data.ExtendedPersistentDataContainer;
import com.willfp.eco.core.data.PlayerProfile;
import com.willfp.eco.core.data.ServerProfile;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import com.willfp.eco.core.drops.DropQueue;
import com.willfp.eco.core.entities.ai.EntityController;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.menu.MenuBuilder;
import com.willfp.eco.core.gui.menu.MenuType;
import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Documented; import java.util.List;
import java.lang.annotation.ElementType; import java.util.Map;
import java.lang.annotation.Retention; import java.util.Set;
import java.lang.annotation.RetentionPolicy; import java.util.UUID;
import java.lang.annotation.Target; import java.util.logging.Logger;
/** /**
* Holds the instance of the eco handler for bridging between the frontend * Holds the instance of eco for bridging between the frontend and backend.
* and backend.
*
* @see Eco#getHandler()
* @see Handler
*/
@ApiStatus.Internal
public final class Eco {
/**
* Instance of eco handler.
*/
@ApiStatus.Internal
private static Handler handler;
/**
* Set the handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void setHandler(@NotNull final Handler handler) {
Validate.isTrue(Eco.handler == null, "Already initialized!");
Eco.handler = handler;
}
/**
* Get the instance of the eco handler; the bridge between the api frontend
* and the implementation backend.
* <p> * <p>
* <strong>Do not use the handler in your plugins!</strong> It can and will contain * <strong>Do not use this in your plugins!</strong> It can and will contain
* breaking changes between minor versions and even patches, and you will create * breaking changes between minor versions and even patches, and you will create compatibility
* compatibility issues by using the handler. All parts of the handler have been abstracted * issues by. All parts of this have been abstracted into logically named API components that you
* into logically named API components that you can use. * can use.
* <p>
* Prior to version 6.12.0, the handler was considered as an API component, but it has
* since been moved into an internal component, and in 6.17.0, the first breaking change
* was introduced to {@link com.willfp.eco.core.config.wrapper.ConfigFactory}. This means
* that any usages of the handler can now cause problems in your plugins.
* *
* @see Eco#get()
*/
@ApiStatus.Internal
public interface Eco {
/**
* Create a scheduler.
*
* @param plugin The plugin.
* @return The scheduler.
*/
@NotNull
Scheduler createScheduler(@NotNull EcoPlugin plugin);
/**
* Create an event manager.
*
* @param plugin The plugin.F
* @return The event manager.
*/
@NotNull
EventManager createEventManager(@NotNull EcoPlugin plugin);
/**
* Create a NamespacedKey factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
NamespacedKeyFactory createNamespacedKeyFactory(@NotNull EcoPlugin plugin);
/**
* Create a MetadataValue factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
MetadataValueFactory createMetadataValueFactory(@NotNull EcoPlugin plugin);
/**
* Create a Runnable factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
RunnableFactory createRunnableFactory(@NotNull EcoPlugin plugin);
/**
* Create an ExtensionLoader.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ExtensionLoader createExtensionLoader(@NotNull EcoPlugin plugin);
/**
* Create a config handler.
*
* @param plugin The plugin.
* @return The handler. * @return The handler.
*/ */
@NotNull
ConfigHandler createConfigHandler(@NotNull EcoPlugin plugin);
/**
* Create a logger.
*
* @param plugin The plugin.
* @return The logger.
*/
@NotNull
Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Create a PAPI integration.
*
* @param plugin The plugin.
*/
void createPAPIIntegration(@NotNull EcoPlugin plugin);
/**
* Create a proxy factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ProxyFactory createProxyFactory(@NotNull EcoPlugin plugin);
/**
* Get eco Spigot plugin.
*
* @return The plugin.
*/
@NotNull
EcoPlugin getEcoPlugin();
/**
* Create PluginCommandBase implementation of {@link PluginCommand}.
*
* @param parentDelegate the enclosing class of this implementation.
* @param plugin the plugin.
* @param name the name of the command.
* @param permission the permission of the command.
* @param playersOnly if the command is players only.
* @return The PluginCommandBase implementation
*/
@NotNull
PluginCommandBase createPluginCommand(@NotNull CommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
boolean playersOnly);
/**
* Create CommandBase implementation of {@link com.willfp.eco.core.command.impl.Subcommand Subcommand}.
*
* @param parentDelegate the enclosing class of this implementation.
* @param plugin the plugin.
* @param name the name of the command.
* @param permission the permission of the command.
* @param playersOnly if the command is players only.
* @return The CommandBase implementation
*/
@NotNull
CommandBase createSubcommand(@NotNull CommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
boolean playersOnly);
/**
* Updatable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param type The config type.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
@NotNull
LoadableConfig createUpdatableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
boolean removeUnused,
@NotNull ConfigType type,
boolean requiresChangesToSave,
@NotNull String... updateBlacklist);
/**
* Loadable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param type The config type.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
@NotNull
LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
@NotNull ConfigType type,
boolean requiresChangesToSave);
/**
* Create config.
*
* @param config The handle.
* @return The config implementation.
*/
@NotNull
Config wrapConfigurationSection(@NotNull ConfigurationSection config);
/**
* Create config.
*
* @param values The values.
* @param type The config type.
* @return The config implementation.
*/
@NotNull
Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type);
/**
* Create config.
*
* @param contents The file contents.
* @param type The type.
* @return The config implementation.
*/
@NotNull
Config createConfig(@NotNull String contents,
@NotNull ConfigType type);
/**
* Create a Drop Queue.
*
* @param player The player.
* @return The drop queue.
*/
@NotNull
DropQueue createDropQueue(@NotNull Player player);
/**
* Create slot builder.
*
* @param provider The provider.
* @return The builder.
*/
@NotNull
SlotBuilder createSlotBuilder(@NotNull SlotProvider provider);
/**
* Create menu builder.
*
* @param rows The amount of rows.
* @param type The type.
* @return The builder.
*/
@NotNull
MenuBuilder createMenuBuilder(int rows,
@NotNull MenuType type);
/**
* Combine the state of two menus together.
*
* @param base The base menu.
* @param additional The additional state.
* @return The menu.
*/
@NotNull
Menu blendMenuState(@NotNull Menu base,
@NotNull Menu additional);
/**
* Clean up ClassLoader (etc.) to allow PlugMan support.
*
* @param plugin The plugin to clean up.
*/
void clean(@NotNull EcoPlugin plugin);
/**
* Add new plugin.
*
* @param plugin The plugin.
*/
void addNewPlugin(@NotNull EcoPlugin plugin);
/**
* Get plugin by name.
*
* @param name The name.
* @return The plugin.
*/
@Nullable
EcoPlugin getPluginByName(@NotNull String name);
/**
* Get all loaded eco plugins.
*
* @return A list of plugin names in lowercase.
*/
@NotNull
List<String> getLoadedPlugins();
/**
* Create a FastItemStack.
*
* @param itemStack The base ItemStack.
* @return The FastItemStack.
*/
@NotNull
FastItemStack createFastItemStack(@NotNull ItemStack itemStack);
/**
* Register bStats metrics.
*
* @param plugin The plugin.
*/
void registerBStats(@NotNull EcoPlugin plugin);
/**
* Get Adventure audiences.
*
* @return The audiences.
*/
@Nullable
BukkitAudiences getAdventure();
/**
* Register a persistent data key to be stored.
*
* @param key The key.
*/
void registerPersistentKey(@NotNull PersistentDataKey<?> key);
/**
* Get all registered keys.
*
* @return The keys.
*/
@NotNull
Set<PersistentDataKey<?>> getRegisteredPersistentDataKeys();
/**
* Load a player profile.
*
* @param uuid The UUID.
* @return The profile.
*/
@NotNull
PlayerProfile loadPlayerProfile(@NotNull UUID uuid);
/**
* Load the server profile.
*
* @return The profile.
*/
@NotNull
ServerProfile getServerProfile();
/**
* Unload a player profile from memory.
* <p>
* This will not save the profile first.
*
* @param uuid The uuid.
*/
void unloadPlayerProfile(@NotNull UUID uuid);
/**
* Create dummy entity - never spawned, exists purely in code.
*
* @param location The location.
* @return The entity.
*/
@NotNull
Entity createDummyEntity(@NotNull Location location);
/**
* Create a {@link NamespacedKey} quickly
* <p>
* Bypasses the constructor, allowing for the creation of invalid keys, therefore this is
* considered unsafe and should only be called after the key has been confirmed to be valid.
*
* @param namespace The namespace.
* @param key The key.
* @return The key.
*/
@NotNull
NamespacedKey createNamespacedKey(@NotNull String namespace,
@NotNull String key);
/**
* Return or get props for a plugin.
*
* @param existing The existing constructor props.
* @param plugin The plugin.
* @return The props.
*/
@NotNull
PluginProps getProps(@Nullable PluginProps existing,
@NotNull Class<? extends EcoPlugin> plugin);
/**
* Format a string with MiniMessage.
*
* @param message The message.
* @return The formatted string.
*/
@NotNull
String formatMiniMessage(@NotNull String message);
/**
* Create controlled entity from a mob.
*
* @param mob The mob.
* @param <T> The mob type.
* @return The controlled entity.
*/
@NotNull <T extends Mob> EntityController<T> createEntityController(@NotNull T mob);
/**
* Adapt base PDC to extended PDC.
*
* @param container The container.
* @return The extended container.
*/
@NotNull
ExtendedPersistentDataContainer adaptPdc(@NotNull PersistentDataContainer container);
/**
* Create new PDC.
*
* @return The container.
*/
@NotNull
PersistentDataContainer newPdc();
/**
* Get item from SNBT.
*
* @param snbt The NBT string.
* @return The ItemStack, or null if invalid.
*/
@Nullable
ItemStack fromSNBT(@NotNull String snbt);
/**
* Convert item to SNBT.
*
* @param itemStack The item.
* @return The NBT string.
*/
@NotNull
String toSNBT(@NotNull ItemStack itemStack);
/**
* Make TestableItem from SNBT.
*
* @param snbt The NBT string.
* @return The TestableItem.
*/
@NotNull
TestableItem testableItemFromSNBT(@NotNull String snbt);
/**
* Get the texture of a skull.
*
* @param meta The skull meta.
* @return The texture, or null if not found.
*/
@Nullable
String getSkullTexture(@NotNull SkullMeta meta);
/**
* Set the texture of a skull.
*
* @param meta The skull meta.
* @param base64 The texture.
*/
void setSkullTexture(@NotNull SkullMeta meta,
@NotNull String base64);
/**
* Get the current server TPS.
*
* @return The TPS.
*/
double getTPS();
/**
* Evaluate an expression.
*
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid.
*/
double evaluate(@NotNull String expression,
@NotNull MathContext context);
/**
* Get the menu a player currently has open.
*
* @param player The player.
* @return The menu, or null if no menu open.
*/
@Nullable
Menu getOpenMenu(@NotNull Player player);
/**
* Sync commands.
*/
void syncCommands();
/**
* Get the command map.
*
* @return The command map.
*/
@NotNull CommandMap getCommandMap();
/**
* Unregister a command.
*
* @param command The command.
*/
void unregisterCommand(@NotNull final PluginCommand command);
/**
* Get the instance of eco; the bridge between the api frontend and the implementation backend.
*
* @return The instance of eco.
*/
@ApiStatus.Internal @ApiStatus.Internal
public static Handler getHandler() { static Eco get() {
return handler; return Instance.get();
} }
/** /**
* Eco Handler components are internals, so if a class is marked as a handler component, * Manages the internal frontend -> backend communication.
* then it should be treated the same as if it was marked with {@link ApiStatus.Internal}.
* <p>
* If a class is marked with {@link HandlerComponent}, <strong>Do not reference it in
* your code!</strong> It can and will contain breaking changes between minor versions and
* even patches, and you will create compatibility issues by using them.
* <p>
* Handler components should also be marked with {@link ApiStatus.Internal} in order to
* cause compiler / IDE warnings.
*/ */
@Documented @ApiStatus.Internal
@Retention(RetentionPolicy.CLASS) final class Instance {
@Target({ElementType.TYPE})
public @interface HandlerComponent {
/**
* Instance of eco.
*/
@ApiStatus.Internal
private static Eco eco;
/**
* Initialize eco.
*
* @param eco The instance of eco.
*/
@ApiStatus.Internal
static void set(@NotNull final Eco eco) {
Validate.isTrue(Instance.eco == null, "Already initialized!");
Instance.eco = eco;
} }
private Eco() { /**
* Get eco.
*
* @return eco.
*/
static Eco get() {
return eco;
}
private Instance() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
} }
} }
}

View File

@@ -13,15 +13,16 @@ import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory; import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory; import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.integrations.IntegrationLoader; import com.willfp.eco.core.integrations.IntegrationLoader;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.proxy.ProxyFactory; import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler; import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.web.UpdateChecker; import com.willfp.eco.core.web.UpdateChecker;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -79,12 +80,12 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
private final Set<String> loadedIntegrations = new HashSet<>(); private final Set<String> loadedIntegrations = new HashSet<>();
/** /**
* The internal plugin scheduler. * The plugin scheduler.
*/ */
private final Scheduler scheduler; private final Scheduler scheduler;
/** /**
* The internal plugin Event Manager. * The plugin Event Manager.
*/ */
private final EventManager eventManager; private final EventManager eventManager;
@@ -99,17 +100,17 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
private final LangYml langYml; private final LangYml langYml;
/** /**
* The internal factory to produce {@link org.bukkit.NamespacedKey}s. * The factory to produce {@link org.bukkit.NamespacedKey}s.
*/ */
private final NamespacedKeyFactory namespacedKeyFactory; private final NamespacedKeyFactory namespacedKeyFactory;
/** /**
* The internal factory to produce {@link org.bukkit.metadata.FixedMetadataValue}s. * The factory to produce {@link org.bukkit.metadata.FixedMetadataValue}s.
*/ */
private final MetadataValueFactory metadataValueFactory; private final MetadataValueFactory metadataValueFactory;
/** /**
* The internal factory to produce {@link com.willfp.eco.core.scheduling.RunnableTask}s. * The factory to produce {@link com.willfp.eco.core.scheduling.RunnableTask}s.
*/ */
private final RunnableFactory runnableFactory; private final RunnableFactory runnableFactory;
@@ -151,6 +152,31 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
@Nullable @Nullable
private final ProxyFactory proxyFactory; private final ProxyFactory proxyFactory;
/**
* The tasks to run on enable.
*/
private final List<Runnable> onEnable = new ArrayList<>();
/**
* The tasks to run on disable.
*/
private final List<Runnable> onDisable = new ArrayList<>();
/**
* The tasks to run on reload.
*/
private final List<Runnable> onReload = new ArrayList<>();
/**
* The tasks to run on load.
*/
private final List<Runnable> onLoad = new ArrayList<>();
/**
* The tasks to run after load.
*/
private final List<Runnable> afterLoad = new ArrayList<>();
/** /**
* Create a new plugin. * Create a new plugin.
* <p> * <p>
@@ -258,42 +284,42 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
*/ */
protected EcoPlugin(@Nullable final PluginProps pluginProps) { protected EcoPlugin(@Nullable final PluginProps pluginProps) {
/* /*
The handler must be initialized before any plugin's constructors Eco must be initialized before any plugin's constructors
are called, as the constructors call Eco#getHandler(). are called, as the constructors call Eco#get().
To fix this, EcoSpigotPlugin an abstract class and the 'actual' To fix this, EcoSpigotPlugin an abstract class and the 'actual'
plugin class is EcoHandler - that way I can create the handler plugin class is EcoImpl - that way I can initialize eco
before any plugins are loaded while still having a separation between before any plugins are loaded while still having a separation between
the plugin class and the handler class (for code clarity). the plugin class and the implementation class (for code clarity).
I don't really like the fact that the handler class *is* the I don't really like the fact that the implementation class *is* the
spigot plugin, but it is what it is. spigot plugin, but it is what it is.
There is probably a better way of doing it - maybe with There is probably a better way of doing it - maybe with
some sort of HandlerCreator interface in order to still have some sort of EcoCrater interface in order to still have
a standalone handler class, but then there would be an interface a standalone eco class, but then there would be an interface
left in the API that doesn't really help anything. left in the API that doesn't really help anything.
The other alternative would be to use reflection to get a 'createHandler' The other alternative would be to use reflection to get a 'createEco'
method that only exists in EcoSpigotPlugin - but that feels filthy, method that only exists in EcoSpigotPlugin - but that feels filthy,
and I'd rather only use reflection where necessary. and I'd rather only use reflection where necessary.
*/ */
if (Eco.getHandler() == null && this instanceof Handler) { if (Eco.get() == null && this instanceof Eco) {
/* /*
This code is only ever called by EcoSpigotPlugin (EcoHandler) This code is only ever called by EcoSpigotPlugin (EcoImpl)
as it's the first plugin to load, and it is a handler. as it's the first plugin to load, and it's an instance of eco.
Any other plugins will never call this code as the handler Any other plugins will never call this code as eco will have already
will have already been initialized. been initialized.
*/ */
Eco.setHandler((Handler) this); Eco.Instance.set((Eco) this);
} }
assert Eco.getHandler() != null; assert Eco.get() != null;
PluginProps generatedProps = Eco.getHandler().getProps(pluginProps, this.getClass()); PluginProps generatedProps = Eco.get().getProps(pluginProps, this.getClass());
generatedProps.validate(); generatedProps.validate();
PluginProps props = this.mutateProps(generatedProps); PluginProps props = this.mutateProps(generatedProps);
props.validate(); props.validate();
@@ -304,23 +330,30 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.color = props.getColor(); this.color = props.getColor();
this.supportingExtensions = props.isSupportingExtensions(); this.supportingExtensions = props.isSupportingExtensions();
this.proxyFactory = this.proxyPackage.equalsIgnoreCase("") ? null : Eco.getHandler().createProxyFactory(this); this.proxyFactory = this.proxyPackage.equalsIgnoreCase("") ? null : Eco.get().createProxyFactory(this);
this.logger = Eco.getHandler().createLogger(this); this.logger = Eco.get().createLogger(this);
this.getLogger().info("Initializing " + this.getColor() + this.getName()); this.getLogger().info("Initializing " + this.getColor() + this.getName());
this.scheduler = Eco.getHandler().createScheduler(this); this.scheduler = Eco.get().createScheduler(this);
this.eventManager = Eco.getHandler().createEventManager(this); this.eventManager = Eco.get().createEventManager(this);
this.namespacedKeyFactory = Eco.getHandler().createNamespacedKeyFactory(this); this.namespacedKeyFactory = Eco.get().createNamespacedKeyFactory(this);
this.metadataValueFactory = Eco.getHandler().createMetadataValueFactory(this); this.metadataValueFactory = Eco.get().createMetadataValueFactory(this);
this.runnableFactory = Eco.getHandler().createRunnableFactory(this); this.runnableFactory = Eco.get().createRunnableFactory(this);
this.extensionLoader = Eco.getHandler().createExtensionLoader(this); this.extensionLoader = Eco.get().createExtensionLoader(this);
this.configHandler = Eco.getHandler().createConfigHandler(this); this.configHandler = Eco.get().createConfigHandler(this);
this.langYml = this.createLangYml(); this.langYml = this.createLangYml();
if (!this.langYml.isValid()) {
this.getLogger().warning("Notify plugin authors " + String.join(", ", this.getDescription().getAuthors()) + " that");
this.getLogger().warning("they are missing crucial lang.yml keys! They can be found");
this.getLogger().warning("in the LangYml class.");
}
this.configYml = this.createConfigYml(); this.configYml = this.createConfigYml();
Eco.getHandler().addNewPlugin(this); Eco.get().addNewPlugin(this);
/* /*
The minimum eco version check was moved here because it's very common The minimum eco version check was moved here because it's very common
@@ -329,7 +362,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
they have an outdated version of eco installed. they have an outdated version of eco installed.
*/ */
DefaultArtifactVersion runningVersion = new DefaultArtifactVersion(Eco.getHandler().getEcoPlugin().getDescription().getVersion()); DefaultArtifactVersion runningVersion = new DefaultArtifactVersion(Eco.get().getEcoPlugin().getDescription().getVersion());
DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(this.getMinimumEcoVersion()); DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(this.getMinimumEcoVersion());
if (!(runningVersion.compareTo(requiredVersion) > 0 || runningVersion.equals(requiredVersion))) { if (!(runningVersion.compareTo(requiredVersion) > 0 || runningVersion.equals(requiredVersion))) {
this.getLogger().severe("You are running an outdated version of eco!"); this.getLogger().severe("You are running an outdated version of eco!");
@@ -350,7 +383,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getLogger().info(""); this.getLogger().info("");
this.getLogger().info("Loading " + this.getColor() + this.getName()); this.getLogger().info("Loading " + this.getColor() + this.getName());
if (this.getResourceId() != 0 && !Eco.getHandler().getEcoPlugin().getConfigYml().getBool("no-update-checker")) { if (this.getResourceId() != 0 && !Eco.get().getEcoPlugin().getConfigYml().getBool("no-update-checker")) {
new UpdateChecker(this).getVersion(version -> { new UpdateChecker(this).getVersion(version -> {
DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(this.getDescription().getVersion()); DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(this.getDescription().getVersion());
DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version); DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version);
@@ -364,7 +397,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
} }
if (this.getBStatsId() != 0) { if (this.getBStatsId() != 0) {
Eco.getHandler().registerBStats(this); Eco.get().registerBStats(this);
} }
Set<String> enabledPlugins = Arrays.stream(Bukkit.getPluginManager().getPlugins()) Set<String> enabledPlugins = Arrays.stream(Bukkit.getPluginManager().getPlugins())
@@ -373,8 +406,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) { if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) {
this.loadedIntegrations.add("PlaceholderAPI"); Eco.get().createPAPIIntegration(this);
PlaceholderManager.addIntegration(Eco.getHandler().createPAPIIntegration(this));
} }
this.loadIntegrationLoaders().forEach(integrationLoader -> { this.loadIntegrationLoaders().forEach(integrationLoader -> {
@@ -384,6 +416,8 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
} }
}); });
this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName()));
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations())); this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
Prerequisite.update(); Prerequisite.update();
@@ -412,10 +446,20 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
} }
this.handleEnable(); this.handleEnable();
this.onEnable.forEach(Runnable::run);
this.getLogger().info(""); this.getLogger().info("");
} }
/**
* Add new task to run on enable.
*
* @param task The task.
*/
public final void onEnable(@NotNull final Runnable task) {
this.onEnable.add(task);
}
/** /**
* Default code to be executed on plugin disable. * Default code to be executed on plugin disable.
*/ */
@@ -427,13 +471,23 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getScheduler().cancelAll(); this.getScheduler().cancelAll();
this.handleDisable(); this.handleDisable();
this.onDisable.forEach(Runnable::run);
if (this.isSupportingExtensions()) { if (this.isSupportingExtensions()) {
this.getExtensionLoader().unloadExtensions(); this.getExtensionLoader().unloadExtensions();
} }
this.getLogger().info("Cleaning up..."); this.getLogger().info("Cleaning up...");
Eco.getHandler().getCleaner().clean(this); Eco.get().clean(this);
}
/**
* Add new task to run on disable.
*
* @param task The task.
*/
public final void onDisable(@NotNull final Runnable task) {
this.onDisable.add(task);
} }
/** /**
@@ -444,6 +498,16 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
super.onLoad(); super.onLoad();
this.handleLoad(); this.handleLoad();
this.onLoad.forEach(Runnable::run);
}
/**
* Add new task to run on load.
*
* @param task The task.
*/
public final void onLoad(@NotNull final Runnable task) {
this.onLoad.add(task);
} }
/** /**
@@ -476,6 +540,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
} }
this.handleAfterLoad(); this.handleAfterLoad();
this.afterLoad.forEach(Runnable::run);
this.reload(); this.reload();
@@ -486,6 +551,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getLogger().info("Loaded " + this.color + this.getName()); this.getLogger().info("Loaded " + this.color + this.getName());
} }
/**
* Add new task to run after load.
*
* @param task The task.
*/
public final void afterLoad(@NotNull final Runnable task) {
this.afterLoad.add(task);
}
/** /**
* Reload the plugin. * Reload the plugin.
*/ */
@@ -497,12 +571,22 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getConfigHandler().callUpdate(); // Call twice to fix issues this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.handleReload(); this.handleReload();
this.onReload.forEach(Runnable::run);
for (Extension extension : this.extensionLoader.getLoadedExtensions()) { for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload(); extension.handleReload();
} }
} }
/**
* Add new task to run on enable.
*
* @param task The task.
*/
public final void onReload(@NotNull final Runnable task) {
this.onReload.add(task);
}
/** /**
* Reload the plugin and return the time taken to reload. * Reload the plugin and return the time taken to reload.
* *
@@ -622,7 +706,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return lang.yml. * @return lang.yml.
*/ */
protected LangYml createLangYml() { protected LangYml createLangYml() {
try {
return new LangYml(this); return new LangYml(this);
} catch (NullPointerException e) {
this.getLogger().severe("Failed to load lang.yml!");
this.getLogger().severe("For the developer of this plugin: make sure you have a lang.yml");
e.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return null;
}
} }
/** /**
@@ -633,7 +725,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return config.yml. * @return config.yml.
*/ */
protected ConfigYml createConfigYml() { protected ConfigYml createConfigYml() {
try {
return new ConfigYml(this); return new ConfigYml(this);
} catch (NullPointerException e) {
this.getLogger().severe("Failed to load config.yml!");
this.getLogger().severe("For the developer of this plugin: make sure you have a config.yml");
e.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return null;
}
} }
/** /**
@@ -746,7 +846,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return The plugin. * @return The plugin.
*/ */
public static EcoPlugin getPlugin(@NotNull final String pluginName) { public static EcoPlugin getPlugin(@NotNull final String pluginName) {
return Eco.getHandler().getPluginByName(pluginName); return Eco.get().getPluginByName(pluginName);
} }
/** /**
@@ -755,7 +855,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return The set of names. * @return The set of names.
*/ */
public static Set<String> getPluginNames() { public static Set<String> getPluginNames() {
return new HashSet<>(Eco.getHandler().getLoadedPlugins()); return new HashSet<>(Eco.get().getLoadedPlugins());
} }
/** /**
@@ -921,4 +1021,26 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
public ProxyFactory getProxyFactory() { public ProxyFactory getProxyFactory() {
return this.proxyFactory; return this.proxyFactory;
} }
/**
* Create a NamespacedKey.
*
* @param key The key.
* @return The namespaced key.
*/
@NotNull
public NamespacedKey createNamespacedKey(@NotNull final String key) {
return this.getNamespacedKeyFactory().create(key);
}
/**
* Create a metadata value.
*
* @param value The value.
* @return The metadata value.
*/
@NotNull
public FixedMetadataValue createMetadataValue(@NotNull final Object value) {
return this.getMetadataValueFactory().create(value);
}
} }

View File

@@ -1,312 +0,0 @@
package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.config.wrapper.ConfigFactory;
import com.willfp.eco.core.data.ExtendedPersistentDataContainer;
import com.willfp.eco.core.data.ProfileHandler;
import com.willfp.eco.core.data.keys.KeyRegistry;
import com.willfp.eco.core.drops.DropQueueFactory;
import com.willfp.eco.core.entities.ai.EntityController;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.gui.GUIFactory;
import com.willfp.eco.core.integrations.placeholder.PlaceholderIntegration;
import com.willfp.eco.core.items.SNBTHandler;
import com.willfp.eco.core.proxy.Cleaner;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.logging.Logger;
/**
* @see Eco#getHandler()
*/
@ApiStatus.Internal
public interface Handler {
/**
* Create a scheduler.
*
* @param plugin The plugin.
* @return The scheduler.
*/
@NotNull
Scheduler createScheduler(@NotNull EcoPlugin plugin);
/**
* Create an event manager.
*
* @param plugin The plugin.
* @return The event manager.
*/
@NotNull
EventManager createEventManager(@NotNull EcoPlugin plugin);
/**
* Create a NamespacedKey factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
NamespacedKeyFactory createNamespacedKeyFactory(@NotNull EcoPlugin plugin);
/**
* Create a MetadataValue factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
MetadataValueFactory createMetadataValueFactory(@NotNull EcoPlugin plugin);
/**
* Create a Runnable factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
RunnableFactory createRunnableFactory(@NotNull EcoPlugin plugin);
/**
* Create an ExtensionLoader.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ExtensionLoader createExtensionLoader(@NotNull EcoPlugin plugin);
/**
* Create a config handler.
*
* @param plugin The plugin.
* @return The handler.
*/
@NotNull
ConfigHandler createConfigHandler(@NotNull EcoPlugin plugin);
/**
* Create a logger.
*
* @param plugin The plugin.
* @return The logger.
*/
@NotNull
Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Create a PAPI integration.
*
* @param plugin The plugin.
* @return The integration.
*/
@NotNull
PlaceholderIntegration createPAPIIntegration(@NotNull EcoPlugin plugin);
/**
* Create a proxy factory.
*
* @param plugin The plugin.
* @return The factory.
*/
@NotNull
ProxyFactory createProxyFactory(@NotNull EcoPlugin plugin);
/**
* Get eco Spigot plugin.
*
* @return The plugin.
*/
@NotNull
EcoPlugin getEcoPlugin();
/**
* Get config factory.
*
* @return The factory.
*/
@NotNull
ConfigFactory getConfigFactory();
/**
* Get drop queue factory.
*
* @return The factory.
*/
@NotNull
DropQueueFactory getDropQueueFactory();
/**
* Get GUI factory.
*
* @return The factory.
*/
@NotNull
GUIFactory getGUIFactory();
/**
* Get cleaner.
*
* @return The cleaner.
*/
@NotNull
Cleaner getCleaner();
/**
* Add new plugin.
*
* @param plugin The plugin.
*/
void addNewPlugin(@NotNull EcoPlugin plugin);
/**
* Get plugin by name.
*
* @param name The name.
* @return The plugin.
*/
@Nullable
EcoPlugin getPluginByName(@NotNull String name);
/**
* Get all loaded eco plugins.
*
* @return A list of plugin names in lowercase.
*/
@NotNull
List<String> getLoadedPlugins();
/**
* Create a FastItemStack.
*
* @param itemStack The base ItemStack.
* @return The FastItemStack.
*/
@NotNull
FastItemStack createFastItemStack(@NotNull ItemStack itemStack);
/**
* Register bStats metrics.
*
* @param plugin The plugin.
*/
void registerBStats(@NotNull EcoPlugin plugin);
/**
* Get Adventure audiences.
*
* @return The audiences.
*/
@Nullable
BukkitAudiences getAdventure();
/**
* Get the key registry.
*
* @return The registry.
*/
@NotNull
KeyRegistry getKeyRegistry();
/**
* Get the PlayerProfile handler.
*
* @return The handler.
*/
@NotNull
ProfileHandler getProfileHandler();
/**
* Create dummy entity - never spawned, exists purely in code.
*
* @param location The location.
* @return The entity.
*/
@NotNull
Entity createDummyEntity(@NotNull Location location);
/**
* Create a {@link NamespacedKey} quickly
* <p>
* Bypasses the constructor, allowing for the creation of invalid keys,
* therefore this is considered unsafe and should only be called after
* the key has been confirmed to be valid.
*
* @param namespace The namespace.
* @param key The key.
* @return The key.
*/
@NotNull
NamespacedKey createNamespacedKey(@NotNull String namespace,
@NotNull String key);
/**
* Return or get props for a plugin.
*
* @param existing The existing constructor props.
* @param plugin The plugin.
* @return The props.
*/
@NotNull
PluginProps getProps(@Nullable PluginProps existing,
@NotNull Class<? extends EcoPlugin> plugin);
/**
* Format a string with MiniMessage.
*
* @param message The message.
* @return The formatted string.
*/
@NotNull
String formatMiniMessage(@NotNull String message);
/**
* Create controlled entity from a mob.
*
* @param mob The mob.
* @param <T> The mob type.
* @return The controlled entity.
*/
@NotNull <T extends Mob> EntityController<T> createEntityController(@NotNull T mob);
/**
* Adapt base PDC to extended PDC.
*
* @param container The container.
* @return The extended container.
*/
@NotNull
ExtendedPersistentDataContainer adaptPdc(@NotNull PersistentDataContainer container);
/**
* Create new PDC.
*
* @return The container.
*/
@NotNull
PersistentDataContainer newPdc();
/**
* Get SNBT handler.
*
* @return The SNBT handler.
*/
@NotNull
SNBTHandler getSNBTHandler();
}

View File

@@ -13,7 +13,9 @@ import org.jetbrains.annotations.NotNull;
* in the constructor. * in the constructor.
* *
* @param <T> The eco plugin type. * @param <T> The eco plugin type.
* @deprecated Leaky inheritance, shouldn't exist.
*/ */
@Deprecated(since = "6.43.0", forRemoval = true)
public abstract class PluginDependent<T extends EcoPlugin> { public abstract class PluginDependent<T extends EcoPlugin> {
/** /**
* The {@link EcoPlugin} that is stored. * The {@link EcoPlugin} that is stored.

View File

@@ -221,7 +221,7 @@ public final class PluginProps {
/** /**
* Create new props from known values. * Create new props from known values.
* * <p>
* Marked as internal as this method will break whenever the properties themselves * Marked as internal as this method will break whenever the properties themselves
* are updated (e.g. if a new property is added) - so to prevent any potential * are updated (e.g. if a new property is added) - so to prevent any potential
* backwards-compatibility bugs, this method cannot be invoked outside eco itself. * backwards-compatibility bugs, this method cannot be invoked outside eco itself.

View File

@@ -70,7 +70,10 @@ public class Prerequisite {
/** /**
* Requires the server to be running an implementation of BungeeCord. * Requires the server to be running an implementation of BungeeCord.
*
* @deprecated This will never return true.
*/ */
@Deprecated(since = "6.49.0", forRemoval = true)
public static final Prerequisite HAS_BUNGEECORD = new Prerequisite( public static final Prerequisite HAS_BUNGEECORD = new Prerequisite(
() -> ClassUtils.exists("net.md_5.bungee.api.event.ServerConnectedEvent"), () -> ClassUtils.exists("net.md_5.bungee.api.event.ServerConnectedEvent"),
"Requires server to be running BungeeCord (or a fork)" "Requires server to be running BungeeCord (or a fork)"
@@ -78,7 +81,10 @@ public class Prerequisite {
/** /**
* Requires the server to be running an implementation of Velocity. * Requires the server to be running an implementation of Velocity.
*
* @deprecated This will never return true.
*/ */
@Deprecated(since = "6.49.0", forRemoval = true)
public static final Prerequisite HAS_VELOCITY = new Prerequisite( public static final Prerequisite HAS_VELOCITY = new Prerequisite(
() -> ClassUtils.exists("com.velocitypowered.api.event.player.ServerConnectedEvent"), () -> ClassUtils.exists("com.velocitypowered.api.event.player.ServerConnectedEvent"),
"Requires server to be running Velocity (or a fork)" "Requires server to be running Velocity (or a fork)"

View File

@@ -1,30 +1,37 @@
package com.willfp.eco.core.command; package com.willfp.eco.core.command;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/** /**
* Interface for all command implementations. * Generic interface for commands.
*/ */
@SuppressWarnings("removal") @SuppressWarnings("null")
public interface CommandBase { public interface CommandBase {
/** /**
* Get command name. * Get command name.
* *
* @return The name. * @return The name.
*/ */
String getName(); @NotNull String getName();
/** /**
* Get command permission. * Get command permission.
* *
* @return The permission. * @return The permission.
*/ */
String getPermission(); @NotNull String getPermission();
/** /**
* If only players can execute the command. * If only players can execute the command.
@@ -39,79 +46,237 @@ public interface CommandBase {
* @param command The subcommand. * @param command The subcommand.
* @return The parent command. * @return The parent command.
*/ */
CommandBase addSubcommand(@NotNull CommandBase command); @NotNull CommandBase addSubcommand(@NotNull CommandBase command);
/**
* Get the subcommands of the command.
*
* @return The subcommands.
*/
@NotNull List<CommandBase> getSubcommands();
/**
* Intended for returning the enclosing CommandBase,
* when this instance is serving as the delegate command base.
*
* @return the wrapping object of this delegate.
*/
default @NotNull CommandBase getWrapped() {
return this;
}
/** /**
* Handle command execution. * Handle command execution.
* <p> * <p>
* Marked as default void with no implementation for backwards compatibility. * This will always be called on command execution.
* *
* @param sender The sender. * @param sender The sender.
* @param args The args. * @param args The args.
* @throws NotificationException naturally, this is handled as a part of the command system.
*/ */
default void onExecute(@NotNull CommandSender sender, default void onExecute(@NotNull final CommandSender sender, @NotNull final List<String> args) throws NotificationException {
@NotNull List<String> args) { // Do nothing.
}
/**
* Handle command execution from players.
* <p>
* This will only be called if the sender is a player.
*
* @param sender The sender.
* @param args The args.
* @throws NotificationException naturally, this is handled as a part of the command system.
*/
default void onExecute(@NotNull final Player sender, @NotNull final List<String> args) throws NotificationException {
// Do nothing. // Do nothing.
} }
/** /**
* Handle tab completion. * Handle tab completion.
* <p> * <p>
* Marked as default void with no implementation for backwards compatibility. * This will always be called on tab completion.
* *
* @param sender The sender. * @param sender The sender.
* @param args The args. * @param args The args.
* @return The results. * @return The results.
*/ */
default List<String> tabComplete(@NotNull CommandSender sender, @NotNull
@NotNull List<String> args) { default List<String> tabComplete(@NotNull final CommandSender sender, @NotNull final List<String> args) {
return new ArrayList<>(); return new ArrayList<>();
} }
/**
* Handle tab completion.
* <p>
* This will only be called if the sender is a player.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
@NotNull
default List<String> tabComplete(@NotNull final Player sender, @NotNull final List<String> args) {
return new ArrayList<>();
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param key The lang.yml key for the message to be sent.
* @throws NotificationException always.
*/
default void notify(@NotNull final String key) throws NotificationException {
throw new NotificationException(key);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed object is null.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param obj The object to test.
* @param key The lang.yml key for the message to be sent.
* @param <T> The object type.
* @return Returns the object, definitely not-null.
* @throws NotificationException If the object is null.
*/
@NotNull
default <T> T notifyNull(@Nullable final T obj,
@NotNull final String key) throws NotificationException {
if (Objects.isNull(obj)) {
notify(key);
}
return Objects.requireNonNull(obj);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed object doesn't match the predicate.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param obj The object to test.
* @param key The lang.yml key for the message to be sent.
* @param predicate The predicate to test the object against.
* @param <T> The type of the object.
* @return Returns the object, definitely not-null.
* @throws NotificationException If the object doesn't satisfy the predicate.
*/
@NotNull
default <T> T notifyFalse(@NotNull final T obj,
@NotNull final String key,
@NotNull final Predicate<T> predicate) throws NotificationException {
notifyFalse(predicate.test(obj), key);
return obj;
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if a condition is false.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param condition The condition to test.
* @param key The lang.yml key for the message to be sent.
* @return True.
* @throws NotificationException If the condition is false.
*/
default boolean notifyFalse(final boolean condition,
@NotNull final String key) throws NotificationException {
if (!condition) {
notify(key);
}
return true;
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed string doesn't relate to a currently online player.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param playerName The player name.
* @param key The lang.yml key for the message to be sent.
* @return Returns the player, definitely not-null.
* @throws NotificationException If the player name is invalid.
*/
@NotNull
default Player notifyPlayerRequired(@Nullable final String playerName, @NotNull final String key) throws NotificationException {
if (playerName == null) {
notify(key);
}
assert playerName != null;
final Player player = Bukkit.getPlayer(playerName);
notifyNull(player, key);
return Objects.requireNonNull(player);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed string doesn't relate to a player on the server.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param playerName The player name.
* @param key The lang.yml key for the message to be sent.
* @return Returns the offline player, definitely not-null.
* @throws NotificationException If the player name is invalid.
*/
@NotNull
default OfflinePlayer notifyOfflinePlayerRequired(@Nullable final String playerName,
@NotNull final String key) throws NotificationException {
if (playerName == null) {
notify(key);
}
assert playerName != null;
@SuppressWarnings("deprecation") final OfflinePlayer player = Bukkit.getOfflinePlayer(playerName);
boolean hasPlayedBefore = player.hasPlayedBefore() || player.isOnline();
notifyFalse(!hasPlayedBefore, key);
return player;
}
/**
* Throws an exception containing a langYml key if player doesn't have permission.
*
* @param player The player.
* @param permission The permission.
* @param key The lang.yml key for the message to be sent.
* @return The player.
* @throws NotificationException If the player doesn't have the required permission.
*/
@NotNull
default Player notifyPermissionRequired(@NotNull final Player player,
@NotNull final String permission,
@NotNull final String key) throws NotificationException {
return notifyFalse(player, key, p -> p.hasPermission(permission));
}
/** /**
* Get the plugin. * Get the plugin.
* *
* @return The plugin. * @return The plugin.
*/ */
EcoPlugin getPlugin(); EcoPlugin getPlugin();
/**
* Get the handler.
*
* @return The handler.
* @see CommandHandler
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
CommandHandler getHandler();
/**
* Set the handler.
*
* @param handler The handler.
* @see CommandHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
void setHandler(@NotNull CommandHandler handler);
/**
* Get the tab completer.
*
* @return The tab completer.
* @see TabCompleteHandler
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
TabCompleteHandler getTabCompleter();
/**
* Set the tab completer.
*
* @param handler The handler.
* @see TabCompleteHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
void setTabCompleter(@NotNull TabCompleteHandler handler);
} }

View File

@@ -1,29 +0,0 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A command handler handles the actual code for a command.
* <p>
* The replacement for {@link org.bukkit.command.CommandExecutor#onCommand(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#onExecute(CommandSender, List)}.
*/
@FunctionalInterface
@Deprecated(since = "6.17.0", forRemoval = true)
public interface CommandHandler {
/**
* The code to be called on execution.
*
* @param sender The sender.
* @param args The arguments.
*/
void onExecute(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -0,0 +1,34 @@
package com.willfp.eco.core.command;
/**
* A notification exception is thrown when {@link org.bukkit.command.CommandSender}s don't
* specify valid arguments in commands.
* <p>
* Methods in eco that throw this will contain automatic handling and thus
* should not be surrounded by try / catch blocks.
*/
public class NotificationException extends Exception {
/**
* The key for the lang.yml message to be sent.
*/
private final String key;
/**
* Creates a notification exception.
*
* @param key The lang key of the notification.
*/
public NotificationException(String key) {
super(key);
this.key = key;
}
/**
* Get the lang key.
*
* @return The lang key.
*/
public String getKey() {
return key;
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Plugin command bases can be registered directly with the server,
* this essentially functions as the interface that is implemented generically
* via {@link com.willfp.eco.core.command.impl.PluginCommand}.
*/
public interface PluginCommandBase extends CommandBase {
/**
* Register the PluginCommandBase to the bukkit commandMap.
*/
void register();
/**
* Unregister the PluginCommandBase from the bukkit commandMap.
*/
void unregister();
/**
* Get aliases. Leave null if this command is from plugin.yml.
*
* @return The aliases.
*/
@NotNull
default List<String> getAliases() {
return new ArrayList<>();
}
/**
* Get description.
*
* @return The description.
*/
@Nullable
default String getDescription() {
return null;
}
}

View File

@@ -1,30 +0,0 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A Tab Complete handler handles the actual tab-completion code.
* <p>
* The replacement for {@link org.bukkit.command.TabCompleter#onTabComplete(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#tabComplete(CommandSender, List)}
*/
@FunctionalInterface
@Deprecated(since = "6.17.0", forRemoval = true)
public interface TabCompleteHandler {
/**
* Handle Tab Completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -0,0 +1,72 @@
package com.willfp.eco.core.command.impl;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Delegates a bukkit command to an eco command (for registrations).
*
* @deprecated Internal command implementations have been removed from the API.
*/
@Deprecated(forRemoval = true, since = "6.49.0")
public final class DelegatedBukkitCommand extends Command implements TabCompleter, PluginIdentifiableCommand {
/**
* The delegate command.
*/
private final PluginCommand delegate;
/**
* Create a new delegated command.
*
* @param delegate The delegate.
*/
public DelegatedBukkitCommand(@NotNull final PluginCommand delegate) {
super(delegate.getName());
this.delegate = delegate;
}
@Override
public boolean execute(@NotNull final CommandSender commandSender,
@NotNull final String label,
@NotNull final String[] args) {
return false;
}
@Override
public List<String> onTabComplete(@NotNull final CommandSender commandSender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
return List.of();
}
@NotNull
@Override
public Plugin getPlugin() {
return this.delegate.getPlugin();
}
@Override
public @NotNull String getPermission() {
return this.delegate.getPermission();
}
@NotNull
@Override
public String getDescription() {
return this.delegate.getDescription() == null ? "" : this.delegate.getDescription();
}
@NotNull
@Override
public List<String> getAliases() {
return this.delegate.getAliases();
}
}

View File

@@ -1,287 +0,0 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Abstract class for commands that can be handled.
* <p>
* Handled commands have a method to pass in raw input from bukkit commands
* in order to execute the command-specific code. It's essentially an internal
* layer, hence why it's a package-private class.
*/
@SuppressWarnings({"DeprecatedIsStillUsed", "removal"})
abstract class HandledCommand implements CommandBase {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The name of the command.
*/
private final String name;
/**
* The permission required to execute the command.
* <p>
* Written out as a string for flexibility with subclasses.
*/
private final String permission;
/**
* Should the command only be allowed to be executed by players?
* <p>
* In other worlds, only allowed to be executed by console.
*/
private final boolean playersOnly;
/**
* The actual code to be executed in the command.
*/
@Deprecated
@Nullable
private com.willfp.eco.core.command.CommandHandler handler = null;
/**
* The tab completion code to be executed in the command.
*/
@Deprecated
@Nullable
private com.willfp.eco.core.command.TabCompleteHandler tabCompleter = null;
/**
* All subcommands for the command.
*/
private final List<CommandBase> subcommands;
/**
* Create a new command.
* <p>
* The name cannot be the same as an existing command as this will conflict.
*
* @param plugin Instance of a plugin.
* @param name The name used in execution.
* @param permission The permission required to execute the command.
* @param playersOnly If only players should be able to execute this command.
*/
HandledCommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
this.plugin = plugin;
this.name = name;
this.permission = permission;
this.playersOnly = playersOnly;
this.subcommands = new ArrayList<>();
}
/**
* Add a subcommand to the command.
*
* @param subcommand The subcommand.
* @return The parent command.
*/
@Override
public final CommandBase addSubcommand(@NotNull final CommandBase subcommand) {
subcommands.add(subcommand);
return this;
}
/**
* Get the plugin.
*
* @return The plugin.
*/
@Override
public EcoPlugin getPlugin() {
return this.plugin;
}
/**
* Handle the command.
*
* @param sender The sender.
* @param args The arguments.
*/
protected final void handle(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!canExecute(sender, this, this.getPlugin())) {
return;
}
if (args.length > 0) {
for (CommandBase subcommand : this.getSubcommands()) {
if (subcommand.getName().equalsIgnoreCase(args[0])) {
if (!canExecute(sender, subcommand, this.getPlugin())) {
return;
}
((HandledCommand) subcommand).handle(sender, Arrays.copyOfRange(args, 1, args.length));
return;
}
}
}
if (this.isPlayersOnly() && !(sender instanceof Player)) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("not-player"));
return;
}
if (this.getHandler() != null) {
this.getHandler().onExecute(sender, Arrays.asList(args));
} else {
this.onExecute(sender, Arrays.asList(args));
}
}
/**
* Handle the tab completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
protected final List<String> handleTabCompletion(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!sender.hasPermission(this.getPermission())) {
return null;
}
if (args.length == 1) {
List<String> completions = new ArrayList<>();
StringUtil.copyPartialMatches(
args[0],
this.getSubcommands().stream()
.filter(subCommand -> sender.hasPermission(subCommand.getPermission()))
.map(CommandBase::getName)
.collect(Collectors.toList()),
completions
);
Collections.sort(completions);
if (!completions.isEmpty()) {
return completions;
}
}
if (args.length >= 2) {
HandledCommand command = null;
for (CommandBase subcommand : this.getSubcommands()) {
if (!sender.hasPermission(subcommand.getPermission())) {
continue;
}
if (args[0].equalsIgnoreCase(subcommand.getName())) {
command = (HandledCommand) subcommand;
}
}
if (command != null) {
return command.handleTabCompletion(sender, Arrays.copyOfRange(args, 1, args.length));
}
}
if (this.getTabCompleter() != null) {
return this.getTabCompleter().tabComplete(sender, Arrays.asList(args));
} else {
return this.tabComplete(sender, Arrays.asList(args));
}
}
/**
* If a sender can execute the command.
*
* @param sender The sender.
* @param command The command.
* @param plugin The plugin.
* @return If the sender can execute.
*/
public static boolean canExecute(@NotNull final CommandSender sender,
@NotNull final CommandBase command,
@NotNull final EcoPlugin plugin) {
if (!sender.hasPermission(command.getPermission()) && sender instanceof Player) {
sender.sendMessage(plugin.getLangYml().getNoPermission());
return false;
}
return true;
}
/**
* Get the command name.
*
* @return The name.
*/
public String getName() {
return this.name;
}
/**
* Get the permission required to execute the command.
*
* @return The permission.
*/
public String getPermission() {
return this.permission;
}
/**
* Get if the command can only be executed by players.
*
* @return If players only.
*/
public boolean isPlayersOnly() {
return this.playersOnly;
}
/**
* Get the subcommands of the command.
*
* @return The subcommands.
*/
public List<CommandBase> getSubcommands() {
return this.subcommands;
}
@Deprecated(forRemoval = true)
@Override
public @Nullable com.willfp.eco.core.command.CommandHandler getHandler() {
return this.handler;
}
@Deprecated(forRemoval = true)
@Override
public @Nullable com.willfp.eco.core.command.TabCompleteHandler getTabCompleter() {
return this.tabCompleter;
}
@Deprecated(forRemoval = true)
@Override
public void setHandler(@Nullable final com.willfp.eco.core.command.CommandHandler handler) {
this.handler = handler;
}
@Deprecated(forRemoval = true)
@Override
public void setTabCompleter(@Nullable final com.willfp.eco.core.command.TabCompleteHandler tabCompleter) {
this.tabCompleter = tabCompleter;
}
}

View File

@@ -1,26 +1,28 @@
package com.willfp.eco.core.command.impl; package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit; import com.willfp.eco.core.command.CommandBase;
import org.bukkit.command.Command; import com.willfp.eco.core.command.PluginCommandBase;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
/** /**
* PluginCommands are the class to be used instead of CommandExecutor, * PluginCommands are the class to be used instead of CommandExecutor, they function as the base
* they function as the base command, e.g. {@code /ecoenchants} would be a base command, with each * command, e.g. {@code /ecoenchants} would be a base command, with each subsequent argument
* subsequent argument functioning as subcommands. * functioning as subcommands.
* <p> * <p>
* The command will not be registered until register() is called. * The command will not be registered until register() is called.
* <p> * <p>
* The name cannot be the same as an existing command as this will conflict. * The name cannot be the same as an existing command as this will conflict.
*/ */
public abstract class PluginCommand extends HandledCommand implements CommandExecutor, TabCompleter { public abstract class PluginCommand implements PluginCommandBase {
/**
* The delegate command.
*/
private final PluginCommandBase delegate;
/** /**
* Create a new command. * Create a new command.
* *
@@ -33,64 +35,51 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
@NotNull final String name, @NotNull final String name,
@NotNull final String permission, @NotNull final String permission,
final boolean playersOnly) { final boolean playersOnly) {
super(plugin, name, permission, playersOnly); this.delegate = Eco.get().createPluginCommand(this, plugin, name, permission, playersOnly);
} }
/**
* Registers the command with the server,
* <p>
* Requires the command name to exist, defined in plugin.yml.
*/
public final void register() {
org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName());
assert command != null;
command.setExecutor(this);
command.setTabCompleter(this);
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link CommandExecutor#onCommand(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name)
* @return If the command was processed by the linked {@link EcoPlugin}
*/
@Override @Override
public final boolean onCommand(@NotNull final CommandSender sender, public @NotNull String getName() {
@NotNull final Command command, return delegate.getName();
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return false;
} }
this.handle(sender, args);
return true;
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link TabCompleter#onTabComplete(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name).
* @return The list of tab-completions.
*/
@Override @Override
public @Nullable List<String> onTabComplete(@NotNull final CommandSender sender, public @NotNull String getPermission() {
@NotNull final Command command, return delegate.getPermission();
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return null;
} }
return this.handleTabCompletion(sender, args); @Override
public boolean isPlayersOnly() {
return delegate.isPlayersOnly();
}
@Override
public @NotNull CommandBase addSubcommand(@NotNull CommandBase command) {
return delegate.addSubcommand(command);
}
@Override
public @NotNull List<CommandBase> getSubcommands() {
return delegate.getSubcommands();
}
@Override
public @NotNull CommandBase getWrapped() {
return this;
}
@Override
public void register() {
delegate.register();
}
@Override
public void unregister() {
delegate.unregister();
}
@Override
public EcoPlugin getPlugin() {
return delegate.getPlugin();
} }
} }

View File

@@ -1,13 +1,21 @@
package com.willfp.eco.core.command.impl; package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase; import com.willfp.eco.core.command.CommandBase;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
/** /**
* Subcommands can be added to PluginCommands or to other Subcommands. * A command implementation that must exist as a subcommand (i.e. cannot be registered directly).
*/ */
public abstract class Subcommand extends HandledCommand { public abstract class Subcommand implements CommandBase {
/**
* The delegate command.
*/
private final CommandBase delegate;
/** /**
* Create subcommand. * Create subcommand.
* *
@@ -20,7 +28,7 @@ public abstract class Subcommand extends HandledCommand {
@NotNull final String name, @NotNull final String name,
@NotNull final String permission, @NotNull final String permission,
final boolean playersOnly) { final boolean playersOnly) {
super(plugin, name, permission, playersOnly); this.delegate = Eco.get().createSubcommand(this, plugin, name, permission, playersOnly);
} }
/** /**
@@ -33,6 +41,41 @@ public abstract class Subcommand extends HandledCommand {
protected Subcommand(@NotNull final EcoPlugin plugin, protected Subcommand(@NotNull final EcoPlugin plugin,
@NotNull final String name, @NotNull final String name,
@NotNull final CommandBase parent) { @NotNull final CommandBase parent) {
super(plugin, name, parent.getPermission(), parent.isPlayersOnly()); this(plugin, name, parent.getPermission(), parent.isPlayersOnly());
}
@Override
public @NotNull String getName() {
return delegate.getName();
}
@Override
public @NotNull String getPermission() {
return delegate.getPermission();
}
@Override
public boolean isPlayersOnly() {
return delegate.isPlayersOnly();
}
@Override
public @NotNull CommandBase addSubcommand(@NotNull CommandBase command) {
return delegate.addSubcommand(command);
}
@Override
public @NotNull List<CommandBase> getSubcommands() {
return delegate.getSubcommands();
}
@Override
public @NotNull CommandBase getWrapped() {
return this;
}
@Override
public EcoPlugin getPlugin() {
return delegate.getPlugin();
} }
} }

View File

@@ -40,7 +40,7 @@ public abstract class BaseConfig extends LoadableConfigWrapper {
final boolean removeUnused, final boolean removeUnused,
@NotNull final ConfigType type, @NotNull final ConfigType type,
final boolean requiresChangeToSave) { final boolean requiresChangeToSave) {
super(Eco.getHandler().getConfigFactory().createUpdatableConfig( super(Eco.get().createUpdatableConfig(
configName, configName,
plugin, plugin,
"", "",

View File

@@ -6,12 +6,12 @@ import org.jetbrains.annotations.Nullable;
/** /**
* Builder for configs to create them programmatically. * Builder for configs to create them programmatically.
*/ */
public class BuildableConfig extends TransientConfig { public class BuildableConfig extends GenericConfig {
/** /**
* Create a new empty config builder. * Create a new empty config builder.
*/ */
public BuildableConfig() { public BuildableConfig() {
super();
} }
/** /**

View File

@@ -14,7 +14,12 @@ public enum ConfigType {
/** /**
* .yml config. * .yml config.
*/ */
YAML("yml"); YAML("yml"),
/**
* .toml config.
*/
TOML("toml");
/** /**
* The file extension. * The file extension.

View File

@@ -0,0 +1,157 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
/**
* Utilities / API methods for configs.
*/
public final class Configs {
/**
* Load a Config from a bukkit {@link ConfigurationSection}.
*
* @param config The ConfigurationSection.
* @return The config.
*/
@NotNull
public static Config fromBukkit(@Nullable final ConfigurationSection config) {
return config == null ? empty() : Eco.get().wrapConfigurationSection(config);
}
/**
* Load a config from an {@link InputStream}.
* <p>
* Only for yaml configs.
*
* @param stream The InputStream.
* @return The config.
*/
@NotNull
public static Config fromStream(@Nullable final InputStream stream) {
return stream != null ? fromBukkit(YamlConfiguration.loadConfiguration(
new InputStreamReader(stream)
)) : empty();
}
/**
* Load a config from a file.
*
* @param file The file.
* @return The config.
*/
@NotNull
public static Config fromFile(@Nullable final File file) {
if (file == null) {
return empty();
}
int lastIndex = file.getName().lastIndexOf(".");
if (lastIndex < 0) {
return empty();
}
for (ConfigType type : ConfigType.values()) {
if (file.getName().substring(lastIndex + 1).equalsIgnoreCase(type.getExtension())) {
return fromFile(file, type);
}
}
return empty();
}
/**
* Load a config from a file.
*
* @param file The file.
* @param type The type.
* @return The config.
*/
@NotNull
public static Config fromFile(@Nullable final File file,
@NotNull final ConfigType type) {
if (file == null) {
return empty();
}
try {
return Eco.get().createConfig(Files.readString(file.toPath()), type);
} catch (IOException e) {
return empty();
}
}
/**
* Load config from map (uses {@link ConfigType#JSON}).
*
* @param values The values.
* @return The config.
*/
@NotNull
public static Config fromMap(@NotNull final Map<String, Object> values) {
return fromMap(values, ConfigType.JSON);
}
/**
* Load config from map.
*
* @param values The values.
* @param type The type.
* @return The config.
*/
@NotNull
public static Config fromMap(@NotNull final Map<String, Object> values,
@NotNull final ConfigType type) {
return Eco.get().createConfig(values, type);
}
/**
* Create empty config (uses {@link ConfigType#JSON}).
*
* @return An empty config.
*/
@NotNull
public static Config empty() {
return fromMap(new HashMap<>(), ConfigType.JSON);
}
/**
* Create empty config.
*
* @param type The type.
* @return An empty config.
*/
@NotNull
public static Config empty(@NotNull final ConfigType type) {
return fromMap(new HashMap<>(), type);
}
/**
* Load config from string.
*
* @param contents The contents of the config.
* @param type The config type.
* @return The config.
*/
@NotNull
public static Config fromString(@NotNull final String contents,
@NotNull final ConfigType type) {
return Eco.get().createConfig(contents, type);
}
private Configs() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -31,7 +31,7 @@ public abstract class ExtendableConfig extends LoadableConfigWrapper {
@NotNull final String subDirectoryPath, @NotNull final String subDirectoryPath,
@NotNull final ConfigType type, @NotNull final ConfigType type,
@NotNull final String... updateBlacklist) { @NotNull final String... updateBlacklist) {
super(Eco.getHandler().getConfigFactory().createUpdatableConfig( super(Eco.get().createUpdatableConfig(
configName, configName,
plugin, plugin,
subDirectoryPath, subDirectoryPath,

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Generic config to simplify creating custom configs without having
* to meddle with delegation.
*/
public abstract class GenericConfig extends ConfigWrapper<Config> {
/**
* Create a new generic config.
*/
protected GenericConfig() {
super(Configs.empty());
}
/**
* Create a new generic config.
*
* @param type The config type.
*/
protected GenericConfig(@NotNull final ConfigType type) {
super(Configs.empty(type));
}
}

View File

@@ -21,7 +21,7 @@ public abstract class StaticBaseConfig extends LoadableConfigWrapper {
protected StaticBaseConfig(@NotNull final String configName, protected StaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin, @NotNull final PluginLike plugin,
@NotNull final ConfigType type) { @NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createLoadableConfig( super(Eco.get().createLoadableConfig(
configName, configName,
plugin, plugin,
"", "",

View File

@@ -20,13 +20,16 @@ import java.util.Map;
* Config that exists purely in the code, not linked to any file. * Config that exists purely in the code, not linked to any file.
* <p> * <p>
* Use for inline configs to move data around or to add subsections to other configs. * Use for inline configs to move data around or to add subsections to other configs.
*
* @deprecated Poorly named class, makes the config system seem needlessly complicated.
*/ */
@Deprecated(since = "6.44.0", forRemoval = true)
public class TransientConfig extends ConfigWrapper<Config> { public class TransientConfig extends ConfigWrapper<Config> {
/** /**
* @param config The ConfigurationSection handle. * @param config The ConfigurationSection handle.
*/ */
public TransientConfig(@NotNull final ConfigurationSection config) { public TransientConfig(@NotNull final ConfigurationSection config) {
super(Eco.getHandler().getConfigFactory().createConfig(config)); super(Eco.get().wrapConfigurationSection(config));
} }
/** /**
@@ -42,7 +45,7 @@ public class TransientConfig extends ConfigWrapper<Config> {
* @param stream The InputStream. * @param stream The InputStream.
*/ */
public TransientConfig(@Nullable final InputStream stream) { public TransientConfig(@Nullable final InputStream stream) {
super(stream != null ? Eco.getHandler().getConfigFactory().createConfig(YamlConfiguration.loadConfiguration( super(stream != null ? Eco.get().wrapConfigurationSection(YamlConfiguration.loadConfiguration(
new InputStreamReader(stream) new InputStreamReader(stream)
)) : new TransientConfig()); )) : new TransientConfig());
} }
@@ -62,7 +65,7 @@ public class TransientConfig extends ConfigWrapper<Config> {
*/ */
public TransientConfig(@Nullable final File file, public TransientConfig(@Nullable final File file,
@NotNull final ConfigType type) { @NotNull final ConfigType type) {
super(file != null ? Eco.getHandler().getConfigFactory().createConfig(readFile(file), type) super(file != null ? Eco.get().createConfig(readFile(file), type)
: new TransientConfig()); : new TransientConfig());
} }
@@ -72,7 +75,7 @@ public class TransientConfig extends ConfigWrapper<Config> {
* @param values The values. * @param values The values.
*/ */
public TransientConfig(@NotNull final Map<String, Object> values) { public TransientConfig(@NotNull final Map<String, Object> values) {
super(Eco.getHandler().getConfigFactory().createConfig(values, ConfigType.YAML)); super(Eco.get().createConfig(values, ConfigType.YAML));
} }
/** /**
@@ -83,7 +86,7 @@ public class TransientConfig extends ConfigWrapper<Config> {
*/ */
public TransientConfig(@NotNull final Map<String, Object> values, public TransientConfig(@NotNull final Map<String, Object> values,
@NotNull final ConfigType type) { @NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(values, type)); super(Eco.get().createConfig(values, type));
} }
/** /**
@@ -99,7 +102,7 @@ public class TransientConfig extends ConfigWrapper<Config> {
*/ */
public TransientConfig(@NotNull final String contents, public TransientConfig(@NotNull final String contents,
@NotNull final ConfigType type) { @NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, type)); super(Eco.get().createConfig(contents, type));
} }
/** /**

View File

@@ -6,10 +6,32 @@ import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.util.StringUtils; import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
/** /**
* Default plugin lang.yml. * Default plugin lang.yml.
*/ */
public class LangYml extends BaseConfig { public class LangYml extends BaseConfig {
/**
* The messages key.
*/
public static final String KEY_MESSAGES = "messages";
/**
* The prefix key.
*/
public static final String KEY_PREFIX = "messages.prefix";
/**
* The no permission key.
*/
public static final String KEY_NO_PERMISSION = "messages.no-permission";
/**
* The not player key.
*/
public static final String KEY_NOT_PLAYER = "messages.not-player";
/** /**
* Lang.yml. * Lang.yml.
* *
@@ -19,13 +41,31 @@ public class LangYml extends BaseConfig {
super("lang", plugin, false, ConfigType.YAML); super("lang", plugin, false, ConfigType.YAML);
} }
/**
* lang.yml requires certain keys to be present.
* <p>
* If the lang.yml does not contain these keys, it is considered to be
* invalid and thus will show a warning in console.
*
* @return If valid.
*/
public boolean isValid() {
for (String key : List.of(KEY_MESSAGES, KEY_PREFIX, KEY_NO_PERMISSION, KEY_NOT_PLAYER)) {
if (!this.has(key)) {
return false;
}
}
return true;
}
/** /**
* Get the prefix for messages in chat. * Get the prefix for messages in chat.
* *
* @return The prefix. * @return The prefix.
*/ */
public String getPrefix() { public String getPrefix() {
return this.getFormattedString("messages.prefix"); return this.getFormattedString(KEY_PREFIX);
} }
/** /**
@@ -34,7 +74,7 @@ public class LangYml extends BaseConfig {
* @return The message. * @return The message.
*/ */
public String getNoPermission() { public String getNoPermission() {
return getPrefix() + this.getFormattedString("messages.no-permission"); return getPrefix() + this.getFormattedString(KEY_NO_PERMISSION);
} }
/** /**
@@ -56,6 +96,6 @@ public class LangYml extends BaseConfig {
*/ */
public String getMessage(@NotNull final String message, public String getMessage(@NotNull final String message,
@NotNull final StringUtils.FormatOption option) { @NotNull final StringUtils.FormatOption option) {
return getPrefix() + this.getFormattedString("messages." + message, option); return getPrefix() + this.getFormattedString(KEY_MESSAGES + "." + message, option);
} }
} }

View File

@@ -2,7 +2,7 @@ package com.willfp.eco.core.config.interfaces;
import com.willfp.eco.core.config.BuildableConfig; import com.willfp.eco.core.config.BuildableConfig;
import com.willfp.eco.core.config.ConfigType; import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.TransientConfig; import com.willfp.eco.core.config.Configs;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder; import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable; import com.willfp.eco.core.placeholder.PlaceholderInjectable;
@@ -103,7 +103,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/ */
@NotNull @NotNull
default Config getSubsection(@NotNull String path) { default Config getSubsection(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionOrNull(path), new TransientConfig()); return Objects.requireNonNullElse(getSubsectionOrNull(path), Configs.empty());
} }
/** /**

View File

@@ -1,87 +0,0 @@
package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* Internal component to create backend config implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface ConfigFactory {
/**
* Updatable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param type The config type.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
LoadableConfig createUpdatableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
boolean removeUnused,
@NotNull ConfigType type,
boolean requiresChangesToSave,
@NotNull String... updateBlacklist);
/**
* Loadable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param type The config type.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
@NotNull ConfigType type,
boolean requiresChangesToSave);
/**
* Create config.
*
* @param config The handle.
* @return The config implementation.
*/
Config createConfig(@NotNull ConfigurationSection config);
/**
* Create config.
*
* @param values The values.
* @param type The config type.
* @return The config implementation.
*/
Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type);
/**
* Create config.
*
* @param contents The file contents.
* @param type The type.
* @return The config implementation.
*/
Config createConfig(@NotNull String contents,
@NotNull ConfigType type);
}

View File

@@ -87,7 +87,7 @@ public interface ExtendedPersistentDataContainer {
* @return The extended container. * @return The extended container.
*/ */
static ExtendedPersistentDataContainer extend(@NotNull PersistentDataContainer base) { static ExtendedPersistentDataContainer extend(@NotNull PersistentDataContainer base) {
return Eco.getHandler().adaptPdc(base); return Eco.get().adaptPdc(base);
} }
/** /**
@@ -96,6 +96,6 @@ public interface ExtendedPersistentDataContainer {
* @return The extended container. * @return The extended container.
*/ */
static ExtendedPersistentDataContainer create() { static ExtendedPersistentDataContainer create() {
return extend(Eco.getHandler().newPdc()); return extend(Eco.get().newPdc());
} }
} }

View File

@@ -31,6 +31,6 @@ public interface PlayerProfile extends Profile {
*/ */
@NotNull @NotNull
static PlayerProfile load(@NotNull final UUID uuid) { static PlayerProfile load(@NotNull final UUID uuid) {
return Eco.getHandler().getProfileHandler().load(uuid); return Eco.get().loadPlayerProfile(uuid);
} }
} }

View File

@@ -1,94 +0,0 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.UUID;
/**
* API to handle profiles.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface ProfileHandler {
/**
* Load a player profile.
*
* @param uuid The UUID.
* @return The profile.
*/
PlayerProfile load(@NotNull UUID uuid);
/**
* Load the server profile.
*
* @return The profile.
*/
ServerProfile loadServerProfile();
/**
* Unload a player profile from memory.
* <p>
* This will not save the profile first.
*
* @param uuid The uuid.
*/
void unloadPlayer(@NotNull UUID uuid);
/**
* Save a player profile.
* <p>
* Can run async if using MySQL.
*
* @param uuid The uuid.
* @deprecated Saving changes is faster and should be used. Saving a player manually is not recommended.
*/
@Deprecated
default void savePlayer(@NotNull UUID uuid) {
this.saveKeysFor(uuid, PersistentDataKey.values());
}
/**
* Save keys for a player.
* <p>
* Can run async if using MySQL.
*
* @param uuid The uuid.
* @param keys The keys.
*/
void saveKeysFor(@NotNull UUID uuid,
@NotNull Set<PersistentDataKey<?>> keys);
/**
* Save all player data.
*
* @param async If the saving should be done asynchronously.
* @deprecated async is now handled automatically depending on implementation.
*/
@Deprecated(forRemoval = true)
default void saveAll(boolean async) {
saveAll();
}
/**
* Save all player data.
* <p>
* Can run async if using MySQL.
*
* @deprecated Never used.
*/
@Deprecated(since = "6.36.0", forRemoval = true)
default void saveAll() {
// Do nothing.
}
/**
* Commit all changes to the file.
* <p>
* Does nothing if using MySQL.
*/
void save();
}

View File

@@ -16,6 +16,6 @@ public interface ServerProfile extends Profile {
*/ */
@NotNull @NotNull
static ServerProfile load() { static ServerProfile load() {
return Eco.getHandler().getProfileHandler().loadServerProfile(); return Eco.get().getServerProfile();
} }
} }

View File

@@ -1,39 +0,0 @@
package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.Eco;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
/**
* API to register persistent data keys.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface KeyRegistry {
/**
* Register a persistent data key to be stored.
*
* @param key The key.
*/
void registerKey(@NotNull PersistentDataKey<?> key);
/**
* Get all registered keys.
*
* @return The keys.
*/
Set<PersistentDataKey<?>> getRegisteredKeys();
/**
* Get persistent data key from namespaced key.
*
* @param namespacedKey The key.
* @return The key, or null if not found.
*/
@Nullable
PersistentDataKey<?> getKeyFrom(@NotNull NamespacedKey namespacedKey);
}

View File

@@ -43,7 +43,7 @@ public final class PersistentDataKey<T> {
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
this.type = type; this.type = type;
Eco.getHandler().getKeyRegistry().registerKey(this); Eco.get().registerPersistentKey(this);
} }
@Override @Override
@@ -126,7 +126,7 @@ public final class PersistentDataKey<T> {
* @return The keys. * @return The keys.
*/ */
public static Set<PersistentDataKey<?>> values() { public static Set<PersistentDataKey<?>> values() {
return Eco.getHandler().getKeyRegistry().getRegisteredKeys(); return Eco.get().getRegisteredPersistentDataKeys();
} }
@Override @Override
@@ -134,7 +134,7 @@ public final class PersistentDataKey<T> {
if (this == o) { if (this == o) {
return true; return true;
} }
if (!(o instanceof PersistentDataKey that)) { if (!(o instanceof PersistentDataKey<?> that)) {
return false; return false;
} }
return Objects.equals(this.getKey(), that.getKey()); return Objects.equals(this.getKey(), that.getKey());

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.data.keys; package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.config.interfaces.Config;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -43,6 +44,11 @@ public final class PersistentDataKeyType<T> {
*/ */
public static final PersistentDataKeyType<List<String>> STRING_LIST = new PersistentDataKeyType<>(null, "STRING_LIST"); public static final PersistentDataKeyType<List<String>> STRING_LIST = new PersistentDataKeyType<>(null, "STRING_LIST");
/**
* Config.
*/
public static final PersistentDataKeyType<Config> CONFIG = new PersistentDataKeyType<>(Config.class, "CONFIG");
/** /**
* The class of the type. * The class of the type.
*/ */
@@ -93,7 +99,7 @@ public final class PersistentDataKeyType<T> {
if (this == that) { if (this == that) {
return true; return true;
} }
if (!(that instanceof PersistentDataKeyType type)) { if (!(that instanceof PersistentDataKeyType<?> type)) {
return false; return false;
} }
return Objects.equals(this.name, type.name); return Objects.equals(this.name, type.name);

View File

@@ -1,11 +1,21 @@
package com.willfp.eco.core.display; package com.willfp.eco.core.display;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/** /**
* Utility class to manage client-side item display. * Utility class to manage client-side item display.
*/ */
@@ -16,9 +26,14 @@ public final class Display {
public static final String PREFIX = "§z"; public static final String PREFIX = "§z";
/** /**
* The display handler. * All registered modules.
*/ */
private static DisplayHandler handler = null; private static final Map<Integer, List<DisplayModule>> REGISTERED_MODULES = new TreeMap<>();
/**
* The finalize key.
*/
private static final NamespacedKey FINALIZE_KEY = NamespacedKeyUtils.createEcoKey("finalized");
/** /**
* Display on ItemStacks. * Display on ItemStacks.
@@ -39,7 +54,49 @@ public final class Display {
*/ */
public static ItemStack display(@NotNull final ItemStack itemStack, public static ItemStack display(@NotNull final ItemStack itemStack,
@Nullable final Player player) { @Nullable final Player player) {
return handler.display(itemStack, player); Map<String, Object[]> pluginVarArgs = new HashMap<>();
for (List<DisplayModule> modules : REGISTERED_MODULES.values()) {
for (DisplayModule module : modules) {
pluginVarArgs.put(module.getPluginName(), module.generateVarArgs(itemStack));
}
}
Display.revert(itemStack);
if (!itemStack.hasItemMeta()) {
return itemStack;
}
ItemStack original = itemStack.clone();
Inventory inventory = player == null ? null : player.getOpenInventory().getTopInventory();
boolean inInventory = inventory != null && inventory.contains(original);
boolean inGui = inventory != null && inventory.getHolder() == null;
DisplayProperties properties = new DisplayProperties(
inInventory,
inGui,
original
);
for (List<DisplayModule> modules : REGISTERED_MODULES.values()) {
for (DisplayModule module : modules) {
Object[] varargs = pluginVarArgs.get(module.getPluginName());
if (varargs == null) {
continue;
}
module.display(itemStack, varargs);
if (player != null) {
module.display(itemStack, player, varargs);
module.display(itemStack, player, properties, varargs);
}
}
}
return itemStack;
} }
/** /**
@@ -71,7 +128,25 @@ public final class Display {
* @return The ItemStack. * @return The ItemStack.
*/ */
public static ItemStack revert(@NotNull final ItemStack itemStack) { public static ItemStack revert(@NotNull final ItemStack itemStack) {
return handler.revert(itemStack); if (Display.isFinalized(itemStack)) {
Display.unfinalize(itemStack);
}
FastItemStack fast = FastItemStack.wrap(itemStack);
List<String> lore = fast.getLore();
if (!lore.isEmpty() && lore.removeIf(line -> line.startsWith(Display.PREFIX))) {
fast.setLore(lore);
}
for (List<DisplayModule> modules : REGISTERED_MODULES.values()) {
for (DisplayModule module : modules) {
module.revert(itemStack);
}
}
return itemStack;
} }
/** /**
@@ -81,7 +156,15 @@ public final class Display {
* @return The ItemStack. * @return The ItemStack.
*/ */
public static ItemStack finalize(@NotNull final ItemStack itemStack) { public static ItemStack finalize(@NotNull final ItemStack itemStack) {
return handler.finalize(itemStack); if (itemStack.getType().getMaxStackSize() > 1) {
return itemStack;
}
FastItemStack.wrap(itemStack)
.getPersistentDataContainer()
.set(FINALIZE_KEY, PersistentDataType.INTEGER, 1);
return itemStack;
} }
/** /**
@@ -91,7 +174,11 @@ public final class Display {
* @return The ItemStack. * @return The ItemStack.
*/ */
public static ItemStack unfinalize(@NotNull final ItemStack itemStack) { public static ItemStack unfinalize(@NotNull final ItemStack itemStack) {
return handler.unfinalize(itemStack); FastItemStack.wrap(itemStack)
.getPersistentDataContainer()
.remove(FINALIZE_KEY);
return itemStack;
} }
/** /**
@@ -101,7 +188,9 @@ public final class Display {
* @return If finalized. * @return If finalized.
*/ */
public static boolean isFinalized(@NotNull final ItemStack itemStack) { public static boolean isFinalized(@NotNull final ItemStack itemStack) {
return handler.isFinalized(itemStack); return FastItemStack.wrap(itemStack)
.getPersistentDataContainer()
.has(FINALIZE_KEY, PersistentDataType.INTEGER);
} }
/** /**
@@ -110,23 +199,15 @@ public final class Display {
* @param module The module. * @param module The module.
*/ */
public static void registerDisplayModule(@NotNull final DisplayModule module) { public static void registerDisplayModule(@NotNull final DisplayModule module) {
handler.registerDisplayModule(module); List<DisplayModule> modules = REGISTERED_MODULES.getOrDefault(
} module.getWeight(),
new ArrayList<>()
);
/** modules.removeIf(it -> it.getPluginName().equalsIgnoreCase(module.getPluginName()));
* Set the display handler. modules.add(module);
* <p>
* Internal API component, you will cause bugs if you create your own handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void setHandler(@NotNull final DisplayHandler handler) {
if (Display.handler != null) {
throw new IllegalStateException("Display already initialized!");
}
Display.handler = handler; REGISTERED_MODULES.put(module.getWeight(), modules);
} }
private Display() { private Display() {

View File

@@ -1,64 +0,0 @@
package com.willfp.eco.core.display;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface for display implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface DisplayHandler {
/**
* Register display module.
*
* @param module The module.
*/
void registerDisplayModule(@NotNull DisplayModule module);
/**
* Display on ItemStacks.
*
* @param itemStack The item.
* @param player The player.
* @return The ItemStack.
*/
ItemStack display(@NotNull ItemStack itemStack,
@Nullable Player player);
/**
* Revert on ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack revert(@NotNull ItemStack itemStack);
/**
* Finalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack finalize(@NotNull ItemStack itemStack);
/**
* Unfinalize an ItemStacks.
*
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack unfinalize(@NotNull ItemStack itemStack);
/**
* If an item is finalized.
*
* @param itemStack The item.
* @return If finalized.
*/
boolean isFinalized(@NotNull ItemStack itemStack);
}

View File

@@ -1,7 +1,6 @@
package com.willfp.eco.core.display; package com.willfp.eco.core.display;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -10,12 +9,17 @@ import org.jetbrains.annotations.Nullable;
/** /**
* Class for all plugin-specific client-side item display modules. * Class for all plugin-specific client-side item display modules.
*/ */
public abstract class DisplayModule extends PluginDependent<EcoPlugin> { public abstract class DisplayModule {
/** /**
* The priority of the module. * The priority of the module.
*/ */
private final int weight; private final int weight;
/**
* The plugin.
*/
private final EcoPlugin plugin;
/** /**
* Create a new display module. * Create a new display module.
* *
@@ -24,8 +28,7 @@ public abstract class DisplayModule extends PluginDependent<EcoPlugin> {
*/ */
protected DisplayModule(@NotNull final EcoPlugin plugin, protected DisplayModule(@NotNull final EcoPlugin plugin,
@NotNull final DisplayPriority priority) { @NotNull final DisplayPriority priority) {
super(plugin); this(plugin, priority.getWeight());
this.weight = priority.getWeight();
} }
/** /**
@@ -36,7 +39,7 @@ public abstract class DisplayModule extends PluginDependent<EcoPlugin> {
*/ */
protected DisplayModule(@NotNull final EcoPlugin plugin, protected DisplayModule(@NotNull final EcoPlugin plugin,
final int weight) { final int weight) {
super(plugin); this.plugin = plugin;
this.weight = weight; this.weight = weight;
} }
@@ -104,7 +107,7 @@ public abstract class DisplayModule extends PluginDependent<EcoPlugin> {
* @return The plugin name. * @return The plugin name.
*/ */
public final String getPluginName() { public final String getPluginName() {
return super.getPlugin().getName(); return this.getPlugin().getName();
} }
/** /**
@@ -132,4 +135,13 @@ public abstract class DisplayModule extends PluginDependent<EcoPlugin> {
public int getWeight() { public int getWeight() {
return this.weight; return this.weight;
} }
/**
* Get the plugin.
*
* @return The plugin.
*/
public EcoPlugin getPlugin() {
return plugin;
}
} }

View File

@@ -21,13 +21,25 @@ public class DropQueue {
/** /**
* The internally used {@link DropQueue}. * The internally used {@link DropQueue}.
*/ */
private final InternalDropQueue handle; private final DropQueue delegate;
/** /**
* Create a new DropQueue.
*
* @param player The player. * @param player The player.
*/ */
public DropQueue(@NotNull final Player player) { public DropQueue(@NotNull final Player player) {
handle = Eco.getHandler().getDropQueueFactory().create(player); this.delegate = Eco.get().createDropQueue(player);
}
/**
* Create a new DropQueue with no delegate.
* <p>
* Call this constructor if you're creating custom DropQueue
* implementations.
*/
protected DropQueue() {
this.delegate = null;
} }
/** /**
@@ -37,7 +49,11 @@ public class DropQueue {
* @return The DropQueue. * @return The DropQueue.
*/ */
public DropQueue addItem(@NotNull final ItemStack item) { public DropQueue addItem(@NotNull final ItemStack item) {
handle.addItem(item); if (delegate == null) {
return this;
}
delegate.addItem(item);
return this; return this;
} }
@@ -48,7 +64,11 @@ public class DropQueue {
* @return The DropQueue. * @return The DropQueue.
*/ */
public DropQueue addItems(@NotNull final Collection<ItemStack> itemStacks) { public DropQueue addItems(@NotNull final Collection<ItemStack> itemStacks) {
handle.addItems(itemStacks); if (delegate == null) {
return this;
}
delegate.addItems(itemStacks);
return this; return this;
} }
@@ -59,7 +79,11 @@ public class DropQueue {
* @return The DropQueue. * @return The DropQueue.
*/ */
public DropQueue addXP(final int amount) { public DropQueue addXP(final int amount) {
handle.addXP(amount); if (delegate == null) {
return this;
}
delegate.addXP(amount);
return this; return this;
} }
@@ -70,7 +94,11 @@ public class DropQueue {
* @return The DropQueue. * @return The DropQueue.
*/ */
public DropQueue setLocation(@NotNull final Location location) { public DropQueue setLocation(@NotNull final Location location) {
handle.setLocation(location); if (delegate == null) {
return this;
}
delegate.setLocation(location);
return this; return this;
} }
@@ -80,7 +108,11 @@ public class DropQueue {
* @return The DropQueue. * @return The DropQueue.
*/ */
public DropQueue forceTelekinesis() { public DropQueue forceTelekinesis() {
handle.forceTelekinesis(); if (delegate == null) {
return this;
}
delegate.forceTelekinesis();
return this; return this;
} }
@@ -88,6 +120,10 @@ public class DropQueue {
* Push the queue. * Push the queue.
*/ */
public void push() { public void push() {
handle.push(); if (delegate == null) {
return;
}
delegate.push();
} }
} }

View File

@@ -1,21 +0,0 @@
package com.willfp.eco.core.drops;
import com.willfp.eco.core.Eco;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Internal component to create backend DropQueue implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface DropQueueFactory {
/**
* Create a DropQueue.
*
* @param player The player.
* @return The Queue.
*/
InternalDropQueue create(@NotNull Player player);
}

View File

@@ -1,60 +0,0 @@
package com.willfp.eco.core.drops;
import com.willfp.eco.core.Eco;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Internal interface for backend DropQueue implementations.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface InternalDropQueue {
/**
* Add item to queue.
*
* @param item The item to add.
* @return The DropQueue.
*/
InternalDropQueue addItem(@NotNull ItemStack item);
/**
* Add multiple items to queue.
*
* @param itemStacks The items to add.
* @return The DropQueue.
*/
InternalDropQueue addItems(@NotNull Collection<ItemStack> itemStacks);
/**
* Add xp to queue.
*
* @param amount The amount to add.
* @return The DropQueue.
*/
InternalDropQueue addXP(int amount);
/**
* Set location of the origin of the drops.
*
* @param location The location.
* @return The DropQueue.
*/
InternalDropQueue setLocation(@NotNull Location location);
/**
* Force the queue to act as if player is telekinetic.
*
* @return The DropQueue.
*/
InternalDropQueue forceTelekinesis();
/**
* Push the queue.
*/
void push();
}

View File

@@ -101,6 +101,6 @@ public interface EntityController<T extends Mob> {
* @return The entity controller. * @return The entity controller.
*/ */
static <T extends Mob> EntityController<T> getFor(@NotNull final T entity) { static <T extends Mob> EntityController<T> getFor(@NotNull final T entity) {
return Eco.getHandler().createEntityController(entity); return Eco.get().createEntityController(entity);
} }
} }

View File

@@ -28,6 +28,6 @@ public class EmptyTestableEntity implements TestableEntity {
public Entity spawn(@NotNull final Location location) { public Entity spawn(@NotNull final Location location) {
Validate.notNull(location.getWorld()); Validate.notNull(location.getWorld());
return Eco.getHandler().createDummyEntity(location); return Eco.get().createDummyEntity(location);
} }
} }

View File

@@ -3,7 +3,7 @@ package com.willfp.eco.core.extensions;
import java.util.Set; import java.util.Set;
/** /**
* Internal component to manage loading and unloading extensions. * Manages the loading and unloading of extensions for a particular plugin.
*/ */
public interface ExtensionLoader { public interface ExtensionLoader {
/** /**

View File

@@ -72,6 +72,7 @@ public interface FastItemStack extends PersistentDataHolder {
* @deprecated Poorly named method. Use getEnchantmentLevel instead. * @deprecated Poorly named method. Use getEnchantmentLevel instead.
*/ */
@Deprecated(since = "6.34.0", forRemoval = true) @Deprecated(since = "6.34.0", forRemoval = true)
@SuppressWarnings("DeprecatedIsStillUsed")
default int getLevelOnItem(@NotNull Enchantment enchantment, default int getLevelOnItem(@NotNull Enchantment enchantment,
boolean checkStored) { boolean checkStored) {
return getEnchantmentLevel(enchantment, checkStored); return getEnchantmentLevel(enchantment, checkStored);
@@ -271,6 +272,6 @@ public interface FastItemStack extends PersistentDataHolder {
* @return The FastItemStack. * @return The FastItemStack.
*/ */
static FastItemStack wrap(@Nullable final ItemStack itemStack) { static FastItemStack wrap(@Nullable final ItemStack itemStack) {
return Eco.getHandler().createFastItemStack(Objects.requireNonNullElseGet(itemStack, () -> new ItemStack(Material.AIR))); return Eco.get().createFastItemStack(Objects.requireNonNullElseGet(itemStack, () -> new ItemStack(Material.AIR)));
} }
} }

View File

@@ -0,0 +1,82 @@
package com.willfp.eco.core.gui;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A GUI Component is a 2-dimensional set of slots that can be
* placed in a menu.
*/
public interface GUIComponent {
/**
* Get the amount of rows in the component.
*
* @return The rows.
*/
int getRows();
/**
* Get the amount of columns in the component.
*
* @return The columns.
*/
int getColumns();
/**
* Initialize the component.
* <p>
* This is called before getRows / getColumns is queried,
* and allows for dynamically sized components.
* <p>
* getRows and getColumns can return values bigger than this,
* it will simply prevent the component from being added at
* this position (for minimum-sized components).
*
* @param maxRows The maximum number of rows.
* @param maxColumns The maximum number of columns.
*/
default void init(final int maxRows,
final int maxColumns) {
// Most components will not require initialization.
}
/**
* Get the slot at a certain position in the component.
* <p>
* It's safe to assume to the row and column will always be in bounds.
*
* @param row The row (1-indexed).
* @param column The column (1-indexed).
* @return The slot, or null if no slot at the location.
*/
@Nullable
default Slot getSlotAt(final int row,
final int column) {
return null;
}
/**
* Get the slot at a certain position in the component.
* <p>
* If your component doesn't use context data (player, menu),
* then it will default to the raw slot.
* <p>
* It's safe to assume to the row and column will always be in bounds.
*
* @param row The row (1-indexed).
* @param column The column (1-indexed).
* @param player The player.
* @param menu The menu.
* @return The slot, or null if no slot at the location.
*/
@Nullable
default Slot getSlotAt(final int row,
final int column,
@NotNull final Player player,
@NotNull final Menu menu) {
return getSlotAt(row, column);
}
}

View File

@@ -1,33 +0,0 @@
package com.willfp.eco.core.gui;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.menu.MenuBuilder;
import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Internal component used by {@link com.willfp.eco.core.gui.menu.Menu#builder(int)}
* and {@link com.willfp.eco.core.gui.slot.Slot#builder(ItemStack)}.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface GUIFactory {
/**
* Create slot builder.
*
* @param provider The provider.
* @return The builder.
*/
SlotBuilder createSlotBuilder(@NotNull SlotProvider provider);
/**
* Create menu builder.
*
* @param rows The amount of rows.
* @return The builder.
*/
MenuBuilder createMenuBuilder(int rows);
}

View File

@@ -1,7 +1,9 @@
package com.willfp.eco.core.gui.menu; package com.willfp.eco.core.gui.menu;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.page.Page;
import com.willfp.eco.core.gui.slot.Slot; import com.willfp.eco.core.gui.slot.Slot;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
@@ -12,7 +14,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* GUI version of {@link Inventory}. * GUI version of {@link Inventory}.
@@ -28,7 +32,19 @@ public interface Menu {
int getRows(); int getRows();
/** /**
* Get slot at given row and column. * Get the amount of columns.
*
* @return The amount of columns.
*/
default int getColumns() {
return 9;
}
/**
* Get a static slot at a given row and column.
* <p>
* If the slot at the location is reactive, this will return
* an empty slot.
* *
* @param row The row. * @param row The row.
* @param column The column. * @param column The column.
@@ -37,6 +53,42 @@ public interface Menu {
Slot getSlot(int row, Slot getSlot(int row,
int column); int column);
/**
* Get a slot at a given row and column.
* <p>
* Defaults to static slot if no reactive slot exists.
*
* @param row The row.
* @param column The column.
* @param player The player
* @param menu The menu.
* @return The slot.
* @deprecated Menu shouldn't be a parameter.
*/
@Deprecated(since = "6.46.0", forRemoval = true)
default Slot getSlot(final int row,
final int column,
@NotNull final Player player,
@NotNull final Menu menu) {
return this.getSlot(row, column, player);
}
/**
* Get a slot at a given row and column.
* <p>
* Defaults to static slot if no reactive slot exists.
*
* @param row The row.
* @param column The column.
* @param player The player
* @return The slot.
*/
default Slot getSlot(final int row,
final int column,
@NotNull final Player player) {
return this.getSlot(row, column);
}
/** /**
* Get the menu title. * Get the menu title.
* *
@@ -61,15 +113,47 @@ public interface Menu {
List<ItemStack> getCaptiveItems(@NotNull Player player); List<ItemStack> getCaptiveItems(@NotNull Player player);
/** /**
* Add state for a player. * Get a captive item at a specific position.
*
* @param player The player.
* @param row The row.
* @param column The column.
* @return The captive item.
*/
@Nullable
default ItemStack getCaptiveItem(@NotNull final Player player,
final int row,
final int column) {
return null;
}
/**
* Set state for a player.
* *
* @param player The player. * @param player The player.
* @param key The key. * @param key The key.
* @param value The state. * @param value The state.
*/ */
void addState(@NotNull Player player, default void setState(@NotNull Player player,
@NotNull String key, @NotNull String key,
@Nullable Object value); @Nullable Object value) {
// Blank method for backwards compatibility.
}
/**
* Add state for a player.
*
* @param player The player.
* @param key The key.
* @param value The state.
* @deprecated Poorly named, use setState instead.
*/
@Deprecated(since = "6.44.0", forRemoval = true)
default void addState(@NotNull Player player,
@NotNull String key,
@Nullable Object value) {
this.setState(player, key, value);
}
/** /**
* Remove state for a player. * Remove state for a player.
@@ -107,6 +191,55 @@ public interface Menu {
*/ */
Map<String, Object> getState(@NotNull Player player); Map<String, Object> getState(@NotNull Player player);
/**
* Re-render the menu for a player.
*
* @param player The player.
*/
void refresh(@NotNull Player player);
/**
* If the menu allows changing the held item.
*
* @return If allowed.
*/
default boolean allowsChangingHeldItem() {
return false;
}
/**
* Call a menu event.
*
* @param player The player.
* @param menuEvent The event.
*/
default void callEvent(@NotNull final Player player,
@NotNull final MenuEvent menuEvent) {
// Override when needed.
}
/**
* Get the current page a player is on.
*
* @param player The player.
* @return The page.
*/
default int getPage(@NotNull final Player player) {
Integer pageState = this.getState(player, Page.PAGE_KEY);
return Objects.requireNonNullElse(pageState, 1);
}
/**
* Get the max page for a player.
*
* @param player The player.
* @return The page.
*/
default int getMaxPage(@NotNull final Player player) {
Integer pageState = this.getState(player, Page.MAX_PAGE_KEY);
return Objects.requireNonNullElse(pageState, Integer.MAX_VALUE);
}
/** /**
* Write data. * Write data.
* *
@@ -119,10 +252,12 @@ public interface Menu {
* @deprecated Use addState instead. * @deprecated Use addState instead.
*/ */
@Deprecated(since = "6.35.0", forRemoval = true) @Deprecated(since = "6.35.0", forRemoval = true)
<T, Z> void writeData(@NotNull Player player, default <T, Z> void writeData(@NotNull final Player player,
@NotNull NamespacedKey key, @NotNull final NamespacedKey key,
@NotNull PersistentDataType<T, Z> type, @NotNull final PersistentDataType<T, Z> type,
@NotNull Z value); @NotNull final Z value) {
this.setState(player, key.toString(), value);
}
/** /**
* Read data. * Read data.
@@ -136,9 +271,11 @@ public interface Menu {
* @deprecated Use getState instead. * @deprecated Use getState instead.
*/ */
@Deprecated(since = "6.35.0", forRemoval = true) @Deprecated(since = "6.35.0", forRemoval = true)
@Nullable <T, Z> T readData(@NotNull Player player, default @Nullable <T, Z> T readData(@NotNull final Player player,
@NotNull NamespacedKey key, @NotNull final NamespacedKey key,
@NotNull PersistentDataType<T, Z> type); @NotNull final PersistentDataType<T, Z> type) {
return this.getState(player, key.toString());
}
/** /**
* Get all data keys for a player. * Get all data keys for a player.
@@ -148,14 +285,12 @@ public interface Menu {
* @deprecated Use getState instead. * @deprecated Use getState instead.
*/ */
@Deprecated(since = "6.35.0", forRemoval = true) @Deprecated(since = "6.35.0", forRemoval = true)
Set<NamespacedKey> getKeys(@NotNull Player player); default Set<NamespacedKey> getKeys(@NotNull final Player player) {
return this.getState(player).keySet().stream()
/** .map(NamespacedKeyUtils::fromStringOrNull)
* Re-render the menu for a player. .filter(Objects::nonNull)
* .collect(Collectors.toSet());
* @param player The player. }
*/
void refresh(@NotNull Player player);
/** /**
* Create a builder with a given amount of rows. * Create a builder with a given amount of rows.
@@ -164,6 +299,19 @@ public interface Menu {
* @return The builder. * @return The builder.
*/ */
static MenuBuilder builder(final int rows) { static MenuBuilder builder(final int rows) {
return Eco.getHandler().getGUIFactory().createMenuBuilder(rows); return Eco.get().createMenuBuilder(
rows,
MenuType.NORMAL
);
}
/**
* Create a builder with a given type.
*
* @param type The menu type.
* @return The builder.
*/
static MenuBuilder builder(@NotNull final MenuType type) {
return Eco.get().createMenuBuilder(type.getDefaultRows(), type);
} }
} }

View File

@@ -1,5 +1,8 @@
package com.willfp.eco.core.gui.menu; package com.willfp.eco.core.gui.menu;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.gui.page.Page;
import com.willfp.eco.core.gui.page.PageBuilder;
import com.willfp.eco.core.gui.slot.FillerMask; import com.willfp.eco.core.gui.slot.FillerMask;
import com.willfp.eco.core.gui.slot.Slot; import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -8,11 +11,12 @@ import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
/** /**
* Builder to create menus. * Builder to create menus.
*/ */
public interface MenuBuilder { public interface MenuBuilder extends PageBuilder {
/** /**
* Set the menu title. * Set the menu title.
* *
@@ -21,6 +25,15 @@ public interface MenuBuilder {
*/ */
MenuBuilder setTitle(@NotNull String title); MenuBuilder setTitle(@NotNull String title);
/**
* Get the menu title.
*
* @return The builder.
*/
default String getTitle() {
return "";
}
/** /**
* Set a slot. * Set a slot.
* *
@@ -29,9 +42,44 @@ public interface MenuBuilder {
* @param slot The slot. * @param slot The slot.
* @return The builder. * @return The builder.
*/ */
MenuBuilder setSlot(int row, @Override
default MenuBuilder setSlot(final int row,
final int column,
@NotNull final Slot slot) {
return this.addComponent(row, column, slot);
}
/**
* Add a component.
*
* @param layer The layer.
* @param row The row of the top left corner.
* @param column The column of the top left corner.
* @param component The component.
* @return The builder.
*/
@Override
MenuBuilder addComponent(@NotNull MenuLayer layer,
int row,
int column, int column,
@NotNull Slot slot); @NotNull GUIComponent component);
/**
* Add a component.
*
* @param row The row of the top left corner.
* @param column The column of the top left corner.
* @param component The component.
* @return The builder.
*/
@Override
default MenuBuilder addComponent(final int row,
final int column,
@NotNull final GUIComponent component) {
return this.addComponent(MenuLayer.MIDDLE, row, column, component);
}
/** /**
* Run function to modify the builder. * Run function to modify the builder.
@@ -47,21 +95,65 @@ public interface MenuBuilder {
* @param mask The mask. * @param mask The mask.
* @return The builder. * @return The builder.
*/ */
MenuBuilder setMask(@NotNull FillerMask mask); @Override
default MenuBuilder setMask(@NotNull final FillerMask mask) {
return this.addComponent(MenuLayer.BACKGROUND, 1, 1, mask);
}
/** /**
* Set the menu close handler. * Add a page.
*
* @param page The page.
* @return The builder.
*/
default MenuBuilder addPage(@NotNull final Page page) {
return this.addComponent(MenuLayer.UPPER, 1, 1, page);
}
/**
* Add a page.
*
* @param pageNumber The page number.
* @param builder The page builder.
* @return The builder.
*/
default MenuBuilder addPage(final int pageNumber,
@NotNull final PageBuilder builder) {
return this.addPage(new Page(pageNumber, ((MenuBuilder) builder).build()));
}
/**
* Set the max pages.
*
* @param pages The max pages.
* @return The builder.
*/
default MenuBuilder maxPages(final int pages) {
return this.maxPages(player -> pages);
}
/**
* Set the max pages dynamically for a player.
*
* @param pages The max pages.
* @return The builder.
*/
default MenuBuilder maxPages(@NotNull final Function<Player, Integer> pages) {
return this.onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
}
/**
* Add a menu close handler.
* *
* @param action The handler. * @param action The handler.
* @return The builder. * @return The builder.
*/ */
default MenuBuilder onClose(@NotNull Consumer<InventoryCloseEvent> action) { default MenuBuilder onClose(@NotNull final Consumer<InventoryCloseEvent> action) {
onClose((event, menu) -> action.accept(event)); return this.onClose((event, menu) -> action.accept(event));
return this;
} }
/** /**
* Set the menu close handler. * Add a menu close handler.
* *
* @param action The handler. * @param action The handler.
* @return The builder. * @return The builder.
@@ -69,7 +161,7 @@ public interface MenuBuilder {
MenuBuilder onClose(@NotNull CloseHandler action); MenuBuilder onClose(@NotNull CloseHandler action);
/** /**
* Set the menu open handler. * Add a menu open handler.
* *
* @param action The handler. * @param action The handler.
* @return The builder. * @return The builder.
@@ -77,13 +169,42 @@ public interface MenuBuilder {
MenuBuilder onOpen(@NotNull OpenHandler action); MenuBuilder onOpen(@NotNull OpenHandler action);
/** /**
* Set the action to run on render. * Add an action to run on render.
* *
* @param action The action. * @param action The action.
* @return The builder. * @return The builder.
*/ */
MenuBuilder onRender(@NotNull BiConsumer<Player, Menu> action); MenuBuilder onRender(@NotNull BiConsumer<Player, Menu> action);
/**
* Add an action to run on an event.
*
* @param action The action.
* @return The builder.
*/
default MenuBuilder onEvent(@NotNull final MenuEventHandler<?> action) {
return this;
}
/**
* Allow the player to change their held item.
*
* @return The builder.
*/
default MenuBuilder allowChangingHeldItem() {
return this;
}
/**
* Add an action to run on build.
*
* @param action The action.
* @return The builder.
*/
default MenuBuilder onBuild(@NotNull Consumer<Menu> action) {
return this;
}
/** /**
* Build the menu. * Build the menu.
* *

View File

@@ -0,0 +1,8 @@
package com.willfp.eco.core.gui.menu;
/**
* Represents an event sent to a menu.
*/
public interface MenuEvent {
}

View File

@@ -0,0 +1,45 @@
package com.willfp.eco.core.gui.menu;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Handles menu events.
*/
public abstract class MenuEventHandler<T extends MenuEvent> {
/**
* The class of the event.
*/
private final Class<T> eventClass;
/**
* Create a new menu event handler.
*
* @param eventClass The class of event to handle.
*/
protected MenuEventHandler(@NotNull final Class<T> eventClass) {
this.eventClass = eventClass;
}
/**
* Performs this operation on the given arguments.
*
* @param player The player.
* @param menu The menu.
* @param event The event.
*/
public abstract void handle(@NotNull Player player,
@NotNull Menu menu,
@NotNull T event);
/**
* Get if this handler can handle a certain event.
*
* @param menuEvent The event
* @return If the event can be handled.
*/
public boolean canHandleEvent(@NotNull final MenuEvent menuEvent) {
return eventClass.isAssignableFrom(menuEvent.getClass());
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.core.gui.menu;
/**
* Different layers of the menu.
*/
public enum MenuLayer {
/**
* Right at the back.
*/
BACKGROUND,
/**
* Second from the back.
*/
LOWER,
/**
* In the middle (default).
*/
MIDDLE,
/**
* Near the top.
*/
UPPER,
/**
* At the absolute top.
*/
TOP
}

View File

@@ -0,0 +1,56 @@
package com.willfp.eco.core.gui.menu;
/**
* The type of menu.
*/
public enum MenuType {
/**
* Normal menu (1x9, 2x9, 3x9, etc).
*/
NORMAL(9, 6),
/**
* Dispenser menu (3x3).
*/
DISPENSER(3, 3);
/**
* The amount of columns.
*/
private final int columns;
/**
* The default amount of rows.
*/
private final int defaultRows;
/**
* Create a new menu type.
*
* @param columns The number of columns.
* @param defaultRows The default number of rows.
*/
MenuType(final int columns,
final int defaultRows) {
this.columns = columns;
this.defaultRows = defaultRows;
}
/**
* Get the amount of columns.
*
* @return The columns.
*/
public int getColumns() {
return columns;
}
/**
* Get the default amount of rows.
*
* @return The default amount of rows.
*/
public int getDefaultRows() {
return defaultRows;
}
}

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.gui.menu.events;
import com.willfp.eco.core.gui.menu.MenuEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
/**
* Represents a captive item change.
*
* @param row The row.
* @param column The column.
* @param before The previous item in the slot.
* @param after The new item in the slot.
*/
public record CaptiveItemChangeEvent(
int row,
int column,
@Nullable ItemStack before,
@Nullable ItemStack after
) implements MenuEvent {
}

View File

@@ -0,0 +1,115 @@
package com.willfp.eco.core.gui.page;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.menu.MenuBuilder;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A page is a component representing another menu.
* This allows full component support in pagination.
*/
public final class Page implements GUIComponent {
/**
* The Menu state key for the current page.
*/
public static final String PAGE_KEY = "page";
/**
* The Menu state key for the amount of pages.
*/
public static final String MAX_PAGE_KEY = "max_page";
/**
* The page number.
*/
private final int pageNumber;
/**
* The base menu.
*/
private final Menu page;
/**
* The delegate menu.
*/
private Menu delegate = null;
/**
* The rows for the page to have.
*/
private int rows = 6;
/**
* The columns for the page to have.
*/
private int columns = 9;
/**
* Create a new page.
*
* @param pageNumber The page number.
* @param page The base menu.
*/
public Page(final int pageNumber,
@NotNull final Menu page) {
this.pageNumber = pageNumber;
this.page = page;
}
/**
* Get the current page number.
*
* @return The page number.
*/
public int getPageNumber() {
return this.pageNumber;
}
@Override
public @Nullable Slot getSlotAt(final int row,
final int column,
@NotNull final Player player,
@NotNull final Menu menu) {
if (menu.getPage(player) != pageNumber) {
return null;
}
if (delegate == null) {
delegate = Eco.get().blendMenuState(page, menu);
}
return page.getSlot(row, column, player);
}
@Override
public void init(final int maxRows,
final int maxColumns) {
this.rows = maxRows;
this.columns = maxColumns;
}
@Override
public int getRows() {
return rows;
}
@Override
public int getColumns() {
return columns;
}
/**
* Create a new page builder.
*
* @param context The context to create the page for.
* @return The page builder.
*/
public static PageBuilder builder(@NotNull final MenuBuilder context) {
return Menu.builder(context.getRows());
}
}

View File

@@ -0,0 +1,92 @@
package com.willfp.eco.core.gui.page;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.menu.MenuLayer;
import com.willfp.eco.core.gui.slot.FillerMask;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
/**
* Builder to create pages.
*/
public interface PageBuilder {
/**
* Get the amount of rows.
*
* @return The amount of rows.
*/
int getRows();
/**
* Get the amount of columns.
*
* @return The amount of columns.
*/
int getColumns();
/**
* Set a slot.
*
* @param row The row.
* @param column The column.
* @param slot The slot.
* @return The builder.
*/
default PageBuilder setSlot(final int row,
final int column,
@NotNull final Slot slot) {
return this.addComponent(row, column, slot);
}
/**
* Add a component.
*
* @param layer The layer.
* @param row The row of the top left corner.
* @param column The column of the top left corner.
* @param component The component.
* @return The builder.
*/
PageBuilder addComponent(@NotNull MenuLayer layer,
int row,
int column,
@NotNull GUIComponent component);
/**
* Add a component.
*
* @param row The row of the top left corner.
* @param column The column of the top left corner.
* @param component The component.
* @return The builder.
*/
default PageBuilder addComponent(final int row,
final int column,
@NotNull final GUIComponent component) {
return this.addComponent(MenuLayer.MIDDLE, row, column, component);
}
/**
* Set the menu mask.
*
* @param mask The mask.
* @return The builder.
*/
default PageBuilder setMask(@NotNull final FillerMask mask) {
return this.addComponent(MenuLayer.BACKGROUND, 1, 1, mask);
}
/**
* Set the action to run on render.
*
* @param action The action.
* @return The builder.
*/
PageBuilder onRender(@NotNull BiConsumer<Player, Menu> action);
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.core.gui.page;
import com.willfp.eco.core.gui.menu.MenuEvent;
/**
* Represents a page change.
*
* @param newPage The new page.
* @param oldPage The old page.
*/
public record PageChangeEvent(
int newPage,
int oldPage
) implements MenuEvent {
}

View File

@@ -0,0 +1,126 @@
package com.willfp.eco.core.gui.page;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.slot.Slot;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A slot loaded in from config.
*/
public final class PageChanger implements GUIComponent {
/**
* The slot to be shown.
*/
private final Slot slot;
/**
* The direction to turn the page.
*/
private final Direction direction;
/**
* Create a new page change slot.
*
* @param itemStack The ItemStack.
* @param direction The direction.
*/
public PageChanger(@NotNull final ItemStack itemStack,
@NotNull final Direction direction) {
this.direction = direction;
slot = Slot.builder(itemStack)
.onLeftClick((event, slot, menu) -> {
Player player = (Player) event.getWhoClicked();
int page = menu.getPage(player);
int newPage = Math.max(
1,
Math.min(
page + direction.getChange(),
menu.getMaxPage(player)
)
);
if (newPage == page) {
return;
}
menu.setState(player, Page.PAGE_KEY, newPage);
menu.callEvent(player, new PageChangeEvent(
newPage,
page
));
})
.build();
}
@Override
public int getRows() {
return 1;
}
@Override
public int getColumns() {
return 1;
}
@Override
public @Nullable Slot getSlotAt(final int row,
final int column,
@NotNull final Player player,
@NotNull final Menu menu) {
int page = menu.getPage(player);
int maxPage = menu.getMaxPage(player);
if (page <= 1 && this.direction == Direction.BACKWARDS) {
return null;
}
if (page >= maxPage && this.direction == Direction.FORWARDS) {
return null;
}
return slot;
}
/**
* The direction to change the page.
*/
public enum Direction {
/**
* Increment the page by 1.
*/
FORWARDS(1),
/**
* Decrement the page by 1.
*/
BACKWARDS(-1);
/**
* The amount of pages to change by.
*/
private final int change;
/**
* Create a new direction.
*
* @param change The amount of pages to change by.
*/
Direction(final int change) {
this.change = change;
}
/**
* Get the amount of pages to change by.
*
* @return The change.
*/
public int getChange() {
return change;
}
}
}

View File

@@ -0,0 +1,143 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.gui.slot.functional.SlotHandler;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* A slot loaded in from config.
*/
public class ConfigSlot extends CustomSlot {
/**
* The config of the slot.
*/
private final Config config;
/**
* Cached handlers, for performance.
*/
private final Map<String, List<CommandToDispatch>> handlers = new HashMap<>();
/**
* Create a new config slot.
*
* @param config The config.
*/
public ConfigSlot(@NotNull final Config config) {
this.config = config;
ItemStack item = Items.lookup(config.getString("item")).getItem();
SlotBuilder builder = Slot.builder((player, menu) -> {
if (!config.has("lore")) {
return item;
} else {
FastItemStack fast = FastItemStack.wrap(item.clone());
List<String> newLore = new ArrayList<>(fast.getLore());
newLore.addAll(
StringUtils.formatList(
config.getStrings("lore"),
player,
StringUtils.FormatOption.WITH_PLACEHOLDERS
)
);
fast.setLore(newLore);
return fast.unwrap();
}
});
for (ClickType clickType : ClickType.values()) {
builder.onClick(
clickType,
dispatchCommandHandler(
clickType.name().toLowerCase(Locale.ROOT)
.replace("_", "-")
+ "-click"
)
);
}
init(builder.build());
}
/**
* Create a slot handler for dispatching commands.
*
* @param configKey The config key.
* @return The handler.
*/
private SlotHandler dispatchCommandHandler(@NotNull final String configKey) {
if (!handlers.containsKey(configKey)) {
List<CommandToDispatch> commands = new ArrayList<>();
for (String command : config.getStrings(configKey)) {
if (command.startsWith("console:")) {
commands.add(new CommandToDispatch(
StringUtils.removePrefix("console:", command),
true
));
} else {
commands.add(new CommandToDispatch(
command,
false
));
}
}
handlers.put(configKey, commands);
}
List<CommandToDispatch> toDispatch = handlers.get(configKey);
return (event, slot, menu) -> {
Player player = (Player) event.getWhoClicked();
for (CommandToDispatch dispatch : toDispatch) {
dispatch.dispatch(player);
}
};
}
/**
* Signifies a command to dispatch.
*
* @param command The command.
* @param console If the command should be run as console.
*/
private record CommandToDispatch(
@NotNull String command,
boolean console
) {
/**
* Dispatch command.
*
* @param player The player.
*/
void dispatch(@NotNull final Player player) {
if (console()) {
Bukkit.dispatchCommand(
Bukkit.getConsoleSender(),
command().replace("%player%", player.getName())
);
} else {
Bukkit.dispatchCommand(
player,
command().replace("%player%", player.getName())
);
}
}
}
}

View File

@@ -0,0 +1,106 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Base class for custom slot implementations.
*/
public abstract class CustomSlot implements Slot {
/**
* The internal slot to delegate to.
*/
private Slot delegate = null;
/**
* Create a new custom slot.
*/
protected CustomSlot() {
}
/**
* Initialize the slot with the delegate.
*
* @param slot The slot to delegate to.
*/
protected void init(@NotNull final Slot slot) {
this.delegate = slot;
}
@Override
public final @NotNull ItemStack getItemStack(@NotNull final Player player) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.getItemStack(player);
}
@Override
public final boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.isCaptive(player, menu);
}
@Override
public final boolean isAllowedCaptive(@NotNull final Player player,
@NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.isAllowedCaptive(player, menu, itemStack);
}
@Override
public final boolean isCaptiveFromEmpty() {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.isCaptiveFromEmpty();
}
@Override
public final @NotNull Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return delegate;
}
@Override
public final int getRows() {
return Slot.super.getRows();
}
@Override
public final int getColumns() {
return Slot.super.getColumns();
}
@Override
public final Slot getSlotAt(int row, int column) {
return Slot.super.getSlotAt(row, column);
}
/**
* Get the delegate slot.
* <p>
* This is not required to add the slot to a menu, but is instead used internally.
*
* @return The slot.
* @deprecated Replaced with {@link Slot#getActionableSlot(Player, Menu)}
*/
@Deprecated(since = "6.43.0", forRemoval = true)
public Slot getDelegate() {
return this.delegate;
}
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.gui.slot; package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.items.builder.ItemStackBuilder; import com.willfp.eco.core.items.builder.ItemStackBuilder;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem; import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.MaterialTestableItem; import com.willfp.eco.core.recipe.parts.MaterialTestableItem;
@@ -7,6 +8,7 @@ import com.willfp.eco.util.ListUtils;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -26,12 +28,17 @@ import java.util.List;
* "11111111" * "11111111"
* ); * );
*/ */
public class FillerMask { public class FillerMask implements GUIComponent {
/** /**
* Mask. * Mask.
*/ */
private final List<List<Slot>> mask; private final List<List<Slot>> mask;
/**
* Rows.
*/
private final int rows;
/** /**
* Create a new filler mask. * Create a new filler mask.
* *
@@ -71,7 +78,8 @@ public class FillerMask {
throw new IllegalArgumentException("Items cannot be empty!"); throw new IllegalArgumentException("Items cannot be empty!");
} }
mask = ListUtils.create2DList(6, 9); rows = pattern.length;
mask = ListUtils.create2DList(rows, 9);
for (int i = 0; i < items.items().length; i++) { for (int i = 0; i < items.items().length; i++) {
ItemStack itemStack = new ItemStackBuilder(items.items()[i]) ItemStack itemStack = new ItemStackBuilder(items.items()[i])
@@ -82,9 +90,6 @@ public class FillerMask {
for (String patternRow : pattern) { for (String patternRow : pattern) {
int column = 0; int column = 0;
if (patternRow.length() != 9) {
throw new IllegalArgumentException("Invalid amount of columns in pattern!");
}
for (char c : patternRow.toCharArray()) { for (char c : patternRow.toCharArray()) {
if (c == '0') { if (c == '0') {
mask.get(row).set(column, null); mask.get(row).set(column, null);
@@ -107,4 +112,20 @@ public class FillerMask {
public List<List<Slot>> getMask() { public List<List<Slot>> getMask() {
return this.mask; return this.mask;
} }
@Override
public int getRows() {
return rows;
}
@Override
public int getColumns() {
return 9;
}
@Override
public @Nullable Slot getSlotAt(final int row,
final int column) {
return mask.get(row - 1).get(column - 1);
}
} }

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.core.gui.slot; package com.willfp.eco.core.gui.slot;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -9,37 +8,16 @@ import org.jetbrains.annotations.NotNull;
* <p> * <p>
* Useful for backgrounds. * Useful for backgrounds.
*/ */
public class FillerSlot implements Slot { public class FillerSlot extends CustomSlot {
/**
* The ItemStack.
*/
private final ItemStack itemStack;
/** /**
* Create new filler slot. * Create new filler slot.
* *
* @param itemStack The ItemStack. * @param itemStack The ItemStack.
*/ */
public FillerSlot(@NotNull final ItemStack itemStack) { public FillerSlot(@NotNull final ItemStack itemStack) {
this.itemStack = itemStack; init(
} Slot.builder(itemStack)
.build()
@Override );
public ItemStack getItemStack(@NotNull final Player player) {
return itemStack;
}
@Override
public boolean isCaptive() {
return false;
}
/**
* Get the ItemStack.
*
* @return The ItemStack.
*/
public ItemStack getItemStack() {
return this.itemStack;
} }
} }

View File

@@ -0,0 +1,70 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Base class for custom slot implementations.
*/
public abstract class ReactiveSlot implements Slot {
/**
* Create a new reactive slot.
*/
protected ReactiveSlot() {
}
/**
* Get the actual slot to be shown.
*
* @param player The player.
* @param menu The menu.
* @return The slot.
*/
@NotNull
public abstract Slot getSlot(@NotNull final Player player,
@NotNull final Menu menu);
@Override
public @NotNull ItemStack getItemStack(@NotNull final Player player) {
return new ItemStack(Material.AIR);
}
@Override
public final boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
return getSlot(player, menu).isCaptive(player, menu);
}
@Override
public final boolean isAllowedCaptive(@NotNull final Player player,
@NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
return getSlot(player, menu).isAllowedCaptive(player, menu, itemStack);
}
@Override
public final @NotNull Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return getSlot(player, menu);
}
@Override
public final int getRows() {
return Slot.super.getRows();
}
@Override
public final int getColumns() {
return Slot.super.getColumns();
}
@Override
public final Slot getSlotAt(int row, int column) {
return Slot.super.getSlotAt(row, column);
}
}

View File

@@ -1,41 +1,85 @@
package com.willfp.eco.core.gui.slot; package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.gui.GUIComponent;
import com.willfp.eco.core.gui.menu.Menu;
import com.willfp.eco.core.gui.slot.functional.SlotProvider; import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function; import java.util.function.Function;
/** /**
* A slot is an item in a GUI that can handle clicks. * A slot is an item in a GUI that can handle clicks.
* <p>
* While you can create custom Slot implementations directly from this class,
* it's heavily encouraged to extend {@link CustomSlot}, which will abstract
* internal functionality away.
* <p>
* A lot of methods here are marked as default as in 6.43.0 the GUI system
* was overhauled, but to preserve backwards compatibility, the new methods
* had to be marked default, and many old methods became deprecated.
*/ */
public interface Slot { public interface Slot extends GUIComponent {
/** /**
* Get the ItemStack that would be shown to a player. * Get the ItemStack that would be shown to a player.
* *
* @param player The player. * @param player The player.
* @return The ItemStack. * @return The ItemStack.
*/ */
@NotNull
ItemStack getItemStack(@NotNull Player player); ItemStack getItemStack(@NotNull Player player);
/** /**
* If the slot is captive. (Can items be placed in it). * If the slot is captive. (Can items be placed in it).
* *
* @param player The player.
* @param menu The menu.
* @return If captive. * @return If captive.
*/ */
boolean isCaptive(); default boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
return false;
}
/** /**
* If the slot is not captive for a player. * If the slot allows a certain item to be placed in it.
* *
* @param player The player. * @param player The player.
* @return If not captive for the player. * @param menu The menu.
* @param itemStack The item; use null if the item is unknown.
* @return If captive.
*/ */
default boolean isNotCaptiveFor(@NotNull Player player) { default boolean isAllowedCaptive(@NotNull final Player player,
return false; @NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
return this.isCaptive(player, menu);
}
/**
* Get the actionable slot to be shown.
* <p>
* This is mostly internal, if you want to implement custom slots you should
* turn to {@link CustomSlot} or {@link ReactiveSlot}, which abstract this
* behaviour away.
* <p>
* **Never** return {@code this} from this method. Always make sure that your
* slots eventually delegate to a slot created by {@link Slot#builder()}.
* <p>
* {@code this} is returned by default for backwards-compatibility.
*
* @param player The player.
* @param menu The menu.
* @return The slot.
*/
@NotNull
default Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return this;
} }
/** /**
@@ -48,13 +92,29 @@ public interface Slot {
return false; return false;
} }
@Override
default int getRows() {
return 1;
}
@Override
default int getColumns() {
return 1;
}
@Override
default Slot getSlotAt(final int row,
final int column) {
return this;
}
/** /**
* Create a builder for an ItemStack. * Create a builder for an ItemStack.
* *
* @return The builder. * @return The builder.
*/ */
static SlotBuilder builder() { static SlotBuilder builder() {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> new ItemStack(Material.AIR)); return Eco.get().createSlotBuilder((player, menu) -> new ItemStack(Material.AIR));
} }
/** /**
@@ -64,7 +124,17 @@ public interface Slot {
* @return The builder. * @return The builder.
*/ */
static SlotBuilder builder(@NotNull final ItemStack itemStack) { static SlotBuilder builder(@NotNull final ItemStack itemStack) {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> itemStack); return Eco.get().createSlotBuilder((player, menu) -> itemStack);
}
/**
* Create a builder for a TestableItem.
*
* @param item The item.
* @return The builder.
*/
static SlotBuilder builder(@NotNull final TestableItem item) {
return Eco.get().createSlotBuilder((player, menu) -> item.getItem());
} }
/** /**
@@ -72,9 +142,11 @@ public interface Slot {
* *
* @param provider The provider. * @param provider The provider.
* @return The builder. * @return The builder.
* @deprecated This method was written incorrectly, should have been a Player + Menu function.
*/ */
@Deprecated(since = "6.45.0", forRemoval = true)
static SlotBuilder builder(@NotNull final Function<Player, ItemStack> provider) { static SlotBuilder builder(@NotNull final Function<Player, ItemStack> provider) {
return Eco.getHandler().getGUIFactory().createSlotBuilder((player, menu) -> provider.apply(player)); return Eco.get().createSlotBuilder((player, menu) -> provider.apply(player));
} }
/** /**
@@ -84,6 +156,29 @@ public interface Slot {
* @return The builder. * @return The builder.
*/ */
static SlotBuilder builder(@NotNull final SlotProvider provider) { static SlotBuilder builder(@NotNull final SlotProvider provider) {
return Eco.getHandler().getGUIFactory().createSlotBuilder(provider); return Eco.get().createSlotBuilder(provider);
}
/**
* If the slot is not captive for a player.
*
* @param player The player.
* @return If not captive for the player.
* @deprecated Captivity is now reactive, this method can produce incorrect results.
*/
@Deprecated(since = "6.43.0", forRemoval = true)
default boolean isNotCaptiveFor(@NotNull Player player) {
return false;
}
/**
* If the slot is captive. (Can items be placed in it).
*
* @return If captive.
* @deprecated Captivity is now reactive, this method can produce incorrect results.
*/
@Deprecated(since = "6.43.0", forRemoval = true)
default boolean isCaptive() {
return false;
} }
} }

View File

@@ -1,9 +1,11 @@
package com.willfp.eco.core.gui.slot; package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.slot.functional.CaptiveFilter;
import com.willfp.eco.core.gui.slot.functional.SlotHandler; import com.willfp.eco.core.gui.slot.functional.SlotHandler;
import com.willfp.eco.core.gui.slot.functional.SlotModifier; import com.willfp.eco.core.gui.slot.functional.SlotModifier;
import com.willfp.eco.core.gui.slot.functional.SlotUpdater; import com.willfp.eco.core.gui.slot.functional.SlotUpdater;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -14,6 +16,28 @@ import java.util.function.Predicate;
* Builder to create slots. * Builder to create slots.
*/ */
public interface SlotBuilder { public interface SlotBuilder {
/**
* Set click handler.
*
* @param type The click type.
* @param handler The handler.
* @return The builder.
*/
SlotBuilder onClick(@NotNull ClickType type,
@NotNull SlotHandler handler);
/**
* Set click handler.
*
* @param type The click type.
* @param action The handler.
* @return The builder.
*/
default SlotBuilder onClick(@NotNull final ClickType type,
@NotNull final BiConsumer<InventoryClickEvent, Slot> action) {
return onClick(type, (event, slot, menu) -> action.accept(event, slot));
}
/** /**
* Set click handler. * Set click handler.
* *
@@ -30,7 +54,9 @@ public interface SlotBuilder {
* @param handler The handler. * @param handler The handler.
* @return The builder. * @return The builder.
*/ */
SlotBuilder onLeftClick(@NotNull SlotHandler handler); default SlotBuilder onLeftClick(@NotNull final SlotHandler handler) {
return onClick(ClickType.LEFT, handler);
}
/** /**
* Set click handler. * Set click handler.
@@ -48,7 +74,9 @@ public interface SlotBuilder {
* @param handler The handler. * @param handler The handler.
* @return The builder. * @return The builder.
*/ */
SlotBuilder onRightClick(@NotNull SlotHandler handler); default SlotBuilder onRightClick(@NotNull final SlotHandler handler) {
return onClick(ClickType.RIGHT, handler);
}
/** /**
* Set click handler. * Set click handler.
@@ -66,7 +94,9 @@ public interface SlotBuilder {
* @param handler The handler. * @param handler The handler.
* @return The builder. * @return The builder.
*/ */
SlotBuilder onShiftLeftClick(@NotNull SlotHandler handler); default SlotBuilder onShiftLeftClick(@NotNull final SlotHandler handler) {
return onClick(ClickType.SHIFT_LEFT, handler);
}
/** /**
* Set click handler. * Set click handler.
@@ -84,7 +114,9 @@ public interface SlotBuilder {
* @param handler The handler. * @param handler The handler.
* @return The builder. * @return The builder.
*/ */
SlotBuilder onShiftRightClick(@NotNull SlotHandler handler); default SlotBuilder onShiftRightClick(@NotNull final SlotHandler handler) {
return onClick(ClickType.SHIFT_RIGHT, handler);
}
/** /**
* Set click handler. * Set click handler.
@@ -102,7 +134,9 @@ public interface SlotBuilder {
* @param handler The handler. * @param handler The handler.
* @return The builder. * @return The builder.
*/ */
SlotBuilder onMiddleClick(@NotNull SlotHandler handler); default SlotBuilder onMiddleClick(@NotNull final SlotHandler handler) {
return onClick(ClickType.MIDDLE, handler);
}
/** /**
* Prevent captive for players that match a predicate. * Prevent captive for players that match a predicate.
@@ -110,21 +144,16 @@ public interface SlotBuilder {
* @param predicate The predicate. Returns true when the slot should not be captive. * @param predicate The predicate. Returns true when the slot should not be captive.
* @return The builder. * @return The builder.
*/ */
SlotBuilder notCaptiveFor(@NotNull Predicate<Player> predicate); SlotBuilder notCaptiveFor(@NotNull final Predicate<Player> predicate);
/** /**
* Modify the ItemStack. * Set a whitelist for allowed captive items.
* *
* @param modifier The modifier. * @param filter The filter.
* @return The builder. * @return The builder.
* @deprecated Use {@link SlotBuilder#setUpdater(SlotUpdater)} instead.
*/ */
@Deprecated default SlotBuilder setCaptiveFilter(@NotNull final CaptiveFilter filter) {
default SlotBuilder setModifier(@NotNull SlotModifier modifier) { return this;
return setUpdater((player, menu, previous) -> {
modifier.modify(player, menu, previous);
return previous;
});
} }
/** /**
@@ -158,4 +187,19 @@ public interface SlotBuilder {
* @return The slot. * @return The slot.
*/ */
Slot build(); Slot build();
/**
* Modify the ItemStack.
*
* @param modifier The modifier.
* @return The builder.
* @deprecated Use {@link SlotBuilder#setUpdater(SlotUpdater)} instead.
*/
@Deprecated
default SlotBuilder setModifier(@NotNull SlotModifier modifier) {
return setUpdater((player, menu, previous) -> {
modifier.modify(player, menu, previous);
return previous;
});
}
} }

View File

@@ -0,0 +1,25 @@
package com.willfp.eco.core.gui.slot.functional;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface to test if a captive slot is allowed to contain an item given a player and a menu.
*/
@FunctionalInterface
public interface CaptiveFilter {
/**
* Get if allowed.
*
* @param player The player.
* @param menu The menu.
* @param itemStack The item.
* @return If captive.
*/
boolean isAllowed(@NotNull Player player,
@NotNull Menu menu,
@Nullable ItemStack itemStack);
}

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Interface to run on slot display. * Interface to run on slot display.
@@ -17,6 +18,7 @@ public interface SlotProvider {
* @param menu The menu. * @param menu The menu.
* @return The ItemStack. * @return The ItemStack.
*/ */
@Nullable
ItemStack provide(@NotNull Player player, ItemStack provide(@NotNull Player player,
@NotNull Menu menu); @NotNull Menu menu);
} }

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Interface to run on slot update. * Interface to run on slot update.
@@ -18,6 +19,7 @@ public interface SlotUpdater {
* @param previous The previous ItemStack. * @param previous The previous ItemStack.
* @return The new ItemStack. * @return The new ItemStack.
*/ */
@Nullable
ItemStack update(@NotNull Player player, ItemStack update(@NotNull Player player,
@NotNull Menu menu, @NotNull Menu menu,
@NotNull ItemStack previous); @NotNull ItemStack previous);

View File

@@ -38,7 +38,7 @@ public final class AnticheatManager {
*/ */
public static void register(@NotNull final AnticheatIntegration anticheat) { public static void register(@NotNull final AnticheatIntegration anticheat) {
if (anticheat instanceof Listener) { if (anticheat instanceof Listener) {
Eco.getHandler().getEcoPlugin().getEventManager().registerListener((Listener) anticheat); Eco.get().getEcoPlugin().getEventManager().registerListener((Listener) anticheat);
} }
ANTICHEATS.removeIf(it -> it.getPluginName().equalsIgnoreCase(anticheat.getPluginName())); ANTICHEATS.removeIf(it -> it.getPluginName().equalsIgnoreCase(anticheat.getPluginName()));
ANTICHEATS.add(anticheat); ANTICHEATS.add(anticheat);

View File

@@ -8,7 +8,19 @@ import java.math.BigDecimal;
/** /**
* Wrapper class for economy integrations. * Wrapper class for economy integrations.
* <p>
* If you're adding your economy to be supported in eco,
* it's recommended to override the {@link BigDecimal} methods
* as opposed to the {@code double} methods.
* <p>
* <strong>You must override at least one of all methods</strong>,
* i.e. one {@code hasAmount}, one {@code giveMoney}, etc.,
* otherwise your integration will cause {@link StackOverflowError}.
* <p>
* All methods are marked as default to preserve compatibility with
* integrations made before 6.43.0.
*/ */
@SuppressWarnings("DeprecatedIsStillUsed")
public interface EconomyIntegration extends Integration { public interface EconomyIntegration extends Integration {
/** /**
* Get if a player has a certain amount. * Get if a player has a certain amount.
@@ -16,9 +28,39 @@ public interface EconomyIntegration extends Integration {
* @param player The player. * @param player The player.
* @param amount The amount. * @param amount The amount.
* @return If the player has the amount. * @return If the player has the amount.
* @deprecated Use {@link BigDecimal} methods instead.
*/ */
boolean hasAmount(@NotNull OfflinePlayer player, @Deprecated(since = "6.43.0")
double amount); default boolean hasAmount(@NotNull OfflinePlayer player,
double amount) {
return hasAmount(player, BigDecimal.valueOf(amount));
}
/**
* Get if a player has a certain amount.
*
* @param player The player.
* @param amount The amount
* @return If the player has the amount.
*/
default boolean hasAmount(@NotNull OfflinePlayer player,
@NotNull BigDecimal amount) {
return hasAmount(player, amount.doubleValue());
}
/**
* Give money to a player.
*
* @param player The player.
* @param amount The amount to give.
* @return If the transaction was a success.
* @deprecated Use {@link BigDecimal} methods instead.
*/
@Deprecated(since = "6.43.0")
default boolean giveMoney(@NotNull OfflinePlayer player,
double amount) {
return giveMoney(player, BigDecimal.valueOf(amount));
}
/** /**
* Give money to a player. * Give money to a player.
@@ -27,8 +69,24 @@ public interface EconomyIntegration extends Integration {
* @param amount The amount to give. * @param amount The amount to give.
* @return If the transaction was a success. * @return If the transaction was a success.
*/ */
boolean giveMoney(@NotNull OfflinePlayer player, default boolean giveMoney(@NotNull OfflinePlayer player,
double amount); @NotNull BigDecimal amount) {
return giveMoney(player, amount.doubleValue());
}
/**
* Remove money from a player.
*
* @param player The player.
* @param amount The amount to remove.
* @return If the transaction was a success.
* @deprecated Use {@link BigDecimal} methods instead.
*/
@Deprecated(since = "6.43.0")
default boolean removeMoney(@NotNull OfflinePlayer player,
double amount) {
return removeMoney(player, BigDecimal.valueOf(amount));
}
/** /**
* Remove money from a player. * Remove money from a player.
@@ -37,16 +95,23 @@ public interface EconomyIntegration extends Integration {
* @param amount The amount to remove. * @param amount The amount to remove.
* @return If the transaction was a success. * @return If the transaction was a success.
*/ */
boolean removeMoney(@NotNull OfflinePlayer player, default boolean removeMoney(@NotNull OfflinePlayer player,
double amount); @NotNull BigDecimal amount) {
return removeMoney(player, amount.doubleValue());
}
/** /**
* Get the balance of a player. * Get the balance of a player.
* *
* @param player The player. * @param player The player.
* @return The balance. * @return The balance.
* @deprecated Use {@link BigDecimal} methods instead.
*/ */
double getBalance(@NotNull OfflinePlayer player); @Deprecated(since = "6.43.0")
default double getBalance(@NotNull OfflinePlayer player) {
return getExactBalance(player).doubleValue();
}
/** /**
* Get the balance of a player. * Get the balance of a player.

View File

@@ -44,6 +44,18 @@ public final class EconomyManager {
*/ */
public static boolean hasAmount(@NotNull final OfflinePlayer player, public static boolean hasAmount(@NotNull final OfflinePlayer player,
final double amount) { final double amount) {
return hasAmount(player, BigDecimal.valueOf(amount));
}
/**
* Get if a player has a certain amount.
*
* @param player The player.
* @param amount The amount.
* @return If the player has the amount.
*/
public static boolean hasAmount(@NotNull final OfflinePlayer player,
final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { for (EconomyIntegration integration : REGISTERED) {
return integration.hasAmount(player, amount); return integration.hasAmount(player, amount);
} }
@@ -60,6 +72,18 @@ public final class EconomyManager {
*/ */
public static boolean giveMoney(@NotNull final OfflinePlayer player, public static boolean giveMoney(@NotNull final OfflinePlayer player,
final double amount) { final double amount) {
return giveMoney(player, BigDecimal.valueOf(amount));
}
/**
* Give money to a player.
*
* @param player The player.
* @param amount The amount to give.
* @return If the transaction was a success.
*/
public static boolean giveMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { for (EconomyIntegration integration : REGISTERED) {
return integration.giveMoney(player, amount); return integration.giveMoney(player, amount);
} }
@@ -76,6 +100,18 @@ public final class EconomyManager {
*/ */
public static boolean removeMoney(@NotNull final OfflinePlayer player, public static boolean removeMoney(@NotNull final OfflinePlayer player,
final double amount) { final double amount) {
return removeMoney(player, BigDecimal.valueOf(amount));
}
/**
* Remove money from a player.
*
* @param player The player.
* @param amount The amount to remove.
* @return If the transaction was a success.
*/
public static boolean removeMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { for (EconomyIntegration integration : REGISTERED) {
return integration.removeMoney(player, amount); return integration.removeMoney(player, amount);
} }
@@ -90,11 +126,7 @@ public final class EconomyManager {
* @return The balance. * @return The balance.
*/ */
public static double getBalance(@NotNull final OfflinePlayer player) { public static double getBalance(@NotNull final OfflinePlayer player) {
for (EconomyIntegration integration : REGISTERED) { return getExactBalance(player).doubleValue();
return integration.getBalance(player);
}
return 0;
} }
/** /**

View File

@@ -33,10 +33,15 @@ public final class McmmoManager {
* @return The bonus drop count. * @return The bonus drop count.
*/ */
public static int getBonusDropCount(@NotNull final Block block) { public static int getBonusDropCount(@NotNull final Block block) {
int finalValue = 0;
for (McmmoIntegration mcmmoIntegration : REGISTERED) { for (McmmoIntegration mcmmoIntegration : REGISTERED) {
return mcmmoIntegration.getBonusDropCount(block); finalValue += mcmmoIntegration.getBonusDropCount(block);
} }
return 0; return finalValue;
} }
/** /**
@@ -47,7 +52,10 @@ public final class McmmoManager {
*/ */
public static boolean isFake(@NotNull final Event event) { public static boolean isFake(@NotNull final Event event) {
for (McmmoIntegration mcmmoIntegration : REGISTERED) { for (McmmoIntegration mcmmoIntegration : REGISTERED) {
return mcmmoIntegration.isFake(event); if (mcmmoIntegration.isFake(event)) {
return true;
}
} }
return false; return false;
} }

View File

@@ -158,13 +158,13 @@ public class PlaceholderEntry {
Placeholder toModernPlaceholder() { Placeholder toModernPlaceholder() {
if (this.requiresPlayer) { if (this.requiresPlayer) {
return new PlayerPlaceholder( return new PlayerPlaceholder(
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()), Objects.requireNonNullElse(plugin, Eco.get().getEcoPlugin()),
identifier, identifier,
function function
); );
} else { } else {
return new PlayerlessPlaceholder( return new PlayerlessPlaceholder(
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()), Objects.requireNonNullElse(plugin, Eco.get().getEcoPlugin()),
identifier, identifier,
() -> function.apply(null) () -> function.apply(null)
); );

View File

@@ -51,10 +51,15 @@ public final class PlaceholderManager {
.expireAfterWrite(50, TimeUnit.MILLISECONDS) .expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player)); .build(key -> key.entry.getValue(key.player));
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^% ]+)[%]");
/** /**
* Empty injectable object. * Empty injectable object.
*/ */
private static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() { public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override @Override
public void clearInjectedPlaceholders() { public void clearInjectedPlaceholders() {
// Do nothing. // Do nothing.
@@ -67,11 +72,6 @@ public final class PlaceholderManager {
} }
}; };
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^%]+)[%]");
/** /**
* Register a new placeholder integration. * Register a new placeholder integration.
* *
@@ -92,7 +92,7 @@ public final class PlaceholderManager {
throw new IllegalArgumentException("Static placeholders cannot be registered!"); throw new IllegalArgumentException("Static placeholders cannot be registered!");
} }
EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : placeholder.getPlugin(); EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.get().getEcoPlugin() : placeholder.getPlugin();
Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>()); .getOrDefault(plugin, new HashMap<>());
pluginPlaceholders.put(placeholder.getIdentifier(), placeholder); pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
@@ -136,11 +136,11 @@ public final class PlaceholderManager {
public static String getResult(@Nullable final Player player, public static String getResult(@Nullable final Player player,
@NotNull final String identifier, @NotNull final String identifier,
@Nullable final EcoPlugin plugin) { @Nullable final EcoPlugin plugin) {
EcoPlugin owner = plugin == null ? Eco.getHandler().getEcoPlugin() : plugin; EcoPlugin owner = plugin == null ? Eco.get().getEcoPlugin() : plugin;
Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase()); Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
if (placeholder == null && plugin != null) { if (placeholder == null && plugin != null) {
Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>()) Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.get().getEcoPlugin(), new HashMap<>())
.get(identifier.toLowerCase()); .get(identifier.toLowerCase());
if (alternate != null) { if (alternate != null) {
placeholder = alternate; placeholder = alternate;

View File

@@ -1,6 +1,8 @@
package com.willfp.eco.core.integrations.shop; package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.integrations.Integration; import com.willfp.eco.core.integrations.Integration;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -29,12 +31,41 @@ public interface ShopIntegration extends Integration {
return null; return null;
} }
/**
* Get if an item is sellable for a player.
*
* @param itemStack The item.
* @param player The player.
* @return If sellable.
*/
default boolean isSellable(@NotNull final ItemStack itemStack,
@NotNull final Player player) {
return false;
}
/**
* Get the value of one of an item for a player.
* <p>
* For example, if you pass in a stack, it will only return the value of <b>one</b> item, not the full stack.
*
* @param itemStack The item.
* @param player The player.
* @return The price.
*/
@NotNull
default Price getUnitValue(@NotNull final ItemStack itemStack,
@NotNull final Player player) {
return new PriceFree();
}
/** /**
* Get the price of an item. * Get the price of an item.
* *
* @param itemStack The item. * @param itemStack The item.
* @return The price. * @return The price.
* @deprecated Use getValue instead.
*/ */
@Deprecated(since = "6.47.0", forRemoval = true)
default double getPrice(@NotNull final ItemStack itemStack) { default double getPrice(@NotNull final ItemStack itemStack) {
// Do nothing unless overridden. // Do nothing unless overridden.
return 0.0; return 0.0;
@@ -46,9 +77,11 @@ public interface ShopIntegration extends Integration {
* @param itemStack The item. * @param itemStack The item.
* @param player The player. * @param player The player.
* @return The price. * @return The price.
* @deprecated Use getValue instead.
*/ */
@Deprecated(since = "6.47.0", forRemoval = true)
default double getPrice(@NotNull final ItemStack itemStack, default double getPrice(@NotNull final ItemStack itemStack,
@NotNull final Player player) { @NotNull final Player player) {
return getPrice(itemStack); return getUnitValue(itemStack, player).getValue(player);
} }
} }

View File

@@ -1,10 +1,9 @@
package com.willfp.eco.core.integrations.shop; package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -30,22 +29,6 @@ public final class ShopManager {
REGISTERED.add(integration); REGISTERED.add(integration);
} }
/**
* Register the events with eco.
*
* @param plugin Instance of eco.
*/
@ApiStatus.Internal
public static void registerEvents(@NotNull final EcoPlugin plugin) {
for (ShopIntegration integration : REGISTERED) {
Listener listener = integration.getSellEventAdapter();
if (listener != null) {
plugin.getEventManager().registerListener(listener);
}
}
}
/** /**
* Register eco item provider for shop plugins. * Register eco item provider for shop plugins.
*/ */
@@ -55,12 +38,57 @@ public final class ShopManager {
} }
} }
/**
* Get if an item is sellable for a player.
*
* @param itemStack The item.
* @param player The player.
* @return If sellable.
*/
public static boolean isSellable(@Nullable final ItemStack itemStack,
@NotNull final Player player) {
if (itemStack == null) {
return false;
}
for (ShopIntegration integration : REGISTERED) {
return integration.isSellable(itemStack, player);
}
return false;
}
/**
* Get the value of one of an item for a player.
* <p>
* For example, if you pass in a stack, it will only return the value of <b>one</b> item, not the full stack.
*
* @param itemStack The item.
* @param player The player.
* @return The price.
*/
@NotNull
public static Price getUnitValue(@Nullable final ItemStack itemStack,
@NotNull final Player player) {
if (itemStack == null) {
return new PriceFree();
}
for (ShopIntegration integration : REGISTERED) {
return integration.getUnitValue(itemStack, player);
}
return new PriceFree();
}
/** /**
* Get the price of an item. * Get the price of an item.
* *
* @param itemStack The item. * @param itemStack The item.
* @return The price. * @return The price.
* @deprecated Use getValue instead. This will always return 0 as prices depend on players.
*/ */
@Deprecated(since = "6.47.0", forRemoval = true)
public static double getItemPrice(@Nullable final ItemStack itemStack) { public static double getItemPrice(@Nullable final ItemStack itemStack) {
return getItemPrice(itemStack, null); return getItemPrice(itemStack, null);
} }
@@ -71,24 +99,31 @@ public final class ShopManager {
* @param itemStack The item. * @param itemStack The item.
* @param player The player. * @param player The player.
* @return The price. * @return The price.
* @deprecated Use getValue instead. Null players / null items will always return 0.
*/ */
@Deprecated(since = "6.47.0", forRemoval = true)
public static double getItemPrice(@Nullable final ItemStack itemStack, public static double getItemPrice(@Nullable final ItemStack itemStack,
@Nullable final Player player) { @Nullable final Player player) {
if (itemStack == null) { if (itemStack == null || player == null) {
return 0.0; return 0.0;
} }
for (ShopIntegration shopIntegration : REGISTERED) { for (ShopIntegration shopIntegration : REGISTERED) {
if (player == null) { return shopIntegration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount());
return shopIntegration.getPrice(itemStack);
} else {
return shopIntegration.getPrice(itemStack, player);
}
} }
return 0.0; return 0.0;
} }
/**
* Get all registered integrations.
*
* @return The integrations.
*/
public static Set<ShopIntegration> getRegisteredIntegrations() {
return new HashSet<>(REGISTERED);
}
private ShopManager() { private ShopManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
} }

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.integrations.shop; package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceEconomy;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
@@ -19,7 +21,12 @@ public class ShopSellEvent extends PlayerEvent {
/** /**
* The sell price. * The sell price.
*/ */
private double price; private Price price;
/**
* The price multiplier.
*/
private double multiplier;
/** /**
* The item to be sold. * The item to be sold.
@@ -33,31 +40,64 @@ public class ShopSellEvent extends PlayerEvent {
* @param who The player. * @param who The player.
* @param price The price. * @param price The price.
* @param item The item. * @param item The item.
* @deprecated Use the price system instead.
*/ */
@Deprecated(since = "6.47.0", forRemoval = true)
public ShopSellEvent(@NotNull final Player who, public ShopSellEvent(@NotNull final Player who,
final double price, final double price,
@Nullable final ItemStack item) { @Nullable final ItemStack item) {
this(who, new PriceEconomy(price), item);
}
/**
* Create new shop sell event.
*
* @param who The player.
* @param price The price.
* @param item The item.
*/
public ShopSellEvent(@NotNull final Player who,
@NotNull final Price price,
@Nullable final ItemStack item) {
this(who, price, item, 1.0);
}
/**
* Create new shop sell event.
*
* @param who The player.
* @param price The price.
* @param item The item.
* @param multiplier The multiplier.
*/
public ShopSellEvent(@NotNull final Player who,
@NotNull final Price price,
@Nullable final ItemStack item,
final double multiplier) {
super(who); super(who);
this.price = price; this.price = price;
this.item = item; this.item = item;
this.multiplier = multiplier;
} }
/** /**
* Get the price. * Get the value.
* *
* @return The price. * @return The value.
*/ */
public double getPrice() { @NotNull
public Price getValue() {
return this.price; return this.price;
} }
/** /**
* Set the price. * Set the value.
* *
* @param price The price. * @param price The value.
*/ */
public void setPrice(final double price) { public void setValue(@NotNull final Price price) {
this.price = price; this.price = price;
} }
@@ -81,6 +121,46 @@ public class ShopSellEvent extends PlayerEvent {
return item != null; return item != null;
} }
/**
* Get the price multiplier.
*
* @return The multiplier.
*/
public double getMultiplier() {
return multiplier;
}
/**
* Set the price multiplier.
*
* @param multiplier The multiplier.
*/
public void setMultiplier(final double multiplier) {
this.multiplier = multiplier;
}
/**
* Get the price.
*
* @return The price.
* @deprecated Use the price system instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public double getPrice() {
return this.getValue().getValue(player);
}
/**
* Set the price.
*
* @param price The price.
* @deprecated Use the price system instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public void setPrice(final double price) {
this.setValue(new PriceEconomy(price));
}
/** /**
* Bukkit parity. * Bukkit parity.
* *

View File

@@ -53,7 +53,7 @@ public class CustomItem implements TestableItem {
immediately after due to registration order; so eco waits until the item should be immediately after due to registration order; so eco waits until the item should be
working in order to check. working in order to check.
*/ */
Eco.getHandler().getEcoPlugin().getScheduler().runLater(() -> { Eco.get().getEcoPlugin().getScheduler().runLater(() -> {
if (!matches(getItem())) { if (!matches(getItem())) {
Bukkit.getLogger().severe("Item with key " + key + " is invalid!"); Bukkit.getLogger().severe("Item with key " + key + " is invalid!");
} }

View File

@@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -81,6 +82,16 @@ public final class Items {
*/ */
private static final ItemsLookupHandler ITEMS_LOOKUP_HANDLER = new ItemsLookupHandler(Items::doParse); private static final ItemsLookupHandler ITEMS_LOOKUP_HANDLER = new ItemsLookupHandler(Items::doParse);
/**
* Instance of EmptyTestableItem.
*/
private static final TestableItem EMPTY_TESTABLE_ITEM = new EmptyTestableItem();
/**
* Friendly material names (without underscores, etc.)
*/
private static final Map<String, Material> FRIENDLY_MATERIAL_NAMES = new HashMap<>();
/** /**
* Register a new custom item. * Register a new custom item.
* *
@@ -187,7 +198,7 @@ public final class Items {
@NotNull @NotNull
public static TestableItem lookup(@NotNull final String key) { public static TestableItem lookup(@NotNull final String key) {
if (key.startsWith("{")) { if (key.startsWith("{")) {
return Eco.getHandler().getSNBTHandler().createTestable(key); return Eco.get().testableItemFromSNBT(key);
} }
return ITEMS_LOOKUP_HANDLER.parseKey(key); return ITEMS_LOOKUP_HANDLER.parseKey(key);
@@ -211,7 +222,7 @@ public final class Items {
if (isWildcard) { if (isWildcard) {
itemType = itemType.substring(1); itemType = itemType.substring(1);
} }
Material material = Material.getMaterial(itemType.toUpperCase()); Material material = FRIENDLY_MATERIAL_NAMES.get(itemType.toLowerCase());
if (material == null || material == Material.AIR) { if (material == null || material == Material.AIR) {
return new EmptyTestableItem(); return new EmptyTestableItem();
} }
@@ -533,7 +544,7 @@ public final class Items {
*/ */
@NotNull @NotNull
public static String toSNBT(@NotNull final ItemStack itemStack) { public static String toSNBT(@NotNull final ItemStack itemStack) {
return Eco.getHandler().getSNBTHandler().toSNBT(itemStack); return Eco.get().toSNBT(itemStack);
} }
/** /**
@@ -544,10 +555,41 @@ public final class Items {
*/ */
@Nullable @Nullable
public static ItemStack fromSNBT(@NotNull final String snbt) { public static ItemStack fromSNBT(@NotNull final String snbt) {
return Eco.getHandler().getSNBTHandler().fromSNBT(snbt); return Eco.get().fromSNBT(snbt);
}
/**
* Get if an item is empty.
*
* @param itemStack The item.
* @return If empty.
*/
public static boolean isEmpty(@Nullable final ItemStack itemStack) {
return EMPTY_TESTABLE_ITEM.matches(itemStack);
} }
private Items() { private Items() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
} }
static {
for (Material material : Material.values()) {
FRIENDLY_MATERIAL_NAMES.put(material.name().toLowerCase(), material);
String oneWord = material.name().toLowerCase().replace("_", "");
if (!FRIENDLY_MATERIAL_NAMES.containsKey(oneWord)) {
FRIENDLY_MATERIAL_NAMES.put(oneWord, material);
}
String plural = material.name().toLowerCase() + "s";
if (!FRIENDLY_MATERIAL_NAMES.containsKey(plural)) {
FRIENDLY_MATERIAL_NAMES.put(plural, material);
}
String oneWordPlural = oneWord + "s";
if (!FRIENDLY_MATERIAL_NAMES.containsKey(oneWordPlural)) {
FRIENDLY_MATERIAL_NAMES.put(oneWordPlural, material);
}
}
}
} }

View File

@@ -1,41 +0,0 @@
package com.willfp.eco.core.items;
import com.willfp.eco.core.Eco;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* API to handle SNBT conversion.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface SNBTHandler {
/**
* Get item from SNBT.
*
* @param snbt The NBT string.
* @return The ItemStack, or null if invalid.
*/
@Nullable
ItemStack fromSNBT(@NotNull String snbt);
/**
* Convert item to SNBT.
*
* @param itemStack The item.
* @return The NBT string.
*/
@NotNull
String toSNBT(@NotNull ItemStack itemStack);
/**
* Make TestableItem from SNBT.
*
* @param snbt The NBT string.
* @return The TestableItem.
*/
@NotNull
TestableItem createTestable(@NotNull String snbt);
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.items.builder; package com.willfp.eco.core.items.builder;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.items.TestableItem; import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.util.StringUtils; import com.willfp.eco.util.StringUtils;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@@ -13,7 +14,6 @@ import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -28,7 +28,7 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
/** /**
* The ItemMeta used while building. * The ItemMeta used while building.
*/ */
private final T meta; private T meta;
/** /**
* The ItemStack. * The ItemStack.
@@ -70,7 +70,7 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
@Override @Override
public U setAmount(final int amount) { public U setAmount(final int amount) {
Validate.isTrue(amount >= 1 && amount <= base.getMaxStackSize()); Validate.isTrue(amount >= 1);
base.setAmount(amount); base.setAmount(amount);
return (U) this; return (U) this;
} }
@@ -113,10 +113,15 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
@Override @Override
public U addLoreLine(@NotNull final String line) { public U addLoreLine(@NotNull final String line) {
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); base.setItemMeta(meta);
assert lore != null;
FastItemStack fis = FastItemStack.wrap(base);
List<String> lore = fis.getLore();
lore.add(StringUtils.format(line)); lore.add(StringUtils.format(line));
meta.setLore(lore); fis.setLore(lore);
meta = (T) base.getItemMeta();
return (U) this; return (U) this;
} }
@@ -130,12 +135,19 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
@Override @Override
public U addLoreLines(@NotNull final List<String> lines) { public U addLoreLines(@NotNull final List<String> lines) {
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); base.setItemMeta(meta);
assert lore != null;
FastItemStack fis = FastItemStack.wrap(base);
List<String> lore = fis.getLore();
for (String line : lines) { for (String line : lines) {
lore.add(StringUtils.format(line)); lore.add(StringUtils.format(line));
} }
meta.setLore(lore);
fis.setLore(lore);
meta = (T) base.getItemMeta();
return (U) this; return (U) this;
} }

View File

@@ -0,0 +1,63 @@
package com.willfp.eco.core.math;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
/**
* Represents a context to do math in.
*
* @param injectableContext The PlaceholderInjectable context.
* @param player The player.
* @param additionalPlayers The additional players.
*/
public record MathContext(
@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers
) {
/**
* Empty math context.
*/
public static final MathContext EMPTY = new MathContext(
PlaceholderManager.EMPTY_INJECTABLE,
null,
Collections.emptyList()
);
/**
* Create MathContext of a PlaceholderInjectable context.
*
* @param injectableContext The PlaceholderInjectable context.
* @return The MathContext.
*/
public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) {
return new MathContext(
injectableContext,
null,
Collections.emptyList()
);
}
/**
* Copy a MathContext with a player.
*
* @param context The context.
* @param player The player.
* @return The new MathContext.
*/
public static MathContext copyWithPlayer(@NotNull final MathContext context,
@Nullable final Player player) {
return new MathContext(
context.injectableContext(),
player,
context.additionalPlayers()
);
}
}

View File

@@ -0,0 +1,28 @@
package com.willfp.eco.core.particle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Create particles.
*/
public interface ParticleFactory {
/**
* Get the names (how the particle looks in lookup strings).
* <p>
* For example, for RGB particles this would be 'rgb', 'color', etc.
*
* @return The allowed names.
*/
@NotNull List<String> getNames();
/**
* Create the particle
*
* @param key The key.
* @return The particle.
*/
@Nullable SpawnableParticle create(@NotNull String key);
}

View File

@@ -0,0 +1,83 @@
package com.willfp.eco.core.particle;
import com.willfp.eco.core.particle.impl.EmptyParticle;
import com.willfp.eco.core.particle.impl.SimpleParticle;
import com.willfp.eco.util.StringUtils;
import org.bukkit.Particle;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class to manage particles.
*/
public final class Particles {
/**
* All factories.
*/
private static final Map<String, ParticleFactory> FACTORIES = new ConcurrentHashMap<>();
/**
* Register a new particle factory.
*
* @param factory The factory.
*/
public static void registerParticleFactory(@NotNull final ParticleFactory factory) {
for (String name : factory.getNames()) {
FACTORIES.put(name.toLowerCase(), factory);
}
}
/**
* Lookup a particle from a string.
* <p>
* A particle string should look like {@code magic}, {@code rgb:00ff00}
*
* @param key The key.
* @return The particle, or an {@link EmptyParticle} if invalid.
*/
@NotNull
public static SpawnableParticle lookup(@NotNull final String key) {
String[] args = StringUtils.parseTokens(key.toLowerCase());
if (args.length == 0) {
return new EmptyParticle();
}
SpawnableParticle spawnableParticle;
String[] split = args[0].split(":");
if (split.length == 1) {
try {
Particle particle = Particle.valueOf(args[0].toUpperCase());
spawnableParticle = new SimpleParticle(particle);
} catch (IllegalArgumentException e) {
spawnableParticle = new EmptyParticle();
}
} else if (split.length == 2) {
String name = split[0];
String factoryKey = split[1];
ParticleFactory factory = FACTORIES.get(name);
if (factory == null) {
spawnableParticle = new EmptyParticle();
} else {
spawnableParticle = factory.create(factoryKey);
}
} else {
return new EmptyParticle();
}
if (spawnableParticle == null || spawnableParticle instanceof EmptyParticle) {
return new EmptyParticle();
}
return spawnableParticle;
}
private Particles() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.core.particle;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* A particle that can be spawned.
*/
public interface SpawnableParticle {
/**
* Spawn the particle at a location.
*
* @param location The location.
* @param amount The amount to spawn.
*/
void spawn(@NotNull Location location,
int amount);
/**
* Spawn the particle at a location.
*
* @param location The location.
*/
default void spawn(@NotNull Location location) {
spawn(location, 1);
}
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.core.particle.impl;
import com.willfp.eco.core.particle.SpawnableParticle;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* Empty (invalid) particle that is spawned when an invalid key is provided.
*/
public final class EmptyParticle implements SpawnableParticle {
@Override
public void spawn(@NotNull final Location location,
final int amount) {
// Do nothing.
}
}

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.core.particle.impl;
import com.willfp.eco.core.particle.SpawnableParticle;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
/**
* Empty (invalid) particle that is spawned when an invalid key is provided.
*/
public final class SimpleParticle implements SpawnableParticle {
/**
* The particle to be spawned.
*/
private final Particle particle;
/**
* Create a new spawnable particle.
*
* @param particle The particle.
*/
public SimpleParticle(@NotNull final Particle particle) {
this.particle = particle;
}
@Override
public void spawn(@NotNull final Location location,
final int amount) {
World world = location.getWorld();
if (world == null) {
return;
}
world.spawnParticle(particle, location, amount, 0, 0, 0, 0, null);
}
}

View File

@@ -9,6 +9,6 @@ import com.willfp.eco.core.EcoPlugin;
public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder { public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder {
@Override @Override
default EcoPlugin getPlugin() { default EcoPlugin getPlugin() {
return Eco.getHandler().getEcoPlugin(); return Eco.get().getEcoPlugin();
} }
} }

View File

@@ -0,0 +1,156 @@
package com.willfp.eco.core.price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A group of {@link ConfiguredPrice}s in order to show them
* to players in one go.
*/
public final class CombinedDisplayPrice {
/**
* Maps configured prices to multipliers.
*/
private final Map<ConfiguredPrice, Double> prices;
/**
* The player to format for.
*/
private final Player player;
/**
* Initialize a new combined price mapping formatters to multipliers.
*
* @param player The player.
* @param prices The prices.
*/
private CombinedDisplayPrice(@NotNull final Player player,
@NotNull final Map<ConfiguredPrice, Double> prices) {
this.player = player;
this.prices = prices;
}
/**
* Get the display strings.
*
* @return The display strings.
*/
@NotNull
public String[] getDisplayStrings() {
List<String> displayStrings = new ArrayList<>();
for (Map.Entry<ConfiguredPrice, Double> entry : prices.entrySet()) {
displayStrings.add(entry.getKey().getDisplay(player, entry.getValue()));
}
return displayStrings.toArray(new String[0]);
}
/**
* The builder.
*/
public static class Builder {
/**
* All multiplied prices.
*/
private final List<MultipliedPrice> prices = new ArrayList<>();
/**
* The player.
*/
private final Player player;
/**
* Create a new builder.
*
* @param player The player.
*/
Builder(@NotNull final Player player) {
this.player = player;
}
/**
* Add a new price with a certain multiplier.
*
* @param price The price.
* @param multiplier The multiplier.
* @return The builder.
*/
@NotNull
public Builder add(@NotNull final ConfiguredPrice price,
final double multiplier) {
prices.add(new MultipliedPrice(price, multiplier));
return this;
}
/**
* Add a new price.
*
* @param price The price.
* @return The builder.
*/
@NotNull
public Builder add(@NotNull final ConfiguredPrice price) {
return this.add(price, 1D);
}
/**
* Build into a {@link CombinedDisplayPrice}.
*
* @return The combined price.
*/
@NotNull
public CombinedDisplayPrice build() {
Map<ConfiguredPrice, Double> unitPrices = new HashMap<>();
// Take first configured price at each ID as the format for all prices with that ID.
for (MultipliedPrice price : prices) {
// Find the base price.
ConfiguredPrice base = unitPrices.keySet()
.stream()
.filter(it -> it.getIdentifier().equals(price.price().getIdentifier()))
.findFirst()
.orElse(price.price());
// Find the multiplier for a value of 1, e.g. a price that's worth 20 will be 0.05.
double unitMultiplier = 1 / base.getValue(player);
double currentMultiplier = unitPrices.getOrDefault(base, 0D);
currentMultiplier += unitMultiplier * price.price().getValue(player, price.multiplier());
unitPrices.put(base, currentMultiplier);
}
return new CombinedDisplayPrice(player, unitPrices);
}
/**
* A price with a multiplier.
*
* @param price The price.
* @param multiplier The multiplier.
*/
private record MultipliedPrice(
@NotNull ConfiguredPrice price,
double multiplier
) {
}
}
/**
* Create a new builder for a player.
*
* @param player The player.
* @return The builder.
*/
@NotNull
public static Builder builder(@NotNull final Player player) {
return new Builder(player);
}
}

View File

@@ -0,0 +1,177 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.serialization.ConfigDeserializer;
import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* A price that can be shown to a player.
*/
public final class ConfiguredPrice implements Price {
/**
* The deserializer.
*/
private static final ConfigDeserializer<ConfiguredPrice> DESERIALIZER = new Deserializer();
/**
* Free.
*/
public static final ConfiguredPrice FREE = new ConfiguredPrice(
new PriceFree(),
"Free"
);
/**
* The price.
*/
private final Price price;
/**
* The format string.
*/
private final String formatString;
/**
* Create a new Configured Price.
*
* @param price The price.
* @param formatString The format string.
*/
public ConfiguredPrice(@NotNull final Price price,
@NotNull final String formatString) {
this.price = price;
this.formatString = formatString;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return this.price.canAfford(player, multiplier);
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
this.price.pay(player, multiplier);
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
this.price.giveTo(player, multiplier);
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return this.price.getValue(player, multiplier);
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.price.getMultiplier(player);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.price.setMultiplier(player, multiplier);
}
@Override
public String getIdentifier() {
return this.price.getIdentifier();
}
/**
* Get the price that this delegates to.
*
* @return The price.
*/
public Price getPrice() {
return price;
}
/**
* Get the display string for a player.
*
* @param player The player.
* @return The display string.
*/
public String getDisplay(@NotNull final Player player) {
return this.getDisplay(player, 1.0);
}
/**
* Get the display string for a player.
*
* @param player The player.
* @param multiplier The multiplier.
* @return The display string.
*/
public String getDisplay(@NotNull final Player player,
final double multiplier) {
return StringUtils.format(
formatString.replace("%value%", NumberUtils.format(this.getPrice().getValue(player, multiplier))),
player,
StringUtils.FormatOption.WITH_PLACEHOLDERS
);
}
/**
* Parse a configured price from config.
*
* @param config The config.
* @return The price, or null if it's invalid.
*/
@Nullable
public static ConfiguredPrice create(@NotNull final Config config) {
return DESERIALIZER.deserialize(config);
}
/**
* Parse a configured price from config.
*
* @param config The config.
* @return The price, or free if invalid.
*/
@NotNull
public static ConfiguredPrice createOrFree(@NotNull final Config config) {
return Objects.requireNonNullElse(create(config), FREE);
}
/**
* The deserializer for {@link ConfiguredPrice}.
*/
private static final class Deserializer implements ConfigDeserializer<ConfiguredPrice> {
@Override
@Nullable
public ConfiguredPrice deserialize(@NotNull final Config config) {
if (!(
config.has("value")
&& config.has("type")
&& config.has("display")
)) {
return null;
}
String formatString = config.getString("display");
Price price = Prices.create(
config.getString("value"),
config.getString("type"),
MathContext.of(config)
);
return new ConfiguredPrice(price, formatString);
}
}
}

View File

@@ -0,0 +1,171 @@
package com.willfp.eco.core.price;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A price that a player should pay.
* <p>
* There are important implementation details:
* <p>
* For backwards compatibility, all methods are default, however you must override the following:
* <ul>
* <li><code>canAfford(Player, double)</code></li>
* <li><code>pay(Player, double)</code></li>
* <li><code>giveTo(Player, double)</code></li>
* <li><code>getValue(Player, double)</code></li>
* <li><code>getMultiplier(Player)</code></li>
* <li><code>setMultiplier(Player, double)</code></li>
* </ul>
* Otherwise, your implementation will throw {@link NotImplementedException}.
* <p>
* Also, getValue() should always return the value with player multipliers applied.
*/
public interface Price {
/**
* Get if a player can afford to pay the price.
*
* @param player The player.
* @return If the player can afford.
*/
default boolean canAfford(@NotNull final Player player) {
return this.canAfford(player, 1);
}
/**
* Get if a player can afford to pay x times the price.
*
* @param player The player.
* @param multiplier The multiplier.
* @return If the player can afford.
*/
default boolean canAfford(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override canAfford(Player, double) in your Price implementation!");
}
/**
* Make the player pay the price.
* <p>
* Check canAfford first.
*
* @param player The player.
*/
default void pay(@NotNull final Player player) {
this.pay(player, 1);
}
/**
* Make the player pay the price x times.
* <p>
* Check canAfford first.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void pay(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override pay(Player, double) in your Price implementation!");
}
/**
* Give the price to the player.
*
* @param player The player.
*/
default void giveTo(@NotNull final Player player) {
this.giveTo(player, 1);
}
/**
* Give the price to the player x times.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void giveTo(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override giveTo(Player, double) in your Price implementation!");
}
/**
* Get the numerical value that backs this price.
*
* @param player The player.
* @return The value.
*/
default double getValue(@NotNull final Player player) {
return getValue(player, 1);
}
/**
* Get the numeral value that backs this price multiplied x times.
*
* @param player The player.
* @param multiplier The multiplier.
* @return The value.
*/
default double getValue(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override getValue(Player, double) in your Price implementation!");
}
/**
* Get the value multiplier for the player.
*
* @param player The player.
* @return The multiplier.
*/
default double getMultiplier(@NotNull final Player player) {
return 1;
}
/**
* Set the value multiplier for the player.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void setMultiplier(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override setMultiplier(Player, double) in your Price implementation!");
}
/**
* Get the identifier of this price (as type/instance checks break with delegation,
* this is used for combining prices, etc.)
* <p>
* By default, this uses the class name, but it's good practice to override this.
* <p>
* It's also good practice to prefix your identifiers with some kind of namespace or
* internal ID, in order to prevent conflicts.
*
* @return The identifier.
*/
default String getIdentifier() {
return this.getClass().getName();
}
/**
* If the price is backed by a value, get it here.
*
* @return The value.
* @deprecated Use getValue(Player) instead.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
default double getValue() {
return 0;
}
/**
* If the price is backed by a value, set it here.
*
* @param value The value.
* @deprecated Values shouldn't be fixed. This method should never work.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
default void setValue(final double value) {
// Override when needed.
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.math.MathContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Function;
/**
* Create prices.
* <p>
* You must override one of the create methods.
*/
public interface PriceFactory {
/**
* Get the names (how the price looks in lookup strings).
* <p>
* For example, for XP Levels this would be 'l', 'xpl', 'levels', etc.
*
* @return The allowed names.
*/
@NotNull List<String> getNames();
/**
* Create the price.
*
* @param value The value.
* @return The price.
*/
default @NotNull Price create(final double value) {
return create(MathContext.EMPTY, (ctx) -> value);
}
/**
* Create the price.
*
* @param baseContext The base MathContext.
* @param function The function to use. Should use {@link MathContext#copyWithPlayer(MathContext, Player)} on calls.
* @return The price.
*/
default @NotNull Price create(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
return create(function.apply(baseContext));
}
}

View File

@@ -0,0 +1,103 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.price.impl.PriceEconomy;
import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.price.impl.PriceItem;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.util.NumberUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Class to manage prices.
*/
public final class Prices {
/**
* All factories.
*/
private static final Map<String, PriceFactory> FACTORIES = new ConcurrentHashMap<>();
/**
* Register a new price factory.
*
* @param factory The factory.
*/
public static void registerPriceFactory(@NotNull final PriceFactory factory) {
for (String name : factory.getNames()) {
FACTORIES.put(name.toLowerCase(), factory);
}
}
/**
* Create price from an expression (representing the value),
* and a price name.
* <p>
* Supports items as price names.
*
* @param expression The expression for the value.
* @param priceName The price name.
* @return The price, or free if invalid.
*/
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName) {
return create(expression, priceName, MathContext.EMPTY);
}
/**
* Create price from an expression (representing the value),
* and a price name. Uses a context to parse the expression.
* <p>
* Supports items as price names.
*
* @param expression The expression for the value.
* @param priceName The price name.
* @param context The math context to parse the expression.
* @return The price, or free if invalid.
*/
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final MathContext context) {
Function<MathContext, Double> function = (ctx) -> NumberUtils.evaluateExpression(
expression,
ctx
);
if (function.apply(context) <= 0) {
return new PriceFree();
}
// Default to economy
if (priceName == null) {
return new PriceEconomy(context, function);
}
// Find price factory
PriceFactory factory = FACTORIES.get(priceName);
// If no price factory, default to item price
if (factory == null) {
TestableItem item = Items.lookup(priceName);
if (item instanceof EmptyTestableItem) {
return new PriceFree();
}
return new PriceItem(context, function, item);
} else {
return factory.create(context, function);
}
}
private Prices() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,93 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
/**
* Economy-based price (for Vault, Treasury, etc.)
*/
public final class PriceEconomy implements Price {
/**
* The value of the price.
*/
private final Function<MathContext, Double> function;
/**
* The base math context.
*/
private final MathContext baseContext;
/**
* The multipliers.
*/
private final Map<UUID, Double> multipliers = new HashMap<>();
/**
* Create a new economy-based price.
*
* @param value The value.
*/
public PriceEconomy(final double value) {
this(MathContext.EMPTY, ctx -> value);
}
/**
* Create a new economy-based price.
*
* @param baseContext The base context.
* @param function The function.
*/
public PriceEconomy(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
this.baseContext = baseContext;
this.function = function;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return EconomyManager.getBalance(player) >= getValue(player, multiplier);
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
EconomyManager.removeMoney(player, getValue(player, multiplier));
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
EconomyManager.giveMoney(player, getValue(player, multiplier));
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier;
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.multipliers.getOrDefault(player.getUniqueId(), 1.0);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.multipliers.put(player.getUniqueId(), multiplier);
}
@Override
public String getIdentifier() {
return "eco:economy";
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Free (default) price.
*/
public final class PriceFree implements Price {
/**
* Create a new free price.
*/
public PriceFree() {
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return true;
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public double getMultiplier(@NotNull final Player player) {
return 1.0;
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return 0;
}
@Override
public String getIdentifier() {
return "eco:free";
}
}

View File

@@ -0,0 +1,160 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.drops.DropQueue;
import com.willfp.eco.core.items.HashedItem;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.price.Price;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
/**
* Item-based price.
*/
public final class PriceItem implements Price {
/**
* The base MathContext.
*/
private final MathContext baseContext;
/**
* The amount of items.
*/
private final Function<MathContext, Double> function;
/**
* The item.
*/
private final TestableItem item;
/**
* The multipliers.
*/
private final Map<UUID, Double> multipliers = new HashMap<>();
/**
* Create a new item-based price.
*
* @param amount The amount.
* @param item The item.
*/
public PriceItem(final int amount,
@NotNull final TestableItem item) {
this(MathContext.EMPTY, ctx -> (double) amount, item);
}
/**
* Create a new item-based price.
*
* @param baseContext The base MathContext.
* @param function The function to get the amount of items to remove.
* @param item The item.
*/
public PriceItem(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function,
@NotNull final TestableItem item) {
this.baseContext = baseContext;
this.function = function;
this.item = item;
}
/**
* Get the item.
*
* @return The item.
*/
public TestableItem getItem() {
return item;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
int toRemove = (int) getValue(player, multiplier);
if (toRemove <= 0) {
return true;
}
int count = 0;
for (ItemStack itemStack : player.getInventory().getContents()) {
if (item.matches(itemStack)) {
count += itemStack.getAmount();
}
}
return count >= toRemove;
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
int toRemove = (int) getValue(player, multiplier);
int count = 0;
for (ItemStack itemStack : player.getInventory().getContents()) {
if (count >= toRemove) {
break;
}
if (item.matches(itemStack)) {
int itemAmount = itemStack.getAmount();
if (itemAmount > toRemove) {
itemStack.setAmount(itemAmount - toRemove);
}
if (itemAmount <= toRemove) {
itemStack.setAmount(0);
itemStack.setType(Material.AIR);
}
count += itemAmount;
}
}
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
ItemStack itemStack = item.getItem().clone();
itemStack.setAmount((int) getValue(player, multiplier));
new DropQueue(player)
.addItem(itemStack)
.forceTelekinesis()
.push();
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player))
* getMultiplier(player) * multiplier
));
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.multipliers.getOrDefault(player.getUniqueId(), 1.0);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.multipliers.put(player.getUniqueId(), multiplier);
}
@Override
public String getIdentifier() {
return "eco:item-" + HashedItem.of(this.item.getItem()).getHash();
}
}

View File

@@ -7,7 +7,10 @@ import org.jetbrains.annotations.NotNull;
* A cleaner is an internal component to fix classloader errors. * A cleaner is an internal component to fix classloader errors.
* <p> * <p>
* Important to allow for PlugMan/ServerUtils support. * Important to allow for PlugMan/ServerUtils support.
*
* @deprecated No reason for this to be in the API.
*/ */
@Deprecated(since = "6.43.0", forRemoval = true)
public interface Cleaner { public interface Cleaner {
/** /**
* Clean up classes left over from plugin. * Clean up classes left over from plugin.

View File

@@ -21,7 +21,8 @@ public final class ProxyConstants {
"v1_17_R1", "v1_17_R1",
"v1_18_R1", "v1_18_R1",
"v1_18_R2", "v1_18_R2",
"v1_19_R1" "v1_19_R1",
"v1_19_R2"
); );
private ProxyConstants() { private ProxyConstants() {

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.core.recipe.recipes;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import com.willfp.eco.core.items.TestableItem; import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.Recipes; import com.willfp.eco.core.recipe.Recipes;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem; import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
@@ -25,7 +24,12 @@ import java.util.List;
/** /**
* Shaped 3x3 crafting recipe. * Shaped 3x3 crafting recipe.
*/ */
public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> implements CraftingRecipe { public final class ShapedCraftingRecipe implements CraftingRecipe {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/** /**
* Recipe parts. * Recipe parts.
*/ */
@@ -56,8 +60,7 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
@NotNull final List<TestableItem> parts, @NotNull final List<TestableItem> parts,
@NotNull final ItemStack output, @NotNull final ItemStack output,
@Nullable final String permission) { @Nullable final String permission) {
super(plugin); this.plugin = plugin;
this.parts = parts; this.parts = parts;
this.key = plugin.getNamespacedKeyFactory().create(key); this.key = plugin.getNamespacedKeyFactory().create(key);
this.displayedKey = plugin.getNamespacedKeyFactory().create(key + "_displayed"); this.displayedKey = plugin.getNamespacedKeyFactory().create(key + "_displayed");
@@ -96,7 +99,7 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
shapedRecipe.setIngredient(character, parts.get(i).getItem().getType()); shapedRecipe.setIngredient(character, parts.get(i).getItem().getType());
} }
if (Eco.getHandler().getEcoPlugin().getConfigYml().getBool("displayed-recipes")) { if (Eco.get().getEcoPlugin().getConfigYml().getBool("displayed-recipes")) {
ShapedRecipe displayedRecipe = new ShapedRecipe(this.getDisplayedKey(), this.getOutput()); ShapedRecipe displayedRecipe = new ShapedRecipe(this.getDisplayedKey(), this.getOutput());
displayedRecipe.shape("012", "345", "678"); displayedRecipe.shape("012", "345", "678");
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
@@ -124,7 +127,7 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
assert lore != null; assert lore != null;
lore.add(""); lore.add("");
String add = Eco.getHandler().getEcoPlugin().getLangYml().getFormattedString("multiple-in-craft"); String add = Eco.get().getEcoPlugin().getLangYml().getFormattedString("multiple-in-craft");
add = add.replace("%amount%", String.valueOf(item.getAmount())); add = add.replace("%amount%", String.valueOf(item.getAmount()));
lore.add(add); lore.add(add);
meta.setLore(lore); meta.setLore(lore);
@@ -145,6 +148,15 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
Bukkit.getServer().addRecipe(shapedRecipe); Bukkit.getServer().addRecipe(shapedRecipe);
} }
/**
* Get the plugin.
*
* @return The plugin.
*/
public EcoPlugin getPlugin() {
return plugin;
}
/** /**
* Create a new recipe builder. * Create a new recipe builder.
* *

Some files were not shown because too many files have changed in this diff Show More