Compare commits

..

187 Commits

Author SHA1 Message Date
Auxilor
c8f710161d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2023-06-13 15:29:12 +02:00
Auxilor
f840a55734 Fixed 1.20 bugs 2023-06-13 15:28:12 +02:00
Auxilor
36677fe503 Updated to 6.63.1 2023-06-08 21:30:26 +01:00
Auxilor
7e74223352 Added 1.20 support 2023-06-08 21:30:16 +01:00
Auxilor
b6f2b9d4ea Added global %player% placeholder 2023-06-05 14:09:17 +01:00
Auxilor
f320e77008 Updated to 6.64.0 2023-06-05 13:44:50 +01:00
Auxilor
c8b255b358 Added placeholder extension methods 2023-06-05 13:44:42 +01:00
Auxilor
5b5e161062 Added head arg parser 2023-06-05 13:22:21 +01:00
Auxilor
ffa511176f Improved packet event error logging 2023-06-03 15:39:38 +01:00
Auxilor
8515ff2b7b Updated to 6.63.0 2023-06-03 15:13:51 +01:00
Auxilor
74aeaec257 Added NumberUtils.evaluateExpressionOrNull 2023-06-03 15:13:42 +01:00
Auxilor
0ce3d294d1 Updated to 6.62.2 2023-06-03 14:23:33 +01:00
Auxilor
7c7052f5b9 Placeholders with identical patterns will now override previous registrations 2023-06-03 14:23:15 +01:00
Auxilor
a6b7dda82d Added check to prevent !!float in configs 2023-06-03 14:20:59 +01:00
Auxilor
04aaefb9ea Updated to 6.62.1 2023-06-03 14:03:23 +01:00
Auxilor
d2fdb4f8e0 Packet events will no longer kick players if exceptions are thrown 2023-06-03 14:03:14 +01:00
Auxilor
87f90d8b26 Updated to 6.62.0 2023-05-24 14:45:37 +01:00
Auxilor
899d5cc054 Added caching to literal pattern compilation 2023-05-24 14:41:15 +01:00
Auxilor
c1ed771eb3 Updated to 6.61.1 2023-05-22 13:22:03 +01:00
Auxilor
08a4d9d6b1 Changed playerflow to use local server ID 2023-05-22 01:35:23 +01:00
Auxilor
7ef8dcfd64 Merge branch 'develop' 2023-05-21 19:07:56 +01:00
Auxilor
5bedf88b4c Updated lang.yml 2023-05-21 19:03:47 +01:00
Auxilor
1d241651b5 Added config option for playerflow 2023-05-21 17:01:13 +01:00
Auxilor
3ff2bfa412 Updated playerflow URL 2023-05-21 16:58:21 +01:00
Auxilor
3a508c693b Updated to 6.61.0 2023-05-21 16:38:35 +01:00
Auxilor
a4c5ff921e Added playerflow 2023-05-21 16:38:17 +01:00
Auxilor
137e9dc7d6 Added global price display names 2023-05-20 18:01:45 +01:00
Auxilor
710cec4bc1 Merge branch 'develop' 2023-05-19 13:17:05 +01:00
Auxilor
fa0ec7d6b0 Updated to 6.60.4 2023-05-19 13:16:20 +01:00
Auxilor
5093799775 Merge remote-tracking branch 'origin/develop' into develop 2023-05-19 13:15:34 +01:00
Will FP
8535f23ede Merge pull request #274
Fix PvPManager integration
2023-05-19 13:15:24 +01:00
Will FP
8e09ae7f4c Merge pull request #275
Add RoyaleEconomy to prices system
2023-05-19 13:14:54 +01:00
Sen2000
bbc2513b40 Fix RoyaleEconomy integration 2023-05-19 16:54:05 +07:00
Sen2000
c7f8063a3a Add RoyaleEconomy to prices system 2023-05-19 09:11:17 +07:00
ChanceSD
14b0f1be0c Fix PvPManager integration 2023-05-18 22:42:49 +01:00
Auxilor
af20bb315b Updated to 6.60.3 2023-05-18 15:35:54 +01:00
Auxilor
6645e216d5 Fixed stupid profile saver bug 2023-05-18 15:35:47 +01:00
Auxilor
eddf240f0c Updated to 6.60.2 2023-05-18 14:33:21 +01:00
Auxilor
4f406353ba Fixed data bug 2023-05-18 14:33:14 +01:00
Auxilor
095494dd2e Fixed PersistentDataKeyType.BIG_DECIMAL and javadoc 2023-05-17 18:39:48 +01:00
Auxilor
fd92645500 Updated to 6.60.1 2023-05-17 17:27:00 +01:00
Auxilor
1a6ac29083 Fixed PersistentDataKeyType.BIG_DECIMAL 2023-05-17 17:26:48 +01:00
Auxilor
7edc00d459 Added createTasks 2023-05-16 14:50:02 +01:00
Auxilor
a51bad058f Updated to 6.60.0 2023-05-16 14:47:06 +01:00
Auxilor
89ebb8ba59 Updated to 6.59.1 2023-05-15 17:43:03 +01:00
Auxilor
f0ae8f4f84 Fixed DelegatedBukkitCommand (again) 2023-05-15 17:42:49 +01:00
Auxilor
7d6cf78442 Cleanup 2023-05-15 16:38:33 +01:00
Auxilor
780d8f3b86 Fixed DefaultMap 2023-05-15 16:33:58 +01:00
Auxilor
146a0130f9 Fixed DefaultMap 2023-05-15 16:33:32 +01:00
Auxilor
df8c3411cb Updated to 6.59.0 2023-05-15 12:06:56 +01:00
Auxilor
4fc3c22a7d Added PersistentDataKeyType#BIG_DECIMAL 2023-05-15 12:05:37 +01:00
Auxilor
cfc4808bb8 Updated to 6.58.1 2023-05-14 16:36:05 +01:00
Auxilor
4ac6325a41 Fixed T?.toSingletonList() 2023-05-14 16:35:56 +01:00
Auxilor
4aed33751d EcoPlugin#afterLoad now runs 2 ticks after, with a preliminary reload 1 tick after startup to fix load order bugs 2023-05-13 17:43:31 +01:00
Auxilor
3fe1c2c69f Added EcoPlugin#cancelsTasksOnReload 2023-05-13 12:10:08 +01:00
Auxilor
5feaa84b2c Revert "Probably janky solution to command aliases"
This reverts commit 862b588c8d.
2023-05-12 16:57:12 +01:00
Auxilor
d8793bc2bb Fixed command aliases 2023-05-12 16:54:03 +01:00
Auxilor
15c512f3ca Cleanup 2023-05-12 15:40:28 +01:00
Auxilor
15ff2fb1d6 Fixed registry locks 2023-05-12 15:39:51 +01:00
Auxilor
862b588c8d Probably janky solution to command aliases 2023-05-12 15:37:29 +01:00
Auxilor
3c2a99b5f4 Added MenuBuilder#defaultPage 2023-05-12 14:53:44 +01:00
Auxilor
2d23c05c47 Added kotlin extensions to NumberUtils methods 2023-05-12 14:26:00 +01:00
Auxilor
8fc55d3393 Merge remote-tracking branch 'origin/develop' into develop 2023-05-12 13:53:45 +01:00
Auxilor
5900a756e4 Added margin options to line wrapping 2023-05-12 13:53:38 +01:00
Auxilor
c18b85f223 AAAAAAA 2023-05-12 13:53:16 +01:00
Auxilor
85116108c2 Oops 2023-05-12 13:47:47 +01:00
Auxilor
044f141bd0 Added margin options to line wrapping 2023-05-12 13:45:21 +01:00
Auxilor
9ca7f99fdb Added StringUtils#lineWrap kotlin extensions 2023-05-12 13:28:21 +01:00
Auxilor
7aa7770a3e Added StringUtils#lineWrap for lists 2023-05-12 13:27:13 +01:00
Auxilor
10202917fa Added StringUtils#lineWrap to wrap strings while preserving formatting 2023-05-11 17:24:05 +01:00
Auxilor
43df79e3b1 Fixed captive slots not forcing a re-render on shift click 2023-05-10 16:03:29 +01:00
Auxilor
5ef244f0bc Added option to store plugin data locally, even if eco is set to use a database 2023-05-10 15:19:15 +01:00
Auxilor
861f076c11 Updated to 6.58.0 2023-05-10 14:52:52 +01:00
Auxilor
7bed43059f Added Slot#shouldRenderOnClick 2023-05-08 18:39:27 +01:00
Auxilor
37e271c96c More optimisations to EcoConfig 2023-05-04 14:32:52 +01:00
Auxilor
3dad48e24d Updated to 6.57.2 2023-05-03 23:45:01 +01:00
Auxilor
ae77e4810b Digsusting hacks to optimise eval pipeline 2023-05-03 23:44:53 +01:00
Auxilor
3d50e37c37 Merge branch 'master' into develop 2023-05-03 23:01:41 +01:00
Auxilor
421fd3bd04 Finally removed LegacyMySQLDataHandler 2023-05-03 16:03:36 +01:00
Auxilor
5ecae0a366 Updated to 6.57.1 2023-05-03 14:19:01 +01:00
Auxilor
5de4914fd7 Fixed expression loading, improved hash codes down evaluation pipeline 2023-05-03 14:18:50 +01:00
Auxilor
0f9bf094ae Ignore case 2023-05-02 18:47:54 +01:00
Auxilor
e67680f397 Improved PlaceholderAPI 2023-05-02 17:39:15 +01:00
Auxilor
73c0a5d655 Fixed more stupidity 2023-05-02 17:17:41 +01:00
Auxilor
220ed26f4a Fixed stupidity 2023-05-02 17:14:03 +01:00
Auxilor
edf2ea41c7 Cleaned up RECIPE ERROR message to be nicer 2023-05-02 16:13:33 +01:00
Auxilor
16859b8ce5 Revert "Temporarily disabled Wolfyscript"
This reverts commit f973281dd9.
2023-05-02 15:01:01 +01:00
Auxilor
10fe7d190a Clean 2023-05-02 14:58:25 +01:00
Auxilor
60a1f2429c Added plugin-version and plugin to extension.yml 2023-05-02 14:26:53 +01:00
Auxilor
cc6dc1e67c Improved PAPI expansion 2023-05-02 13:57:23 +01:00
Auxilor
f4f5941691 Fixed duplicate config placeholder bug 2023-05-02 13:56:10 +01:00
Auxilor
f973281dd9 Temporarily disabled Wolfyscript 2023-05-02 13:55:50 +01:00
Auxilor
8acd76f363 Updated to 6.57.0 2023-05-01 19:53:45 +01:00
Auxilor
4a165a86dc Added PluginLike#getFile 2023-05-01 19:53:37 +01:00
Auxilor
43b7c393b9 Cleaned up data desync PR 2023-04-29 15:52:13 +01:00
Auxilor
714952bc45 PR Cleanup 2023-04-29 15:47:42 +01:00
Will FP
322e179b81 Merge pull request #269
Add PlayerPoints to prices system
2023-04-29 15:43:54 +01:00
Auxilor
469be73ada Added option to translate placeholders without a context 2023-04-29 15:03:02 +01:00
Auxilor
7eac60146f Added placeholderContext#copy 2023-04-29 13:44:03 +01:00
Auxilor
ad0223e1bb Cleaned up PlaceholderContext 2023-04-29 13:22:18 +01:00
BuildTools
430117f342 Add PlayerPoints to prices system 2023-04-29 10:20:53 +07:00
Auxilor
fa87cae81e Clarified PlaceholderParser 2023-04-28 19:59:13 +01:00
Auxilor
5695750fc5 Optimised additional player placeholder parsing 2023-04-28 17:05:40 +01:00
Auxilor
45e8a57880 Fixed DynamicInjectablePlaceholder 2023-04-28 16:24:34 +01:00
Auxilor
17fcd2c1b5 Added DynamicPlaceholder and DynamicInjectablePlaceholder 2023-04-28 16:24:18 +01:00
Auxilor
0c1e17c351 Massively optimised placeholder parsing with ListViewOfCollection 2023-04-28 16:11:21 +01:00
Auxilor
9415515849 Simplified enable logs 2023-04-28 14:34:30 +01:00
Auxilor
d0e957ea37 Cleaned up imports 2023-04-28 14:23:44 +01:00
Will FP
69514e2f85 Merge pull request #268
Denizen Integration
2023-04-28 14:22:29 +01:00
FireML
259e35c978 Script Name Alternative 2023-04-28 00:47:01 +01:00
FireML
40aec26f15 Initial Denizen Support 2023-04-27 23:03:34 +01:00
Auxilor
67b2fdd594 Merge branch 'master' into develop
# Conflicts:
#	gradle.properties
2023-04-27 19:27:57 +01:00
Auxilor
83f86983f6 Updated to 6.55.4 2023-04-27 19:22:30 +01:00
Auxilor
3ba98a9a5e Fixed UltraEconomy 2023-04-27 19:22:20 +01:00
Auxilor
f19e565fbe Moved to native fastToDoubleOrNull function 2023-04-27 19:08:10 +01:00
Auxilor
ee3ecb643b Cleanup 2023-04-27 18:55:03 +01:00
Auxilor
3aefb0e481 Switched back to ConcurrentHashMap in EcoConfig 2023-04-27 18:04:02 +01:00
Auxilor
d9eb1e1c26 Merge branch 'fix-data-desync' into develop 2023-04-27 18:02:09 +01:00
Auxilor
70631e67c5 Config injections no longer use a ConcurrentHashMap 2023-04-27 15:51:16 +01:00
Auxilor
82797e7342 Rewrote placeholder parsing 2023-04-27 15:42:46 +01:00
Auxilor
d2917341b1 Removed use-lower-protocollib-priority 2023-04-26 20:31:53 +01:00
Auxilor
760f42be0c Refactor, made math cache TTL configurable 2023-04-26 20:31:44 +01:00
Auxilor
bb01af2ab2 Optimised StringUtils#replaceQuickly 2023-04-26 18:22:56 +01:00
Auxilor
517d890c05 More optimisations to evaluation pipeline 2023-04-26 17:37:31 +01:00
Auxilor
f02fc56778 Added alternate crunch evaluation option 2023-04-26 16:55:40 +01:00
Auxilor
4417b09c14 Various optimisations along the evaluation pipeline 2023-04-26 16:10:04 +01:00
Auxilor
6b37fafa90 Further simplified Config 2023-04-26 15:25:50 +01:00
Auxilor
f8c5c9f06d Revert "Added KPlaceholderContext"
This reverts commit 0fd6cbaa08.
2023-04-26 15:02:05 +01:00
Auxilor
0fd6cbaa08 Added KPlaceholderContext 2023-04-26 14:58:48 +01:00
Auxilor
81afa32eb0 Added placeholderContext kotlin builder 2023-04-26 14:47:40 +01:00
Auxilor
7387fe2332 Improved default config methods 2023-04-26 14:44:13 +01:00
Auxilor
80fa05da98 Added PlaceholderContext#copyWithItem 2023-04-26 14:07:22 +01:00
Auxilor
77754249ad Added SimplePlaceholder and SimpleInjectablePlaceholder 2023-04-26 13:23:29 +01:00
Auxilor
1843cf0f8a PlaceholderManager#translatePlaceholders now includes injections 2023-04-26 13:13:59 +01:00
Auxilor
05eb5ee993 Rewrote more placeholder backend, deprecated MathContext 2023-04-26 13:07:18 +01:00
Auxilor
7bc11ee716 Added new format options 2023-04-25 19:19:43 +01:00
Auxilor
f566aec00e PlaceholderContext now extends MathContext 2023-04-25 19:11:15 +01:00
Auxilor
36d47c55a1 Updated to 6.56.0 2023-04-25 18:57:22 +01:00
Auxilor
40463213ac Added PlaceholderContext as a unified way to parse placeholders 2023-04-25 18:57:06 +01:00
Auxilor
6ec80d30ad Updated to 6.55.3 2023-04-25 10:28:38 +01:00
Auxilor
7ccee60a0c Fixed IntegrationRegistry#executeSafely 2023-04-25 10:28:23 +01:00
Auxilor
0805b48763 Updated to 6.55.2 2023-04-24 22:12:47 +01:00
Auxilor
d737322aaa Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2023-04-24 22:12:19 +01:00
Auxilor
4ddc150e1c Updated to 6.54.1 2023-04-24 22:11:55 +01:00
Auxilor
0da119d89d Finally reimplemented BlockUtils#getVein 2023-04-24 22:10:42 +01:00
Auxilor
fc0a07d1c5 Config injections now use a ConcurrentHashMap 2023-04-24 22:05:37 +01:00
Cyramek
4ce2850138 fix data desync 2023-04-23 14:35:17 +02:00
Auxilor
58316c2a06 Fixed locale bug with Registry 2023-04-20 20:32:07 +01:00
Auxilor
d96ad10960 Added locker to registry 2023-04-20 19:27:52 +01:00
Auxilor
21283b0928 Added ExternalDataStoreObjectAdapter 2023-04-20 19:19:00 +01:00
Auxilor
2797687f01 Cleanup 2023-04-20 17:54:23 +01:00
Auxilor
9c68d1fbc3 Merge branch 'master' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/customitems/CustomItemsOraxen.kt
2023-04-20 17:45:06 +01:00
Auxilor
9371d9b88d Upstream 2023-04-20 17:44:58 +01:00
Auxilor
14e2ead488 Fixed javadoc, updated Kingdoms 2023-04-20 17:42:46 +01:00
Auxilor
5473bb8ef8 Cleaned up IntegrationRegistry 2023-04-20 17:34:18 +01:00
Auxilor
90c55849ae Isolated integration registration 2023-04-20 17:24:56 +01:00
Auxilor
9809140cf9 Isolated remaining integrations 2023-04-20 17:23:24 +01:00
Auxilor
49a82dc005 Isolated ShopManager 2023-04-20 17:15:18 +01:00
Auxilor
92dec03b9a Added IntegrationRegistry 2023-04-20 17:09:46 +01:00
Auxilor
7453c70b87 Fixed imports 2023-04-20 16:52:49 +01:00
Auxilor
3038ea43d0 Updated to 6.55.0 2023-04-20 16:51:11 +01:00
Auxilor
3c6ddd8255 Added OutdatedEcoVersionError 2023-04-20 16:50:35 +01:00
Auxilor
925ee04cc1 Added Version as a cross-version compatible version of DefaultArtifactVersion 2023-04-20 16:48:20 +01:00
Auxilor
6f7de8716b Added KRegistrable 2023-04-20 16:41:35 +01:00
Auxilor
a17b951a8b Made registry lockable and iterable 2023-04-20 16:40:06 +01:00
Auxilor
f003ed06a8 Fixed imports 2023-04-19 18:00:19 +01:00
Auxilor
f864953da2 Updated to 6.54.1 2023-04-19 17:38:22 +01:00
Auxilor
6ef31444ac Removed Checkstyle CI, cleaned up Oraxen PR 2023-04-19 17:38:09 +01:00
Will FP
99258116de Merge pull request #264 from MCCasper/master
fix oraxen and SNBT
2023-04-19 12:33:14 -04:00
casper
a59c68102e fix SNBT matching the same tag for diff materials 2023-04-19 11:09:26 -05:00
casper
2482525fe2 load oraxen later 2023-04-18 22:23:49 -05:00
Auxilor
acb326c0c8 Updated to 6.54.0 2023-04-13 12:13:32 -04:00
Auxilor
11d947e24b Updated to 6.53.2 2023-04-13 12:09:14 -04:00
Auxilor
3cced3012c Added Prerequisite#HAS_1_19_4 2023-04-13 11:59:04 -04:00
Auxilor
520523e903 Isolated integration loading 2023-04-13 11:57:49 -04:00
Auxilor
ee36cc74f8 Improved lifecycle error logging 2023-04-13 11:50:32 -04:00
Auxilor
55344e0550 Improved lifecycle error handling, isolated tasks 2023-04-13 11:49:43 -04:00
Auxilor
75f6f05c7d Merge branch 'master' into develop 2023-03-30 14:05:09 +01:00
Auxilor
17fa519501 Revert "Fixed ContinuallyAppliedPersistentDataContainer"
This reverts commit 1852ff86ec.
2023-03-30 14:04:43 +01:00
Auxilor
1a72cf3ca9 Revert "Updated to 6.53.2"
This reverts commit d11f355c44.
2023-03-30 14:04:43 +01:00
Auxilor
d11f355c44 Updated to 6.53.2 2023-03-30 12:08:37 +01:00
Auxilor
1852ff86ec Fixed ContinuallyAppliedPersistentDataContainer 2023-03-30 12:08:26 +01:00
Auxilor
ffe9219f45 Changed README to show repo.auxilor.io 2023-03-29 13:33:24 +01:00
Auxilor
f67a5d3b3d Added repo.auxilor.io 2023-03-29 13:30:15 +01:00
Auxilor
bbd541abe0 Added repo.auxilor.io 2023-03-29 13:21:40 +01:00
Auxilor
f5e289966f Fixed tab completion for dynamically registered commands 2023-03-28 17:12:35 +01:00
Auxilor
bcfa4bd82e Added json() extension 2023-03-25 17:48:27 +00:00
151 changed files with 4646 additions and 1374 deletions

View File

@@ -1,15 +0,0 @@
name: Check PR Codestyle
on: [ pull_request ]
jobs:
checkstyle:
name: Checkstyle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dbelyaev/action-checkstyle@v0.5.1
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
level: warning
checkstyle_config: ../../config/checkstyle/checkstyle.xml

View File

@@ -28,6 +28,8 @@ jobs:
- name: Publish artifact - name: Publish artifact
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
# The GITHUB_REF tag comes in the format 'refs/tags/xxx'. # The GITHUB_REF tag comes in the format 'refs/tags/xxx'.
# So if we split on '/' and take the 3rd value, we can get the release name. # So if we split on '/' and take the 3rd value, we can get the release name.

View File

@@ -63,7 +63,7 @@ eco comes packed with all the tools you need in your plugins:
# For developers # For developers
## Javadoc ## Javadoc
The 6.49.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.49.0/javadoc/) The 6.53.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.53.0/javadoc/)
## Plugin Information ## Plugin Information
@@ -75,31 +75,31 @@ depend:
- eco - eco
``` ```
## Get from JitPack: ## Dependency Information:
Gradle: Gradle:
```groovy ```kts
repositories { repositories {
maven { url 'https://jitpack.io' } maven("https://repo.auxilor.io/repository/maven-public/")
} }
``` ```
```groovy ```groovy
dependencies { dependencies {
compileOnly 'com.willfp:eco:Tag' compileOnly("com.willfp:eco:Tag")
} }
``` ```
Replace `Tag` with a release tag for eco, eg `6.49.0`. Replace `Tag` with a release tag for eco, eg `6.53.0`.
Maven: Maven:
```xml ```xml
<repository> <repository>
<id>jitpack.io</id> <id>auxilor</id>
<url>https://jitpack.io</url> <url>https://repo.auxilor.io/repository/maven-public/</url>
</repository> </repository>
``` ```
@@ -112,7 +112,7 @@ Maven:
</dependency> </dependency>
``` ```
Replace `Tag` with a release tag for eco, eg `6.49.0`. Replace `Tag` with a release tag for eco, eg `6.53.0`.
## Build locally: ## Build locally:

View File

@@ -27,6 +27,7 @@ dependencies {
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")) implementation(project(path = ":eco-core:core-nms:v1_19_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R3", configuration = "reobf")) implementation(project(path = ":eco-core:core-nms:v1_19_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R1", configuration = "reobf"))
} }
allprojects { allprojects {
@@ -82,6 +83,12 @@ allprojects {
// UltraEconomy // UltraEconomy
maven("https://repo.techscode.com/repository/maven-releases/") maven("https://repo.techscode.com/repository/maven-releases/")
// PlayerPoints
maven("https://repo.rosewooddev.io/repository/public/")
// Denizen
maven("https://maven.citizensnpcs.co/repo")
} }
dependencies { dependencies {

View File

@@ -39,4 +39,17 @@ publishing {
} }
} }
} }
publishing {
repositories {
maven {
name = "Auxilor"
url = uri("https://repo.auxilor.io/repository/maven-releases/")
credentials {
username = System.getenv("MAVEN_USERNAME")
password = System.getenv("MAVEN_PASSWORD")
}
}
}
}
} }

View File

@@ -25,8 +25,8 @@ import com.willfp.eco.core.gui.menu.MenuType;
import com.willfp.eco.core.gui.slot.SlotBuilder; import com.willfp.eco.core.gui.slot.SlotBuilder;
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 com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.packet.Packet; import com.willfp.eco.core.packet.Packet;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
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 net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
@@ -135,6 +135,14 @@ public interface Eco {
@NotNull @NotNull
Logger createLogger(@NotNull EcoPlugin plugin); Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Get NOOP logger.
*
* @return The logger.
*/
@NotNull
Logger getNOOPLogger();
/** /**
* Create a PAPI integration. * Create a PAPI integration.
* *
@@ -170,7 +178,7 @@ public interface Eco {
* @return The PluginCommandBase implementation * @return The PluginCommandBase implementation
*/ */
@NotNull @NotNull
PluginCommandBase createPluginCommand(@NotNull CommandBase parentDelegate, PluginCommandBase createPluginCommand(@NotNull PluginCommandBase parentDelegate,
@NotNull EcoPlugin plugin, @NotNull EcoPlugin plugin,
@NotNull String name, @NotNull String name,
@NotNull String permission, @NotNull String permission,
@@ -393,15 +401,6 @@ public interface Eco {
@NotNull @NotNull
ServerProfile getServerProfile(); 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. * Create dummy entity - never spawned, exists purely in code.
* *
@@ -528,10 +527,11 @@ public interface Eco {
* *
* @param expression The expression. * @param expression The expression.
* @param context The context. * @param context The context.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or null if invalid.
*/ */
double evaluate(@NotNull String expression, @Nullable
@NotNull MathContext context); Double evaluate(@NotNull String expression,
@NotNull PlaceholderContext context);
/** /**
* Get the menu a player currently has open. * Get the menu a player currently has open.
@@ -563,6 +563,30 @@ public interface Eco {
void sendPacket(@NotNull Player player, void sendPacket(@NotNull Player player,
@NotNull Packet packet); @NotNull Packet packet);
/**
* Translate placeholders in a string.
*
* @param text The text.
* @param context The context.
* @return The translated text.
*/
@NotNull
String translatePlaceholders(@NotNull String text,
@NotNull PlaceholderContext context);
/**
* Get the value of a placeholder.
*
* @param plugin The plugin that owns the placeholder.
* @param args The placeholder arguments.
* @param context The context.
* @return The value, or null if invalid.
*/
@Nullable
String getPlaceholderValue(@Nullable EcoPlugin plugin,
@NotNull String args,
@NotNull PlaceholderContext context);
/** /**
* Get the instance of eco; the bridge between the api frontend and the implementation backend. * Get the instance of eco; the bridge between the api frontend and the implementation backend.
* *

View File

@@ -19,9 +19,10 @@ import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.registry.Registrable; import com.willfp.eco.core.registry.Registrable;
import com.willfp.eco.core.registry.Registry; import com.willfp.eco.core.registry.Registry;
import com.willfp.eco.core.scheduling.Scheduler; import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.version.OutdatedEcoVersionError;
import com.willfp.eco.core.version.Version;
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.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
@@ -32,12 +33,14 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -123,7 +126,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
/** /**
* The logger for the plugin. * The logger for the plugin.
*/ */
private final Logger logger; private Logger logger;
/** /**
* If the server is running an outdated version of the plugin. * If the server is running an outdated version of the plugin.
@@ -161,6 +164,11 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
*/ */
private final ListMap<LifecyclePosition, Runnable> afterLoad = new ListMap<>(); private final ListMap<LifecyclePosition, Runnable> afterLoad = new ListMap<>();
/**
* The tasks to run on task creation.
*/
private final ListMap<LifecyclePosition, Runnable> createTasks = new ListMap<>();
/** /**
* Create a new plugin. * Create a new plugin.
* <p> * <p>
@@ -346,14 +354,14 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
they have an outdated version of eco installed. they have an outdated version of eco installed.
*/ */
DefaultArtifactVersion runningVersion = new DefaultArtifactVersion(Eco.get().getEcoPlugin().getDescription().getVersion()); Version runningVersion = new Version(Eco.get().getEcoPlugin().getDescription().getVersion());
DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(this.getMinimumEcoVersion()); Version requiredVersion = new Version(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!");
this.getLogger().severe("You must be on at least" + this.getMinimumEcoVersion()); this.getLogger().severe("You must be on at least" + this.getMinimumEcoVersion());
this.getLogger().severe("Download the newest version here:"); this.getLogger().severe("Download the newest version here:");
this.getLogger().severe("https://polymart.org/download/773/recent/JSpprMspkuyecf5y1wQ2Jn8OoLQSQ_IW"); this.getLogger().severe("https://polymart.org/download/773/recent/JSpprMspkuyecf5y1wQ2Jn8OoLQSQ_IW");
Bukkit.getPluginManager().disablePlugin(this); throw new OutdatedEcoVersionError("This plugin requires at least eco version " + this.getMinimumEcoVersion() + " to run.");
} }
} }
@@ -364,13 +372,12 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
public final void onEnable() { public final void onEnable() {
super.onEnable(); super.onEnable();
this.getLogger().info("");
this.getLogger().info("Loading " + this.getColor() + this.getName()); this.getLogger().info("Loading " + this.getColor() + this.getName());
if (this.getResourceId() != 0 && !Eco.get().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()); Version currentVersion = new Version(this.getDescription().getVersion());
DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version); Version mostRecentVersion = new Version(version);
if (!(currentVersion.compareTo(mostRecentVersion) > 0 || currentVersion.equals(mostRecentVersion))) { if (!(currentVersion.compareTo(mostRecentVersion) > 0 || currentVersion.equals(mostRecentVersion))) {
this.outdated = true; this.outdated = true;
this.getLogger().warning(this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")"); this.getLogger().warning(this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")");
@@ -392,14 +399,21 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.loadIntegrationLoaders().forEach(integrationLoader -> { this.loadIntegrationLoaders().forEach(integrationLoader -> {
if (enabledPlugins.contains(integrationLoader.getPluginName().toLowerCase())) { if (enabledPlugins.contains(integrationLoader.getPluginName().toLowerCase())) {
this.loadedIntegrations.add(integrationLoader.getPluginName()); try {
integrationLoader.load(); integrationLoader.load();
this.loadedIntegrations.add(integrationLoader.getPluginName());
} catch (Exception e) {
this.getLogger().warning("Failed to load integration for " + integrationLoader.getPluginName());
e.printStackTrace();
}
} }
}); });
this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName())); this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName()));
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations())); if (!this.getLoadedIntegrations().isEmpty()) {
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
}
Prerequisite.update(); Prerequisite.update();
@@ -416,24 +430,35 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.loadPluginCommands().forEach(PluginCommand::register); this.loadPluginCommands().forEach(PluginCommand::register);
this.getScheduler().runLater(this::afterLoad, 1); // Run preliminary reload to resolve load order issues
this.getScheduler().runLater(() -> {
Logger before = this.getLogger();
// Temporary silence logger.
this.logger = Eco.get().getNOOPLogger();
this.reload(false);
this.logger = before;
}, 1);
this.getScheduler().runLater(this::afterLoad, 2);
if (this.isSupportingExtensions()) { if (this.isSupportingExtensions()) {
this.getExtensionLoader().loadExtensions(); this.getExtensionLoader().loadExtensions();
if (this.getExtensionLoader().getLoadedExtensions().isEmpty()) { if (!this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
this.getLogger().info("&cNo extensions found"); List<String> loadedExtensions = this.getExtensionLoader().getLoadedExtensions().stream().map(
} else { extension -> extension.getName() + " v" + extension.getVersion()
this.getLogger().info("Extensions Loaded:"); ).toList();
this.getExtensionLoader().getLoadedExtensions().forEach(extension -> this.getLogger().info("- " + extension.getName() + " v" + extension.getVersion()));
this.getLogger().info(
"Loaded extensions: " +
String.join(", ", loadedExtensions)
);
} }
} }
this.onEnable.get(LifecyclePosition.START).forEach(Runnable::run); this.handleLifecycle(this.onEnable, this::handleEnable);
this.handleEnable();
this.onEnable.get(LifecyclePosition.END).forEach(Runnable::run);
this.getLogger().info("");
} }
/** /**
@@ -466,9 +491,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.getEventManager().unregisterAllListeners(); this.getEventManager().unregisterAllListeners();
this.getScheduler().cancelAll(); this.getScheduler().cancelAll();
this.onDisable.get(LifecyclePosition.START).forEach(Runnable::run); this.handleLifecycle(this.onDisable, this::handleDisable);
this.handleDisable();
this.onDisable.get(LifecyclePosition.END).forEach(Runnable::run);
if (this.isSupportingExtensions()) { if (this.isSupportingExtensions()) {
this.getExtensionLoader().unloadExtensions(); this.getExtensionLoader().unloadExtensions();
@@ -505,9 +528,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
public final void onLoad() { public final void onLoad() {
super.onLoad(); super.onLoad();
this.onLoad.get(LifecyclePosition.START).forEach(Runnable::run); this.handleLifecycle(this.onLoad, this::handleLoad);
this.handleLoad();
this.onLoad.get(LifecyclePosition.END).forEach(Runnable::run);
} }
/** /**
@@ -561,9 +582,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.getLogger().severe(""); this.getLogger().severe("");
} }
this.afterLoad.get(LifecyclePosition.START).forEach(Runnable::run); this.handleLifecycle(this.afterLoad, this::handleAfterLoad);
this.handleAfterLoad();
this.afterLoad.get(LifecyclePosition.END).forEach(Runnable::run);
this.reload(); this.reload();
@@ -598,15 +617,29 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Reload the plugin. * Reload the plugin.
*/ */
public final void reload() { public final void reload() {
this.reload(true);
}
/**
* Reload the plugin.
*
* @param cancelTasks If tasks should be cancelled.
*/
public final void reload(final boolean cancelTasks) {
this.getConfigHandler().updateConfigs(); this.getConfigHandler().updateConfigs();
this.getScheduler().cancelAll(); if (cancelTasks) {
this.getScheduler().cancelAll();
}
this.getConfigHandler().callUpdate(); this.getConfigHandler().callUpdate();
this.getConfigHandler().callUpdate(); // Call twice to fix issues this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.onReload.get(LifecyclePosition.START).forEach(Runnable::run); this.handleLifecycle(this.onReload, this::handleReload);
this.handleReload();
this.onReload.get(LifecyclePosition.END).forEach(Runnable::run); if (cancelTasks) {
this.handleLifecycle(this.createTasks, this::createTasks);
}
for (Extension extension : this.extensionLoader.getLoadedExtensions()) { for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload(); extension.handleReload();
@@ -646,6 +679,43 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
return System.currentTimeMillis() - startTime; return System.currentTimeMillis() - startTime;
} }
/**
* Handle lifecycle.
*
* @param tasks The tasks.
* @param handler The handler.
*/
private void handleLifecycle(@NotNull final ListMap<LifecyclePosition, Runnable> tasks,
@NotNull final Runnable handler) {
for (Runnable task : tasks.get(LifecyclePosition.START)) {
try {
task.run();
} catch (final Exception e) {
this.getLogger().log(Level.SEVERE, "Error while running lifecycle task!");
this.getLogger().log(Level.SEVERE, "The plugin may not function properly");
e.printStackTrace();
}
}
try {
handler.run();
} catch (final Exception e) {
this.getLogger().log(Level.SEVERE, "Error while running lifecycle task!");
this.getLogger().log(Level.SEVERE, "The plugin may not function properly");
e.printStackTrace();
}
for (Runnable task : tasks.get(LifecyclePosition.END)) {
try {
task.run();
} catch (final Exception e) {
this.getLogger().log(Level.SEVERE, "Error while running lifecycle task!");
this.getLogger().log(Level.SEVERE, "The plugin may not function properly");
e.printStackTrace();
}
}
}
/** /**
* The plugin-specific code to be executed on enable. * The plugin-specific code to be executed on enable.
* <p> * <p>
@@ -684,6 +754,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
} }
/**
* The plugin-specific code to create tasks.
* <p>
* Override when needed.
*/
protected void createTasks() {
}
/** /**
* The plugin-specific code to be executed after the server is up. * The plugin-specific code to be executed after the server is up.
* <p> * <p>
@@ -1048,7 +1127,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* *
* @return The config handler. * @return The config handler.
*/ */
public ConfigHandler getConfigHandler() { public @NotNull ConfigHandler getConfigHandler() {
return this.configHandler; return this.configHandler;
} }
@@ -1112,9 +1191,24 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
return this.getMetadataValueFactory().create(value); return this.getMetadataValueFactory().create(value);
} }
/**
* Get if all {@link com.willfp.eco.core.data.keys.PersistentDataKey}'s for this
* plugin should be saved locally (via data.yml.) even if eco is using a database.
*
* @return If using local storage.
*/
public boolean isUsingLocalStorage() {
return this.configYml.isUsingLocalStorage();
}
@Override @Override
@NotNull @NotNull
public final String getID() { public final String getID() {
return Registry.tryFitPattern(this.getName()); return Registry.tryFitPattern(this.getName());
} }
@Override
public @NotNull File getFile() {
return super.getFile();
}
} }

View File

@@ -1,6 +1,8 @@
package com.willfp.eco.core; package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler; import com.willfp.eco.core.config.updating.ConfigHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -13,12 +15,13 @@ import java.util.logging.Logger;
*/ */
public interface PluginLike { public interface PluginLike {
/** /**
* Get the data folder of the object. * Get the data folder.
* <p> * <p>
* Returns the plugin data folder for a plugin, or the extension's parent plugin's folder * Returns the plugin data folder for a plugin, or the extension's parent plugin's folder
* *
* @return The data folder. * @return The data folder.
*/ */
@NotNull
File getDataFolder(); File getDataFolder();
/** /**
@@ -26,6 +29,7 @@ public interface PluginLike {
* *
* @return The config handler. * @return The config handler.
*/ */
@NotNull
ConfigHandler getConfigHandler(); ConfigHandler getConfigHandler();
/** /**
@@ -33,5 +37,16 @@ public interface PluginLike {
* *
* @return The logger. * @return The logger.
*/ */
@NotNull
Logger getLogger(); Logger getLogger();
/**
* Get the actual file.
*
* @return The file, i.e. the jar file.
*/
@Nullable
default File getFile() {
return null;
}
} }

View File

@@ -37,11 +37,27 @@ public class Prerequisite {
"Requires server to have ProtocolLib" "Requires server to have ProtocolLib"
); );
/**
* Requires the server to be running 1.20.
*/
public static final Prerequisite HAS_1_20 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20"),
"Requires server to be running 1.20+"
);
/**
* Requires the server to be running 1.19.4.
*/
public static final Prerequisite HAS_1_19_4 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19_R3") || HAS_1_20.isMet(),
"Requires server to be running 1.19.4+"
);
/** /**
* Requires the server to be running 1.19. * Requires the server to be running 1.19.
*/ */
public static final Prerequisite HAS_1_19 = new Prerequisite( public static final Prerequisite HAS_1_19 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("19"), () -> ProxyConstants.NMS_VERSION.contains("19") || HAS_1_20.isMet(),
"Requires server to be running 1.19+" "Requires server to be running 1.19+"
); );

View File

@@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull;
* Default plugin config.yml. * Default plugin config.yml.
*/ */
public class ConfigYml extends BaseConfig { public class ConfigYml extends BaseConfig {
/**
* The use local storage key.
*/
public static final String KEY_USES_LOCAL_STORAGE = "use-local-storage";
/** /**
* Config.yml. * Config.yml.
* *
@@ -52,4 +57,13 @@ public class ConfigYml extends BaseConfig {
final boolean removeUnused) { final boolean removeUnused) {
super(name, plugin, removeUnused, ConfigType.YAML); super(name, plugin, removeUnused, ConfigType.YAML);
} }
/**
* Get if the plugin is using local storage.
*
* @return The prefix.
*/
public boolean isUsingLocalStorage() {
return this.getBool(KEY_USES_LOCAL_STORAGE);
}
} }

View File

@@ -6,6 +6,7 @@ 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;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.NumberUtils; import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils; import com.willfp.eco.util.StringUtils;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
@@ -134,7 +135,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid. * @return The computed value, or 0 if not found or invalid.
*/ */
default int getIntFromExpression(@NotNull String path) { default int getIntFromExpression(@NotNull String path) {
return getIntFromExpression(path, null); return getIntFromExpression(path, PlaceholderContext.of(this));
} }
/** /**
@@ -163,6 +164,18 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return Double.valueOf(getDoubleFromExpression(path, player, additionalPlayers)).intValue(); return Double.valueOf(getDoubleFromExpression(path, player, additionalPlayers)).intValue();
} }
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path,
@NotNull PlaceholderContext context) {
return Double.valueOf(getDoubleFromExpression(path, context)).intValue();
}
/** /**
* Get an integer from config. * Get an integer from config.
@@ -256,6 +269,22 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getString(path, true, option); return getString(path, true, option);
} }
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getFormattedString(@NotNull String path,
@NotNull PlaceholderContext context) {
return Objects.requireNonNullElse(
getFormattedStringOrNull(path, context),
""
);
}
/** /**
* Get a string from config. * Get a string from config.
* <p> * <p>
@@ -288,7 +317,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* Get a formatted string from config. * Get a formatted string from config.
* *
* @param path The key to fetch the value from. * @param path The key to fetch the value from.
* @return The found value, or an empty string if not found. * @return The found value, or null if not found.
*/ */
@Nullable @Nullable
default String getFormattedStringOrNull(@NotNull String path) { default String getFormattedStringOrNull(@NotNull String path) {
@@ -300,7 +329,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* *
* @param path The key to fetch the value from. * @param path The key to fetch the value from.
* @param option The format option. * @param option The format option.
* @return The found value, or an empty string if not found. * @return The found value, or null if not found.
*/ */
@Nullable @Nullable
default String getFormattedStringOrNull(@NotNull String path, default String getFormattedStringOrNull(@NotNull String path,
@@ -308,6 +337,25 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStringOrNull(path, true, option); return getStringOrNull(path, true, option);
} }
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or null if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path,
@NotNull PlaceholderContext context) {
String nullable = getStringOrNull(path);
if (nullable == null) {
return null;
}
return StringUtils.format(nullable, context.withInjectableContext(this));
}
/** /**
* Get a string from config. * Get a string from config.
* <p> * <p>
@@ -362,6 +410,24 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStrings(path, true, option); return getStrings(path, true, option);
} }
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getFormattedStrings(@NotNull String path,
@NotNull PlaceholderContext context) {
return Objects.requireNonNullElse(
getFormattedStringsOrNull(path, context),
new ArrayList<>()
);
}
/** /**
* Get a list of strings from config. * Get a list of strings from config.
* <p> * <p>
@@ -418,6 +484,30 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStringsOrNull(path, true, option); return getStringsOrNull(path, true, option);
} }
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or null if not found.
*/
@Nullable
default List<String> getFormattedStringsOrNull(@NotNull String path,
@NotNull PlaceholderContext context) {
List<String> nullable = getStringsOrNull(path);
if (nullable == null) {
return null;
}
return StringUtils.formatList(
nullable,
context.withInjectableContext(this)
);
}
/** /**
* Get a list of strings from config. * Get a list of strings from config.
* <p> * <p>
@@ -463,7 +553,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid. * @return The computed value, or 0 if not found or invalid.
*/ */
default double getDoubleFromExpression(@NotNull String path) { default double getDoubleFromExpression(@NotNull String path) {
return getDoubleFromExpression(path, null); return getDoubleFromExpression(path, PlaceholderContext.of(this));
} }
/** /**
@@ -475,21 +565,38 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/ */
default double getDoubleFromExpression(@NotNull String path, default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) { @Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player, this); return getDoubleFromExpression(path, player, Collections.emptyList());
} }
/** /**
* Get a decimal value via a mathematical expression. * Get a decimal value via a mathematical expression.
* *
* @param path The key to fetch the value from. * @param path The key to fetch the value from.
* @param player The player to evaluate placeholders with respect to. * @param player The player to evaluate placeholders with respect to.
* @param additionalPlayers The additional players to evaluate placeholders with respect to. * @param additionalPlayers The additional players to evaluate placeholders with respect to.
* @return The computed value, or 0 if not found or invalid. * @return The computed value, or 0 if not found or invalid.
*/ */
default double getDoubleFromExpression(@NotNull String path, default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player, @Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers) { @NotNull Collection<AdditionalPlayer> additionalPlayers) {
return NumberUtils.evaluateExpression(this.getString(path), player, this, additionalPlayers); return getDoubleFromExpression(path, new PlaceholderContext(
player,
null,
this,
additionalPlayers
));
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path,
@NotNull PlaceholderContext context) {
return NumberUtils.evaluateExpression(this.getString(path), context.withInjectableContext(this));
} }
/** /**

View File

@@ -3,17 +3,26 @@ package com.willfp.eco.core.data;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* A simple store key-value store for data to be stored outside of plugins. * A simple store key-value store for data to be stored outside of plugins.
*/ */
@SuppressWarnings("unchecked")
public final class ExternalDataStore { public final class ExternalDataStore {
/** /**
* The store. * The store.
*/ */
private static final HashMap<String, Object> data = new HashMap<>(); private static final Map<String, Object> DATA = new HashMap<>();
/**
* The store adapters.
*/
private static final List<ExternalDataStoreObjectAdapter<?, ?>> STORE_ADAPTERS = new ArrayList<>();
/** /**
* Put data into the store. * Put data into the store.
@@ -23,7 +32,29 @@ public final class ExternalDataStore {
*/ */
public static void put(@NotNull final String key, public static void put(@NotNull final String key,
@NotNull final Object value) { @NotNull final Object value) {
data.put(key, value); doPut(key, value);
}
/**
* Put data into the store.
*
* @param key The key.
* @param value The value.
* @param <A> The stored type.
*/
private static <A> void doPut(@NotNull final String key,
@NotNull final A value) {
Object storedValue = value;
for (ExternalDataStoreObjectAdapter<?, ?> unknownAdapter : STORE_ADAPTERS) {
if (unknownAdapter.getAccessedClass().isInstance(value)) {
ExternalDataStoreObjectAdapter<A, ?> adapter = (ExternalDataStoreObjectAdapter<A, ?>) unknownAdapter;
storedValue = adapter.toStoredObject(value);
break;
}
}
DATA.put(key, storedValue);
} }
/** /**
@@ -37,7 +68,30 @@ public final class ExternalDataStore {
@Nullable @Nullable
public static <T> T get(@NotNull final String key, public static <T> T get(@NotNull final String key,
@NotNull final Class<T> clazz) { @NotNull final Class<T> clazz) {
Object value = data.get(key); return doGet(key, clazz);
}
/**
* Get data from the store.
*
* @param key The key.
* @param clazz The class.
* @param <A> The accessed type.
* @param <S> The stored type.
* @return The value.
*/
@Nullable
private static <A, S> A doGet(@NotNull final String key,
@NotNull final Class<A> clazz) {
Object value = DATA.get(key);
for (ExternalDataStoreObjectAdapter<?, ?> unknownAdapter : STORE_ADAPTERS) {
if (unknownAdapter.getStoredClass().isInstance(value) && unknownAdapter.getAccessedClass().equals(clazz)) {
ExternalDataStoreObjectAdapter<A, S> adapter = (ExternalDataStoreObjectAdapter<A, S>) unknownAdapter;
value = adapter.toAccessedObject((S) value);
break;
}
}
if (clazz.isInstance(value)) { if (clazz.isInstance(value)) {
return clazz.cast(value); return clazz.cast(value);
@@ -79,6 +133,15 @@ public final class ExternalDataStore {
return get(key, clazz, defaultValue.get()); return get(key, clazz, defaultValue.get());
} }
/**
* Register a new adapter.
*
* @param adapter The adapter.
*/
public static void registerAdapter(@NotNull final ExternalDataStoreObjectAdapter<?, ?> adapter) {
STORE_ADAPTERS.add(adapter);
}
private ExternalDataStore() { private ExternalDataStore() {
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

@@ -0,0 +1,69 @@
package com.willfp.eco.core.data;
import org.jetbrains.annotations.NotNull;
/**
* An adapter for objects stored in {@link ExternalDataStore}.
*
* @param <A> The accessed class.
* @param <S> The stored class.
*/
public abstract class ExternalDataStoreObjectAdapter<A, S> {
/**
* The class that is accessed (read / written).
*/
private final Class<? extends A> accessedClass;
/**
* The class that is stored internally.
*/
private final Class<? extends S> storedClass;
/**
* Create a new adapter.
*
* @param accessedClass The class that is accessed (read / written).
* @param storedClass The class that is stored internally.
*/
protected ExternalDataStoreObjectAdapter(@NotNull final Class<? extends A> accessedClass,
@NotNull final Class<? extends S> storedClass) {
this.accessedClass = accessedClass;
this.storedClass = storedClass;
}
/**
* Convert an object to the stored object.
*
* @param obj The object.
* @return The stored object.
*/
@NotNull
public abstract S toStoredObject(@NotNull final A obj);
/**
* Convert an object to the accessed object.
*
* @param obj The object.
* @return The accessed object.
*/
@NotNull
public abstract A toAccessedObject(@NotNull final S obj);
/**
* Get the class that is accessed (read / written).
*
* @return The class.
*/
public Class<? extends A> getAccessedClass() {
return accessedClass;
}
/**
* Get the class that is stored internally.
*
* @return The class.
*/
public Class<? extends S> getStoredClass() {
return storedClass;
}
}

View File

@@ -9,6 +9,22 @@ import org.jetbrains.annotations.NotNull;
* Profiles save automatically, so there is no need to save after changes. * Profiles save automatically, so there is no need to save after changes.
*/ */
public interface ServerProfile extends Profile { public interface ServerProfile extends Profile {
/**
* Get the server ID.
*
* @return The server ID.
*/
@NotNull
String getServerID();
/**
* Get the local server ID.
*
* @return The local server ID.
*/
@NotNull
String getLocalServerID();
/** /**
* Load the server profile. * Load the server profile.
* *

View File

@@ -4,6 +4,7 @@ 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;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -49,6 +50,11 @@ public final class PersistentDataKeyType<T> {
*/ */
public static final PersistentDataKeyType<Config> CONFIG = new PersistentDataKeyType<>("CONFIG"); public static final PersistentDataKeyType<Config> CONFIG = new PersistentDataKeyType<>("CONFIG");
/**
* Big Decimal.
*/
public static final PersistentDataKeyType<BigDecimal> BIG_DECIMAL = new PersistentDataKeyType<>("BIG_DECIMAL");
/** /**
* The name of the key type. * The name of the key type.
*/ */

View File

@@ -135,20 +135,26 @@ public abstract class Extension implements PluginLike {
} }
@Override @Override
public File getDataFolder() { public @NotNull File getDataFolder() {
return this.plugin.getDataFolder(); return this.plugin.getDataFolder();
} }
@Override @Override
public ConfigHandler getConfigHandler() { public @NotNull ConfigHandler getConfigHandler() {
return this.plugin.getConfigHandler(); return this.plugin.getConfigHandler();
} }
@Override @Override
public Logger getLogger() { public @NotNull Logger getLogger() {
return this.plugin.getLogger(); return this.plugin.getLogger();
} }
@Override
public @NotNull File getFile() {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.file();
}
/** /**
* Get the plugin for the extension. * Get the plugin for the extension.
* *

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.extensions;
import org.jetbrains.annotations.NotNull;
/**
* Generic exception in extension loading.
*/
public class ExtensionLoadException extends RuntimeException {
/**
* Create a new ExtensionLoadException.
*
* @param errorMessage The error message to show.
*/
public ExtensionLoadException(@NotNull final String errorMessage) {
super(errorMessage);
}
}

View File

@@ -1,7 +1,10 @@
package com.willfp.eco.core.extensions; package com.willfp.eco.core.extensions;
import com.willfp.eco.core.version.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
/** /**
* The extension's metadata. * The extension's metadata.
* <p> * <p>
@@ -13,6 +16,23 @@ import org.jetbrains.annotations.NotNull;
*/ */
public record ExtensionMetadata(@NotNull String version, public record ExtensionMetadata(@NotNull String version,
@NotNull String name, @NotNull String name,
@NotNull String author) { @NotNull String author,
@NotNull File file,
@NotNull Version minimumPluginVersion) {
/**
* Legacy constructor.
*
* @param version The extension version.
* @param name The extension name.
* @param author The extension's author.
* @deprecated Use {@link ExtensionMetadata#ExtensionMetadata(String, String, String, File, Version)} instead.
*/
@SuppressWarnings("ConstantConditions")
@Deprecated(since = "6.57.0", forRemoval = true)
public ExtensionMetadata(@NotNull String version,
@NotNull String name,
@NotNull String author) {
this(version, name, author, null, null);
throw new UnsupportedOperationException("Legacy constructor is not supported.");
}
} }

View File

@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
* Missing or invalid extension.yml. * Missing or invalid extension.yml.
* Invalid filetype. * Invalid filetype.
*/ */
public class MalformedExtensionException extends RuntimeException { public class MalformedExtensionException extends ExtensionLoadException {
/** /**
* Create a new MalformedExtensionException. * Create a new MalformedExtensionException.
* *

View File

@@ -142,6 +142,26 @@ public interface MenuBuilder extends PageBuilder {
return this.onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player))); return this.onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
} }
/**
* Set the default page.
*
* @param page The page.
* @return The builder.
*/
default MenuBuilder defaultPage(final int page) {
return this.maxPages(player -> page);
}
/**
* Set the default page dynamically for a player.
*
* @param page The default page.
* @return The builder.
*/
default MenuBuilder defaultPage(@NotNull final Function<Player, Integer> page) {
return this.onOpen((player, menu) -> menu.setState(player, Page.PAGE_KEY, page.apply(player)));
}
/** /**
* Add a menu close handler. * Add a menu close handler.
* *

View File

@@ -76,6 +76,15 @@ public abstract class CustomSlot implements Slot {
return delegate; return delegate;
} }
@Override
public boolean shouldRenderOnClick() {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.shouldRenderOnClick();
}
@Override @Override
public final int getRows() { public final int getRows() {
return Slot.super.getRows(); return Slot.super.getRows();

View File

@@ -92,6 +92,15 @@ public interface Slot extends GUIComponent {
return false; return false;
} }
/**
* If the slot should re-render the menu if clicked.
*
* @return If the slot should re-render.
*/
default boolean shouldRenderOnClick() {
return true;
}
@Override @Override
default int getRows() { default int getRows() {
return 1; return 1;

View File

@@ -1,13 +1,22 @@
package com.willfp.eco.core.integrations; package com.willfp.eco.core.integrations;
import com.willfp.eco.core.registry.Registrable;
import com.willfp.eco.core.registry.Registry;
import org.jetbrains.annotations.NotNull;
/** /**
* Abstract class for integrations. * Abstract class for integrations.
*/ */
public interface Integration { public interface Integration extends Registrable {
/** /**
* Get the name of integration. * Get the name of integration.
* *
* @return The name. * @return The name.
*/ */
String getPluginName(); String getPluginName();
@Override
default @NotNull String getID() {
return Registry.tryFitPattern(this.getPluginName());
}
} }

View File

@@ -0,0 +1,138 @@
package com.willfp.eco.core.integrations;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Registry for integrations.
*
* @param <T> The type of integration.
*/
public class IntegrationRegistry<T extends Integration> extends Registry<T> {
/**
* Create a new integration registry.
*/
public IntegrationRegistry() {
super();
}
@Override
public @NotNull T register(@NotNull final T element) {
return executeSafely(() -> super.register(element), element);
}
/**
* Iterate over all integrations, safely.
*
* @param action The action to perform.
*/
public void forEachSafely(@NotNull final Consumer<T> action) {
for (T integration : new HashSet<>(this.values())) {
executeSafely(() -> action.accept(integration), integration);
}
}
/**
* If any integrations return true, safely.
*
* @param predicate The predicate to test.
* @return If any integrations return true.
*/
public boolean anySafely(@NotNull final Predicate<T> predicate) {
for (T integration : new HashSet<>(this.values())) {
Boolean result = executeSafely(() -> predicate.test(integration), integration);
if (result != null && result) {
return true;
}
}
return false;
}
/**
* Get the first integration that returns a value, safely.
*
* @param function The function to apply.
* @param defaultValue The default value.
* @param <R> The type of value.
* @return The first value that returns a value.
*/
@NotNull
public <R> R firstSafely(@NotNull final Function<T, R> function,
@NotNull final R defaultValue) {
if (this.isEmpty()) {
return defaultValue;
}
T integration = this.iterator().next();
return executeSafely(() -> function.apply(integration), integration, defaultValue);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
*/
private void executeSafely(@NotNull final Runnable action,
@NotNull final T integration) {
executeSafely(() -> {
action.run();
return null;
}, integration);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
* @param <R> The return type of the action.
* @return The result of the action, or null if an exception was thrown.
*/
private <R> R executeSafely(@NotNull final Supplier<R> action,
@NotNull final T integration) {
return executeSafely(action, integration, null);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
* @param defaultValue The default value to return if an exception is thrown.
* @param <R> The return type of the action.
* @return The result of the action, or the default value if an exception was thrown.
*/
private <R> R executeSafely(@NotNull final Supplier<R> action,
@NotNull final T integration,
@Nullable final R defaultValue) {
try {
return action.get();
} catch (final Exception e) {
Eco.get().getEcoPlugin().getLogger().warning("Integration for " + integration.getPluginName() + " threw an exception!");
Eco.get().getEcoPlugin().getLogger().warning("The integration will be disabled.");
e.printStackTrace();
this.remove(integration);
return defaultValue;
}
}
/**
* If all integrations return true, safely.
*
* @param predicate The predicate to test.
* @return If all integrations return true.
*/
public boolean allSafely(@NotNull final Predicate<T> predicate) {
return !this.anySafely(predicate.negate());
}
}

View File

@@ -1,11 +1,9 @@
package com.willfp.eco.core.integrations.afk; package com.willfp.eco.core.integrations.afk;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle afk integrations. * Class to handle afk integrations.
*/ */
@@ -13,7 +11,7 @@ public final class AFKManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<AFKIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<AFKIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -21,8 +19,7 @@ public final class AFKManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final AFKIntegration integration) { public static void register(@NotNull final AFKIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -32,13 +29,7 @@ public final class AFKManager {
* @return If afk. * @return If afk.
*/ */
public static boolean isAfk(@NotNull final Player player) { public static boolean isAfk(@NotNull final Player player) {
for (AFKIntegration integration : REGISTERED) { return REGISTRY.anySafely(integration -> integration.isAfk(player));
if (integration.isAfk(player)) {
return true;
}
}
return false;
} }
private AFKManager() { private AFKManager() {

View File

@@ -1,13 +1,11 @@
package com.willfp.eco.core.integrations.anticheat; package com.willfp.eco.core.integrations.anticheat;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle anticheat integrations. * Class to handle anticheat integrations.
*/ */
@@ -15,7 +13,7 @@ public final class AnticheatManager {
/** /**
* A set of all registered anticheats. * A set of all registered anticheats.
*/ */
private static final Set<AnticheatIntegration> ANTICHEATS = new HashSet<>(); private static final IntegrationRegistry<AnticheatIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new anticheat. * Register a new anticheat.
@@ -26,8 +24,7 @@ public final class AnticheatManager {
if (anticheat instanceof Listener) { if (anticheat instanceof Listener) {
Eco.get().getEcoPlugin().getEventManager().registerListener((Listener) anticheat); Eco.get().getEcoPlugin().getEventManager().registerListener((Listener) anticheat);
} }
ANTICHEATS.removeIf(it -> it.getPluginName().equalsIgnoreCase(anticheat.getPluginName())); REGISTRY.register(anticheat);
ANTICHEATS.add(anticheat);
} }
/** /**
@@ -36,17 +33,16 @@ public final class AnticheatManager {
* @param player The player to exempt. * @param player The player to exempt.
*/ */
public static void exemptPlayer(@NotNull final Player player) { public static void exemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.exempt(player)); REGISTRY.forEachSafely(anticheat -> anticheat.exempt(player));
} }
/** /**
* Unexempt a player from triggering anticheats. * Unexempt a player from triggering anticheats.
* This is ran a tick after it is called to ensure that there are no event timing conflicts.
* *
* @param player The player to remove the exemption. * @param player The player to remove the exemption.
*/ */
public static void unexemptPlayer(@NotNull final Player player) { public static void unexemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.unexempt(player)); REGISTRY.forEachSafely(anticheat -> anticheat.unexempt(player));
} }
private AnticheatManager() { private AnticheatManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.antigrief; package com.willfp.eco.core.integrations.antigrief;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
@@ -16,7 +17,7 @@ public final class AntigriefManager {
/** /**
* Registered antigriefs. * Registered antigriefs.
*/ */
private static final Set<AntigriefIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<AntigriefIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new AntiGrief/Land Management integration. * Register a new AntiGrief/Land Management integration.
@@ -24,8 +25,7 @@ public final class AntigriefManager {
* @param antigrief The integration to register. * @param antigrief The integration to register.
*/ */
public static void register(@NotNull final AntigriefIntegration antigrief) { public static void register(@NotNull final AntigriefIntegration antigrief) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(antigrief.getPluginName())); REGISTRY.register(antigrief);
REGISTERED.add(antigrief);
} }
/** /**
@@ -34,8 +34,7 @@ public final class AntigriefManager {
* @param antigrief The integration to unregister. * @param antigrief The integration to unregister.
*/ */
public static void unregister(@NotNull final AntigriefIntegration antigrief) { public static void unregister(@NotNull final AntigriefIntegration antigrief) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(antigrief.getPluginName())); REGISTRY.remove(antigrief);
REGISTERED.remove(antigrief);
} }
/** /**
@@ -47,7 +46,7 @@ public final class AntigriefManager {
*/ */
public static boolean canPickupItem(@NotNull final Player player, public static boolean canPickupItem(@NotNull final Player player,
@NotNull final Location location) { @NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canPickupItem(player, location)); return REGISTRY.allSafely(integration -> integration.canPickupItem(player, location));
} }
/** /**
@@ -59,7 +58,7 @@ public final class AntigriefManager {
*/ */
public static boolean canBreakBlock(@NotNull final Player player, public static boolean canBreakBlock(@NotNull final Player player,
@NotNull final Block block) { @NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canBreakBlock(player, block)); return REGISTRY.allSafely(integration -> integration.canBreakBlock(player, block));
} }
/** /**
@@ -71,7 +70,7 @@ public final class AntigriefManager {
*/ */
public static boolean canCreateExplosion(@NotNull final Player player, public static boolean canCreateExplosion(@NotNull final Player player,
@NotNull final Location location) { @NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canCreateExplosion(player, location)); return REGISTRY.allSafely(integration -> integration.canCreateExplosion(player, location));
} }
/** /**
@@ -83,7 +82,7 @@ public final class AntigriefManager {
*/ */
public static boolean canPlaceBlock(@NotNull final Player player, public static boolean canPlaceBlock(@NotNull final Player player,
@NotNull final Block block) { @NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canPlaceBlock(player, block)); return REGISTRY.allSafely(integration -> integration.canPlaceBlock(player, block));
} }
/** /**
@@ -95,7 +94,7 @@ public final class AntigriefManager {
*/ */
public static boolean canInjure(@NotNull final Player player, public static boolean canInjure(@NotNull final Player player,
@NotNull final LivingEntity victim) { @NotNull final LivingEntity victim) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canInjure(player, victim)); return REGISTRY.allSafely(integration -> integration.canInjure(player, victim));
} }
private AntigriefManager() { private AntigriefManager() {

View File

@@ -1,10 +1,8 @@
package com.willfp.eco.core.integrations.customentities; package com.willfp.eco.core.integrations.customentities;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle custom entity integrations. * Class to handle custom entity integrations.
*/ */
@@ -12,7 +10,7 @@ public final class CustomEntitiesManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<CustomEntitiesIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<CustomEntitiesIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -20,8 +18,7 @@ public final class CustomEntitiesManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final CustomEntitiesIntegration integration) { public static void register(@NotNull final CustomEntitiesIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -30,9 +27,7 @@ public final class CustomEntitiesManager {
* @see com.willfp.eco.core.entities.Entities * @see com.willfp.eco.core.entities.Entities
*/ */
public static void registerAllEntities() { public static void registerAllEntities() {
for (CustomEntitiesIntegration integration : REGISTERED) { REGISTRY.forEachSafely(CustomEntitiesIntegration::registerAllEntities);
integration.registerAllEntities();
}
} }
private CustomEntitiesManager() { private CustomEntitiesManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.customitems; package com.willfp.eco.core.integrations.customitems;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet; import java.util.HashSet;
@@ -12,7 +13,7 @@ public final class CustomItemsManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<CustomItemsIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<CustomItemsIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -20,8 +21,7 @@ public final class CustomItemsManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final CustomItemsIntegration integration) { public static void register(@NotNull final CustomItemsIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -30,9 +30,7 @@ public final class CustomItemsManager {
* @see com.willfp.eco.core.items.Items * @see com.willfp.eco.core.items.Items
*/ */
public static void registerAllItems() { public static void registerAllItems() {
for (CustomItemsIntegration customItemsIntegration : REGISTERED) { REGISTRY.forEachSafely(CustomItemsIntegration::registerAllItems);
customItemsIntegration.registerAllItems();
}
} }
/** /**
@@ -41,9 +39,7 @@ public final class CustomItemsManager {
* @see com.willfp.eco.core.items.Items * @see com.willfp.eco.core.items.Items
*/ */
public static void registerProviders() { public static void registerProviders() {
for (CustomItemsIntegration customItemsIntegration : REGISTERED) { REGISTRY.forEachSafely(CustomItemsIntegration::registerProvider);
customItemsIntegration.registerProvider();
}
} }
private CustomItemsManager() { private CustomItemsManager() {

View File

@@ -1,11 +1,10 @@
package com.willfp.eco.core.integrations.economy; package com.willfp.eco.core.integrations.economy;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle economy. * Class to handle economy.
@@ -14,7 +13,7 @@ public final class EconomyManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<EconomyIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<EconomyIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -22,8 +21,7 @@ public final class EconomyManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final EconomyIntegration integration) { public static void register(@NotNull final EconomyIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -32,7 +30,7 @@ public final class EconomyManager {
* @return If any economy. * @return If any economy.
*/ */
public static boolean hasRegistrations() { public static boolean hasRegistrations() {
return !REGISTERED.isEmpty(); return REGISTRY.isNotEmpty();
} }
/** /**
@@ -56,11 +54,10 @@ public final class EconomyManager {
*/ */
public static boolean hasAmount(@NotNull final OfflinePlayer player, public static boolean hasAmount(@NotNull final OfflinePlayer player,
final BigDecimal amount) { final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.hasAmount(player, amount); integration -> integration.hasAmount(player, amount),
} false
);
return false;
} }
/** /**
@@ -84,11 +81,10 @@ public final class EconomyManager {
*/ */
public static boolean giveMoney(@NotNull final OfflinePlayer player, public static boolean giveMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) { @NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.giveMoney(player, amount); integration -> integration.giveMoney(player, amount),
} false
);
return false;
} }
/** /**
@@ -112,11 +108,10 @@ public final class EconomyManager {
*/ */
public static boolean removeMoney(@NotNull final OfflinePlayer player, public static boolean removeMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) { @NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.removeMoney(player, amount); integration -> integration.removeMoney(player, amount),
} false
);
return false;
} }
/** /**
@@ -136,11 +131,10 @@ public final class EconomyManager {
* @return The balance. * @return The balance.
*/ */
public static BigDecimal getExactBalance(@NotNull final OfflinePlayer player) { public static BigDecimal getExactBalance(@NotNull final OfflinePlayer player) {
for (EconomyIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.getExactBalance(player); integration -> integration.getExactBalance(player),
} BigDecimal.ZERO
);
return BigDecimal.ZERO;
} }
private EconomyManager() { private EconomyManager() {

View File

@@ -1,12 +1,10 @@
package com.willfp.eco.core.integrations.guidetection; package com.willfp.eco.core.integrations.guidetection;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import com.willfp.eco.util.MenuUtils; import com.willfp.eco.util.MenuUtils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/** /**
* Class to handle GUI detection. * Class to handle GUI detection.
*/ */
@@ -14,7 +12,7 @@ public final class GUIDetectionManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<GUIDetectionIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<GUIDetectionIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -22,8 +20,7 @@ public final class GUIDetectionManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final GUIDetectionIntegration integration) { public static void register(@NotNull final GUIDetectionIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -37,13 +34,7 @@ public final class GUIDetectionManager {
return true; return true;
} }
for (GUIDetectionIntegration integration : REGISTERED) { return REGISTRY.anySafely(integration -> integration.hasGUIOpen(player));
if (integration.hasGUIOpen(player)) {
return true;
}
}
return false;
} }
private GUIDetectionManager() { private GUIDetectionManager() {

View File

@@ -1,11 +1,10 @@
package com.willfp.eco.core.integrations.hologram; package com.willfp.eco.core.integrations.hologram;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.Location; import org.bukkit.Location;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* Class to handle hologram integrations. * Class to handle hologram integrations.
@@ -14,7 +13,7 @@ public final class HologramManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<HologramIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<HologramIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -22,8 +21,7 @@ public final class HologramManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final HologramIntegration integration) { public static void register(@NotNull final HologramIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -35,11 +33,10 @@ public final class HologramManager {
*/ */
public static Hologram createHologram(@NotNull final Location location, public static Hologram createHologram(@NotNull final Location location,
@NotNull final List<String> contents) { @NotNull final List<String> contents) {
for (HologramIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.createHologram(location, contents); integration -> integration.createHologram(location, contents),
} new DummyHologram()
);
return new DummyHologram();
} }
private HologramManager() { private HologramManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.mcmmo; package com.willfp.eco.core.integrations.mcmmo;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -14,7 +15,7 @@ public final class McmmoManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<McmmoIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<McmmoIntegration> REGISTERED = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -22,8 +23,7 @@ public final class McmmoManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final McmmoIntegration integration) { public static void register(@NotNull final McmmoIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTERED.register(integration);
REGISTERED.add(integration);
} }
/** /**
@@ -34,13 +34,11 @@ public final class McmmoManager {
*/ */
public static int getBonusDropCount(@NotNull final Block block) { public static int getBonusDropCount(@NotNull final Block block) {
int finalValue = 0; int finalValue = 0;
for (McmmoIntegration mcmmoIntegration : REGISTERED) { for (McmmoIntegration mcmmoIntegration : REGISTERED) {
finalValue += mcmmoIntegration.getBonusDropCount(block); finalValue += mcmmoIntegration.getBonusDropCount(block);
} }
return finalValue; return finalValue;
} }
@@ -51,13 +49,7 @@ public final class McmmoManager {
* @return If the event is fake. * @return If the event is fake.
*/ */
public static boolean isFake(@NotNull final Event event) { public static boolean isFake(@NotNull final Event event) {
for (McmmoIntegration mcmmoIntegration : REGISTERED) { return REGISTERED.anySafely(integration -> integration.isFake(event));
if (mcmmoIntegration.isFake(event)) {
return true;
}
}
return false;
} }
private McmmoManager() { private McmmoManager() {

View File

@@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Wrapper class for placeholder integrations. * Wrapper class for arguments integrations.
*/ */
public interface PlaceholderIntegration extends Integration { public interface PlaceholderIntegration extends Integration {
/** /**

View File

@@ -1,21 +1,15 @@
package com.willfp.eco.core.integrations.placeholder; package com.willfp.eco.core.integrations.placeholder;
import com.github.benmanes.caffeine.cache.Cache; import com.google.common.collect.ImmutableSet;
import com.github.benmanes.caffeine.cache.Caffeine; import com.willfp.eco.core.Eco;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.map.DefaultMap;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.DynamicPlaceholder;
import com.willfp.eco.core.placeholder.InjectablePlaceholder; import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.Placeholder; import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable; import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.PlayerDynamicPlaceholder; import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import com.willfp.eco.util.StringUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -23,58 +17,34 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* Class to handle placeholder integrations. * Class to handle arguments integrations.
*/ */
public final class PlaceholderManager { public final class PlaceholderManager {
/** /**
* All registered placeholders. * All registered placeholders.
*/ */
private static final Map<EcoPlugin, Map<Pattern, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>(); private static final DefaultMap<EcoPlugin, Set<Placeholder>> REGISTERED_PLACEHOLDERS = new DefaultMap<>(HashSet::new);
/** /**
* All registered placeholder integrations. * All registered arguments integrations.
*/ */
private static final Set<PlaceholderIntegration> REGISTERED_INTEGRATIONS = new HashSet<>(); private static final Set<PlaceholderIntegration> REGISTERED_INTEGRATIONS = new HashSet<>();
/**
* Placeholder Lookup Cache.
*/
private static final Cache<PlaceholderLookup, Optional<Placeholder>> PLACEHOLDER_LOOKUP_CACHE = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build();
/**
* Placeholder Cache.
*/
private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player));
/**
* Dynamic Placeholder Cache.
*/
private static final LoadingCache<DynamicEntryWithPlayer, String> DYNAMIC_PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.args, key.player));
/** /**
* The default PlaceholderAPI pattern; brought in for compatibility. * The default PlaceholderAPI pattern; brought in for compatibility.
*/ */
private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%"); private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%");
/** /**
* Empty injectable object. * Empty injectableContext object.
*/ */
public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() { public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override @Override
@@ -105,97 +75,69 @@ public final class PlaceholderManager {
} }
/** /**
* Register a placeholder. * Register a arguments.
* *
* @param placeholder The placeholder to register. * @param placeholder The arguments to register.
* @deprecated Use {@link #registerPlaceholder(RegistrablePlaceholder)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
public static void registerPlaceholder(@NotNull final Placeholder placeholder) { public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
if (placeholder instanceof StaticPlaceholder || placeholder instanceof PlayerStaticPlaceholder) { if (!(placeholder instanceof RegistrablePlaceholder)) {
throw new IllegalArgumentException("Static placeholders cannot be registered!"); throw new IllegalArgumentException("Placeholder must be RegistrablePlaceholder!");
} }
Map<Pattern, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS registerPlaceholder((RegistrablePlaceholder) placeholder);
.getOrDefault(placeholder.getPlugin(), new HashMap<>());
pluginPlaceholders.put(placeholder.getPattern(), placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), pluginPlaceholders);
} }
/** /**
* Get the result of a placeholder with respect to a player. * Register a arguments.
* *
* @param player The player to get the result from. * @param placeholder The arguments to register.
* @param identifier The placeholder identifier.
* @return The value of the placeholder.
* @deprecated Specify a plugin to get the result from.
*/ */
@Deprecated(since = "6.52.2", forRemoval = true) public static void registerPlaceholder(@NotNull final RegistrablePlaceholder placeholder) {
@SuppressWarnings("unused") // Storing as immutable set leads to slower times to register placeholders, but much
public static String getResult(@Nullable final Player player, // faster times to access registrations.
@NotNull final String identifier) { Set<Placeholder> pluginPlaceholders = new HashSet<>(REGISTERED_PLACEHOLDERS.get(placeholder.getPlugin()));
throw new UnsupportedOperationException("Please specify a plugin to get the result from!"); pluginPlaceholders.removeIf(p -> p.getPattern().equals(placeholder.getPattern()));
pluginPlaceholders.add(placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), ImmutableSet.copyOf(pluginPlaceholders));
} }
/** /**
* Get the result of a placeholder with respect to a player. * Get the result of a placeholder with respect to a player.
* *
* @param player The player to get the result from. * @param player The player to get the result from.
* @param identifier The placeholder identifier. * @param identifier The placeholder args.
* @param plugin The plugin for the placeholder. * @param plugin The plugin for the arguments.
* @return The value of the placeholder. * @return The value of the arguments.
*/ */
@NotNull @NotNull
public static String getResult(@Nullable final Player player, public static String getResult(@Nullable final Player player,
@NotNull final String identifier, @NotNull final String identifier,
@NotNull final EcoPlugin plugin) { @Nullable final EcoPlugin plugin) {
Validate.notNull(plugin, "Plugin cannot be null!"); return Objects.requireNonNullElse(
getResult(
plugin,
identifier,
new PlaceholderContext(player)
),
""
);
}
// This is really janky, and it sucks, but it works so? /**
// Compensating for regex being slow so that's why we get it. * Get the result of a placeholder given a plugin and arguments.
Placeholder placeholder = PLACEHOLDER_LOOKUP_CACHE.get( *
new PlaceholderLookup(identifier, plugin), * @param plugin The plugin for the placeholder.
(it) -> { * @param args The arguments.
// I hate the streams API. * @param context The context.
return REGISTERED_PLACEHOLDERS * @return The value of the arguments.
.getOrDefault(plugin, new HashMap<>()) */
.entrySet() @Nullable
.stream().filter(entry -> entry.getKey().matcher(identifier).matches()) public static String getResult(@Nullable final EcoPlugin plugin,
.map(Map.Entry::getValue) @NotNull final String args,
.findFirst(); @NotNull final PlaceholderContext context) {
} return Eco.get().getPlaceholderValue(plugin, args, context);
).orElse(null);
if (placeholder == null) {
return "";
}
/*
This code here is *really* not very good. It's mega externalized logic hacked
together and made worse by the addition of dynamic placeholders. But it works,
and it means I don't have to rewrite the whole placeholder system. So it's
good enough for me.
*/
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
if (player == null) {
return "";
} else {
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(playerPlaceholder, player));
}
} else if (placeholder instanceof PlayerlessPlaceholder playerlessPlaceholder) {
return playerlessPlaceholder.getValue();
} else if (placeholder instanceof PlayerDynamicPlaceholder playerDynamicPlaceholder) {
if (player == null) {
return "";
} else {
return DYNAMIC_PLACEHOLDER_CACHE.get(new DynamicEntryWithPlayer(playerDynamicPlaceholder, identifier, player));
}
} else if (placeholder instanceof DynamicPlaceholder dynamicPlaceholder) {
return dynamicPlaceholder.getValue(identifier);
} else {
return "";
}
} }
/** /**
@@ -204,7 +146,10 @@ public final class PlaceholderManager {
* @param text The text that may contain placeholders to translate. * @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to. * @param player The player to translate the placeholders with respect to.
* @return The text, translated. * @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text, public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player) { @Nullable final Player player) {
return translatePlaceholders(text, player, EMPTY_INJECTABLE); return translatePlaceholders(text, player, EMPTY_INJECTABLE);
@@ -215,9 +160,12 @@ public final class PlaceholderManager {
* *
* @param text The text that may contain placeholders to translate. * @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to. * @param player The player to translate the placeholders with respect to.
* @param context The injectable context. * @param context The injectableContext parseContext.
* @return The text, translated. * @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text, public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player, @Nullable final Player player,
@NotNull final PlaceholderInjectable context) { @NotNull final PlaceholderInjectable context) {
@@ -229,84 +177,50 @@ public final class PlaceholderManager {
* *
* @param text The text that may contain placeholders to translate. * @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to. * @param player The player to translate the placeholders with respect to.
* @param context The injectable context. * @param context The injectableContext parseContext.
* @param additionalPlayers Additional players to translate placeholders for. * @param additionalPlayers Additional players to translate placeholders for.
* @return The text, translated. * @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text, public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player, @Nullable final Player player,
@NotNull final PlaceholderInjectable context, @NotNull final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) { @NotNull final Collection<AdditionalPlayer> additionalPlayers) {
String processed = text; return translatePlaceholders(
text,
new PlaceholderContext(
player,
null,
context,
additionalPlayers
)
);
}
/* /**
* Translate all placeholders without a placeholder context.
*
* @param text The text that may contain placeholders to translate.
* @return The text, translated.
*/
@NotNull
public static String translatePlaceholders(@NotNull final String text) {
return Eco.get().translatePlaceholders(text, PlaceholderContext.EMPTY);
}
Why am I doing statics at the start, but player statics at the end? /**
* Translate all placeholders in a translation context.
Additional players let you use something like victim as a player to parse in relation to, *
for example doing %victim_player_health%, which would parse the health of the victim. * @param text The text that may contain placeholders to translate.
* @param context The translation context.
However, something like libreforge will also inject %victim_max_health%, which is unrelated * @return The text, translated.
to additional players, and instead holds a constant value. So, eco saw this, smartly thought */
"ah, it's an additional player, let's parse it", and then tried to parse %max_health% with @NotNull
relation to the victim, which resolved to zero. So, we have to parse statics and player statics public static String translatePlaceholders(@NotNull final String text,
that might include a prefix first, then additional players, then player statics with the support @NotNull final PlaceholderContext context) {
of additional players. return Eco.get().translatePlaceholders(text, context);
This was a massive headache and took so many reports before I clocked what was going on.
Oh well, at least it's fixed now.
*/
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
if (injection instanceof StaticPlaceholder placeholder) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
} else if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue(player));
}
}
// Prevent running 2 scans if there are no additional players.
if (!additionalPlayers.isEmpty()) {
List<String> found = findPlaceholdersIn(text);
for (AdditionalPlayer additionalPlayer : additionalPlayers) {
for (String placeholder : found) {
String prefix = "%" + additionalPlayer.getIdentifier() + "_";
if (placeholder.startsWith(prefix)) {
processed = processed.replace(
placeholder,
translatePlaceholders(
"%" + StringUtils.removePrefix(prefix, placeholder),
additionalPlayer.getPlayer()
)
);
}
}
}
}
// Only run jank code if there are no integrations.
if (REGISTERED_INTEGRATIONS.isEmpty()) {
processed = setWithoutIntegration(processed, player);
}
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player);
}
// DON'T REMOVE THIS, IT'S NOT DUPLICATE CODE.
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
// Do I know this is a bad way of doing this? Yes.
if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue(player));
}
}
return processed;
} }
/** /**
@@ -331,108 +245,22 @@ public final class PlaceholderManager {
} }
/** /**
* Set placeholders without any integrations. * Get all registered placeholder integrations.
* <p>
* This is fallback if for some reason you don't have PAPI installed.
* It's a cut-down version of the actual PAPI code, and I don't
* really know how it works.
* <p>
* Original source
* <a href="https://github.com/PlaceholderAPI/PlaceholderAPI/blob/master/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java">here</a>.
* *
* @param text The text. * @return The integrations.
* @param player The player.
* @return The text.
*/ */
private static String setWithoutIntegration(@NotNull final String text, public static Set<PlaceholderIntegration> getRegisteredIntegrations() {
@Nullable final Player player) { return Set.copyOf(REGISTERED_INTEGRATIONS);
char[] chars = text.toCharArray();
StringBuilder builder = new StringBuilder(text.length());
StringBuilder identifier = new StringBuilder();
StringBuilder parameters = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
char currentChar = chars[i];
if (currentChar == '%' && i + 1 < chars.length) {
boolean identified = false;
boolean badPlaceholder = true;
boolean hadSpace = false;
while (true) {
i++;
if (i >= chars.length) {
break;
}
char p = chars[i];
if (p == ' ' && !identified) {
hadSpace = true;
break;
}
if (p == '%') {
badPlaceholder = false;
break;
}
if (p == '_' && !identified) {
identified = true;
} else if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
String pluginName = identifier.toString().toLowerCase();
EcoPlugin plugin = EcoPlugin.getPlugin(pluginName);
String placeholderIdentifier = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (badPlaceholder) {
builder.append('%').append(pluginName);
if (identified) {
builder.append('_').append(placeholderIdentifier);
}
if (hadSpace) {
builder.append(' ');
}
} else {
if (plugin == null) {
builder.append('%').append(pluginName);
if (identified) {
builder.append('_');
}
builder.append(placeholderIdentifier).append('%');
} else {
builder.append(getResult(player, placeholderIdentifier, plugin));
}
}
} else {
builder.append(currentChar);
}
}
return builder.toString();
} }
private record PlaceholderLookup(@NotNull String identifier, /**
@Nullable EcoPlugin plugin) { * Get all registered placeholders for a plugin.
*
} * @param plugin The plugin.
* @return The placeholders.
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry, */
@NotNull Player player) { public static Set<Placeholder> getRegisteredPlaceholders(@NotNull final EcoPlugin plugin) {
return REGISTERED_PLACEHOLDERS.get(plugin);
}
private record DynamicEntryWithPlayer(@NotNull PlayerDynamicPlaceholder entry,
@NotNull String args,
@NotNull Player player) {
} }
private PlaceholderManager() { private PlaceholderManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.shop; package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import com.willfp.eco.core.price.Price; import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree; import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -13,11 +14,12 @@ import java.util.Set;
/** /**
* Class to handle shop integrations. * Class to handle shop integrations.
*/ */
@SuppressWarnings("DeprecatedIsStillUsed")
public final class ShopManager { public final class ShopManager {
/** /**
* A set of all registered integrations. * A set of all registered integrations.
*/ */
private static final Set<ShopIntegration> REGISTERED = new HashSet<>(); private static final IntegrationRegistry<ShopIntegration> REGISTRY = new IntegrationRegistry<>();
/** /**
* Register a new integration. * Register a new integration.
@@ -25,17 +27,14 @@ public final class ShopManager {
* @param integration The integration to register. * @param integration The integration to register.
*/ */
public static void register(@NotNull final ShopIntegration integration) { public static void register(@NotNull final ShopIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName())); REGISTRY.register(integration);
REGISTERED.add(integration);
} }
/** /**
* Register eco item provider for shop plugins. * Register eco item provider for shop plugins.
*/ */
public static void registerEcoProvider() { public static void registerEcoProvider() {
for (ShopIntegration shopIntegration : REGISTERED) { REGISTRY.forEachSafely(ShopIntegration::registerEcoProvider);
shopIntegration.registerEcoProvider();
}
} }
/** /**
@@ -51,11 +50,7 @@ public final class ShopManager {
return false; return false;
} }
for (ShopIntegration integration : REGISTERED) { return REGISTRY.anySafely(integration -> integration.isSellable(itemStack, player));
return integration.isSellable(itemStack, player);
}
return false;
} }
/** /**
@@ -74,11 +69,10 @@ public final class ShopManager {
return new PriceFree(); return new PriceFree();
} }
for (ShopIntegration integration : REGISTERED) { return REGISTRY.firstSafely(
return integration.getUnitValue(itemStack, player); integration -> integration.getUnitValue(itemStack, player),
} new PriceFree()
);
return new PriceFree();
} }
/** /**
@@ -108,11 +102,10 @@ public final class ShopManager {
return 0.0; return 0.0;
} }
for (ShopIntegration shopIntegration : REGISTERED) { return REGISTRY.firstSafely(
return shopIntegration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount()); integration -> integration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount()),
} 0.0
);
return 0.0;
} }
/** /**
@@ -121,7 +114,7 @@ public final class ShopManager {
* @return The integrations. * @return The integrations.
*/ */
public static Set<ShopIntegration> getRegisteredIntegrations() { public static Set<ShopIntegration> getRegisteredIntegrations() {
return new HashSet<>(REGISTERED); return new HashSet<>(REGISTRY.values());
} }
private ShopManager() { private ShopManager() {

View File

@@ -1,7 +1,6 @@
package com.willfp.eco.core.items; package com.willfp.eco.core.items;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -55,7 +54,7 @@ public class CustomItem implements TestableItem {
*/ */
Eco.get().getEcoPlugin().getScheduler().runLater(() -> { Eco.get().getEcoPlugin().getScheduler().runLater(() -> {
if (!matches(getItem())) { if (!matches(getItem())) {
Bukkit.getLogger().severe("Item with key " + key + " is invalid!"); Eco.get().getEcoPlugin().getLogger().severe("Item with key " + key + " is invalid!");
} }
}, 1); }, 1);
} }

View File

@@ -150,7 +150,7 @@ public class DefaultMap<K, V> implements Map<K, V> {
*/ */
@NotNull @NotNull
public static <K, K1, V> DefaultMap<K, Map<K1, V>> createNestedMap() { public static <K, K1, V> DefaultMap<K, Map<K1, V>> createNestedMap() {
return new DefaultMap<>(new HashMap<>()); return new DefaultMap<>(HashMap::new);
} }
/** /**
@@ -163,6 +163,6 @@ public class DefaultMap<K, V> implements Map<K, V> {
*/ */
@NotNull @NotNull
public static <K, K1, V> DefaultMap<K, ListMap<K1, V>> createNestedListMap() { public static <K, K1, V> DefaultMap<K, ListMap<K1, V>> createNestedListMap() {
return new DefaultMap<>(new ListMap<>()); return new DefaultMap<>(ListMap::new);
} }
} }

View File

@@ -3,27 +3,25 @@ package com.willfp.eco.core.math;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable; import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
/** /**
* Represents a context to do math in. * Represents a context to parse math in.
* *
* @param injectableContext The PlaceholderInjectable context. * @deprecated Use {@link PlaceholderContext} instead.
* @param player The player.
* @param additionalPlayers The additional players.
*/ */
public record MathContext( @SuppressWarnings("DeprecatedIsStillUsed")
@NotNull PlaceholderInjectable injectableContext, @Deprecated(since = "6.56.0", forRemoval = true)
@Nullable Player player, public class MathContext {
@NotNull Collection<AdditionalPlayer> additionalPlayers
) {
/** /**
* Empty math context. * Returns an empty math parseContext.
*/ */
public static final MathContext EMPTY = new MathContext( public static final MathContext EMPTY = new MathContext(
PlaceholderManager.EMPTY_INJECTABLE, PlaceholderManager.EMPTY_INJECTABLE,
@@ -32,9 +30,157 @@ public record MathContext(
); );
/** /**
* Create MathContext of a PlaceholderInjectable context. * The PlaceholderInjectable parse context.
*/
@NotNull
private final PlaceholderInjectable injectableContext;
/**
* The player.
*/
@Nullable
private final Player player;
/**
* The additional players.
*/
@NotNull
private final Collection<AdditionalPlayer> additionalPlayers;
/**
* Constructs a new MathContext with the given parameters.
* *
* @param injectableContext The PlaceholderInjectable context. * @param injectableContext The PlaceholderInjectable parseContext.
* @param player The player.
* @param additionalPlayers The additional players.
*/
public MathContext(@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers) {
this.injectableContext = injectableContext;
this.player = player;
this.additionalPlayers = additionalPlayers;
}
/**
* Returns the PlaceholderInjectable parse context.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The injectable context.
* @deprecated Use {@link #getInjectableContext()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
public PlaceholderInjectable injectableContext() {
return injectableContext;
}
/**
* Returns the PlaceholderInjectable parse context.
*
* @return The injectable context.
*/
@NotNull
public PlaceholderInjectable getInjectableContext() {
return injectableContext;
}
/**
* Returns the player.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The player.
* @deprecated Use {@link #getPlayer()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@Nullable
public Player player() {
return player;
}
/**
* Returns the player.
*
* @return The player.
*/
@Nullable
public Player getPlayer() {
return player;
}
/**
* Returns the additional players.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The additional players.
* @deprecated Use {@link #getAdditionalPlayers()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public Collection<AdditionalPlayer> additionalPlayers() {
return additionalPlayers;
}
/**
* Returns the additional players.
*
* @return The additional players.
*/
@NotNull
public Collection<AdditionalPlayer> getAdditionalPlayers() {
return additionalPlayers;
}
/**
* Convert to PlaceholderContext.
*
* @return The PlaceholderContext.
*/
@NotNull
public PlaceholderContext toPlaceholderContext() {
return new PlaceholderContext(
this.player,
null,
this.injectableContext,
this.additionalPlayers
);
}
@Override
public String toString() {
return "MathContext{" +
"injectableContext=" + injectableContext +
", player=" + player +
", additionalPlayers=" + additionalPlayers +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MathContext that)) {
return false;
}
return injectableContext.equals(that.injectableContext) &&
Objects.equals(player, that.player) &&
additionalPlayers.equals(that.additionalPlayers);
}
@Override
public int hashCode() {
return Objects.hash(injectableContext, player, additionalPlayers);
}
/**
* Create MathContext of a PlaceholderInjectable parseContext.
*
* @param injectableContext The PlaceholderInjectable parseContext.
* @return The MathContext. * @return The MathContext.
*/ */
public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) { public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) {
@@ -48,16 +194,16 @@ public record MathContext(
/** /**
* Copy a MathContext with a player. * Copy a MathContext with a player.
* *
* @param context The context. * @param context The parseContext.
* @param player The player. * @param player The player.
* @return The new MathContext. * @return The new MathContext.
*/ */
public static MathContext copyWithPlayer(@NotNull final MathContext context, public static MathContext copyWithPlayer(@NotNull final MathContext context,
@Nullable final Player player) { @Nullable final Player player) {
return new MathContext( return new MathContext(
context.injectableContext(), context.injectableContext,
player, player,
context.additionalPlayers() context.additionalPlayers
); );
} }
} }

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -12,24 +12,24 @@ import java.util.regex.Pattern;
/** /**
* A placeholder that does not require a player and supports dynamic styles. * A placeholder that does not require a player and supports dynamic styles.
*/ */
public final class DynamicPlaceholder implements Placeholder { public final class DynamicPlaceholder implements RegistrablePlaceholder {
/** /**
* The placeholder pattern. * The arguments pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder. * The function to retrieve the output of the arguments.
*/ */
private final Function<String, String> function; private final Function<@NotNull String, @Nullable String> function;
/** /**
* The plugin for the placeholder. * The plugin for the arguments.
*/ */
private final EcoPlugin plugin; private final EcoPlugin plugin;
/** /**
* Create a new dynamic placeholder. * Create a new dynamic arguments.
* *
* @param plugin The plugin. * @param plugin The plugin.
* @param pattern The pattern. * @param pattern The pattern.
@@ -37,31 +37,33 @@ public final class DynamicPlaceholder implements Placeholder {
*/ */
public DynamicPlaceholder(@NotNull final EcoPlugin plugin, public DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern, @NotNull final Pattern pattern,
@NotNull final Function<String, String> function) { @NotNull final Function<@NotNull String, @Nullable String> function) {
this.plugin = plugin; this.plugin = plugin;
this.pattern = pattern; this.pattern = pattern;
this.function = function; this.function = function;
} }
/** @Override
* Get the value of the placeholder. @Nullable
* public String getValue(@NotNull final String args,
* @param args The args. @NotNull final PlaceholderContext context) {
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args) {
return function.apply(args); return function.apply(args);
} }
/** /**
* Register the placeholder. * Get the value of the arguments.
* *
* @return The placeholder. * @param args The args.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/ */
public DynamicPlaceholder register() { @Deprecated(since = "6.56.0", forRemoval = true)
PlaceholderManager.registerPlaceholder(this); @NotNull
return this; public String getValue(@NotNull final String args) {
return Objects.requireNonNullElse(
function.apply(args),
""
);
} }
@Override @Override
@@ -69,18 +71,17 @@ public final class DynamicPlaceholder implements Placeholder {
return this.plugin; return this.plugin;
} }
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull @NotNull
@Override @Override
public Pattern getPattern() { public Pattern getPattern() {
return this.pattern; return this.pattern;
} }
@Override
public @NotNull DynamicPlaceholder register() {
return (DynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override @Override
public boolean equals(@Nullable final Object o) { public boolean equals(@Nullable final Object o) {
if (this == o) { if (this == o) {
@@ -97,6 +98,6 @@ public final class DynamicPlaceholder implements Placeholder {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin()); return Objects.hash(this.getPattern(), this.getPlugin());
} }
} }

View File

@@ -1,15 +1,20 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;
/** /**
* Placeholders that can be injected into {@link PlaceholderInjectable} objects. * Placeholders that can be injected into {@link PlaceholderInjectable} objects.
*/ */
public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder { public interface InjectablePlaceholder extends Placeholder {
/**
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@Nullable
@Override @Override
default @NotNull EcoPlugin getPlugin() { default EcoPlugin getPlugin() {
return Eco.get().getEcoPlugin(); return null;
} }
} }

View File

@@ -1,38 +1,64 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* A placeholder represents a string that can hold a value. * A placeholder represents a string that can hold a value.
*/ */
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder, public interface Placeholder {
DynamicPlaceholder, PlayerDynamicPlaceholder, InjectablePlaceholder {
/** /**
* Get the plugin that holds the placeholder. * Get the plugin that holds the arguments.
* *
* @return The plugin. * @return The plugin.
*/ */
@NotNull @Nullable
EcoPlugin getPlugin(); EcoPlugin getPlugin();
/** /**
* Get the identifier for the placeholder. * Get the value of the arguments.
* *
* @return The identifier. * @param args The args.
* @param context The context.
* @return The value.
*/ */
@NotNull @Nullable
String getIdentifier(); String getValue(@NotNull String args,
@NotNull PlaceholderContext context);
/** /**
* Get the pattern for the placeholder. * Get the pattern for the arguments.
* *
* @return The pattern. * @return The pattern.
*/ */
@NotNull @NotNull
default Pattern getPattern() { Pattern getPattern();
return Pattern.compile(this.getIdentifier());
/**
* Try to translate all instances of this placeholder in text quickly.
*
* @param text The text to translate.
* @param context The context.
* @return The translated text.
*/
default String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text;
}
/**
* Get the identifier for the arguments.
*
* @return The identifier.
* @deprecated Some arguments may not have an identifier. Use {@link #getPattern()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
default String getIdentifier() {
return this.getPattern().pattern();
} }
} }

View File

@@ -9,7 +9,7 @@ import java.util.List;
*/ */
public interface PlaceholderInjectable { public interface PlaceholderInjectable {
/** /**
* Inject placeholder. * Inject arguments.
* *
* @param placeholders The placeholders. * @param placeholders The placeholders.
*/ */
@@ -18,7 +18,7 @@ public interface PlaceholderInjectable {
} }
/** /**
* Inject placeholder. * Inject arguments.
* *
* @param placeholders The placeholders. * @param placeholders The placeholders.
*/ */
@@ -29,7 +29,7 @@ public interface PlaceholderInjectable {
/** /**
* Inject placeholders. * Inject placeholders.
* <p> * <p>
* When implementing a PlaceholderInjectable object, override this method. * If a placeholder already has the same pattern, it should be replaced.
* *
* @param placeholders The placeholders. * @param placeholders The placeholders.
*/ */
@@ -43,7 +43,7 @@ public interface PlaceholderInjectable {
/** /**
* Get injected placeholders. * Get injected placeholders.
* <p> * <p>
* Override this method in implementations. * This method should always return an immutable list.
* *
* @return Injected placeholders. * @return Injected placeholders.
*/ */

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -11,26 +11,26 @@ import java.util.function.BiFunction;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* A placeholder that does not require a player and supports dynamic styles. * A arguments that does not require a player and supports dynamic styles.
*/ */
public final class PlayerDynamicPlaceholder implements Placeholder { public final class PlayerDynamicPlaceholder implements RegistrablePlaceholder {
/** /**
* The placeholder pattern. * The arguments pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder. * The function to retrieve the output of the arguments.
*/ */
private final BiFunction<String, Player, String> function; private final BiFunction<@NotNull String, @NotNull Player, @Nullable String> function;
/** /**
* The plugin for the placeholder. * The plugin for the arguments.
*/ */
private final EcoPlugin plugin; private final EcoPlugin plugin;
/** /**
* Create a new dynamic placeholder. * Create a new dynamic arguments.
* *
* @param plugin The plugin. * @param plugin The plugin.
* @param pattern The pattern. * @param pattern The pattern.
@@ -38,33 +38,40 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
*/ */
public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin, public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern, @NotNull final Pattern pattern,
@NotNull final BiFunction<String, Player, String> function) { @NotNull final BiFunction<@NotNull String, @NotNull Player, @Nullable String> function) {
this.plugin = plugin; this.plugin = plugin;
this.pattern = pattern; this.pattern = pattern;
this.function = function; this.function = function;
} }
/** @Override
* Get the value of the placeholder. public @Nullable String getValue(@NotNull final String args,
* @NotNull final PlaceholderContext context) {
* @param args The args. Player player = context.getPlayer();
* @param player The player.
* @return The value. if (player == null) {
*/ return null;
@NotNull }
public String getValue(@NotNull final String args,
@NotNull final Player player) {
return function.apply(args, player); return function.apply(args, player);
} }
/** /**
* Register the placeholder. * Get the value of the arguments.
* *
* @return The placeholder. * @param args The args.
* @param player The player.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/ */
public PlayerDynamicPlaceholder register() { @Deprecated(since = "6.56.0", forRemoval = true)
PlaceholderManager.registerPlaceholder(this); @NotNull
return this; public String getValue(@NotNull final String args,
@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(args, player),
""
);
} }
@Override @Override
@@ -72,18 +79,17 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
return this.plugin; return this.plugin;
} }
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull @NotNull
@Override @Override
public Pattern getPattern() { public Pattern getPattern() {
return this.pattern; return this.pattern;
} }
@Override
public @NotNull PlayerDynamicPlaceholder register() {
return (PlayerDynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override @Override
public boolean equals(@Nullable final Object o) { public boolean equals(@Nullable final Object o) {
if (this == o) { if (this == o) {
@@ -100,6 +106,6 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin()); return Objects.hash(this.getPattern(), this.getPlugin());
} }
} }

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -11,31 +12,26 @@ import java.util.function.Function;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* A placeholder that requires a player. * A arguments that requires a player.
*/ */
public final class PlayerPlaceholder implements Placeholder { public final class PlayerPlaceholder implements RegistrablePlaceholder {
/** /**
* The name of the placeholder. * The arguments pattern.
*/
private final String identifier;
/**
* The placeholder pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder given a player. * The function to retrieve the output of the arguments given a player.
*/ */
private final Function<Player, String> function; private final Function<@NotNull Player, @Nullable String> function;
/** /**
* The plugin for the placeholder. * The plugin for the arguments.
*/ */
private final EcoPlugin plugin; private final EcoPlugin plugin;
/** /**
* Create a new player placeholder. * Create a new player arguments.
* *
* @param plugin The plugin. * @param plugin The plugin.
* @param identifier The identifier. * @param identifier The identifier.
@@ -43,32 +39,38 @@ public final class PlayerPlaceholder implements Placeholder {
*/ */
public PlayerPlaceholder(@NotNull final EcoPlugin plugin, public PlayerPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier, @NotNull final String identifier,
@NotNull final Function<Player, String> function) { @NotNull final Function<@NotNull Player, @Nullable String> function) {
this.plugin = plugin; this.plugin = plugin;
this.identifier = identifier; this.pattern = PatternUtils.compileLiteral(identifier);
this.pattern = Pattern.compile(identifier);
this.function = function; this.function = function;
} }
/** @Override
* Get the value of the placeholder for a given player. public @Nullable String getValue(@NotNull final String args,
* @NotNull final PlaceholderContext context) {
* @param player The player. Player player = context.getPlayer();
* @return The value.
*/ if (player == null) {
@NotNull return null;
public String getValue(@NotNull final Player player) { }
return function.apply(player); return function.apply(player);
} }
/** /**
* Register the placeholder. * Get the value of the arguments for a given player.
* *
* @return The placeholder. * @param player The player.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/ */
public PlayerPlaceholder register() { @Deprecated(since = "6.56.0", forRemoval = true)
PlaceholderManager.registerPlaceholder(this); @NotNull
return this; public String getValue(@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(player),
""
);
} }
@Override @Override
@@ -76,17 +78,17 @@ public final class PlayerPlaceholder implements Placeholder {
return this.plugin; return this.plugin;
} }
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull @NotNull
@Override @Override
public Pattern getPattern() { public Pattern getPattern() {
return this.pattern; return this.pattern;
} }
@Override
public @NotNull PlayerPlaceholder register() {
return (PlayerPlaceholder) RegistrablePlaceholder.super.register();
}
@Override @Override
public boolean equals(@Nullable final Object o) { public boolean equals(@Nullable final Object o) {
if (this == o) { if (this == o) {
@@ -95,12 +97,12 @@ public final class PlayerPlaceholder implements Placeholder {
if (!(o instanceof PlayerPlaceholder that)) { if (!(o instanceof PlayerPlaceholder that)) {
return false; return false;
} }
return Objects.equals(this.getIdentifier(), that.getIdentifier()) return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin()); && Objects.equals(this.getPlugin(), that.getPlugin());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin()); return Objects.hash(this.getPattern(), this.getPlugin());
} }
} }

View File

@@ -1,5 +1,8 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -13,47 +16,72 @@ import java.util.regex.Pattern;
*/ */
public final class PlayerStaticPlaceholder implements InjectablePlaceholder { public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
/** /**
* The name of the placeholder. * The identifier.
*/ */
private final String identifier; private final String identifier;
/** /**
* The placeholder pattern. * The arguments pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder. * The function to retrieve the output of the arguments.
*/ */
private final Function<Player, String> function; private final Function<@NotNull Player, @Nullable String> function;
/** /**
* Create a new player placeholder. * Create a new player arguments.
* *
* @param identifier The identifier. * @param identifier The identifier.
* @param function The function to retrieve the value. * @param function The function to retrieve the value.
*/ */
public PlayerStaticPlaceholder(@NotNull final String identifier, public PlayerStaticPlaceholder(@NotNull final String identifier,
@NotNull final Function<Player, String> function) { @NotNull final Function<@NotNull Player, @Nullable String> function) {
this.identifier = identifier; this.identifier = "%" + identifier + "%";
this.pattern = Pattern.compile(identifier); this.pattern = PatternUtils.compileLiteral(identifier);
this.function = function; this.function = function;
} }
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
return this.getValue(player);
}
/** /**
* Get the value of the placeholder. * Get the value of the arguments.
* *
* @param player The player. * @param player The player.
* @return The value. * @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull @NotNull
public String getValue(@NotNull final Player player) { public String getValue(@NotNull final Player player) {
return function.apply(player); return Objects.requireNonNullElse(
function.apply(player),
""
);
} }
@Override @Override
public @NotNull String getIdentifier() { public String tryTranslateQuickly(@NotNull final String text,
return this.identifier; @NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(
this.getValue(identifier, context),
""
)
);
} }
@NotNull @NotNull
@@ -70,11 +98,11 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof PlayerStaticPlaceholder that)) { if (!(o instanceof PlayerStaticPlaceholder that)) {
return false; return false;
} }
return Objects.equals(this.getIdentifier(), that.getIdentifier()); return Objects.equals(this.getPattern(), that.getPattern());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier()); return Objects.hash(this.getPattern());
} }
} }

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -10,31 +11,26 @@ import java.util.function.Supplier;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* A placeholder that does not require a player. * A arguments that does not require a player.
*/ */
public final class PlayerlessPlaceholder implements Placeholder { public final class PlayerlessPlaceholder implements RegistrablePlaceholder {
/** /**
* The placeholder identifier. * The arguments pattern.
*/
private final String identifier;
/**
* The placeholder pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder. * The function to retrieve the output of the arguments.
*/ */
private final Supplier<String> function; private final Supplier<@Nullable String> function;
/** /**
* The plugin for the placeholder. * The plugin for the arguments.
*/ */
private final EcoPlugin plugin; private final EcoPlugin plugin;
/** /**
* Create a new player placeholder. * Create a new player arguments.
* *
* @param plugin The plugin. * @param plugin The plugin.
* @param identifier The identifier. * @param identifier The identifier.
@@ -42,30 +38,31 @@ public final class PlayerlessPlaceholder implements Placeholder {
*/ */
public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin, public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier, @NotNull final String identifier,
@NotNull final Supplier<String> function) { @NotNull final Supplier<@Nullable String> function) {
this.plugin = plugin; this.plugin = plugin;
this.identifier = identifier; this.pattern = PatternUtils.compileLiteral(identifier);
this.pattern = Pattern.compile(identifier);
this.function = function; this.function = function;
} }
/** @Override
* Get the value of the placeholder. public @Nullable String getValue(@NotNull final String args,
* @NotNull final PlaceholderContext context) {
* @return The value.
*/
public String getValue() {
return function.get(); return function.get();
} }
/** /**
* Register the placeholder. * Get the value of the arguments.
* *
* @return The placeholder. * @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/ */
public PlayerlessPlaceholder register() { @Deprecated(since = "6.56.0", forRemoval = true)
PlaceholderManager.registerPlaceholder(this); @NotNull
return this; public String getValue() {
return Objects.requireNonNullElse(
this.function.get(),
""
);
} }
@Override @Override
@@ -73,17 +70,17 @@ public final class PlayerlessPlaceholder implements Placeholder {
return this.plugin; return this.plugin;
} }
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull @NotNull
@Override @Override
public Pattern getPattern() { public Pattern getPattern() {
return this.pattern; return this.pattern;
} }
@Override
public @NotNull PlayerlessPlaceholder register() {
return (PlayerlessPlaceholder) RegistrablePlaceholder.super.register();
}
@Override @Override
public boolean equals(@Nullable final Object o) { public boolean equals(@Nullable final Object o) {
if (this == o) { if (this == o) {
@@ -92,12 +89,12 @@ public final class PlayerlessPlaceholder implements Placeholder {
if (!(o instanceof PlayerlessPlaceholder that)) { if (!(o instanceof PlayerlessPlaceholder that)) {
return false; return false;
} }
return Objects.equals(this.getIdentifier(), that.getIdentifier()) return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin()); && Objects.equals(this.getPlugin(), that.getPlugin());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin()); return Objects.hash(this.getPattern(), this.getPlugin());
} }
} }

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.jetbrains.annotations.NotNull;
/**
* Represents a placeholder that can be registered.
*/
public interface RegistrablePlaceholder extends Placeholder {
/**
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@NotNull
@Override
EcoPlugin getPlugin();
/**
* Register the arguments.
*
* @return The arguments.
*/
@NotNull
default RegistrablePlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
}

View File

@@ -1,5 +1,8 @@
package com.willfp.eco.core.placeholder; package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -8,50 +11,66 @@ import java.util.function.Supplier;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* A placeholder that cannot be registered, and exists purely in injection. * A arguments that cannot be registered, and exists purely in injection.
*/ */
public final class StaticPlaceholder implements InjectablePlaceholder { public final class StaticPlaceholder implements InjectablePlaceholder {
/** /**
* The name of the placeholder. * The name of the arguments.
*/ */
private final String identifier; private final String identifier;
/** /**
* The placeholder pattern. * The arguments pattern.
*/ */
private final Pattern pattern; private final Pattern pattern;
/** /**
* The function to retrieve the output of the placeholder. * The function to retrieve the output of the arguments.
*/ */
private final Supplier<String> function; private final Supplier<@Nullable String> function;
/** /**
* Create a new player placeholder. * Create a new player arguments.
* *
* @param identifier The identifier. * @param identifier The identifier.
* @param function The function to retrieve the value. * @param function The function to retrieve the value.
*/ */
public StaticPlaceholder(@NotNull final String identifier, public StaticPlaceholder(@NotNull final String identifier,
@NotNull final Supplier<String> function) { @NotNull final Supplier<@Nullable String> function) {
this.identifier = identifier; this.identifier = "%" + identifier + "%";
this.pattern = Pattern.compile(identifier); this.pattern = PatternUtils.compileLiteral(identifier);
this.function = function; this.function = function;
} }
/** @Override
* Get the value of the placeholder. public @Nullable String getValue(@NotNull final String args,
* @NotNull final PlaceholderContext context) {
* @return The value.
*/
@NotNull
public String getValue() {
return function.get(); return function.get();
} }
/**
* Get the value of the arguments.
*
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue() {
return Objects.requireNonNullElse(
function.get(),
""
);
}
@Override @Override
public @NotNull String getIdentifier() { public String tryTranslateQuickly(@NotNull final String text,
return this.identifier; @NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
} }
@NotNull @NotNull
@@ -68,11 +87,11 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof StaticPlaceholder that)) { if (!(o instanceof StaticPlaceholder that)) {
return false; return false;
} }
return Objects.equals(this.getIdentifier(), that.getIdentifier()); return Objects.equals(this.getPattern(), that.getPattern());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.getIdentifier()); return Objects.hash(this.getPattern());
} }
} }

View File

@@ -0,0 +1,92 @@
package com.willfp.eco.core.placeholder.context;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* A merged injectable context.
*/
public class MergedInjectableContext implements PlaceholderInjectable {
/**
* The base context.
*/
private final PlaceholderInjectable baseContext;
/**
* The additional context.
*/
private final PlaceholderInjectable additionalContext;
/**
* Extra injections.
*/
private final Set<InjectablePlaceholder> extraInjections = new HashSet<>();
/**
* Create a new merged injectable context.
*
* @param baseContext The base context.
* @param additionalContext The additional context.
*/
public MergedInjectableContext(@NotNull final PlaceholderInjectable baseContext,
@NotNull final PlaceholderInjectable additionalContext) {
this.baseContext = baseContext;
this.additionalContext = additionalContext;
}
@Override
public void addInjectablePlaceholder(@NotNull final Iterable<InjectablePlaceholder> placeholders) {
for (InjectablePlaceholder placeholder : placeholders) {
extraInjections.add(placeholder);
}
}
@Override
public void clearInjectedPlaceholders() {
baseContext.clearInjectedPlaceholders();
additionalContext.clearInjectedPlaceholders();
extraInjections.clear();
}
@Override
public @NotNull List<InjectablePlaceholder> getPlaceholderInjections() {
List<InjectablePlaceholder> base = baseContext.getPlaceholderInjections();
List<InjectablePlaceholder> additional = additionalContext.getPlaceholderInjections();
List<InjectablePlaceholder> injections = new ArrayList<>(base.size() + additional.size() + extraInjections.size());
injections.addAll(base);
injections.addAll(additional);
injections.addAll(extraInjections);
return injections;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MergedInjectableContext that)) {
return false;
}
return Objects.equals(baseContext, that.baseContext)
&& Objects.equals(additionalContext, that.additionalContext)
&& Objects.equals(extraInjections, that.extraInjections);
}
@Override
public int hashCode() {
return Objects.hash(baseContext, additionalContext, extraInjections);
}
}

View File

@@ -0,0 +1,252 @@
package com.willfp.eco.core.placeholder.context;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Represents a context to translate placeholders in.
*/
public class PlaceholderContext {
/**
* An empty injectable.
*/
private static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override
public void addInjectablePlaceholder(@NotNull final Iterable<InjectablePlaceholder> placeholders) {
// Do nothing.
}
@Override
public void clearInjectedPlaceholders() {
// Do nothing.
}
@Override
public @NotNull List<InjectablePlaceholder> getPlaceholderInjections() {
return Collections.emptyList();
}
};
/**
* An empty context.
*/
public static final PlaceholderContext EMPTY = new PlaceholderContext(
null,
null,
null,
Collections.emptyList()
);
/**
* The player.
*/
@Nullable
private final Player player;
/**
* The ItemStack.
*/
@Nullable
private final ItemStack itemStack;
/**
* The PlaceholderInjectable context.
*/
@NotNull
private final PlaceholderInjectable injectableContext;
/**
* The additional players.
*/
@NotNull
private final Collection<AdditionalPlayer> additionalPlayers;
/**
* Create an empty PlaceholderContext.
*/
public PlaceholderContext() {
this(null, null, null, Collections.emptyList());
}
/**
* Create a PlaceholderContext for a player.
*
* @param player The player.
*/
public PlaceholderContext(@Nullable final Player player) {
this(player, null, null, Collections.emptyList());
}
/**
* Constructs a new PlaceholderContext with the given parameters.
*
* @param player The player.
* @param itemStack The ItemStack.
* @param injectableContext The PlaceholderInjectable parseContext.
* @param additionalPlayers The additional players.
*/
public PlaceholderContext(@Nullable final Player player,
@Nullable final ItemStack itemStack,
@Nullable final PlaceholderInjectable injectableContext,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
this.player = player;
this.itemStack = itemStack;
this.injectableContext = Objects.requireNonNullElse(injectableContext, EMPTY_INJECTABLE);
this.additionalPlayers = additionalPlayers;
}
/**
* Get the player.
*
* @return The player.
*/
@Nullable
public Player getPlayer() {
return player;
}
/**
* Get the ItemStack.
*
* @return The ItemStack.
*/
@Nullable
public ItemStack getItemStack() {
return itemStack;
}
/**
* Get the PlaceholderInjectable context.
*
* @return The PlaceholderInjectable context.
*/
@NotNull
public PlaceholderInjectable getInjectableContext() {
return injectableContext;
}
/**
* Get the additional players.
*
* @return The additional players.
*/
@NotNull
public Collection<AdditionalPlayer> getAdditionalPlayers() {
return additionalPlayers;
}
/**
* Convert to a {@link com.willfp.eco.core.math.MathContext}.
*
* @return The math context.
* @deprecated MathContext is deprecated, use {@link PlaceholderContext} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
public com.willfp.eco.core.math.MathContext toMathContext() {
return new com.willfp.eco.core.math.MathContext(this.getInjectableContext(), this.getPlayer(), this.getAdditionalPlayers());
}
/**
* Copy with a player.
*
* @param player The player.
* @return The new context.
*/
public PlaceholderContext copyWithPlayer(@Nullable final Player player) {
return new PlaceholderContext(
player,
this.getItemStack(),
this.getInjectableContext(),
this.getAdditionalPlayers()
);
}
/**
* Copy with an item.
*
* @param itemStack The ItemStack.
* @return The new context.
*/
public PlaceholderContext copyWithItem(@Nullable final ItemStack itemStack) {
return new PlaceholderContext(
this.getPlayer(),
itemStack,
this.getInjectableContext(),
this.getAdditionalPlayers()
);
}
/**
* Copy with an extra injectable context.
*
* @param injectableContext The injectable context to add.
* @return The new context.
*/
public PlaceholderContext withInjectableContext(@NotNull final PlaceholderInjectable injectableContext) {
return new PlaceholderContext(
this.getPlayer(),
this.getItemStack(),
new MergedInjectableContext(this.getInjectableContext(), injectableContext),
this.getAdditionalPlayers()
);
}
@Override
public String toString() {
return "PlaceholderContext{" +
"player=" + player +
", itemStack=" + itemStack +
", injectableContext=" + injectableContext +
", additionalPlayers=" + additionalPlayers +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PlaceholderContext that)) {
return false;
}
return Objects.equals(
getPlayer(), that.getPlayer())
&& Objects.equals(getItemStack(), that.getItemStack())
&& getInjectableContext().equals(that.getInjectableContext())
&& getAdditionalPlayers().equals(that.getAdditionalPlayers()
);
}
@Override
public int hashCode() {
return Objects.hash(getPlayer(), getItemStack(), getInjectableContext(), getAdditionalPlayers());
}
/**
* Create PlaceholderContext of a PlaceholderInjectable parseContext.
*
* @param injectableContext The PlaceholderInjectable parseContext.
* @return The context.
*/
public static PlaceholderContext of(@NotNull final PlaceholderInjectable injectableContext) {
return new PlaceholderContext(
null,
null,
injectableContext,
Collections.emptyList()
);
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.core.placeholder.context;
import org.jetbrains.annotations.NotNull;
/**
* A supplier that takes a {@link PlaceholderContext} and returns a value.
*
* @param <T> The type of value to return.
*/
public interface PlaceholderContextSupplier<T> {
/**
* Get the value.
*
* @param context The context.
* @return The value.
*/
@NotNull
T get(@NotNull PlaceholderContext context);
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for dynamic placeholders.
*/
public abstract class DynamicInjectablePlaceholder implements InjectablePlaceholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new dynamic injectable placeholder.
*
* @param pattern The pattern.
*/
protected DynamicInjectablePlaceholder(@NotNull final Pattern pattern) {
this.pattern = pattern;
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@Override
public String toString() {
return "DynamicInjectablePlaceholder{" +
"pattern='" + pattern + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicInjectablePlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern());
}
@Override
public int hashCode() {
return Objects.hash(pattern);
}
}

View File

@@ -0,0 +1,73 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for dynamic placeholders.
*/
public abstract class DynamicPlaceholder implements RegistrablePlaceholder {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
*/
protected DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern) {
this.plugin = plugin;
this.pattern = pattern;
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@NotNull
@Override
public EcoPlugin getPlugin() {
return plugin;
}
@Override
public String toString() {
return "DynamicPlaceholder{" +
"plugin='" + plugin + '\'' +
"pattern='" + pattern + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicPlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern())
&& Objects.equals(plugin, that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(pattern, plugin);
}
}

View File

@@ -0,0 +1,73 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for simple placeholders.
*/
public abstract class SimpleInjectablePlaceholder implements InjectablePlaceholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new simple injectable placeholder.
*
* @param identifier The identifier.
*/
protected SimpleInjectablePlaceholder(@NotNull final String identifier) {
this.identifier = identifier;
this.pattern = PatternUtils.compileLiteral(identifier);
}
@Override
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text.replace(
"%" + this.identifier + "%",
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@Override
public String toString() {
return "SimpleInjectablePlaceholder{" +
"identifier='" + identifier + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SimpleInjectablePlaceholder that)) {
return false;
}
return Objects.equals(identifier, that.identifier);
}
@Override
public int hashCode() {
return Objects.hash(identifier);
}
}

View File

@@ -0,0 +1,90 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.PatternUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for simple placeholders.
*/
public abstract class SimplePlaceholder implements RegistrablePlaceholder {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new simple placeholder.
*
* @param plugin The plugin.
* @param identifier The identifier.
*/
protected SimplePlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = PatternUtils.compileLiteral(identifier);
}
@Override
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text.replace(
"%" + this.identifier + "%",
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@NotNull
@Override
public EcoPlugin getPlugin() {
return plugin;
}
@Override
public String toString() {
return "SimplePlaceholder{" +
"plugin='" + plugin + '\'' +
"identifier='" + identifier + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SimplePlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern())
&& Objects.equals(plugin, that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(identifier, plugin);
}
}

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.price; package com.willfp.eco.core.price;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.math.MathContext; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.price.impl.PriceFree; import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.serialization.ConfigDeserializer; import com.willfp.eco.core.serialization.ConfigDeserializer;
import com.willfp.eco.util.NumberUtils; import com.willfp.eco.util.NumberUtils;
@@ -158,17 +159,32 @@ public final class ConfiguredPrice implements Price {
if (!( if (!(
config.has("value") config.has("value")
&& config.has("type") && config.has("type")
&& config.has("display")
)) { )) {
return null; return null;
} }
String formatString = config.getString("display"); String formatString;
String langConfig = Eco.get().getEcoPlugin().getLangYml()
.getSubsections("price-display")
.stream()
.filter(section -> section.getString("type").equalsIgnoreCase(config.getString("type")))
.findFirst()
.map(section -> section.getString("display"))
.orElse(null);
if (langConfig != null) {
formatString = langConfig;
} else if (config.has("display")) {
formatString = config.getString("display");
} else {
return null;
}
Price price = Prices.create( Price price = Prices.create(
config.getString("value"), config.getString("value"),
config.getString("type"), config.getString("type"),
MathContext.of(config) PlaceholderContext.of(config)
); );
return new ConfiguredPrice(price, formatString); return new ConfiguredPrice(price, formatString);

View File

@@ -1,6 +1,7 @@
package com.willfp.eco.core.price; package com.willfp.eco.core.price;
import com.willfp.eco.core.math.MathContext; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -10,7 +11,8 @@ import java.util.function.Function;
/** /**
* Create prices. * Create prices.
* <p> * <p>
* You must override one of the create methods. * Override create(PlaceholderContext, PlaceholderContextSupplier), other methods
* are for backwards compatibility.
*/ */
public interface PriceFactory { public interface PriceFactory {
/** /**
@@ -27,20 +29,38 @@ public interface PriceFactory {
* *
* @param value The value. * @param value The value.
* @return The price. * @return The price.
* @deprecated Use {@link #create(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/ */
@Deprecated(since = "6.56.0", forRemoval = true)
default @NotNull Price create(final double value) { default @NotNull Price create(final double value) {
return create(MathContext.EMPTY, (ctx) -> value); return create(PlaceholderContext.EMPTY, (ctx) -> value);
} }
/** /**
* Create the price. * Create the price.
* *
* @param baseContext The base MathContext. * @param baseContext The base MathContext.
* @param function The function to use. Should use {@link MathContext#copyWithPlayer(MathContext, Player)} on calls. * @param function The function to use. Should use {@link com.willfp.eco.core.math.MathContext#copyWithPlayer(com.willfp.eco.core.math.MathContext, Player)} on calls.
* @return The price.
* @deprecated Use {@link #create(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
default @NotNull Price create(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function) {
return create(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()));
}
/**
* Create the price.
*
* @param baseContext The base PlaceholderContext.
* @param function The function to use. Should use {@link PlaceholderContext#copyWithPlayer(Player)} on calls.
* @return The price. * @return The price.
*/ */
default @NotNull Price create(@NotNull final MathContext baseContext, @SuppressWarnings("removal")
@NotNull final Function<MathContext, Double> function) { default @NotNull Price create(@NotNull final PlaceholderContext baseContext,
return create(function.apply(baseContext)); @NotNull final PlaceholderContextSupplier<Double> function) {
return create(baseContext.toMathContext(), (com.willfp.eco.core.math.MathContext ctx) -> function.get(ctx.toPlaceholderContext()));
} }
} }

View File

@@ -2,7 +2,8 @@ package com.willfp.eco.core.price;
import com.willfp.eco.core.items.Items; import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.items.TestableItem; import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.impl.PriceEconomy; import com.willfp.eco.core.price.impl.PriceEconomy;
import com.willfp.eco.core.price.impl.PriceFree; import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.price.impl.PriceItem; import com.willfp.eco.core.price.impl.PriceItem;
@@ -13,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/** /**
* Class to manage prices. * Class to manage prices.
@@ -48,7 +48,28 @@ public final class Prices {
@NotNull @NotNull
public static Price create(@NotNull final String expression, public static Price create(@NotNull final String expression,
@Nullable final String priceName) { @Nullable final String priceName) {
return create(expression, priceName, MathContext.EMPTY); return create(expression, priceName, PlaceholderContext.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.
* @deprecated Use {@link #create(String, String, PlaceholderContext)} instead.
*/
@NotNull
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final com.willfp.eco.core.math.MathContext context) {
return create(expression, priceName, context.toPlaceholderContext());
} }
/** /**
@@ -65,8 +86,8 @@ public final class Prices {
@NotNull @NotNull
public static Price create(@NotNull final String expression, public static Price create(@NotNull final String expression,
@Nullable final String priceName, @Nullable final String priceName,
@NotNull final MathContext context) { @NotNull final PlaceholderContext context) {
Function<MathContext, Double> function = (ctx) -> NumberUtils.evaluateExpression( PlaceholderContextSupplier<Double> function = (ctx) -> NumberUtils.evaluateExpression(
expression, expression,
ctx ctx
); );

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.price.impl; package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.integrations.economy.EconomyManager; import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.math.MathContext; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.Price; import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -18,12 +19,12 @@ public final class PriceEconomy implements Price {
/** /**
* The value of the price. * The value of the price.
*/ */
private final Function<MathContext, Double> function; private final PlaceholderContextSupplier<Double> function;
/** /**
* The base math context. * The base placeholder context.
*/ */
private final MathContext baseContext; private final PlaceholderContext baseContext;
/** /**
* The multipliers. * The multipliers.
@@ -36,7 +37,21 @@ public final class PriceEconomy implements Price {
* @param value The value. * @param value The value.
*/ */
public PriceEconomy(final double value) { public PriceEconomy(final double value) {
this(MathContext.EMPTY, ctx -> value); this(PlaceholderContext.EMPTY, (PlaceholderContext ctx) -> value);
}
/**
* Create a new economy-based price.
*
* @param baseContext The base context.
* @param function The function.
* @deprecated Use {@link #PriceEconomy(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public PriceEconomy(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function) {
this(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()));
} }
/** /**
@@ -45,8 +60,8 @@ public final class PriceEconomy implements Price {
* @param baseContext The base context. * @param baseContext The base context.
* @param function The function. * @param function The function.
*/ */
public PriceEconomy(@NotNull final MathContext baseContext, public PriceEconomy(@NotNull final PlaceholderContext baseContext,
@NotNull final Function<MathContext, Double> function) { @NotNull final PlaceholderContextSupplier<Double> function) {
this.baseContext = baseContext; this.baseContext = baseContext;
this.function = function; this.function = function;
} }
@@ -72,7 +87,7 @@ public final class PriceEconomy implements Price {
@Override @Override
public double getValue(@NotNull final Player player, public double getValue(@NotNull final Player player,
final double multiplier) { final double multiplier) {
return this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier; return this.function.get(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier;
} }
@Override @Override

View File

@@ -3,7 +3,8 @@ package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.drops.DropQueue; import com.willfp.eco.core.drops.DropQueue;
import com.willfp.eco.core.items.HashedItem; import com.willfp.eco.core.items.HashedItem;
import com.willfp.eco.core.items.TestableItem; import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext; import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.Price; import com.willfp.eco.core.price.Price;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -20,14 +21,14 @@ import java.util.function.Function;
*/ */
public final class PriceItem implements Price { public final class PriceItem implements Price {
/** /**
* The base MathContext. * The base PlaceholderContext.
*/ */
private final MathContext baseContext; private final PlaceholderContext baseContext;
/** /**
* The amount of items. * The amount of items.
*/ */
private final Function<MathContext, Double> function; private final PlaceholderContextSupplier<Double> function;
/** /**
* The item. * The item.
@@ -47,7 +48,23 @@ public final class PriceItem implements Price {
*/ */
public PriceItem(final int amount, public PriceItem(final int amount,
@NotNull final TestableItem item) { @NotNull final TestableItem item) {
this(MathContext.EMPTY, ctx -> (double) amount, item); this(PlaceholderContext.EMPTY, (PlaceholderContext 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.
* @deprecated Use {@link #PriceItem(PlaceholderContext, PlaceholderContextSupplier, TestableItem)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public PriceItem(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function,
@NotNull final TestableItem item) {
this(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()), item);
} }
/** /**
@@ -57,8 +74,8 @@ public final class PriceItem implements Price {
* @param function The function to get the amount of items to remove. * @param function The function to get the amount of items to remove.
* @param item The item. * @param item The item.
*/ */
public PriceItem(@NotNull final MathContext baseContext, public PriceItem(@NotNull final PlaceholderContext baseContext,
@NotNull final Function<MathContext, Double> function, @NotNull final PlaceholderContextSupplier<Double> function,
@NotNull final TestableItem item) { @NotNull final TestableItem item) {
this.baseContext = baseContext; this.baseContext = baseContext;
this.function = function; this.function = function;
@@ -137,7 +154,7 @@ public final class PriceItem implements Price {
public double getValue(@NotNull final Player player, public double getValue(@NotNull final Player player,
final double multiplier) { final double multiplier) {
return Math.toIntExact(Math.round( return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player)) this.function.get(baseContext.copyWithPlayer(player))
* getMultiplier(player) * multiplier * getMultiplier(player) * multiplier
)); ));
} }

View File

@@ -23,7 +23,8 @@ public final class ProxyConstants {
"v1_18_R2", "v1_18_R2",
"v1_19_R1", "v1_19_R1",
"v1_19_R2", "v1_19_R2",
"v1_19_R3" "v1_19_R3",
"v1_20_R1"
); );
private ProxyConstants() { private ProxyConstants() {

View File

@@ -9,7 +9,6 @@ import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.recipe.recipes.CraftingRecipe; import com.willfp.eco.core.recipe.recipes.CraftingRecipe;
import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe; import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe;
import com.willfp.eco.util.NamespacedKeyUtils; import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -127,8 +126,8 @@ public final class Recipes {
} }
if (builder.isAir()) { if (builder.isAir()) {
Bukkit.getLogger().warning("RECIPE ERROR! " + plugin.getName() + ":" + key + " consists only"); plugin.getLogger().warning("Crafting recipe " + plugin.getID() + ":" + key + " consists only");
Bukkit.getLogger().warning("of air or invalid items! Please change that or disable this recipe."); plugin.getLogger().warning("of air or invalid items! It will not be registered.");
return null; return null;
} }

View File

@@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -14,7 +16,7 @@ import java.util.regex.Pattern;
* *
* @param <T> The type of {@link Registrable}. * @param <T> The type of {@link Registrable}.
*/ */
public class Registry<T extends Registrable> { public class Registry<T extends Registrable> implements Iterable<T> {
/** /**
* The ID pattern. * The ID pattern.
*/ */
@@ -25,6 +27,17 @@ public class Registry<T extends Registrable> {
*/ */
private final Map<String, T> registry = new HashMap<>(); private final Map<String, T> registry = new HashMap<>();
/**
* If the registry is locked.
*/
private boolean isLocked = false;
/**
* The locker, used to 'secure' registries and prevent random unlocking.
*/
@Nullable
private Object locker = null;
/** /**
* Instantiate a new registry. * Instantiate a new registry.
*/ */
@@ -40,6 +53,10 @@ public class Registry<T extends Registrable> {
*/ */
@NotNull @NotNull
public T register(@NotNull final T element) { public T register(@NotNull final T element) {
if (this.isLocked) {
throw new IllegalStateException("Cannot add to locked registry! (ID: " + element.getID() + ")");
}
Validate.isTrue(ID_PATTERN.matcher(element.getID()).matches(), "ID must match pattern: " + ID_PATTERN.pattern() + " (was " + element.getID() + ")"); Validate.isTrue(ID_PATTERN.matcher(element.getID()).matches(), "ID must match pattern: " + ID_PATTERN.pattern() + " (was " + element.getID() + ")");
registry.put(element.getID(), element); registry.put(element.getID(), element);
@@ -56,6 +73,10 @@ public class Registry<T extends Registrable> {
* @return The element. * @return The element.
*/ */
public T remove(@NotNull final T element) { public T remove(@NotNull final T element) {
if (this.isLocked) {
throw new IllegalStateException("Cannot remove from locked registry! (ID: " + element.getID() + ")");
}
element.onRemove(); element.onRemove();
registry.remove(element.getID()); registry.remove(element.getID());
@@ -71,6 +92,10 @@ public class Registry<T extends Registrable> {
*/ */
@Nullable @Nullable
public T remove(@NotNull final String id) { public T remove(@NotNull final String id) {
if (this.isLocked) {
throw new IllegalStateException("Cannot remove from locked registry! (ID: " + id + ")");
}
T element = registry.get(id); T element = registry.get(id);
if (element != null) { if (element != null) {
@@ -109,6 +134,67 @@ public class Registry<T extends Registrable> {
return Set.copyOf(registry.values()); return Set.copyOf(registry.values());
} }
/**
* Get if the registry is locked.
*
* @return If the registry is locked.
*/
public boolean isLocked() {
return isLocked;
}
/**
* Lock the registry.
*
* @param locker The locker.
*/
public void lock(@Nullable final Object locker) {
if (this.isLocked && this.locker != locker) {
throw new IllegalArgumentException("Registry is already locked with a different locker!");
}
this.locker = locker;
isLocked = true;
}
/**
* Unlock the registry.
*
* @param locker The locker.
*/
public void unlock(@Nullable final Object locker) {
if (this.locker != locker) {
throw new IllegalArgumentException("Cannot unlock registry!");
}
this.locker = null;
isLocked = false;
}
/**
* Get if the registry is empty.
*
* @return If the registry is empty.
*/
public boolean isEmpty() {
return registry.isEmpty();
}
/**
* Get if the registry is not empty.
*
* @return If the registry is not empty.
*/
public boolean isNotEmpty() {
return !isEmpty();
}
@NotNull
@Override
public Iterator<T> iterator() {
return values().iterator();
}
/** /**
* Try to fit a string to the ID pattern. * Try to fit a string to the ID pattern.
* *
@@ -120,6 +206,6 @@ public class Registry<T extends Registrable> {
return string.replace(" ", "_") return string.replace(" ", "_")
.replace(".", "_") .replace(".", "_")
.replace("-", "_") .replace("-", "_")
.toLowerCase(); .toLowerCase(Locale.ENGLISH);
} }
} }

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.version;
import org.jetbrains.annotations.NotNull;
/**
* An error thrown when eco is outdated.
*/
public class OutdatedEcoVersionError extends Error {
/**
* Create a new OutdatedEcoVersionError.
*
* @param message The message.
*/
public OutdatedEcoVersionError(@NotNull final String message) {
super(message);
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.core.version;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.jetbrains.annotations.NotNull;
/**
* A minimal class to represent a version, uses DefaultArtifactVersion under the hood.
* <p>
* This class exists to resolve issues where 1.17 doesn't include DefaultArtifactVersion.
*/
public class Version implements Comparable<Version> {
/**
* The version.
*/
private final DefaultArtifactVersion version;
/**
* Create a new version.
*
* @param version The version.
*/
public Version(@NotNull final String version) {
this.version = new DefaultArtifactVersion(version);
}
@Override
public int compareTo(@NotNull final Version o) {
return this.version.compareTo(o.version);
}
@Override
public String toString() {
return this.version.toString();
}
@Override
public boolean equals(@NotNull final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Version other)) {
return false;
}
return this.version.equals(other.version);
}
@Override
public int hashCode() {
return this.version.hashCode();
}
}

View File

@@ -8,42 +8,15 @@ import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Queue;
import java.util.Set; import java.util.Set;
/** /**
* Utilities / API methods for blocks. * Utilities / API methods for blocks.
*/ */
public final class BlockUtils { public final class BlockUtils {
/**
* Max blocks to mine (yes, this is to prevent a stack overflow).
*/
private static final int MAX_BLOCKS = 2500;
private static Set<Block> getNearbyBlocks(@NotNull final Block start,
@NotNull final List<Material> allowedMaterials,
@NotNull final Set<Block> blocks,
final int limit) {
for (BlockFace face : BlockFace.values()) {
Block block = start.getRelative(face);
if (blocks.contains(block)) {
continue;
}
if (allowedMaterials.contains(block.getType())) {
blocks.add(block);
if (blocks.size() > limit || blocks.size() > MAX_BLOCKS) {
return blocks;
}
blocks.addAll(getNearbyBlocks(block, allowedMaterials, blocks, limit));
}
}
return blocks;
}
/** /**
* Get a set of all blocks in contact with each other of a specific type. * Get a set of all blocks in contact with each other of a specific type.
* *
@@ -56,7 +29,32 @@ public final class BlockUtils {
public static Set<Block> getVein(@NotNull final Block start, public static Set<Block> getVein(@NotNull final Block start,
@NotNull final List<Material> allowedMaterials, @NotNull final List<Material> allowedMaterials,
final int limit) { final int limit) {
return getNearbyBlocks(start, allowedMaterials, new HashSet<>(), limit); Set<Block> blocks = new HashSet<>();
Queue<Block> toProcess = new LinkedList<>();
if (allowedMaterials.contains(start.getType())) {
toProcess.add(start);
}
while (!toProcess.isEmpty() && blocks.size() < limit) {
Block currentBlock = toProcess.poll();
if (blocks.contains(currentBlock)) {
continue;
}
blocks.add(currentBlock);
for (BlockFace face : BlockFace.values()) {
Block adjacentBlock = currentBlock.getRelative(face);
if (!blocks.contains(adjacentBlock) && allowedMaterials.contains(adjacentBlock.getType())) {
toProcess.add(adjacentBlock);
}
}
}
return blocks;
} }
/** /**

View File

@@ -1,10 +1,9 @@
package com.willfp.eco.util; package com.willfp.eco.util;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable; import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -13,6 +12,7 @@ import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -210,7 +210,7 @@ public final class NumberUtils {
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or zero if invalid.
*/ */
public static double evaluateExpression(@NotNull final String expression) { public static double evaluateExpression(@NotNull final String expression) {
return evaluateExpression(expression, MathContext.EMPTY); return evaluateExpression(expression, PlaceholderContext.EMPTY);
} }
/** /**
@@ -222,7 +222,7 @@ public final class NumberUtils {
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) { @Nullable final Player player) {
return evaluateExpression(expression, player, PlaceholderManager.EMPTY_INJECTABLE); return evaluateExpression(expression, player, null);
} }
/** /**
@@ -230,12 +230,12 @@ public final class NumberUtils {
* *
* @param expression The expression. * @param expression The expression.
* @param player The player. * @param player The player.
* @param context The injectable placeholders. * @param context The injectableContext placeholders.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or zero if invalid.
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player, @Nullable final Player player,
@NotNull final PlaceholderInjectable context) { @Nullable final PlaceholderInjectable context) {
return evaluateExpression(expression, player, context, new ArrayList<>()); return evaluateExpression(expression, player, context, new ArrayList<>());
} }
@@ -244,30 +244,65 @@ public final class NumberUtils {
* *
* @param expression The expression. * @param expression The expression.
* @param player The player. * @param player The player.
* @param context The injectable placeholders. * @param context The injectableContext placeholders.
* @param additionalPlayers Additional players to parse placeholders for. * @param additionalPlayers Additional players to parse placeholders for.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or zero if invalid.
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player, @Nullable final Player player,
@NotNull final PlaceholderInjectable context, @Nullable final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) { @NotNull final Collection<AdditionalPlayer> additionalPlayers) {
return Eco.get().evaluate(expression, new MathContext( return evaluateExpression(
context, expression,
player, new PlaceholderContext(
additionalPlayers player,
)); null,
context,
additionalPlayers
)
);
} }
/** /**
* Evaluate an expression with respect to a player (for placeholders). * Evaluate an expression in a context.
* *
* @param expression The expression. * @param expression The expression.
* @param context The math context. * @param context The context.
* @return The value of the expression, or zero if invalid.
* @deprecated Use {@link #evaluateExpression(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
public static double evaluateExpression(@NotNull final String expression,
@NotNull final com.willfp.eco.core.math.MathContext context) {
return evaluateExpression(expression, context.toPlaceholderContext());
}
/**
* Evaluate an expression in a context.
*
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or zero if invalid.
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@NotNull final MathContext context) { @NotNull final PlaceholderContext context) {
return Objects.requireNonNullElse(
evaluateExpressionOrNull(expression, context),
0.0
);
}
/**
* Evaluate an expression in a context.
*
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid.
*/
@Nullable
public static Double evaluateExpressionOrNull(@NotNull final String expression,
@NotNull final PlaceholderContext context) {
return Eco.get().evaluate(expression, context); return Eco.get().evaluate(expression, context);
} }

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.util;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* Utilities / API methods for patterns.
*/
public final class PatternUtils {
/**
* Cache of compiled literal patterns.
*/
private static final Cache<String, Pattern> LITERAL_PATTERN_CACHE = Caffeine.newBuilder()
.expireAfterAccess(1, TimeUnit.MINUTES)
.build();
/**
* Compile a literal pattern.
*
* @param pattern The pattern.
* @return The compiled pattern.
*/
@NotNull
public static Pattern compileLiteral(@NotNull final String pattern) {
return LITERAL_PATTERN_CACHE.get(pattern, (it) -> Pattern.compile(it, Pattern.LITERAL));
}
private PatternUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -8,7 +8,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -200,6 +203,26 @@ public final class StringUtils {
return translated; return translated;
} }
/**
* Format a list of strings.
* <p>
* Coverts color codes and placeholders.
*
* @param list The messages to format.
* @param context The context.
* @return The message, format.
*/
@NotNull
public static List<String> formatList(@NotNull final List<String> list,
@NotNull PlaceholderContext context) {
List<String> translated = new ArrayList<>();
for (String string : list) {
translated.add(format(string, context));
}
return translated;
}
/** /**
* Format a string. * Format a string.
* <p> * <p>
@@ -242,7 +265,7 @@ public final class StringUtils {
@NotNull @NotNull
public static String format(@NotNull final String message, public static String format(@NotNull final String message,
@NotNull final FormatOption option) { @NotNull final FormatOption option) {
return format(message, null, option); return format(message, (Player) null, option);
} }
/** /**
@@ -287,7 +310,7 @@ public final class StringUtils {
@NotNull @NotNull
public static Component formatToComponent(@NotNull final String message, public static Component formatToComponent(@NotNull final String message,
@NotNull final FormatOption option) { @NotNull final FormatOption option) {
return formatToComponent(message, null, option); return formatToComponent(message, (Player) null, option);
} }
/** /**
@@ -321,10 +344,49 @@ public final class StringUtils {
public static String format(@NotNull final String message, public static String format(@NotNull final String message,
@Nullable final Player player, @Nullable final Player player,
@NotNull final FormatOption option) { @NotNull final FormatOption option) {
String processedMessage = message;
if (option == FormatOption.WITH_PLACEHOLDERS) { if (option == FormatOption.WITH_PLACEHOLDERS) {
processedMessage = PlaceholderManager.translatePlaceholders(processedMessage, player); return format(
message,
new PlaceholderContext(player)
);
} }
return STRING_FORMAT_CACHE.get(message);
}
/**
* Format a string to a component.
* <p>
* Converts color codes and placeholders if specified.
*
* @param message The message to translate.
* @param context The placeholder context.
* @return The message, formatted, as a component.
* @see StringUtils#format(String, Player)
*/
@NotNull
public static Component formatToComponent(@NotNull final String message,
@NotNull final PlaceholderContext context) {
return toComponent(format(message, context));
}
/**
* Format a string.
* <p>
* Coverts color codes and placeholders if specified.
*
* @param message The message to format.
* @param context The context to translate placeholders with respect to.
* @return The message, formatted.
*/
@NotNull
public static String format(@NotNull final String message,
@NotNull final PlaceholderContext context) {
String processedMessage = message;
processedMessage = PlaceholderManager.translatePlaceholders(
processedMessage,
context
);
return STRING_FORMAT_CACHE.get(processedMessage); return STRING_FORMAT_CACHE.get(processedMessage);
} }
@@ -674,6 +736,177 @@ public final class StringUtils {
return builder.toString(); return builder.toString();
} }
/**
* Fast implementation of {@link String#replace(CharSequence, CharSequence)}
*
* @param input The input string.
* @param target The target string.
* @param replacement The replacement string.
* @return The replaced string.
*/
@NotNull
public static String replaceQuickly(@NotNull final String input,
@NotNull final String target,
@NotNull final String replacement) {
int targetLength = target.length();
// Count the number of original occurrences
int count = 0;
for (
int index = input.indexOf(target);
index != -1;
index = input.indexOf(target, index + targetLength)
) {
count++;
}
if (count == 0) {
return input;
}
int replacementLength = replacement.length();
int inputLength = input.length();
// Pre-calculate the final size of the StringBuilder
int newSize = inputLength + (replacementLength - targetLength) * count;
StringBuilder result = new StringBuilder(newSize);
int start = 0;
for (
int index = input.indexOf(target);
index != -1;
index = input.indexOf(target, start)
) {
result.append(input, start, index);
result.append(replacement);
start = index + targetLength;
}
result.append(input, start, inputLength);
return result.toString();
}
/**
* Line wrap a list of strings while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final List<String> input,
final int lineLength) {
return lineWrap(input, lineLength, true);
}
/**
* Line wrap a list of strings while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @param preserveMargin If the string has a margin, add it to the next line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final List<String> input,
final int lineLength,
final boolean preserveMargin) {
return input.stream()
.flatMap(line -> lineWrap(line, lineLength, preserveMargin).stream())
.toList();
}
/**
* Line wrap a string while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final String input,
final int lineLength) {
return lineWrap(input, lineLength, true);
}
/**
* Line wrap a string while preserving formatting.
*
* @param input The input string.
* @param lineLength The length of each line.
* @param preserveMargin If the string has a margin, add it to the start of each line.
* @return The wrapped string.
*/
@NotNull
public static List<String> lineWrap(@NotNull final String input,
final int lineLength,
final boolean preserveMargin) {
int margin = preserveMargin ? getMargin(input) : 0;
TextComponent space = Component.text(" ");
Component asComponent = toComponent(input);
// The component contains the text as its children, so the child components
// are accessed like this:
List<TextComponent> children = new ArrayList<>();
if (asComponent instanceof TextComponent) {
children.add((TextComponent) asComponent);
}
for (Component child : asComponent.children()) {
children.add((TextComponent) child);
}
// Start by splitting the component into individual characters.
List<TextComponent> letters = new ArrayList<>();
for (TextComponent child : children) {
for (char c : child.content().toCharArray()) {
letters.add(Component.text(c).mergeStyle(child));
}
}
List<Component> lines = new ArrayList<>();
List<TextComponent> currentLine = new ArrayList<>();
boolean isFirstLine = true;
for (TextComponent letter : letters) {
if (currentLine.size() > lineLength && letter.content().isBlank()) {
lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine));
currentLine.clear();
isFirstLine = false;
} else {
// Add margin if starting a new line.
if (currentLine.isEmpty() && !isFirstLine) {
if (preserveMargin) {
for (int i = 0; i < margin; i++) {
currentLine.add(space);
}
}
}
currentLine.add(letter);
}
}
// Push last line.
lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine));
// Convert back to legacy strings.
return lines.stream().map(StringUtils::toLegacy)
.collect(Collectors.toList());
}
/**
* Get a string's margin.
*
* @param input The input string.
* @return The margin.
*/
public static int getMargin(@NotNull final String input) {
return input.indexOf(input.trim());
}
/** /**
* Options for formatting. * Options for formatting.
*/ */

View File

@@ -38,6 +38,15 @@ class DSLConfig internal constructor(type: ConfigType) : GenericConfig(type) {
fun config(type: ConfigType = ConfigType.YAML, builder: DSLConfig.() -> Unit): Config = fun config(type: ConfigType = ConfigType.YAML, builder: DSLConfig.() -> Unit): Config =
DSLConfig(type).apply(builder) DSLConfig(type).apply(builder)
/**
* Helper function to create JSON configs with a kotlin DSL.
*
* @param builder The builder.
* @return The config.
*/
fun json(builder: DSLConfig.() -> Unit): Config =
config(ConfigType.JSON, builder)
/** @see Configs.empty */ /** @see Configs.empty */
fun emptyConfig() = Configs.empty() fun emptyConfig() = Configs.empty()

View File

@@ -8,7 +8,7 @@ package com.willfp.eco.core.map
* @see ListMap * @see ListMap
*/ */
@Suppress("RedundantOverride") @Suppress("RedundantOverride")
class MutableListMap<K : Any, V : Any> : ListMap<K, V>() { class MutableListMap<K : Any, V> : ListMap<K, V>() {
/** /**
* Override with enforced MutableList type. * Override with enforced MutableList type.
*/ */
@@ -18,7 +18,7 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
/** /**
* Override with enforced MutableList type. * Override with enforced MutableList type.
*/ */
override fun getOrDefault(key: K, defaultValue: MutableList<V>?): MutableList<V> { override fun getOrDefault(key: K, defaultValue: MutableList<V>): MutableList<V> {
return super.getOrDefault(key, defaultValue) return super.getOrDefault(key, defaultValue)
} }
} }
@@ -29,6 +29,12 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
fun <K : Any, V : Any> defaultMap(defaultValue: V) = fun <K : Any, V : Any> defaultMap(defaultValue: V) =
DefaultMap<K, V>(defaultValue) DefaultMap<K, V>(defaultValue)
/**
* @see DefaultMap
*/
fun <K : Any, V : Any> defaultMap(defaultValue: () -> V) =
DefaultMap<K, V>(defaultValue())
/** /**
* @see ListMap * @see ListMap
*/ */
@@ -38,11 +44,13 @@ fun <K : Any, V : Any> listMap() =
/** /**
* @see DefaultMap.createNestedMap * @see DefaultMap.createNestedMap
*/ */
fun <K : Any, K1 : Any, V : Any> nestedMap() = fun <K : Any, K1 : Any, V> nestedMap() =
DefaultMap.createNestedMap<K, K1, V>() DefaultMap.createNestedMap<K, K1, V>()
/** /**
* @see DefaultMap.createNestedListMap * @see DefaultMap.createNestedListMap
*/ */
fun <K : Any, K1 : Any, V : Any> nestedListMap() = fun <K : Any, K1 : Any, V> nestedListMap() =
DefaultMap<K, MutableListMap<K1, V>>(MutableListMap()) DefaultMap<K, MutableListMap<K1, V>>() {
MutableListMap()
}

View File

@@ -0,0 +1,14 @@
@file:JvmName("PlaceholderExtensions")
package com.willfp.eco.core.placeholder
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.context.PlaceholderContext
/** @see PlaceholderManager.translatePlaceholders */
fun String.translatePlaceholders(context: PlaceholderContext) =
PlaceholderManager.translatePlaceholders(this, context)
/** @see PlaceholderManager.findPlaceholdersIn */
fun String.findPlaceholders(): List<String> =
PlaceholderManager.findPlaceholdersIn(this)

View File

@@ -0,0 +1,25 @@
@file:JvmName("PlaceholderContextExtensions")
package com.willfp.eco.core.placeholder.context
import com.willfp.eco.core.placeholder.AdditionalPlayer
import com.willfp.eco.core.placeholder.PlaceholderInjectable
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
/** @see PlaceholderContext */
@JvmOverloads
fun placeholderContext(
player: Player? = null,
item: ItemStack? = null,
injectable: PlaceholderInjectable? = null,
additionalPlayers: Collection<AdditionalPlayer> = emptyList()
): PlaceholderContext = PlaceholderContext(player, item, injectable, additionalPlayers)
/** @see PlaceholderContext */
fun PlaceholderContext.copy(
player: Player? = this.player,
item: ItemStack? = this.itemStack,
injectable: PlaceholderInjectable? = this.injectableContext,
additionalPlayers: Collection<AdditionalPlayer> = this.additionalPlayers
): PlaceholderContext = PlaceholderContext(player, item, injectable, additionalPlayers)

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.core.registry
/**
* A registrable that has a string ID, for use with Kotlin.
*/
interface KRegistrable : Registrable {
/**
* The ID of the registrable.
*/
val id: String
override fun getID() = id
}

View File

@@ -15,5 +15,5 @@ fun <T> create2DList(rows: Int, columns: Int): MutableList<MutableList<T>> =
ListUtils.create2DList(rows, columns) ListUtils.create2DList(rows, columns)
/** @see ListUtils.toSingletonList */ /** @see ListUtils.toSingletonList */
fun <T> T.toSingletonList(): List<T> = fun <T> T?.toSingletonList(): List<T> =
ListUtils.toSingletonList(this) ListUtils.toSingletonList(this)

View File

@@ -2,6 +2,36 @@
package com.willfp.eco.util package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
/** @see NumberUtils.toNumeral */ /** @see NumberUtils.toNumeral */
fun Number.toNumeral(): String = fun Number.toNumeral(): String =
NumberUtils.toNumeral(this.toInt()) NumberUtils.toNumeral(this.toInt())
/** @see NumberUtils.fromNumeral */
fun String.parseNumeral(): Int =
NumberUtils.fromNumeral(this)
/** @see NumberUtils.randInt */
fun randInt(min: Int, max: Int) =
NumberUtils.randInt(min, max)
/** @see NumberUtils.randFloat */
fun randDouble(min: Double, max: Double) =
NumberUtils.randFloat(min, max)
/** @see NumberUtils.randFloat */
fun randFloat(min: Float, max: Float) =
NumberUtils.randFloat(min.toDouble(), max.toDouble()).toFloat()
/** @see NumberUtils.evaluateExpression */
fun evaluateExpression(expression: String) =
NumberUtils.evaluateExpression(expression)
/** @see NumberUtils.evaluateExpression */
fun evaluateExpression(expression: String, context: PlaceholderContext) =
NumberUtils.evaluateExpression(expression, context)
/** @see NumberUtils.evaluateExpressionOrNull */
fun evaluateExpressionOrNull(expression: String, context: PlaceholderContext) =
NumberUtils.evaluateExpressionOrNull(expression, context)

View File

@@ -2,6 +2,7 @@
package com.willfp.eco.util package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -31,6 +32,14 @@ fun String.formatEco(
if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS
) )
/** @see StringUtils.format */
fun String.formatEco(
context: PlaceholderContext
) = StringUtils.format(
this,
context
)
/** @see StringUtils.formatList */ /** @see StringUtils.formatList */
fun List<String>.formatEco( fun List<String>.formatEco(
player: Player? = null, player: Player? = null,
@@ -41,6 +50,14 @@ fun List<String>.formatEco(
if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS
) )
/** @see StringUtils.formatList */
fun List<String>.formatEco(
context: PlaceholderContext
): List<String> = StringUtils.formatList(
this,
context
)
/** @see StringUtils.splitAround */ /** @see StringUtils.splitAround */
fun String.splitAround(separator: String): Array<String> = fun String.splitAround(separator: String): Array<String> =
StringUtils.splitAround(this, separator) StringUtils.splitAround(this, separator)
@@ -48,3 +65,19 @@ fun String.splitAround(separator: String): Array<String> =
/** @see StringUtils.toNiceString */ /** @see StringUtils.toNiceString */
fun Any?.toNiceString(): String = fun Any?.toNiceString(): String =
StringUtils.toNiceString(this) StringUtils.toNiceString(this)
/** @see StringUtils.replaceQuickly */
fun String.replaceQuickly(target: String, replacement: String): String =
StringUtils.replaceQuickly(this, target, replacement)
/** @see StringUtils.lineWrap */
fun String.lineWrap(width: Int, preserveMargin: Boolean = true): List<String> =
StringUtils.lineWrap(this, width, preserveMargin)
/** @see StringUtils.lineWrap */
fun List<String>.lineWrap(width: Int, preserveMargin: Boolean = true): List<String> =
StringUtils.lineWrap(this, width, preserveMargin)
/** @see StringUtils.getMargin */
val String.margin: Int
get() = StringUtils.getMargin(this)

View File

@@ -8,10 +8,12 @@ import org.bukkit.command.TabCompleter
class DelegatedBukkitCommand( class DelegatedBukkitCommand(
private val delegate: EcoPluginCommand private val delegate: EcoPluginCommand
) : Command(delegate.name), TabCompleter, PluginIdentifiableCommand { ) : Command(
private var _aliases: List<String>? = null delegate.name,
private var _description: String? = null delegate.description ?: "",
"/${delegate.name}",
delegate.aliases
), TabCompleter, PluginIdentifiableCommand {
override fun execute(sender: CommandSender, label: String, args: Array<out String>?): Boolean { override fun execute(sender: CommandSender, label: String, args: Array<out String>?): Boolean {
return delegate.onCommand(sender, this, label, args) return delegate.onCommand(sender, this, label, args)
} }
@@ -36,16 +38,4 @@ class DelegatedBukkitCommand(
override fun getPlugin() = delegate.plugin override fun getPlugin() = delegate.plugin
override fun getPermission() = delegate.permission override fun getPermission() = delegate.permission
override fun getDescription() = _description ?: delegate.description ?: ""
override fun getAliases(): List<String> = _aliases ?: delegate.aliases
override fun setDescription(description: String): Command {
this._description = description
return this
}
override fun setAliases(aliases: List<String>): Command {
this._aliases = aliases
return this
}
} }

View File

@@ -7,7 +7,7 @@ import com.willfp.eco.core.command.PluginCommandBase
import org.bukkit.Bukkit import org.bukkit.Bukkit
class EcoPluginCommand( class EcoPluginCommand(
parentDelegate: CommandBase, private val parentDelegate: PluginCommandBase,
plugin: EcoPlugin, plugin: EcoPlugin,
name: String, name: String,
permission: String, permission: String,
@@ -39,6 +39,9 @@ class EcoPluginCommand(
Eco.get().syncCommands() Eco.get().syncCommands()
} }
override fun getAliases(): List<String> = parentDelegate.aliases
override fun getDescription(): String? = parentDelegate.description
} }
class EcoSubcommand( class EcoSubcommand(

View File

@@ -30,6 +30,8 @@ internal fun Any?.constrainConfigTypes(type: ConfigType): Any? = when (this) {
this.toMutableList() this.toMutableList()
} }
} }
is Float -> this.toDouble() // Should prevent !!float from being written
else -> this else -> this
} }

View File

@@ -3,7 +3,8 @@ package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.placeholder.InjectablePlaceholder import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.StaticPlaceholder import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.internal.fast.listView
import com.willfp.eco.util.StringUtils import com.willfp.eco.util.StringUtils
import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.configuration.file.YamlConfiguration
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@@ -15,7 +16,7 @@ open class EcoConfig(
private val values = ConcurrentHashMap<String, Any?>() private val values = ConcurrentHashMap<String, Any?>()
@Transient @Transient
var injections = mutableListOf<InjectablePlaceholder>() var injections = ConcurrentHashMap<String, InjectablePlaceholder>()
fun init(values: Map<String, Any?>) { fun init(values: Map<String, Any?>) {
this.values.clear() this.values.clear()
@@ -104,12 +105,12 @@ open class EcoConfig(
} }
override fun getSubsectionOrNull(path: String): Config? { override fun getSubsectionOrNull(path: String): Config? {
return (get(path) as? Config)?.apply { this.addInjectablePlaceholder(injections) } return (get(path) as? Config)?.apply { this.addInjectablePlaceholder(injections.values) }
} }
override fun getSubsectionsOrNull(path: String): List<Config>? { override fun getSubsectionsOrNull(path: String): List<Config>? {
return getList<Config>(path) return getList<Config>(path)
?.map { it.apply { this.addInjectablePlaceholder(injections) } } ?.map { it.apply { this.addInjectablePlaceholder(injections.values) } }
?.toList() ?.toList()
} }
@@ -141,9 +142,7 @@ open class EcoConfig(
var string = get(path)?.toString() ?: return null var string = get(path)?.toString() ?: return null
if (format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) { if (format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) {
for (injection in placeholderInjections) { for (injection in placeholderInjections) {
if (injection is StaticPlaceholder) { string = injection.tryTranslateQuickly(string, PlaceholderContext.EMPTY)
string = string.replace("%${injection.identifier}%", injection.value)
}
} }
} }
return if (format) StringUtils.format(string, option) else string return if (format) StringUtils.format(string, option) else string
@@ -161,9 +160,7 @@ open class EcoConfig(
strings.replaceAll { strings.replaceAll {
var string = it var string = it
for (injection in placeholderInjections) { for (injection in placeholderInjections) {
if (injection is StaticPlaceholder) { string = injection.tryTranslateQuickly(string, PlaceholderContext.EMPTY)
string = string.replace("%${injection.identifier}%", injection.value)
}
} }
string string
} }
@@ -180,12 +177,13 @@ open class EcoConfig(
} }
override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) { override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) {
injections.removeIf { placeholders.any { placeholder -> it.identifier == placeholder.identifier } } for (placeholder in placeholders) {
injections.addAll(placeholders) injections[placeholder.pattern.pattern()] = placeholder
}
} }
override fun getPlaceholderInjections(): List<InjectablePlaceholder> { override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
return injections.toList() return injections.values.listView() // Faster than .toList()
} }
override fun clearInjectedPlaceholders() { override fun clearInjectedPlaceholders() {
@@ -208,14 +206,6 @@ open class EcoConfig(
return bukkit return bukkit
} }
override fun clone(): Config {
return EcoConfigSection(type, this.values.toMutableMap(), injections)
}
override fun toString(): String {
return this.toPlaintext()
}
private inline fun <reified T> getList(path: String): List<T>? { private inline fun <reified T> getList(path: String): List<T>? {
val asIterable = get(path) as? Iterable<*> ?: return null val asIterable = get(path) as? Iterable<*> ?: return null
val asList = asIterable.toList() val asList = asIterable.toList()
@@ -226,4 +216,44 @@ open class EcoConfig(
return asList as List<T> return asList as List<T>
} }
override fun clone(): Config {
return EcoConfigSection(type, this.values.toMutableMap(), injections)
}
override fun toString(): String {
return this.toPlaintext()
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other !is EcoConfig) {
return false
}
// Hey! Don't care. This works.
return this.hashCode() == other.hashCode()
}
override fun hashCode(): Int {
/*
The keys are completely redundant, as they are only used to prevent
duplicate keys in the map. Therefore, we can ignore them and just
hash the actual placeholder values.
*/
var injectionHash = 0
injections.forEachValue(5) {
injectionHash = injectionHash xor (it.hashCode() shl 5)
}
// hashCode() has to compute extremely quickly, so we're using bitwise, because why not?
// Fucking filthy to use identityHashCode here, but it should be extremely fast
val identityHash = System.identityHashCode(this)
return (identityHash shl 5) - (identityHash xor configType.hashCode()) + injectionHash
}
} }

View File

@@ -2,14 +2,15 @@ package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.placeholder.InjectablePlaceholder import com.willfp.eco.core.placeholder.InjectablePlaceholder
import java.util.concurrent.ConcurrentHashMap
class EcoConfigSection( class EcoConfigSection(
type: ConfigType, type: ConfigType,
values: Map<String, Any?> = emptyMap(), values: Map<String, Any?> = emptyMap(),
injections: Collection<InjectablePlaceholder> = emptyList() injections: Map<String, InjectablePlaceholder> = emptyMap()
) : EcoConfig(type) { ) : EcoConfig(type) {
init { init {
this.init(values) this.init(values)
this.injections = injections.toMutableList() this.injections = ConcurrentHashMap(injections)
} }
} }

View File

@@ -1,12 +1,12 @@
package com.willfp.eco.internal.config package com.willfp.eco.internal.config
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.nodes.Node import org.yaml.snakeyaml.nodes.Node
import org.yaml.snakeyaml.representer.Represent import org.yaml.snakeyaml.representer.Represent
import org.yaml.snakeyaml.representer.Representer import org.yaml.snakeyaml.representer.Representer
@Suppress("DEPRECATION") class EcoRepresenter : Representer(DumperOptions()) {
class EcoRepresenter : Representer() {
init { init {
multiRepresenters[Config::class.java] = RepresentConfig(multiRepresenters[Map::class.java]!!) multiRepresenters[Config::class.java] = RepresentConfig(multiRepresenters[Map::class.java]!!)
} }

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.internal.data
import com.willfp.eco.core.data.ExternalDataStoreObjectAdapter
import com.willfp.eco.core.version.Version
object VersionToStringAdapter: ExternalDataStoreObjectAdapter<Version, String>(
Version::class.java,
String::class.java
) {
override fun toAccessedObject(obj: String): Version = Version(obj)
override fun toStoredObject(obj: Version): String = obj.toString()
}
class MavenVersionToStringAdapter(
className: String
): ExternalDataStoreObjectAdapter<Any, String>(
Class.forName(className),
String::class.java
) {
private val constructor = Class.forName(className)
.getConstructor(String::class.java)
override fun toAccessedObject(obj: String): Any = constructor.newInstance(obj)
override fun toStoredObject(obj: Any): String = obj.toString()
}

View File

@@ -20,7 +20,15 @@ private val listeners = mutableMapOf<PacketPriority, MutableList<RegisteredPacke
fun PacketEvent.handleSend() { fun PacketEvent.handleSend() {
for (priority in PacketPriority.values()) { for (priority in PacketPriority.values()) {
for (listener in listeners[priority] ?: continue) { for (listener in listeners[priority] ?: continue) {
listener.listener.onSend(this) try {
listener.listener.onSend(this)
} catch (e: Exception) {
listener.plugin.logger.warning(
"Exception in packet listener ${listener.listener.javaClass.name}" +
" for packet ${packet.handle.javaClass.name}!"
)
e.printStackTrace()
}
} }
} }
} }
@@ -28,7 +36,15 @@ fun PacketEvent.handleSend() {
fun PacketEvent.handleReceive() { fun PacketEvent.handleReceive() {
for (priority in PacketPriority.values()) { for (priority in PacketPriority.values()) {
for (listener in listeners[priority] ?: continue) { for (listener in listeners[priority] ?: continue) {
listener.listener.onReceive(this) try {
listener.listener.onReceive(this)
} catch (e: Exception) {
listener.plugin.logger.warning(
"Exception in packet listener ${listener.listener.javaClass.name}" +
" for packet ${packet.handle.javaClass.name}!"
)
e.printStackTrace()
}
} }
} }
} }

View File

@@ -5,9 +5,11 @@ import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.toConfig import com.willfp.eco.core.config.toConfig
import com.willfp.eco.core.extensions.Extension import com.willfp.eco.core.extensions.Extension
import com.willfp.eco.core.extensions.ExtensionLoadException
import com.willfp.eco.core.extensions.ExtensionLoader import com.willfp.eco.core.extensions.ExtensionLoader
import com.willfp.eco.core.extensions.ExtensionMetadata import com.willfp.eco.core.extensions.ExtensionMetadata
import com.willfp.eco.core.extensions.MalformedExtensionException import com.willfp.eco.core.extensions.MalformedExtensionException
import com.willfp.eco.core.version.Version
import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.configuration.file.YamlConfiguration
import java.io.File import java.io.File
import java.io.InputStreamReader import java.io.InputStreamReader
@@ -32,7 +34,7 @@ class EcoExtensionLoader(
} }
runCatching { loadExtension(extensionJar) }.onFailure { runCatching { loadExtension(extensionJar) }.onFailure {
this.plugin.logger.warning(extensionJar.name + " caused an error!") this.plugin.logger.warning(extensionJar.name + " caused an error: ${it.message ?: "Unknown error"}")
if (Eco.get().ecoPlugin.configYml.getBool("log-full-extension-errors")) { if (Eco.get().ecoPlugin.configYml.getBool("log-full-extension-errors")) {
it.printStackTrace() it.printStackTrace()
} }
@@ -54,7 +56,12 @@ class EcoExtensionLoader(
var name = extensionYml.getStringOrNull("name") var name = extensionYml.getStringOrNull("name")
var version = extensionYml.getStringOrNull("version") var version = extensionYml.getStringOrNull("version")
var author = extensionYml.getStringOrNull("author") var author = extensionYml.getStringOrNull("author")
val pluginVersion = Version(extensionYml.getStringOrNull("plugin-version") ?: "0.0.0")
val pluginName = extensionYml.getStringOrNull("plugin")
if (pluginName != null && !pluginName.equals(this.plugin.description.name, ignoreCase = true)) {
throw ExtensionLoadException("${extensionJar.name} is only compatible with $pluginName!")
}
if (mainClass == null) { if (mainClass == null) {
throw MalformedExtensionException("Invalid extension.yml found in " + extensionJar.name) throw MalformedExtensionException("Invalid extension.yml found in " + extensionJar.name)
@@ -75,7 +82,11 @@ class EcoExtensionLoader(
author = "Unnamed Author" author = "Unnamed Author"
} }
val metadata = ExtensionMetadata(version, name, author) if (Version(this.plugin.description.version) < pluginVersion) {
throw ExtensionLoadException("Plugin version is too low for ${extensionJar.name}!")
}
val metadata = ExtensionMetadata(version, name, author, extensionJar, pluginVersion)
val cls: Class<*> = classLoader.loadClass(mainClass) val cls: Class<*> = classLoader.loadClass(mainClass)
val extension: Extension = cls.getConstructor(EcoPlugin::class.java).newInstance(this.plugin) as Extension val extension: Extension = cls.getConstructor(EcoPlugin::class.java).newInstance(this.plugin) as Extension
@@ -85,7 +96,7 @@ class EcoExtensionLoader(
extensions[extension] = classLoader extensions[extension] = classLoader
} }
override fun getLoadedExtensions(): MutableSet<Extension> { override fun getLoadedExtensions(): Set<Extension> {
return ImmutableSet.copyOf(extensions.keys) return ImmutableSet.copyOf(extensions.keys)
} }
@@ -97,4 +108,4 @@ class EcoExtensionLoader(
extensions.clear() extensions.clear()
} }
} }

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.fast
class ListViewOfCollection<T>(private val collection: Collection<T>) : List<T> {
/*
The backing list is lazy-loaded because it provides a performance hit
to copy the contents of the contents of the collection into a list.
Since the only required operator for most use-cases is .iterator(),
we can just use the collection's iterator.
*/
private val backingList: List<T> by lazy { collection.toList() }
override val size: Int
get() = collection.size
override fun containsAll(elements: Collection<T>) =
collection.containsAll(elements)
override fun get(index: Int): T =
backingList[index]
override fun indexOf(element: T): Int = backingList.indexOf(element)
override fun contains(element: T): Boolean = collection.contains(element)
override fun isEmpty() =
collection.isEmpty()
override fun iterator() =
collection.iterator()
override fun listIterator() =
backingList.listIterator()
override fun listIterator(index: Int) =
backingList.listIterator(index)
override fun subList(fromIndex: Int, toIndex: Int) =
backingList.subList(fromIndex, toIndex)
override fun lastIndexOf(element: T) =
backingList.lastIndexOf(element)
}
inline fun <reified T> Collection<T>.listView(): List<T> {
return ListViewOfCollection(this)
}

View File

@@ -34,4 +34,6 @@ open class EcoSlot(
} }
override fun getActionableSlot(player: Player, menu: Menu): EcoSlot = this override fun getActionableSlot(player: Player, menu: Menu): EcoSlot = this
override fun shouldRenderOnClick() = handlers.values.any { it.isNotEmpty() }
} }

View File

@@ -6,6 +6,7 @@ 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.SlotProvider import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.gui.slot.functional.SlotUpdater import com.willfp.eco.core.gui.slot.functional.SlotUpdater
import com.willfp.eco.core.map.listMap
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.ClickType
import java.util.function.Predicate import java.util.function.Predicate
@@ -15,14 +16,14 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
private var captiveFromEmpty = false private var captiveFromEmpty = false
private var updater: SlotUpdater = SlotUpdater { player, menu, _ -> provider.provide(player, menu) } private var updater: SlotUpdater = SlotUpdater { player, menu, _ -> provider.provide(player, menu) }
private val handlers = mutableMapOf<ClickType, MutableList<SlotHandler>>() private val handlers = listMap<ClickType, SlotHandler>()
private var captiveFilter = private var captiveFilter =
CaptiveFilter { _, _, _ -> true } CaptiveFilter { _, _, _ -> true }
private var notCaptiveFor: (Player) -> Boolean = { _ -> false} private var notCaptiveFor: (Player) -> Boolean = { _ -> false}
override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder { override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder {
handlers.computeIfAbsent(type) { mutableListOf() } += action handlers[type] += action
return this return this
} }

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.internal.integrations
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.context.placeholderContext
import me.clip.placeholderapi.expansion.PlaceholderExpansion import me.clip.placeholderapi.expansion.PlaceholderExpansion
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -33,7 +34,19 @@ class PAPIExpansion(private val plugin: EcoPlugin) : PlaceholderExpansion() {
override fun onPlaceholderRequest( override fun onPlaceholderRequest(
player: Player?, player: Player?,
identifier: String identifier: String
): String { ): String? {
return PlaceholderManager.getResult(player, identifier, plugin) return PlaceholderManager.getResult(
plugin,
identifier,
placeholderContext(
player = player,
item = player?.inventory?.itemInMainHand
)
)
}
override fun getPlaceholders(): List<String> {
return PlaceholderManager.getRegisteredPlaceholders(plugin)
.map { "%${this.plugin.name.lowercase()}_${it.pattern.pattern()}%" }
} }
} }

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.internal.items
import com.willfp.eco.core.items.args.LookupArgParser
import org.bukkit.Bukkit
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.meta.SkullMeta
import java.util.function.Predicate
object ArgParserHead : LookupArgParser {
override fun parseArguments(args: Array<out String>, meta: ItemMeta): Predicate<ItemStack>? {
if (meta !is SkullMeta) {
return null
}
var playerName: String? = null
for (arg in args) {
val argSplit = arg.split(":")
if (!argSplit[0].equals("head", ignoreCase = true)) {
continue
}
if (argSplit.size < 2) {
continue
}
playerName = argSplit[1]
}
playerName ?: return null
@Suppress("DEPRECATION")
val player = Bukkit.getOfflinePlayer(playerName)
meta.owningPlayer = player
return Predicate {
val testMeta = it.itemMeta as? SkullMeta ?: return@Predicate false
testMeta.owningPlayer?.uniqueId == player.uniqueId
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (meta !is SkullMeta) {
return null
}
if (meta.owningPlayer == null) {
return null
}
return "head:${meta.owningPlayer?.name}"
}
}

View File

@@ -0,0 +1,10 @@
package com.willfp.eco.internal.logging
import java.util.logging.LogRecord
import java.util.logging.Logger
object NOOPLogger : Logger("eco_noop", null as String?) {
override fun log(record: LogRecord?) {
return
}
}

View File

@@ -0,0 +1,65 @@
package com.willfp.eco.internal.placeholder
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.templates.SimpleInjectablePlaceholder
import java.util.regex.Pattern
/*
A set of global placeholders that are always available.
*/
private val globalPlaceholders = setOf<Placeholder>(
object : SimpleInjectablePlaceholder("player") {
override fun getValue(args: String, context: PlaceholderContext): String? {
return context.player?.name
}
},
)
class PlaceholderLookup(
val args: String,
val plugin: EcoPlugin?,
private val injections: Collection<InjectablePlaceholder>?
) {
fun findMatchingPlaceholder(): Placeholder? {
if (plugin != null) {
val pluginPlaceholders = PlaceholderManager.getRegisteredPlaceholders(plugin)
for (placeholder in pluginPlaceholders) {
if (placeholder.matches(this)) {
return placeholder
}
}
}
if (injections != null) {
for (placeholder in injections) {
if (placeholder.matches(this)) {
return placeholder
}
}
}
for (placeholder in globalPlaceholders) {
if (placeholder.matches(this)) {
return placeholder
}
}
return null
}
private fun Placeholder.matches(lookup: PlaceholderLookup): Boolean {
val pattern = this.pattern
val patternString = pattern.pattern()
val patternFlags = pattern.flags()
val isLiteral = Pattern.LITERAL and patternFlags != 0
return if (isLiteral) lookup.args == patternString else pattern.matcher(lookup.args).matches()
}
}

View File

@@ -0,0 +1,207 @@
package com.willfp.eco.internal.placeholder
import com.github.benmanes.caffeine.cache.Caffeine
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.util.StringUtils
import java.util.concurrent.TimeUnit
/*
A lot of methods here are centered around minimising calls to getPlaceholderInjections,
which tends to be slow for things like configs. This was optimised with ListViewOfCollection,
but it's still best to minimise the memory overhead.
*/
class PlaceholderParser {
private val placeholderRegex = Regex("%([^% ]+)%")
private val placeholderLookupCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build<PlaceholderLookup, Placeholder?>()
fun translatePlacholders(text: String, context: PlaceholderContext): String {
return translatePlacholders(text, context, context.injectableContext.placeholderInjections)
}
private fun translatePlacholders(
text: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>,
translateEcoPlaceholders: Boolean = true
): String {
/*
Why am I doing injections at the start, and again at the end?
Additional players let you use something like victim as a player to parse in relation to,
for example doing %victim_player_health%, which would parse the health of the victim.
However, something like libreforge will also inject %victim_max_health%, which is unrelated
to additional players, and instead holds a constant value. So, eco saw this, smartly thought
"ah, it's an additional player, let's parse it", and then tried to parse %max_health% with
relation to the victim, which resolved to zero. So, we have to parse statics and player statics
that might include a prefix first, then additional players, then player statics with the support
of additional players.
This was a massive headache and took so many reports before I clocked what was going on.
Oh well, at least it's fixed now.
*/
// Apply injections first
var processed = injections.fold(text) { acc, injection ->
injection.tryTranslateQuickly(acc, context)
}
// Prevent running 2 scans if there are no additional players.
if (context.additionalPlayers.isNotEmpty()) {
val found = PlaceholderManager.findPlaceholdersIn(text)
for (additionalPlayer in context.additionalPlayers) {
val prefix = "%${additionalPlayer.identifier}_"
processed = found.fold(processed) { acc, placeholder ->
if (placeholder.startsWith(prefix)) {
val newPlaceholder = "%${StringUtils.removePrefix(prefix, placeholder)}"
val translation = translatePlacholders(
newPlaceholder,
context.copyWithPlayer(additionalPlayer.player),
injections
)
acc.replace(placeholder, translation)
} else {
acc
}
}
}
}
// Translate eco placeholders
if (translateEcoPlaceholders) {
processed = translateEcoPlaceholdersIn(processed, context, injections)
}
// Apply registered integrations
processed = PlaceholderManager.getRegisteredIntegrations().fold(processed) { acc, integration ->
integration.translate(acc, context.player)
}
// Apply injections again
return injections.fold(processed) { acc, injection ->
injection.tryTranslateQuickly(acc, context)
}
}
fun getPlaceholderResult(
plugin: EcoPlugin?,
args: String,
context: PlaceholderContext
): String? {
// Only scan for injections if plugin is null.
val injections = if (plugin == null) context.injectableContext.placeholderInjections else null
return doGetResult(plugin, args, injections, context)
}
// Injections are sent separately here to prevent multiple calls to getPlaceholderInjections
private fun doGetResult(
plugin: EcoPlugin?,
args: String?,
injections: Collection<InjectablePlaceholder>?,
context: PlaceholderContext
): String? {
if (args == null) {
return null
}
val lookup = PlaceholderLookup(args, plugin, injections)
val placeholder = placeholderLookupCache.get(lookup) {
it.findMatchingPlaceholder()
}
return placeholder?.getValue(args, context)
}
private fun translateEcoPlaceholdersIn(
text: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>
): String {
val output = StringBuilder()
var lastAppendPosition = 0
for (matchResult in placeholderRegex.findAll(text)) {
val placeholder = matchResult.groups[1]?.value ?: ""
val injectableResult = doGetResult(null, placeholder, injections, context)
val parts = placeholder.split("_", limit = 2)
var result: String? = null
if (injectableResult != null) {
result = injectableResult
} else if (parts.size == 2) {
val plugin = EcoPlugin.getPlugin(parts[0])
if (plugin != null) {
result = doGetResult(plugin, parts[1], null, context)
}
}
output.append(text.substring(lastAppendPosition, matchResult.range.first))
output.append(result ?: matchResult.value)
lastAppendPosition = matchResult.range.last + 1
}
output.append(text.substring(lastAppendPosition))
return output.toString()
}
fun parseIndividualPlaceholders(strings: Collection<String>, context: PlaceholderContext): Collection<String> {
val injections = context.injectableContext.placeholderInjections
return strings.map {
parseIndividualEcoPlaceholder(it, context, injections)
?: translatePlacholders(
it,
context,
injections,
translateEcoPlaceholders = false
) // Default to slower translation
}
}
private fun parseIndividualEcoPlaceholder(
string: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>
): String? {
val placeholder = string.substring(1, string.length - 1)
val injectableResult = doGetResult(null, placeholder, injections, context)
if (injectableResult != null) {
return injectableResult
}
val parts = placeholder.split("_", limit = 2)
if (parts.size == 2) {
val plugin = EcoPlugin.getPlugin(parts[0])
if (plugin != null) {
return doGetResult(plugin, parts[1], null, context)
}
}
return null
}
}

View File

@@ -1,10 +1,10 @@
package com.willfp.eco.internal.price package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory import com.willfp.eco.core.price.PriceFactory
import com.willfp.eco.core.price.impl.PriceEconomy import com.willfp.eco.core.price.impl.PriceEconomy
import java.util.function.Function
object PriceFactoryEconomy : PriceFactory { object PriceFactoryEconomy : PriceFactory {
override fun getNames() = listOf( override fun getNames() = listOf(
@@ -12,7 +12,7 @@ object PriceFactoryEconomy : PriceFactory {
"$" "$"
) )
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price { override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceEconomy(baseContext, function) return PriceEconomy(baseContext, function)
} }
} }

View File

@@ -1,11 +1,11 @@
package com.willfp.eco.internal.price package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory import com.willfp.eco.core.price.PriceFactory
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.util.UUID import java.util.UUID
import java.util.function.Function
import kotlin.math.roundToInt import kotlin.math.roundToInt
object PriceFactoryXP : PriceFactory { object PriceFactoryXP : PriceFactory {
@@ -15,13 +15,13 @@ object PriceFactoryXP : PriceFactory {
"experience" "experience"
) )
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price { override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceXP(baseContext) { function.apply(it).roundToInt() } return PriceXP(baseContext) { function.get(it).roundToInt() }
} }
private class PriceXP( private class PriceXP(
private val baseContext: MathContext, private val baseContext: PlaceholderContext,
private val xp: (MathContext) -> Int private val xp: (PlaceholderContext) -> Int
) : Price { ) : Price {
private val multipliers = mutableMapOf<UUID, Double>() private val multipliers = mutableMapOf<UUID, Double>()
@@ -37,7 +37,7 @@ object PriceFactoryXP : PriceFactory {
} }
override fun getValue(player: Player, multiplier: Double): Double { override fun getValue(player: Player, multiplier: Double): Double {
return xp(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier return xp(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier
} }
override fun getMultiplier(player: Player): Double { override fun getMultiplier(player: Player): Double {

View File

@@ -1,11 +1,11 @@
package com.willfp.eco.internal.price package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory import com.willfp.eco.core.price.PriceFactory
import org.bukkit.entity.Player import org.bukkit.entity.Player
import java.util.UUID import java.util.UUID
import java.util.function.Function
import kotlin.math.roundToInt import kotlin.math.roundToInt
object PriceFactoryXPLevels : PriceFactory { object PriceFactoryXPLevels : PriceFactory {
@@ -16,13 +16,13 @@ object PriceFactoryXPLevels : PriceFactory {
"explevels", "explevels",
) )
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price { override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceXPLevel(baseContext) { function.apply(it).roundToInt() } return PriceXPLevel(baseContext) { function.get(it).roundToInt() }
} }
private class PriceXPLevel( private class PriceXPLevel(
private val baseContext: MathContext, private val baseContext: PlaceholderContext,
private val level: (MathContext) -> Int private val level: (PlaceholderContext) -> Int
) : Price { ) : Price {
private val multipliers = mutableMapOf<UUID, Double>() private val multipliers = mutableMapOf<UUID, Double>()
@@ -37,7 +37,7 @@ object PriceFactoryXPLevels : PriceFactory {
} }
override fun getValue(player: Player, multiplier: Double): Double { override fun getValue(player: Player, multiplier: Double): Double {
return level(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier return level(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier
} }
override fun getMultiplier(player: Player): Double { override fun getMultiplier(player: Player): Double {

View File

@@ -45,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack) val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag()) val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count") nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
} }
override fun getItem(): ItemStack = item override fun getItem(): ItemStack = item

View File

@@ -45,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack) val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag()) val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count") nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
} }
override fun getItem(): ItemStack = item override fun getItem(): ItemStack = item

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