Compare commits

...

368 Commits

Author SHA1 Message Date
Auxilor
4a8935f06d Added custom blocks API with initial oraxen integration 2024-07-30 15:57:44 +01:00
Auxilor
c89edd05f5 Updated to 6.73.2 2024-07-29 18:46:53 +01:00
Auxilor
ce2a53e689 Fixed FancyHolograms integration 2024-07-29 18:46:47 +01:00
Auxilor
6658824f9a Updated to 6.73.1 2024-07-24 15:24:59 +01:00
Auxilor
956d4fa10c Enchantment arg parser is now case insensitive 2024-07-24 15:24:49 +01:00
Auxilor
6a7a825376 Added extra apis to VanillaItemTag 2024-07-21 12:57:55 +01:00
Auxilor
cd232e804c Improved enchantment arg parser 2024-07-21 11:38:35 +01:00
Auxilor
7cf2fa4b4b Improved consistency with custom model data arg parser 2024-07-21 11:22:40 +01:00
Auxilor
cbf88ce678 Improved item tags 2024-07-20 15:36:26 +01:00
Auxilor
79db5978bd Improved ItemTag 2024-07-20 15:16:47 +01:00
Auxilor
0f11f9846c CustomItemTag is now an abstract class 2024-07-20 15:09:26 +01:00
Auxilor
1f460d7a00 Updated to 6.73.0 2024-07-20 15:07:55 +01:00
Auxilor
2fb9525175 Added Item Tags 2024-07-20 15:07:55 +01:00
Auxilor
a9a961ff2b Added FancyHolograms integration 2024-07-20 12:49:07 +01:00
Auxilor
25d572c0db Added AutocrafterPatch 2024-07-20 09:59:58 +01:00
Auxilor
8a4243e434 Fixed XP price 2024-07-19 20:01:20 +01:00
Auxilor
6c40670f5e Updated to 6.72.0 2024-07-19 19:46:26 +01:00
Auxilor
6e94f3cee8 Added support for multiple display modules per plugin 2024-07-19 19:46:26 +01:00
Auxilor
4968d3ff22 Updated to 6.71.6 2024-07-14 16:04:21 +01:00
Auxilor
eecd80be1c Fixed display name 2024-07-14 16:04:14 +01:00
Auxilor
52c1b52f6d Fixed tab completion bug 2024-07-12 18:18:40 +01:00
Auxilor
f321296227 Updated to 6.71.5 2024-07-12 18:08:35 +01:00
Auxilor
ddd12db420 Fixed ExtendedPersistentDataContainerFactory 2024-07-12 18:07:53 +01:00
Auxilor
bd09791b5b Improved command tab-completion 2024-07-08 18:49:29 +01:00
Auxilor
ce9549f03d Updated to 6.71.4 2024-07-08 15:14:24 +01:00
Auxilor
52367dbb95 Fixed Java 17 compatibility 2024-07-08 15:14:18 +01:00
Auxilor
0080c32c23 Updated to 6.71.3 2024-06-25 02:30:45 +01:00
Auxilor
581094a930 Fixed DurabilityUtils 2024-06-25 02:30:37 +01:00
Auxilor
5d9c8775e8 Updated to 6.71.2 2024-06-24 18:44:16 +01:00
Auxilor
9ab51d2c87 Added fix for TopInventory on pre-1.21 2024-06-24 18:42:32 +01:00
Auxilor
b0de341d7f Updated to 6.71.1 2024-06-23 16:45:17 +01:00
Auxilor
1bada835ea Added 1.21 item arg parsers 2024-06-23 14:20:49 +01:00
Auxilor
de04833f0c Updated custom name and lore on 1.21 2024-06-22 23:44:29 +01:00
Auxilor
75dd6be539 Fixed custom name on 1.21 2024-06-22 22:52:01 +01:00
Auxilor
9b47e4777a Dropped 1.20.6 support and made modern commons module for 1.21+ 2024-06-22 22:23:51 +01:00
Will FP
e2a6d6d9ac Merge pull request #359 from MCCasper/master
fix empty nbt
2024-06-22 22:11:36 +01:00
Nikolai Connolly
4c1bc76ee2 fix empty nbt 2024-06-22 17:09:23 -04:00
Auxilor
61c90d85ac Fixed 1.20.6+ getEnchants 2024-06-20 23:14:09 +01:00
Auxilor
6faaac2257 Fixed 1.20.6+ getEnchants 2024-06-20 23:13:52 +01:00
Auxilor
9ad489b9d9 Updated to 6.71.0 2024-06-20 18:48:19 +01:00
Auxilor
3a0e1eaf4d Added 1.21 support and fixed paperweight issues for Java 17 versions 2024-06-18 20:53:04 +01:00
Jason Penilla
80afa9127f Explicitly declare toolchains/make compile work consistently
Signed-off-by: WillFP <william.favierparsons1@gmail.com>
2024-06-18 20:53:04 +01:00
Auxilor
ad44891f5f 1.21 groundwork 2024-06-18 20:53:04 +01:00
Will FP
303f5eedac Updated paperweight 2024-05-13 19:18:55 +01:00
Will FP
830199d8ca Updated to 6.70.1 2024-05-13 19:14:15 +01:00
Will FP
aa86426895 Moved ShopGUIPlus into loadbefore 2024-05-13 19:14:02 +01:00
Will FP
edf20d8ab8 Removed Ssomar plugins from plugin.yml 2024-05-12 23:00:25 +01:00
Will FP
d99121ad47 Added onRegister and onRemove master functions to registries 2024-05-11 17:42:56 +01:00
Will FP
f71fa64ccd Merge remote-tracking branch 'origin/master' 2024-05-11 16:26:37 +01:00
Will FP
697e0b7c9c Fixed FastItemStack on 1.20.6 2024-05-11 16:26:29 +01:00
Will FP
08f6715305 Merge pull request #354
Fix IllegalArgumentException
2024-05-11 16:24:09 +01:00
Will FP
bff502c281 Fixed item SNBT conversions 2024-05-07 13:01:13 +01:00
Will FP
2500258166 1.20.6 Improvements 2024-05-06 17:30:32 +01:00
Will FP
a8d6aaad30 Fixed several things, updated version 2024-05-04 16:22:25 +01:00
Will FP
160635a2a7 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	eco-core/core-nms/build.gradle.kts
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/antigrief/AntigriefIridiumSkyblock.kt
#	gradle/wrapper/gradle-wrapper.jar
2024-05-04 15:05:02 +01:00
Will FP
6fd4eb7e4c Added preliminary 1.20.6 support 2024-05-04 15:04:36 +01:00
24Cr
5941060479 Fix IllegalArgumentException 2024-05-03 21:49:32 +03:00
Auxilor
a9ba317e7d Updated to 6.69.2 2024-04-10 16:36:10 +01:00
Auxilor
1adbcc105e Minor code reformatting from PR 2024-04-10 16:30:26 +01:00
Auxilor
c4c0f38969 Improved modern NMS version tooling 2024-04-10 16:30:10 +01:00
Will FP
9059ddf856 Merge pull request #351 from bridgelol/master
Fixed MongoDB data handler
2024-04-08 13:04:07 +01:00
Will FP
4209161046 Merge pull request #348 from Auxilor/develop
Added ability to modify items on DropQueuePushEvent
2024-04-08 12:47:01 +01:00
bridge
79ac0838d0 refactor(mongo): use non-deprecated mongodb driver and migrate to kotlinx serialization for mongodb codecs 2024-04-05 03:40:39 +02:00
bridge
cf0a96426c fix: MongoDataHandler 2024-04-05 02:52:48 +02:00
bridge
5ccb9ca30a chore: add toolchain for both java & kotlin (paperweight support) 2024-04-05 02:37:58 +02:00
bridge
033cc44200 chore: update paperweight 2024-04-05 02:35:55 +02:00
bridge
67ae0ec8cd chore: update gradle wrapper 2024-04-05 02:31:37 +02:00
bridge
c8329d050c feat(mongodb): add configurable database name 2024-04-05 02:19:51 +02:00
Auxilor
ab73c2202a Updated to 6.69.1 2024-03-29 16:50:04 +00:00
Auxilor
af640881b0 Fixed IridiumSkyblock 2024-03-29 16:49:57 +00:00
Auxilor
9bd5cb5046 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2024-03-29 16:39:42 +00:00
Will FP
2829baf5b0 Preliminary support for new NMS versioning 2024-03-22 19:00:57 +00:00
often
7ef928c360 Fixed additional player placeholders 2024-03-16 20:21:14 +03:00
often
17db6dbf7f Merge branch 'master' into develop 2024-03-16 20:20:21 +03:00
Will FP
446e7a9534 Fixed ExtendedPersistentDataContainerFactory on 1.20.4 2024-03-10 17:03:51 +00:00
often
dbbab8518e Added ability to modify items on DropQueuePushEvent 2024-02-24 21:13:01 +03:00
Will FP
1908d9a5c2 Added workflow dispatch 2024-02-08 19:37:26 +00:00
Will FP
4ab7e888ef Updated to 6.69.0 2024-02-08 14:22:59 +00:00
Will FP
47c8617d3d Added onCreateTasks 2024-02-08 14:22:47 +00:00
Will FP
c977cb2602 Fixed PR 2024-02-08 14:21:34 +00:00
Will FP
2402fd6736 Merge pull request #344 from Auxilor/develop
Additions and fixes
2024-02-08 14:20:20 +00:00
often
db41e11dab Added HuskClaims antigrief support
Added HuskTowns antigrief support
Added ItemBridge lookup support (`itembridge:key__id)
2024-01-27 14:33:36 +03:00
often
6b46ea7e67 Added random function for math taking min and max arguments 2024-01-27 14:30:59 +03:00
often
7defdcc1ff Merge branch 'master' into develop 2024-01-26 17:21:58 +03:00
Will FP
35496c60fc Updated to 6.68.6 2024-01-23 14:48:40 +00:00
Will FP
9b4af3eeab Fixed SCore 2024-01-23 14:48:19 +00:00
often
b6b4d7e2f8 Fixed console commands in config slots 2024-01-22 16:55:57 +03:00
often
74a8dcea90 Added ability to configure literal cache ttl 2024-01-22 16:55:22 +03:00
often
e0f631b3b4 Merge remote-tracking branch 'origin/master' into develop 2024-01-22 16:24:00 +03:00
Will FP
67981b4f9a Updated to 6.68.5 2024-01-15 23:17:27 +00:00
Will FP
03eb2f5d0a Fixed DeluxeCombat 2024-01-15 23:17:17 +00:00
Will FP
2df60bffee Updated to 6.68.4 2024-01-14 16:44:31 +00:00
Will FP
b97506ae70 Fixed bugs on spigot and old paper versions 2024-01-14 16:44:09 +00:00
often
747e702d54 Bring develop up to date 2024-01-08 04:31:28 +03:00
often
9e583f18de Merge branch 'master' into develop 2024-01-08 04:04:01 +03:00
Will FP
75afe1f2b0 Moved CMI to loadbefore 2024-01-07 14:31:22 +00:00
Will FP
4464d3bf75 Updated to 6.68.3 2024-01-07 12:41:16 +00:00
Will FP
75f217b141 Added math expression parsing to placeholders, {{expr}} and {^{expr}} (to format values nicely) 2024-01-07 12:41:06 +00:00
Will FP
2387849adb Updated to 6.68.2 2024-01-06 13:36:33 +00:00
Will FP
e7a5d8d155 Reimplemented lost commits 2024-01-06 13:36:19 +00:00
Will FP
d6e50e34e1 Updated to 6.68.1 2024-01-06 13:28:23 +00:00
Will FP
89e58be1a3 Added %value_commas% to ConfiguredPrice 2024-01-06 13:28:04 +00:00
Auxilor
8a8606bea4 Suppressed paper deprecations 2024-01-04 17:09:23 +00:00
Auxilor
3f751e8865 Updated to 6.68.1 2024-01-04 17:08:15 +00:00
Auxilor
6a035426b4 Fixed namespacedkey creation 2024-01-04 17:05:47 +00:00
Auxilor
f9bf97c90c Improved DropQueue telekinesis for paper users 2024-01-04 17:05:36 +00:00
Will FP
476faeec61 Fixed issues with CustomCrafting 2023-12-26 13:38:39 +01:00
Will FP
64647e3dad Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2023-12-26 13:34:19 +01:00
Will FP
dba640f8ee Updated to 6.68.0 2023-12-26 13:33:56 +01:00
Will FP
f52a760bbe Added yaml autosaving 2023-12-26 13:33:46 +01:00
Auxilor
c80cdf7b60 Oops 2023-12-20 12:59:59 +00:00
Auxilor
749f6229ac Updated to 6.67.3 2023-12-20 12:56:22 +00:00
Auxilor
e6318c0727 Updated DecentHOlos 2023-12-20 12:56:14 +00:00
Auxilor
989118a653 Updated IridiumSkyblock integration 2023-12-20 12:52:57 +00:00
Auxilor
6134e8de04 Fixed ESGUI circular load 2023-12-20 12:28:07 +00:00
Auxilor
5a8c2828f7 Fixed loadbefore 2023-12-20 12:23:52 +00:00
Will FP
0dba790bb2 Switched some plugins to loadbefore 2023-12-08 12:16:07 +00:00
Will FP
85991b6d7e Updated to 6.67.2 2023-12-08 12:12:44 +00:00
Will FP
ad272a2f6b Added clientside entity display name API for all versions 2023-12-08 12:12:04 +00:00
Will FP
9c4a65dc19 Updated to 6.67.1 2023-12-07 15:30:48 +00:00
Will FP
8d2d6153e5 Added 1.20.3 support 2023-12-07 15:30:37 +00:00
_OfTeN_
6248dafc70 Removed the custom biomes manager (moved to libreforge) 2023-12-04 01:29:06 +03:00
Will FP
9ad12c2578 Finally deprecated @ConfigUpdater for removal 2023-12-03 15:28:44 +00:00
Will FP
24d9052080 Updated dependencies, including Kotlin 2023-12-03 15:22:32 +00:00
Will FP
e2d2e955a4 Removed jitpack.yml 2023-12-03 14:47:41 +00:00
Will FP
c5cbdc2449 Fixed README 2023-12-03 14:47:29 +00:00
Will FP
1a4e8be1b2 Removed paper-plugin.yml 2023-12-03 14:14:23 +00:00
Will FP
1a75dd8e5b Improved local keys 2023-12-03 14:06:01 +00:00
Will FP
77de51b1fd Merge remote-tracking branch 'origin/develop' 2023-12-03 14:03:26 +00:00
Will FP
3495d2317d Merge remote-tracking branch 'origin/dependabot/gradle/org.spigotmc-spigot-api-1.20.2-R0.1-SNAPSHOT'
# Conflicts:
#	eco-core/core-backend/build.gradle.kts
2023-12-03 14:02:36 +00:00
Will FP
0284aec119 Merge remote-tracking branch 'origin/dependabot/gradle/org.litote.kmongo-kmongo-coroutine-4.10.0' 2023-12-03 14:01:54 +00:00
Will FP
0d4289b756 Merge remote-tracking branch 'origin/dependabot/gradle/me.clip-placeholderapi-2.11.4' 2023-12-03 14:01:48 +00:00
Will FP
6931ba0393 Merge remote-tracking branch 'origin/dependabot/gradle/com.github.TownyAdvanced-Towny-0.99.5.21' 2023-12-03 14:01:45 +00:00
Will FP
cb49fa2690 Packet listeners will no longer kick players if LinkageErrors are thrown 2023-12-03 13:56:49 +00:00
Will FP
4898ca4a25 Fixed 1.20.2 2023-12-03 13:51:34 +00:00
_OfTeN_
67d70098ef - Added CustomBiomesManager to add support for custom world generators and their biomes
- Added a Terra generator integration for Custom Biomes
- Added an entity arg parser to set entity type for spawners (`item: spawner entity:zombie`)
2023-12-03 05:34:00 +03:00
Auxilor
d072ac15a0 Changed to MIT license 2023-12-03 01:22:05 +03:00
Auxilor
fea3152660 Updated to 6.66.1 2023-12-03 01:22:05 +03:00
Auxilor
8fc9c718ce Fixed custom AI goals on 1.20 2023-12-03 01:22:05 +03:00
Auxilor
1fe6a3caeb Updated to 6.66.0 2023-12-03 01:22:05 +03:00
Auxilor
8668d36304 Fixed GUI lag exploit 2023-12-03 01:22:05 +03:00
Auxilor
72608c381e Updated to 6.65.7 2023-12-03 01:22:05 +03:00
Auxilor
80cdaf6ea0 Updated FabledSkyblock integration 2023-12-03 01:22:05 +03:00
Auxilor
de6a3e76fd Fixed IntegrationRegistry 2023-12-03 01:22:05 +03:00
Auxilor
80d24b8366 Updated to 6.65.6 2023-12-03 01:22:05 +03:00
Auxilor
4d6399dd6c Added 1.20.2 support 2023-12-03 01:22:04 +03:00
Auxilor
f1815aee4f Updated to 6.65.5 2023-12-03 01:22:04 +03:00
Auxilor
18e05da958 Patched GUI bug 2023-12-03 01:22:04 +03:00
Will FP
12d6ff180e Fixed ModelEngineBridge 2023-11-29 19:18:14 +00:00
Will FP
1c0f40880f Fixed repos 2023-11-29 19:05:41 +00:00
Will FP
6625385e1c Added support for ModelEngine 4 2023-11-29 18:58:13 +00:00
Will FP
75ca3dc9a6 Updated to 6.67.0 2023-11-28 14:28:23 +00:00
Will FP
1db2671bc5 Added client side display names (requires Paper 1.20+) 2023-11-28 14:27:48 +00:00
Auxilor
70dd3c0d64 Changed to MIT license 2023-11-23 09:17:23 +00:00
Auxilor
8665df51cc Updated to 6.66.1 2023-11-07 13:46:39 +00:00
Auxilor
e306190109 Fixed custom AI goals on 1.20 2023-11-07 13:46:32 +00:00
Auxilor
3e0a8b1838 Updated to 6.66.0 2023-10-26 18:30:01 +01:00
Auxilor
29d69d77b4 Fixed GUI lag exploit 2023-10-26 18:24:32 +01:00
Auxilor
9e625c004e Updated to 6.65.7 2023-10-16 22:24:24 +01:00
Will FP
59dfeeb012 Merge pull request #318
Fix RoyaleEconomy integration
2023-10-16 22:24:04 +01:00
Auxilor
6fdbb2d63f Updated FabledSkyblock integration 2023-10-16 22:23:32 +01:00
Auxilor
e77c8ab9aa Fixed IntegrationRegistry 2023-10-14 14:11:24 +01:00
dependabot[bot]
48e1618c84 Bump com.github.TownyAdvanced:Towny from 0.97.2.6 to 0.99.5.21
Bumps com.github.TownyAdvanced:Towny from 0.97.2.6 to 0.99.5.21.

---
updated-dependencies:
- dependency-name: com.github.TownyAdvanced:Towny
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 22:06:27 +00:00
dependabot[bot]
19f60afaf9 Bump me.clip:placeholderapi from 2.10.10 to 2.11.4
Bumps me.clip:placeholderapi from 2.10.10 to 2.11.4.

---
updated-dependencies:
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 22:06:15 +00:00
Auxilor
26e2282385 Updated to 6.65.6 2023-09-22 19:45:58 +01:00
Auxilor
2e9d5c36f2 Added 1.20.2 support 2023-09-22 19:45:23 +01:00
dependabot[bot]
b4fb66ffa0 Bump org.spigotmc:spigot-api
Bumps org.spigotmc:spigot-api from 1.17.1-R0.1-SNAPSHOT to 1.20.2-R0.1-SNAPSHOT.

---
updated-dependencies:
- dependency-name: org.spigotmc:spigot-api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-21 21:25:15 +00:00
Auxilor
ba0bf5ee8f Updated to 6.65.5 2023-09-12 16:51:33 +01:00
Auxilor
3bf9589cd2 Patched GUI bug 2023-09-12 16:51:20 +01:00
Sen2000
510034af0d Fix RoyaleEconomy integration 2023-09-03 18:03:19 +07:00
_OfTeN_
8052ffa706 Added ability to specify local storage usage individually for each persistent data key via a constructor parameter 2023-08-20 17:39:22 +03:00
Auxilor
ed41c1091c Suppression 2023-07-31 16:17:49 +02:00
Auxilor
4fcf8c0368 Updated to 6.65.4 2023-07-31 16:13:16 +02:00
Auxilor
4ae4af6f1d Fixed UltraEconomy 2023-07-31 16:10:32 +02:00
Auxilor
bba0900183 Fixed UltraEconomy build 2023-07-31 16:04:23 +02:00
Auxilor
641aee3965 Patched an exploit 2023-07-31 15:38:42 +02:00
Auxilor
18ea6cc568 Clarifications and fixes for other commits 2023-07-31 15:14:01 +02:00
Auxilor
8335f6bae5 Added quick patch for an exploit 2023-07-31 15:13:45 +02:00
Auxilor
514a8e76f9 Revert "Reworked EntityDamageByEntityEvent, finally"
This reverts commit 5cfb87e0
2023-07-31 15:12:48 +02:00
_OfTeN_
f34af36690 Added ArmorTrim arg parser (now without NMS) 2023-07-30 21:27:09 +03:00
_OfTeN_
143148d93e Made backend use 1.20 API 2023-07-30 21:26:21 +03:00
_OfTeN_
4f6c023217 Stupidity fix 2023-07-30 20:44:45 +03:00
_OfTeN_
620e1d3042 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt
#	eco-core/core-plugin/src/main/resources/config.yml
2023-07-30 20:39:00 +03:00
_OfTeN_
d3c831c19b Added a config option to enforce additional crafting recipe check (in case you're encountering issues with 3d party custom item plugins like ItemsAdder or Oraxen) 2023-07-30 20:38:09 +03:00
dependabot[bot]
47e87fa6ce Bump org.litote.kmongo:kmongo-coroutine from 4.6.0 to 4.10.0
Bumps [org.litote.kmongo:kmongo-coroutine](https://github.com/Litote/kmongo) from 4.6.0 to 4.10.0.
- [Release notes](https://github.com/Litote/kmongo/releases)
- [Commits](https://github.com/Litote/kmongo/compare/kmongo-4.6.0...kmongo-4.10.0)

---
updated-dependencies:
- dependency-name: org.litote.kmongo:kmongo-coroutine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-24 21:38:07 +00:00
Auxilor
22d9dbdf66 Added display-without-meta option 2023-07-22 16:13:31 +01:00
Auxilor
238ea86e09 Merge branch 'master' into develop 2023-07-22 14:16:42 +01:00
Auxilor
5cfb87e03c Reworked EntityDamageByEntityEvent, finally 2023-07-22 14:16:34 +01:00
Auxilor
ff61527939 Updated to 6.65.3 2023-07-20 13:01:19 +01:00
Auxilor
7ff578f89b Fixed clearInjectedPlaceholders 2023-07-20 12:36:41 +01:00
Auxilor
27da03e8db Optimised config hashing 2023-07-20 12:19:10 +01:00
Auxilor
f1bef38046 Updated to 6.65.2 2023-07-16 13:03:50 +01:00
Auxilor
ee237f9c58 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-api/src/main/java/com/willfp/eco/core/config/interfaces/Config.java
2023-07-16 13:01:28 +01:00
Auxilor
0a80518755 Merge remote-tracking branch 'origin/develop' into develop 2023-07-13 15:17:13 +01:00
Auxilor
134859fea0 Extremely obvious optimisation to expression parsing 2023-07-13 15:16:23 +01:00
_OfTeN_
5c267d500a Fixed head arg parser breaking back serialization when used with texture 2023-07-12 23:42:52 +03:00
Auxilor
4c9735583c Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/math/ExpressionHandlers.kt
2023-07-10 15:07:15 +01:00
Auxilor
739d9cfffb Optimised Config#getDoubleFromExpression for constant values 2023-07-06 17:35:52 +01:00
Auxilor
4301d83e91 Removed ItemMetaProxy, can be reimplemented much better without NMS 2023-07-06 17:33:44 +01:00
Auxilor
84d5c5e665 Merge branch 'master' into develop 2023-07-05 15:33:07 +01:00
Auxilor
50f217eebe Switched to a standard map for LazyPlaceholderTranslationExpressionHandler 2023-07-05 15:30:06 +01:00
Auxilor
041fce69cc Updated gradle 2023-07-05 15:00:34 +01:00
Auxilor
238bd08502 Merge remote-tracking branch 'origin/dependabot/gradle/org.javassist-javassist-3.29.2-GA' 2023-07-05 14:51:57 +01:00
Auxilor
940e6e8b5b Merge remote-tracking branch 'origin/dependabot/gradle/io.lumine-Mythic-5.3.5' 2023-07-05 14:51:56 +01:00
Auxilor
eb07622496 Merge remote-tracking branch 'origin/dependabot/gradle/com.github.johnrengelman.shadow-8.1.1' 2023-07-05 14:51:54 +01:00
Auxilor
841ed52bdd Merge remote-tracking branch 'origin/dependabot/gradle/com.arcaniax-HeadDatabase-API-1.3.1' 2023-07-05 14:51:52 +01:00
Auxilor
342f6dbc7b Updated to 6.65.1 2023-07-05 14:44:31 +01:00
Auxilor
245b577adc Expressions that are just numbers will be immediately converted rather than being evaluated 2023-07-05 14:44:22 +01:00
dependabot[bot]
1258305755 Bump io.lumine:Mythic from 5.2.1 to 5.3.5
Bumps io.lumine:Mythic from 5.2.1 to 5.3.5.

---
updated-dependencies:
- dependency-name: io.lumine:Mythic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-04 21:35:54 +00:00
_OfTeN_
cd8ed5a823 Implemented ItemMetaProxy with an ability to set/get trim from the 1.20 armor meta
Added an ArgParserTrim to add trims to armor via item lookup strings
2023-06-24 16:38:10 +03:00
_OfTeN_
795ead57bf Fixed MythicMobs mob test predicate 2023-06-23 11:24:03 +03:00
_OfTeN_
910cdaf992 Updated KingdomsX antigrief integration 2023-06-23 10:41:15 +03:00
_OfTeN_
85c02d3402 Merge remote-tracking branch 'origin/master' into develop 2023-06-23 09:42:26 +03:00
dependabot[bot]
b4ad2fd4d7 Bump com.github.johnrengelman.shadow from 7.1.2 to 8.1.1
Bumps com.github.johnrengelman.shadow from 7.1.2 to 8.1.1.

---
updated-dependencies:
- dependency-name: com.github.johnrengelman.shadow
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 22:00:27 +00:00
dependabot[bot]
3c5190da3a Bump com.arcaniax:HeadDatabase-API from 1.3.0 to 1.3.1
Bumps [com.arcaniax:HeadDatabase-API](https://github.com/Arcaniax-Development/HeadDatabase-API) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/Arcaniax-Development/HeadDatabase-API/releases)
- [Commits](https://github.com/Arcaniax-Development/HeadDatabase-API/compare/1.3.0...1.3.1)

---
updated-dependencies:
- dependency-name: com.arcaniax:HeadDatabase-API
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 21:59:50 +00:00
dependabot[bot]
0f0f003f23 Bump org.javassist:javassist from 3.28.0-GA to 3.29.2-GA
Bumps [org.javassist:javassist](https://github.com/jboss-javassist/javassist) from 3.28.0-GA to 3.29.2-GA.
- [Release notes](https://github.com/jboss-javassist/javassist/releases)
- [Changelog](https://github.com/jboss-javassist/javassist/blob/master/Changes.md)
- [Commits](https://github.com/jboss-javassist/javassist/commits)

---
updated-dependencies:
- dependency-name: org.javassist:javassist
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 21:59:28 +00:00
Auxilor
7e5e5162c3 LazyPlaceholderTranslationExpressionHandler now uses a regular map rather than a caffeine cache 2023-06-18 22:03:48 +02:00
Auxilor
09417d0834 Expressions that are just numbers will be immediately converted rather than being evaluated 2023-06-18 21:11:14 +02:00
Auxilor
c85b8c08c7 Merge remote-tracking branch 'origin/master' 2023-06-18 14:35:31 +02:00
Auxilor
a65ac0aa4f Updated to 6.65.0 2023-06-18 14:23:18 +02:00
Auxilor
9d03ba2bd2 Fixed deprecated use of placeholder methods 2023-06-18 14:14:52 +02:00
Auxilor
8eb62d65b4 Fixed PersistentDataKeyType.BIG_DECIMAL 2023-06-18 14:11:50 +02:00
Will FP
178f21b1f3 Create CODEOWNERS 2023-06-17 21:14:57 +02:00
Will FP
a3fe4f97ff Merge pull request #280 from Auxilor/develop
Fixed not player messages
2023-06-17 21:09:31 +02:00
Auxilor
ff356fcde5 Fixed not player messages 2023-06-17 20:57:36 +02:00
Will FP
59b57b17ba Create master-pr.yml 2023-06-17 20:35:49 +02:00
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
Cyramek
4ce2850138 fix data desync 2023-04-23 14:35:17 +02:00
385 changed files with 10249 additions and 2420 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @WillFP

View File

@@ -1,6 +1,6 @@
name: Java CI
on: [ push, pull_request ]
on: [ push, pull_request, workflow_dispatch ]
jobs:
build:
@@ -14,11 +14,11 @@ jobs:
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: 17
java-version: 21
- name: Setup build cache
uses: actions/cache@v2.1.6

21
.github/workflows/master-pr.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: PR Alert for Master Branch
on:
pull_request:
branches:
- master
jobs:
alert:
runs-on: ubuntu-latest
steps:
- name: Comment PR
uses: actions/github-script@v5
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '⚠️ PRs should not be opened against the `master` branch directly. Please use the `develop` branch as the base for your PRs. ⚠️'
})

View File

@@ -12,11 +12,11 @@ jobs:
- name: Checkout latest code
uses: actions/checkout@v2
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: 17
java-version: 21
- name: Setup build cache
uses: actions/cache@v2.1.6

View File

@@ -14,11 +14,11 @@ jobs:
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: 17
java-version: 21
- name: Setup build cache
uses: actions/cache@v2.1.6

View File

@@ -1,674 +1,23 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
The MIT License (MIT)
=====================
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -127,7 +127,7 @@ cd eco
## License
eco is licensed under GNU GPL3. *Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).*
eco is licensed under the MIT license. *Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).*
<h1 align="center">
Check out our partners!

View File

@@ -4,16 +4,17 @@ buildscript {
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21")
}
}
plugins {
id("java-library")
id("com.github.johnrengelman.shadow") version "7.1.2"
id("io.github.goooler.shadow") version "8.1.7"
id("maven-publish")
id("java")
kotlin("jvm") version "1.7.10"
kotlin("jvm") version "1.9.21"
kotlin("plugin.serialization") version "1.9.21"
}
dependencies {
@@ -21,28 +22,33 @@ dependencies {
implementation(project(path = ":eco-core:core-plugin", configuration = "shadow"))
implementation(project(":eco-core:core-proxy"))
implementation(project(":eco-core:core-backend"))
implementation(project(":eco-core:core-backend-modern"))
implementation(project(path = ":eco-core:core-nms:v1_17_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_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_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_20_R3", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_21", configuration = "reobf"))
}
allprojects {
apply(plugin = "java")
apply(plugin = "java-library")
apply(plugin = "maven-publish")
apply(plugin = "com.github.johnrengelman.shadow")
apply(plugin = "io.github.goooler.shadow")
apply(plugin = "kotlin")
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
repositories {
mavenCentral()
mavenLocal()
maven("https://jitpack.io")
// CustomCrafting
maven("https://maven.wolfyscript.com/repository/public/")
maven("https://repo.auxilor.io/repository/maven-public/")
maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") }
}
// SuperiorSkyblock2
maven("https://repo.bg-software.com/repository/api/")
@@ -80,22 +86,34 @@ allprojects {
// LibsDisguises
maven("https://repo.md-5.net/content/groups/public/")
// UltraEconomy
maven("https://repo.techscode.com/repository/maven-releases/")
// PlayerPoints
maven("https://repo.rosewooddev.io/repository/public/")
// Denizen
maven("https://maven.citizensnpcs.co/repo")
// IridiumSkyblock
maven("https://nexus.iridiumdevelopment.net/repository/maven-releases/")
// HuskPlugins
maven("https://repo.william278.net/releases")
// FancyHolograms
maven("https://repo.fancyplugins.de/releases")
}
dependencies {
// Kotlin
implementation(kotlin("stdlib", version = "1.7.10"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
implementation(kotlin("stdlib", version = "1.9.21"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// Included in spigot jar, no need to move to implementation
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("com.google.guava:guava:31.1-jre")
// Test
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
// Adventure
implementation("net.kyori:adventure-api:4.10.1")
@@ -105,8 +123,8 @@ allprojects {
implementation("net.kyori:adventure-text-serializer-legacy:4.10.1")
// Other
implementation("com.github.ben-manes.caffeine:caffeine:3.1.0")
implementation("org.apache.maven:maven-artifact:3.8.5")
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
implementation("org.apache.maven:maven-artifact:3.9.0")
}
tasks.withType<JavaCompile> {
@@ -130,12 +148,48 @@ allprojects {
}
tasks {
withType<Jar> {
duplicatesStrategy = DuplicatesStrategy.WARN
}
compileKotlin {
kotlinOptions {
jvmTarget = "17"
}
}
compileJava {
dependsOn(clean)
options.encoding = "UTF-8"
}
test {
useJUnitPlatform()
// Show test results.
testLogging {
events("passed", "skipped", "failed")
}
}
build {
dependsOn(shadowJar)
}
withType<JavaCompile>().configureEach {
options.release = 17
}
}
java {
withSourcesJar()
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
}
tasks {
shadowJar {
relocate("org.bstats", "com.willfp.eco.libs.bstats")
relocate("redempt.crunch", "com.willfp.eco.libs.crunch")
@@ -156,45 +210,19 @@ allprojects {
relocate("google.protobuf", "com.willfp.eco.libs.protobuf") // Still don't know
relocate("com.zaxxer.hikari", "com.willfp.eco.libs.hikari")
//relocate("com.mysql", "com.willfp.eco.libs.mysql")
relocate("de.undercouch.bson4jackson", "com.willfp.eco.libs.bson4jackson")
relocate("com.fasterxml.jackson", "com.willfp.eco.libs.jackson")
relocate("com.mongodb", "com.willfp.eco.libs.mongodb")
relocate("org.bson", "com.willfp.eco.libs.bson")
relocate("org.litote", "com.willfp.eco.libs.litote")
relocate("org.reactivestreams", "com.willfp.eco.libs.reactivestreams")
relocate("reactor.", "com.willfp.eco.libs.reactor.") // Dot in name to be safe
relocate("com.moandjiezana.toml", "com.willfp.eco.libs.toml")
relocate("com.willfp.modelenginebridge", "com.willfp.eco.libs.modelenginebridge")
/*
Kotlin and caffeine are not shaded so that they can be accessed directly by eco plugins.
Also, not relocating adventure, because it's a pain in the ass, and it doesn't *seem* to be causing loader constraint violations.
*/
}
compileJava {
dependsOn(clean)
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
withSourcesJar()
}
test {
useJUnitPlatform()
// Show test results.
testLogging {
events("passed", "skipped", "failed")
}
}
build {
dependsOn(shadowJar)
}
}
}
group = "com.willfp"

View File

@@ -3,7 +3,8 @@ dependencies {
compileOnly("net.kyori:adventure-platform-bukkit:4.1.0")
// Other
compileOnly("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT")
compileOnly("org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT")
compileOnly("commons-lang:commons-lang:2.6")
compileOnly("com.comphenix.protocol:ProtocolLib:4.6.1-SNAPSHOT")
compileOnly("com.google.code.gson:gson:2.8.8")
}

View File

@@ -25,16 +25,18 @@ import com.willfp.eco.core.gui.menu.MenuType;
import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.packet.Packet;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -135,6 +137,14 @@ public interface Eco {
@NotNull
Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Get NOOP logger.
*
* @return The logger.
*/
@NotNull
Logger getNOOPLogger();
/**
* Create a PAPI integration.
*
@@ -170,7 +180,7 @@ public interface Eco {
* @return The PluginCommandBase implementation
*/
@NotNull
PluginCommandBase createPluginCommand(@NotNull CommandBase parentDelegate,
PluginCommandBase createPluginCommand(@NotNull PluginCommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
@@ -393,15 +403,6 @@ public interface Eco {
@NotNull
ServerProfile getServerProfile();
/**
* Unload a player profile from memory.
* <p>
* This will not save the profile first.
*
* @param uuid The uuid.
*/
void unloadPlayerProfile(@NotNull UUID uuid);
/**
* Create dummy entity - never spawned, exists purely in code.
*
@@ -528,10 +529,11 @@ public interface Eco {
*
* @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 null if invalid.
*/
double evaluate(@NotNull String expression,
@NotNull MathContext context);
@Nullable
Double evaluate(@NotNull String expression,
@NotNull PlaceholderContext context);
/**
* Get the menu a player currently has open.
@@ -563,6 +565,43 @@ public interface Eco {
void sendPacket(@NotNull Player player,
@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);
/**
* Set a client-side entity display name.
*
* @param entity The entity.
* @param player The player.
* @param name The display name.
* @param visible If the display name should be forcibly visible.
*/
void setClientsideDisplayName(@NotNull LivingEntity entity,
@NotNull Player player,
@NotNull Component name,
boolean visible);
/**
* Get the instance of eco; the bridge between the api frontend and the implementation backend.
*

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core;
import com.google.common.collect.ImmutableList;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.eco.core.config.base.ConfigYml;
import com.willfp.eco.core.config.base.LangYml;
@@ -33,8 +34,11 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -119,13 +123,21 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
/**
* The display module for the plugin.
*
* @deprecated Plugins can now have multiple display modules.
*/
@Deprecated(since = "6.72.0")
private DisplayModule displayModule;
/**
* The display modules for the plugin.
*/
private List<DisplayModule> displayModules = new ArrayList<>();
/**
* The logger for the plugin.
*/
private final Logger logger;
private Logger logger;
/**
* If the server is running an outdated version of the plugin.
@@ -163,6 +175,11 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
*/
private final ListMap<LifecyclePosition, Runnable> afterLoad = new ListMap<>();
/**
* The tasks to run on task creation.
*/
private final ListMap<LifecyclePosition, Runnable> onCreateTasks = new ListMap<>();
/**
* Create a new plugin.
* <p>
@@ -366,7 +383,6 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
public final void onEnable() {
super.onEnable();
this.getLogger().info("");
this.getLogger().info("Loading " + this.getColor() + this.getName());
if (this.getResourceId() != 0 && !Eco.get().getEcoPlugin().getConfigYml().getBool("no-update-checker")) {
@@ -406,7 +422,9 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName()));
if (!this.getLoadedIntegrations().isEmpty()) {
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
}
Prerequisite.update();
@@ -423,22 +441,35 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
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()) {
this.getExtensionLoader().loadExtensions();
if (this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
this.getLogger().info("&cNo extensions found");
} else {
this.getLogger().info("Extensions Loaded:");
this.getExtensionLoader().getLoadedExtensions().forEach(extension -> this.getLogger().info("- " + extension.getName() + " v" + extension.getVersion()));
if (!this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
List<String> loadedExtensions = this.getExtensionLoader().getLoadedExtensions().stream().map(
extension -> extension.getName() + " v" + extension.getVersion()
).toList();
this.getLogger().info(
"Loaded extensions: " +
String.join(", ", loadedExtensions)
);
}
}
this.handleLifecycle(this.onEnable, this::handleEnable);
this.getLogger().info("");
}
/**
@@ -535,10 +566,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Default code to be executed after the server is up.
*/
public final void afterLoad() {
this.displayModule = createDisplayModule();
DisplayModule module = createDisplayModule();
if (module != null) {
Display.registerDisplayModule(module);
this.displayModules.add(module);
}
if (this.getDisplayModule() != null) {
Display.registerDisplayModule(this.getDisplayModule());
for (DisplayModule displayModule : this.loadDisplayModules()) {
Display.registerDisplayModule(displayModule);
this.displayModules.add(displayModule);
}
if (Prerequisite.HAS_PROTOCOLLIB.isMet()) {
@@ -555,8 +591,9 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.getLogger().severe("");
this.getLogger().severe("You don't seem to be running paper!");
this.getLogger().severe("Paper is strongly recommended for all servers,");
this.getLogger().severe("and some things may not function properly without it");
this.getLogger().severe("Download Paper from &fhttps://papermc.io");
this.getLogger().severe("and many features may not function properly without it");
this.getLogger().severe("Download Paper from https://papermc.io");
this.getLogger().severe("It's a drop-in replacement for Spigot, so it's easy to switch.");
this.getLogger().severe("");
this.getLogger().severe("----------------------------");
this.getLogger().severe("");
@@ -597,14 +634,30 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Reload the plugin.
*/
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();
if (cancelTasks) {
this.getScheduler().cancelAll();
}
this.getConfigHandler().callUpdate();
this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.handleLifecycle(this.onReload, this::handleReload);
if (cancelTasks) {
this.handleLifecycle(this.onCreateTasks, this::createTasks);
}
for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload();
}
@@ -630,6 +683,26 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.onReload.append(position, task);
}
/**
* Add new task to run on createTasks.
*
* @param task The task.
*/
public final void onCreateTasks(@NotNull final Runnable task) {
this.onCreateTasks(LifecyclePosition.END, task);
}
/**
* Add new task to run on createTasks.
*
* @param position The position to run the task.
* @param task The task.
*/
public final void onCreateTasks(@NotNull final LifecyclePosition position,
@NotNull final Runnable task) {
this.onCreateTasks.append(position, task);
}
/**
* Reload the plugin and return the time taken to reload.
*
@@ -718,6 +791,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.
* <p>
@@ -833,14 +915,25 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Create the display module for the plugin.
*
* @return The display module, or null.
* @deprecated Use {@link #loadDisplayModules()} instead.
*/
@Nullable
@Deprecated(since = "6.72.0")
protected DisplayModule createDisplayModule() {
Validate.isTrue(this.getDisplayModule() == null, "Display module exists!");
return null;
}
/**
* Load display modules.
*
* @return The display modules.
*/
protected List<DisplayModule> loadDisplayModules() {
return new ArrayList<>();
}
/**
* Get the minimum version of eco to use the plugin.
*
@@ -1082,7 +1175,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
*
* @return The config handler.
*/
public ConfigHandler getConfigHandler() {
public @NotNull ConfigHandler getConfigHandler() {
return this.configHandler;
}
@@ -1090,12 +1183,23 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Get the plugin's display module.
*
* @return The display module.
* @deprecated Use {@link #getDisplayModules()} instead.
*/
@Nullable
@Deprecated(since = "6.72.0", forRemoval = true)
public DisplayModule getDisplayModule() {
return this.displayModule;
}
/**
* Get the plugin's display modules.
*
* @return The display modules.
*/
public List<DisplayModule> getDisplayModules() {
return ImmutableList.copyOf(this.displayModules);
}
/**
* Get if the plugin is outdated.
*
@@ -1146,9 +1250,24 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
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
@NotNull
public final String getID() {
return Registry.tryFitPattern(this.getName());
}
@Override
public @NotNull File getFile() {
return super.getFile();
}
}

View File

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

View File

@@ -55,7 +55,7 @@ public final class PluginProps {
private final Map<String, String> environment = new HashMap<>();
/**
* If the plugin uses reflective reload (via {@link com.willfp.eco.core.config.updating.ConfigUpdater}).
* If the plugin uses reflective reload.
*/
private boolean usesReflectiveReload = true;

View File

@@ -37,11 +37,44 @@ public class Prerequisite {
"Requires server to have ProtocolLib"
);
/**
* Requires the server to be running 1.21.
*/
public static final Prerequisite HAS_1_21 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("1_21"),
"Requires server to be running 1.21+"
);
/**
* Requires the server to be running 1.20.5.
*/
public static final Prerequisite HAS_1_20_5 = new Prerequisite(
() -> (ProxyConstants.NMS_VERSION.contains("1_20_") && !ProxyConstants.NMS_VERSION.contains("R"))
|| HAS_1_21.isMet(),
"Requires server to be running 1.20.5+"
);
/**
* Requires the server to be running 1.20.3.
*/
public static final Prerequisite HAS_1_20_3 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20_R3") || HAS_1_20_5.isMet(),
"Requires server to be running 1.20.3+"
);
/**
* Requires the server to be running 1.20.
*/
public static final Prerequisite HAS_1_20 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("20") || HAS_1_20_3.isMet(),
"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"),
() -> ProxyConstants.NMS_VERSION.contains("19_R3") || HAS_1_20.isMet(),
"Requires server to be running 1.19.4+"
);
@@ -49,7 +82,7 @@ public class Prerequisite {
* Requires the server to be running 1.19.
*/
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+"
);

View File

@@ -0,0 +1,149 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.MaterialTestableBlock;
import com.willfp.eco.core.blocks.impl.UnrestrictedMaterialTestableBlock;
import com.willfp.eco.core.blocks.provider.BlockProvider;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class to manage all custom and vanilla blocks.
*/
public final class Blocks {
/**
* All entities.
*/
private static final Map<NamespacedKey, TestableBlock> REGISTRY = new ConcurrentHashMap<>();
/**
* All block providers.
*/
private static final Map<String, BlockProvider> PROVIDERS = new ConcurrentHashMap<>();
/**
* The lookup handler.
*/
private static final BlocksLookupHandler BLOCKS_LOOKUP_HANDLER = new BlocksLookupHandler(Blocks::doParse);
/**
* Register a new custom block.
*
* @param key The key of the block.
* @param block The block.
*/
public static void registerCustomBlock(@NotNull final NamespacedKey key,
@NotNull final TestableBlock block) {
REGISTRY.put(key, block);
}
/**
* Register a new block provider.
*
* @param provider The provider.
*/
public static void registerBlockProvider(@NotNull final BlockProvider provider) {
PROVIDERS.put(provider.getNamespace(), provider);
}
/**
* Remove a block.
*
* @param key The key of the block.
*/
public static void removeCustomBlock(@NotNull final NamespacedKey key) {
REGISTRY.remove(key);
}
/**
* This is the backbone of the eco block system.
* <p>
* You can look up a TestableBlock for any material or custom block,
* and it will return it.
* <p>
* If you want to get a Block instance from this, then just call
* {@link TestableBlock#place(Location)}.
*
* @param key The lookup string.
* @return The testable block, or an empty testable block if not found.
*/
@NotNull
public static TestableBlock lookup(@NotNull final String key) {
return BLOCKS_LOOKUP_HANDLER.parseKey(key);
}
@NotNull
private static TestableBlock doParse(@NotNull final String[] args) {
if (args.length == 0) {
return new EmptyTestableBlock();
}
String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
if (args[0].startsWith("*")) {
Material type = Material.getMaterial(args[0].substring(1));
return (type == null) ? new EmptyTestableBlock() : new UnrestrictedMaterialTestableBlock(type);
} else {
Material type = Material.getMaterial(args[0].toUpperCase());
return (type == null) ? new EmptyTestableBlock() : new MaterialTestableBlock(type);
}
}
NamespacedKey namespacedKey = NamespacedKeyUtils.create(split[0], split[1]);
TestableBlock block = REGISTRY.get(namespacedKey);
if (block != null) {
return block;
}
BlockProvider provider = PROVIDERS.get(split[0]);
if (provider == null) {
return new EmptyTestableBlock();
}
block = provider.provideForKey(split[1]);
if (block == null) {
return new EmptyTestableBlock();
}
registerCustomBlock(namespacedKey, block);
return block;
}
/**
* Get if block is a custom block.
*
* @param block The block to check.
* @return If is custom.
*/
public static boolean isCustomBlock(@NotNull final Block block) {
for (TestableBlock testable : REGISTRY.values()) {
if (testable.matches(block)) {
return true;
}
}
return false;
}
/**
* Get all registered custom blocks.
*
* @return A set of all blocks.
*/
public static Set<TestableBlock> getCustomBlocks() {
return new HashSet<>(REGISTRY.values());
}
private Blocks() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.GroupedTestableBlocks;
import com.willfp.eco.core.lookup.LookupHandler;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.Function;
/**
* Handle block lookup strings.
*/
public class BlocksLookupHandler implements LookupHandler<TestableBlock> {
/**
* The parser.
*/
private final Function<String[], @NotNull TestableBlock> parser;
/**
* Create new lookup handler.
*
* @param parser The parser.
*/
public BlocksLookupHandler(@NotNull final Function<String[], @NotNull TestableBlock> parser) {
this.parser = parser;
}
@Override
public @NotNull TestableBlock parse(@NotNull final String[] args) {
return parser.apply(args);
}
@Override
public boolean validate(@NotNull final TestableBlock object) {
return !(object instanceof EmptyTestableBlock);
}
@Override
public @NotNull TestableBlock getFailsafe() {
return new EmptyTestableBlock();
}
@Override
public @NotNull TestableBlock join(@NotNull final Collection<TestableBlock> options) {
return new GroupedTestableBlocks(options);
}
}

View File

@@ -0,0 +1,84 @@
package com.willfp.eco.core.blocks;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* A custom block has 3 components.
*
* <ul>
* <li>The key to identify it</li>
* <li>The test to check if any block is this custom block</li>
* <li>The supplier to spawn the custom {@link Block}</li>
* </ul>
*/
public class CustomBlock implements TestableBlock {
/**
* The key.
*/
private final NamespacedKey key;
/**
* The test for block to pass.
*/
private final Predicate<@NotNull Block> test;
/**
* The provider to spawn the block.
*/
private final Function<Location, Block> provider;
/**
* Create a new custom block.
*
* @param key The block key.
* @param test The test.
* @param provider The provider to spawn the block.
*/
public CustomBlock(@NotNull final NamespacedKey key,
@NotNull final Predicate<@NotNull Block> test,
@NotNull final Function<Location, Block> provider) {
this.key = key;
this.test = test;
this.provider = provider;
}
@Override
public boolean matches(@Nullable final Block other) {
if (other == null) {
return false;
}
return test.test(other);
}
@Override
public @NotNull Block place(@NotNull final Location location) {
Validate.notNull(location.getWorld());
return provider.apply(location);
}
/**
* Register the block.
*/
public void register() {
Blocks.registerCustomBlock(this.getKey(), this);
}
/**
* Get the key.
*
* @return The key.
*/
public NamespacedKey getKey() {
return this.key;
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.core.blocks;
import com.willfp.eco.core.lookup.Testable;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A block with a test.
*/
public interface TestableBlock extends Testable<Block> {
/**
* If a Block matches the test.
*
* @param other The other block.
* @return If the block matches.
*/
@Override
boolean matches(@Nullable Block other);
/**
* Place the block.
*
* @param location The location.
* @return The block.
*/
@NotNull
Block place(@NotNull Location location);
}

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Empty block.
*/
public class EmptyTestableBlock implements TestableBlock {
/**
* Create a new empty testable block.
*/
public EmptyTestableBlock() {
}
@Override
public boolean matches(@Nullable final Block other) {
return false;
}
@Override
public @NotNull Block place(@NotNull final Location location) {
return location.getBlock();
}
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import com.willfp.eco.util.NumberUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
/**
* A group of testable blocks.
*/
public class GroupedTestableBlocks implements TestableBlock {
/**
* The children.
*/
private final Collection<TestableBlock> children;
/**
* Create a new group of testable blocks.
*
* @param children The children.
*/
public GroupedTestableBlocks(@NotNull final Collection<TestableBlock> children) {
Validate.isTrue(!children.isEmpty(), "Group must have at least one child!");
this.children = children;
}
@Override
public boolean matches(@Nullable final Block other) {
for (TestableBlock child : children) {
if (child.matches(other)) {
return true;
}
}
return false;
}
@Override
public @NotNull Block place(@NotNull final Location location) {
return new ArrayList<>(children)
.get(NumberUtils.randInt(0, children.size() - 1))
.place(location);
}
/**
* Get the children.
*
* @return The children.
*/
public Collection<TestableBlock> getChildren() {
return new ArrayList<>(children);
}
}

View File

@@ -0,0 +1,59 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.Blocks;
import com.willfp.eco.core.blocks.TestableBlock;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A testable block for vanilla materials.
*/
public class MaterialTestableBlock implements TestableBlock {
/**
* The block type.
*/
private final Material material;
/**
* Create a new unrestricted material testable block.
*
* @param material The material.
*/
public MaterialTestableBlock(@NotNull final Material material) {
this.material = material;
}
@Override
public boolean matches(@Nullable final Block block) {
boolean simpleMatches = block != null && block.getType() == material;
if (!simpleMatches) {
return false;
}
return !Blocks.isCustomBlock(block);
}
@Override
public @NotNull Block place(@NotNull Location location) {
Validate.notNull(location.getWorld());
Block block = location.getWorld().getBlockAt(location);
block.setType(material);
return block;
}
/**
* Get the material.
*
* @return The material.
*/
public Material getMaterial() {
return this.material;
}
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.core.blocks.impl;
import com.willfp.eco.core.blocks.TestableBlock;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A testable block for materials regardless of data.
*/
public class UnrestrictedMaterialTestableBlock implements TestableBlock {
/**
* The block type.
*/
private final Material material;
/**
* Create a new unrestricted material testable block.
*
* @param material The material.
*/
public UnrestrictedMaterialTestableBlock(@NotNull final Material material) {
this.material = material;
}
@Override
public boolean matches(@Nullable final Block other) {
return other != null && other.getType() == material;
}
@Override
public @NotNull Block place(@NotNull Location location) {
Validate.notNull(location.getWorld());
Block block = location.getWorld().getBlockAt(location);
block.setType(material);
return block;
}
/**
* Get the material.
*
* @return The material.
*/
public Material getMaterial() {
return this.material;
}
}

View File

@@ -0,0 +1,49 @@
package com.willfp.eco.core.blocks.provider;
import com.willfp.eco.core.blocks.TestableBlock;
import com.willfp.eco.core.registry.Registry;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Block providers are call-site registrations for blocks. In other words,
* they only register their blocks when a request is made. This is marginally
* slower, however it is required for certain plugins, and fixes bugs related to
* loading orders.
*
* @see TestableBlock
*/
public abstract class BlockProvider {
/**
* The namespace.
*/
private final String namespace;
/**
* Create a new BlockProvider for a specific namespace.
*
* @param namespace The namespace.
*/
protected BlockProvider(@NotNull final String namespace) {
this.namespace = namespace;
}
/**
* Provide a TestableBlock for a given key.
*
* @param key The block ID.
* @return The TestableBlock, or null if not found.
*/
@Nullable
public abstract TestableBlock provideForKey(@NotNull String key);
/**
* Get the namespace.
*
* @return The namespace.
*/
public String getNamespace() {
return this.namespace;
}
}

View File

@@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull;
* Default plugin config.yml.
*/
public class ConfigYml extends BaseConfig {
/**
* The use local storage key.
*/
public static final String KEY_USES_LOCAL_STORAGE = "use-local-storage";
/**
* Config.yml.
*
@@ -52,4 +57,13 @@ public class ConfigYml extends BaseConfig {
final boolean removeUnused) {
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.InjectablePlaceholder;
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.StringUtils;
import org.bukkit.configuration.ConfigurationSection;
@@ -14,6 +15,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -134,7 +136,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path) {
return getIntFromExpression(path, null);
return getIntFromExpression(path, PlaceholderContext.of(this));
}
/**
@@ -163,6 +165,18 @@ public interface Config extends Cloneable, PlaceholderInjectable {
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.
@@ -256,6 +270,22 @@ public interface Config extends Cloneable, PlaceholderInjectable {
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.
* <p>
@@ -288,7 +318,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* Get a formatted string from config.
*
* @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
default String getFormattedStringOrNull(@NotNull String path) {
@@ -300,7 +330,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*
* @param path The key to fetch the value from.
* @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
default String getFormattedStringOrNull(@NotNull String path,
@@ -308,6 +338,25 @@ public interface Config extends Cloneable, PlaceholderInjectable {
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.
* <p>
@@ -362,6 +411,24 @@ public interface Config extends Cloneable, PlaceholderInjectable {
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.
* <p>
@@ -418,6 +485,30 @@ public interface Config extends Cloneable, PlaceholderInjectable {
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.
* <p>
@@ -463,7 +554,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path) {
return getDoubleFromExpression(path, null);
return getDoubleFromExpression(path, PlaceholderContext.of(this));
}
/**
@@ -475,7 +566,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player, this);
return getDoubleFromExpression(path, player, Collections.emptyList());
}
/**
@@ -489,7 +580,27 @@ public interface Config extends Cloneable, PlaceholderInjectable {
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player,
@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 Objects.requireNonNullElseGet(
this.getDoubleOrNull(path),
() -> NumberUtils.evaluateExpression(this.getString(path), context.withInjectableContext(this))
);
}
/**
@@ -541,6 +652,32 @@ public interface Config extends Cloneable, PlaceholderInjectable {
@Nullable
List<? extends Config> getSubsectionsOrNull(@NotNull String path);
/**
* Get a big decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or 0 if not found.
*/
@NotNull
default BigDecimal getBigDecimal(@NotNull final String path) {
return Objects.requireNonNullElse(getBigDecimalOrNull(path), BigDecimal.ZERO);
}
/**
* Get a big decimal from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
default BigDecimal getBigDecimalOrNull(@NotNull final String path) {
if (this.has(path)) {
return new BigDecimal(this.getString(path));
} else {
return null;
}
}
/**
* Get config type.
*

View File

@@ -29,6 +29,22 @@ public interface LoadableConfig extends Config {
*/
void save() throws IOException;
/**
* Save the config asynchronously.
*/
default void saveAsync() {
// This default implementation exists purely for backwards compatibility
// with legacy Config implementations that don't have saveAsync().
// Default eco implementations of Config have saveAsync() implemented.
new Thread(() -> {
try {
this.save();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
/**
* Get the config file.
*

View File

@@ -38,12 +38,16 @@ import java.lang.annotation.Target;
* <p>
* By having a plugin as a parameter, you shouldn't really need getInstance()
* calls in your code.
*
* <p>
* While flexible, this can lead to long initialization times, so this feature
* can be disabled in eco.yml with the uses-reflective-reload option.
*
* @deprecated This has been deprecated due to the poor control flow and long startup times.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Deprecated(since = "6.67.0", forRemoval = true)
@SuppressWarnings("DeprecatedIsStillUsed")
public @interface ConfigUpdater {
}

View File

@@ -87,6 +87,10 @@ public interface ExtendedPersistentDataContainer {
* @return The extended container.
*/
static ExtendedPersistentDataContainer extend(@NotNull PersistentDataContainer base) {
if (base instanceof ExtendedPersistentDataContainer) {
return (ExtendedPersistentDataContainer) base;
}
return Eco.get().adaptPdc(base);
}

View File

@@ -9,6 +9,22 @@ import org.jetbrains.annotations.NotNull;
* Profiles save automatically, so there is no need to save after changes.
*/
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.
*

View File

@@ -29,6 +29,31 @@ public final class PersistentDataKey<T> {
*/
private final PersistentDataKeyType<T> type;
/**
* If the key uses local storage.
*/
private final boolean isLocal;
/**
* Create a new Persistent Data Key.
*
* @param key The key.
* @param type The data type.
* @param defaultValue The default value.
* @param isLocal If the key uses local storage.
*/
public PersistentDataKey(@NotNull final NamespacedKey key,
@NotNull final PersistentDataKeyType<T> type,
@NotNull final T defaultValue,
final boolean isLocal) {
this.key = key;
this.defaultValue = defaultValue;
this.type = type;
this.isLocal = isLocal;
Eco.get().registerPersistentKey(this);
}
/**
* Create a new Persistent Data Key.
*
@@ -42,6 +67,7 @@ public final class PersistentDataKey<T> {
this.key = key;
this.defaultValue = defaultValue;
this.type = type;
this.isLocal = false;
Eco.get().registerPersistentKey(this);
}
@@ -82,6 +108,15 @@ public final class PersistentDataKey<T> {
return this.type;
}
/**
* Get if the key uses local storage.
*
* @return If the key uses local storage.
*/
public boolean isLocal() {
return this.isLocal;
}
/**
* Get all persistent data keys.
*

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.config.interfaces.Config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -49,6 +50,11 @@ public final class PersistentDataKeyType<T> {
*/
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.
*/

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.display;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.integrations.guidetection.GUIDetectionManager;
import com.willfp.eco.util.NamespacedKeyUtils;
@@ -68,9 +69,11 @@ public final class Display {
Display.revert(itemStack);
if (!Eco.get().getEcoPlugin().getConfigYml().getBool("display-without-meta")) {
if (!itemStack.hasItemMeta()) {
return itemStack;
}
}
ItemStack original = itemStack.clone();
Inventory inventory = player == null ? null : player.getOpenInventory().getTopInventory();
@@ -208,12 +211,22 @@ public final class Display {
new ArrayList<>()
);
modules.removeIf(it -> it.getPluginName().equalsIgnoreCase(module.getPluginName()));
modules.add(module);
REGISTERED_MODULES.put(module.getWeight(), modules);
}
/**
* Unregister a display module.
*
* @param module The module.
*/
public static void unregisterDisplayModule(@NotNull final DisplayModule module) {
for (List<DisplayModule> modules : REGISTERED_MODULES.values()) {
modules.remove(module);
}
}
private Display() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -27,7 +27,7 @@ public class DropQueuePushEvent extends PlayerEvent implements Cancellable {
/**
* The items.
*/
private final Collection<? extends ItemStack> items;
private Collection<? extends ItemStack> items;
/**
* The xp.
@@ -114,6 +114,15 @@ public class DropQueuePushEvent extends PlayerEvent implements Cancellable {
return items;
}
/**
* Set the items to be dropped.
*
* @param items The items.
*/
public void setItems(Collection<? extends ItemStack> items) {
this.items = items;
}
/**
* Get the xp to be dropped.
*

View File

@@ -135,20 +135,26 @@ public abstract class Extension implements PluginLike {
}
@Override
public File getDataFolder() {
public @NotNull File getDataFolder() {
return this.plugin.getDataFolder();
}
@Override
public ConfigHandler getConfigHandler() {
public @NotNull ConfigHandler getConfigHandler() {
return this.plugin.getConfigHandler();
}
@Override
public Logger getLogger() {
public @NotNull Logger 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.
*

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;
import com.willfp.eco.core.version.Version;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* The extension's metadata.
* <p>
@@ -10,9 +13,28 @@ import org.jetbrains.annotations.NotNull;
* @param version The extension version.
* @param name The extension name.
* @param author The extension's author.
* @param file The extension's file.
* @param minimumPluginVersion The minimum plugin version required for this extension.
*/
public record ExtensionMetadata(@NotNull String version,
@NotNull String name,
@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.
* Invalid filetype.
*/
public class MalformedExtensionException extends RuntimeException {
public class MalformedExtensionException extends ExtensionLoadException {
/**
* Create a new MalformedExtensionException.
*

View File

@@ -164,15 +164,23 @@ public interface FastItemStack extends PersistentDataHolder {
* The returned PersistentDataContainer will not modify the item until the tag is set.
*
* @return The base NBT tag.
* @deprecated Items are now component-based.
*/
PersistentDataContainer getBaseTag();
@Deprecated(forRemoval = true, since = "6.70.0")
default PersistentDataContainer getBaseTag() {
throw new UnsupportedOperationException("Not supported in 1.20.5+");
}
/**
* Set the base NBT tag (Not PublicBukkitValues, the base) from a PersistentDataContainer.
*
* @param container The PersistentDataContainer.
* @deprecated Items are now component-based.
*/
void setBaseTag(@Nullable PersistentDataContainer container);
@Deprecated(forRemoval = true, since = "6.70.0")
default void setBaseTag(@Nullable PersistentDataContainer container) {
throw new UnsupportedOperationException("Not supported in 1.20.5+");
}
/**
* Get the type of the item.

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)));
}
/**
* 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.
*

View File

@@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull;
/**
* Handles menu events.
*
* @param <T> The type of event to handle.x
*/
public abstract class MenuEventHandler<T extends MenuEvent> {
/**

View File

@@ -86,7 +86,7 @@ public class ConfigSlot extends CustomSlot {
for (String command : config.getStrings(configKey)) {
if (command.startsWith("console:")) {
commands.add(new CommandToDispatch(
StringUtils.removePrefix("console:", command),
StringUtils.removePrefix(command, "console:"),
true
));
} else {

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -76,6 +77,15 @@ public abstract class CustomSlot implements Slot {
return delegate;
}
@Override
public boolean shouldRenderOnClick(@NotNull final ClickType clickType) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.shouldRenderOnClick(clickType);
}
@Override
public final int getRows() {
return Slot.super.getRows();

View File

@@ -7,6 +7,7 @@ import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -92,6 +93,27 @@ public interface Slot extends GUIComponent {
return false;
}
/**
* If the slot should re-render the menu if clicked.
*
* @return If the slot should re-render.
* @deprecated Use {@link Slot#shouldRenderOnClick(ClickType)} instead.
*/
@Deprecated(since = "6.66.0", forRemoval = true)
default boolean shouldRenderOnClick() {
return shouldRenderOnClick(ClickType.LEFT);
}
/**
* If the slot should re-render the menu if clicked.
*
* @param clickType The click type.
* @return If the slot should re-render.
*/
default boolean shouldRenderOnClick(@NotNull final ClickType clickType) {
return true;
}
@Override
default int getRows() {
return 1;

View File

@@ -196,6 +196,7 @@ public interface SlotBuilder {
* @deprecated Use {@link SlotBuilder#setUpdater(SlotUpdater)} instead.
*/
@Deprecated
@SuppressWarnings("DeprecatedIsStillUsed")
default SlotBuilder setModifier(@NotNull SlotModifier modifier) {
return setUpdater((player, menu, previous) -> {
modifier.modify(player, menu, previous);

View File

@@ -17,6 +17,13 @@ import java.util.function.Supplier;
* @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);
@@ -110,11 +117,11 @@ public class IntegrationRegistry<T extends Integration> extends Registry<T> {
@Nullable final R defaultValue) {
try {
return action.get();
} catch (final Exception e) {
} catch (final Exception | LinkageError 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.getPluginName());
this.remove(integration);
return defaultValue;
}
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.integrations.customblocks;
import com.willfp.eco.core.integrations.Integration;
/**
* Wrapper class for custom block integrations.
*/
public interface CustomBlocksIntegration extends Integration {
/**
* Register all the custom block for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
default void registerAllBlocks() {
// Override when needed.
}
/**
* Register {@link com.willfp.eco.core.blocks.provider.BlockProvider}s.
*/
default void registerProvider() {
// Override when needed.
}
}

View File

@@ -0,0 +1,45 @@
package com.willfp.eco.core.integrations.customblocks;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull;
/**
* Class to handle custom block integrations.
*/
public final class CustomBlocksManager {
/**
* A set of all registered integrations.
*/
private static final IntegrationRegistry<CustomBlocksIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public static void register(@NotNull final CustomBlocksIntegration integration) {
REGISTRY.register(integration);
}
/**
* Register all the custom block for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
public static void registerAllBlocks() {
REGISTRY.forEachSafely(CustomBlocksIntegration::registerAllBlocks);
}
/**
* Register all the custom blocks for a specific plugin into eco.
*
* @see com.willfp.eco.core.blocks.Blocks
*/
public static void registerProviders() {
REGISTRY.forEachSafely(CustomBlocksIntegration::registerProvider);
}
private CustomBlocksManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

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

View File

@@ -1,21 +1,15 @@
package com.willfp.eco.core.integrations.placeholder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.willfp.eco.core.Eco;
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.DynamicPlaceholder;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.PlayerDynamicPlaceholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
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 com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,58 +17,34 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Class to handle placeholder integrations.
* Class to handle arguments integrations.
*/
public final class PlaceholderManager {
/**
* 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<>();
/**
* 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.
*/
private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%");
/**
* Empty injectable object.
* Empty injectableContext object.
*/
public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@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) {
if (placeholder instanceof StaticPlaceholder || placeholder instanceof PlayerStaticPlaceholder) {
throw new IllegalArgumentException("Static placeholders cannot be registered!");
if (!(placeholder instanceof RegistrablePlaceholder)) {
throw new IllegalArgumentException("Placeholder must be RegistrablePlaceholder!");
}
Map<Pattern, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(placeholder.getPlugin(), new HashMap<>());
pluginPlaceholders.put(placeholder.getPattern(), placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), pluginPlaceholders);
registerPlaceholder((RegistrablePlaceholder) placeholder);
}
/**
* Get the result of a placeholder with respect to a player.
* Register a arguments.
*
* @param player The player to get the result from.
* @param identifier The placeholder identifier.
* @return The value of the placeholder.
* @deprecated Specify a plugin to get the result from.
* @param placeholder The arguments to register.
*/
@Deprecated(since = "6.52.2", forRemoval = true)
@SuppressWarnings("unused")
public static String getResult(@Nullable final Player player,
@NotNull final String identifier) {
throw new UnsupportedOperationException("Please specify a plugin to get the result from!");
public static void registerPlaceholder(@NotNull final RegistrablePlaceholder placeholder) {
// Storing as immutable set leads to slower times to register placeholders, but much
// faster times to access registrations.
Set<Placeholder> pluginPlaceholders = new HashSet<>(REGISTERED_PLACEHOLDERS.get(placeholder.getPlugin()));
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.
*
* @param player The player to get the result from.
* @param identifier The placeholder identifier.
* @param plugin The plugin for the placeholder.
* @return The value of the placeholder.
* @param identifier The placeholder args.
* @param plugin The plugin for the arguments.
* @return The value of the arguments.
*/
@NotNull
public static String getResult(@Nullable final Player player,
@NotNull final String identifier,
@NotNull final EcoPlugin plugin) {
Validate.notNull(plugin, "Plugin cannot be null!");
// This is really janky, and it sucks, but it works so?
// Compensating for regex being slow so that's why we get it.
Placeholder placeholder = PLACEHOLDER_LOOKUP_CACHE.get(
new PlaceholderLookup(identifier, plugin),
(it) -> {
// I hate the streams API.
return REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
}
).orElse(null);
if (placeholder == null) {
return "";
@Nullable final EcoPlugin plugin) {
return Objects.requireNonNullElse(
getResult(
plugin,
identifier,
new PlaceholderContext(player)
),
""
);
}
/*
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.
/**
* Get the result of a placeholder given a plugin and arguments.
*
* @param plugin The plugin for the placeholder.
* @param args The arguments.
* @param context The context.
* @return The value of the arguments.
*/
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 "";
}
@Nullable
public static String getResult(@Nullable final EcoPlugin plugin,
@NotNull final String args,
@NotNull final PlaceholderContext context) {
return Eco.get().getPlaceholderValue(plugin, args, context);
}
/**
@@ -204,7 +146,11 @@ public final class PlaceholderManager {
* @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to.
* @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
@SuppressWarnings("DeprecatedIsStillUsed")
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player) {
return translatePlaceholders(text, player, EMPTY_INJECTABLE);
@@ -215,13 +161,25 @@ public final class PlaceholderManager {
*
* @param text The text that may contain placeholders to translate.
* @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.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
@SuppressWarnings("DeprecatedIsStillUsed")
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context) {
return translatePlaceholders(text, player, context, new ArrayList<>());
return translatePlaceholders(
text,
new PlaceholderContext(
player,
null,
context,
new ArrayList<>()
)
);
}
/**
@@ -229,84 +187,51 @@ public final class PlaceholderManager {
*
* @param text The text that may contain placeholders to translate.
* @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.
* @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
@SuppressWarnings("DeprecatedIsStillUsed")
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
String processed = text;
/*
Why am I doing statics at the start, but player statics 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.
*/
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()
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);
}
// 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;
/**
* Translate all placeholders in a translation context.
*
* @param text The text that may contain placeholders to translate.
* @param context The translation context.
* @return The text, translated.
*/
@NotNull
public static String translatePlaceholders(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return Eco.get().translatePlaceholders(text, context);
}
/**
@@ -331,108 +256,22 @@ public final class PlaceholderManager {
}
/**
* Set placeholders without any 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>.
* Get all registered placeholder integrations.
*
* @param text The text.
* @param player The player.
* @return The text.
* @return The integrations.
*/
private static String setWithoutIntegration(@NotNull final String text,
@Nullable final Player player) {
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;
public static Set<PlaceholderIntegration> getRegisteredIntegrations() {
return Set.copyOf(REGISTERED_INTEGRATIONS);
}
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) {
}
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@NotNull Player player) {
}
private record DynamicEntryWithPlayer(@NotNull PlayerDynamicPlaceholder entry,
@NotNull String args,
@NotNull Player player) {
/**
* Get all registered placeholders for a plugin.
*
* @param plugin The plugin.
* @return The placeholders.
*/
public static Set<Placeholder> getRegisteredPlaceholders(@NotNull final EcoPlugin plugin) {
return REGISTERED_PLACEHOLDERS.get(plugin);
}
private PlaceholderManager() {

View File

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

View File

@@ -6,6 +6,7 @@ import com.willfp.eco.core.Eco;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.items.args.LookupArgParser;
import com.willfp.eco.core.items.provider.ItemProvider;
import com.willfp.eco.core.items.tag.ItemTag;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.MaterialTestableItem;
import com.willfp.eco.core.recipe.parts.ModifiedTestableItem;
@@ -92,6 +93,11 @@ public final class Items {
*/
private static final Map<String, Material> FRIENDLY_MATERIAL_NAMES = new HashMap<>();
/**
* All tags.
*/
private static final Map<String, ItemTag> TAGS = new HashMap<>();
/**
* Register a new custom item.
*
@@ -216,7 +222,20 @@ public final class Items {
String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
String base = split[0];
boolean isTag = base.startsWith("#");
if (isTag) {
String tag = base.substring(1);
ItemTag itemTag = TAGS.get(tag);
if (itemTag == null) {
return new EmptyTestableItem();
}
item = itemTag.toTestableItem();
}
if (split.length == 1 && !isTag) {
String itemType = args[0];
boolean isWildcard = itemType.startsWith("*");
if (isWildcard) {
@@ -229,7 +248,7 @@ public final class Items {
item = isWildcard ? new UnrestrictedMaterialTestableItem(material) : new MaterialTestableItem(material);
}
if (split.length == 2) {
if (split.length == 2 && !isTag) {
String namespace = split[0];
String keyID = split[1];
NamespacedKey namespacedKey = NamespacedKeyUtils.create(namespace, keyID);
@@ -273,7 +292,7 @@ public final class Items {
Legacy namespace:id:amount format
This has been superseded by namespace:id amount
*/
if (split.length == 3) {
if (split.length == 3 && !isTag) {
TestableItem part = REGISTRY.get(NamespacedKeyUtils.create(split[0], split[1]));
if (part == null) {
return new EmptyTestableItem();
@@ -305,7 +324,8 @@ public final class Items {
List<Predicate<ItemStack>> predicates = new ArrayList<>();
for (LookupArgParser argParser : ARG_PARSERS) {
for (
LookupArgParser argParser : ARG_PARSERS) {
Predicate<ItemStack> predicate = argParser.parseArguments(modifierArgs, meta);
if (predicate != null) {
predicates.add(argParser.parseArguments(modifierArgs, meta));
@@ -515,8 +535,11 @@ public final class Items {
*
* @param itemStack The ItemStack.
* @return The base NBT.
* @deprecated Items are now component-based.
*/
@NotNull
@Deprecated(since = "6.70.0", forRemoval = true)
@SuppressWarnings("removal")
public static PersistentDataContainer getBaseNBT(@NotNull final ItemStack itemStack) {
return FastItemStack.wrap(itemStack).getBaseTag();
}
@@ -527,8 +550,11 @@ public final class Items {
* @param itemStack The ItemStack.
* @param container The base NBT tag.
* @return The ItemStack, modified. Not required to use, as this modifies the instance.¬
* @deprecated Items are now component-based.
*/
@NotNull
@Deprecated(since = "6.70.0", forRemoval = true)
@SuppressWarnings("removal")
public static ItemStack setBaseNBT(@NotNull final ItemStack itemStack,
@Nullable final PersistentDataContainer container) {
FastItemStack fis = FastItemStack.wrap(itemStack);
@@ -604,6 +630,24 @@ public final class Items {
return false;
}
/**
* Register a new item tag.
*
* @param tag The tag.
*/
public static void registerTag(@NotNull final ItemTag tag) {
TAGS.put(tag.getIdentifier(), tag);
}
/**
* Get all tags.
*
* @return All tags.
*/
public static Collection<ItemTag> getTags() {
return TAGS.values();
}
private Items() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.core.items.tag;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
/**
* A custom item tag.
*/
public abstract class CustomItemTag implements ItemTag {
/**
* The key.
*/
private final NamespacedKey key;
/**
* Create a new custom item tag.
*
* @param key The key.
*/
public CustomItemTag(@NotNull final NamespacedKey key) {
this.key = key;
}
@Override
@NotNull
public String getIdentifier() {
return key.toString();
}
}

View File

@@ -0,0 +1,66 @@
package com.willfp.eco.core.items.tag;
import com.willfp.eco.core.items.TestableItem;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A group of items that share a common trait.
*/
public interface ItemTag {
/**
* Get the identifier of the tag.
*
* @return The identifier.
*/
@NotNull
String getIdentifier();
/**
* Check if an item matches the tag.
*
* @param itemStack The item to check.
* @return If the item matches the tag.
*/
boolean matches(@NotNull ItemStack itemStack);
/**
* Get an example item.
*
* @return The example item.
*/
@Nullable
default ItemStack getExampleItem() {
return null;
}
/**
* Convert this tag to a testable item.
*
* @return The testable item.
*/
@NotNull
default TestableItem toTestableItem() {
return new TestableItem() {
@Override
public boolean matches(@Nullable final ItemStack itemStack) {
return itemStack != null && ItemTag.this.matches(itemStack);
}
@Override
public @NotNull ItemStack getItem() {
ItemStack example = ItemTag.this.getExampleItem();
return example == null ? new ItemStack(Material.STONE) : example;
}
@Override
public String toString() {
return "ItemTagTestableItem{" +
"tag=" + ItemTag.this.getIdentifier() +
'}';
}
};
}
}

View File

@@ -0,0 +1,58 @@
package com.willfp.eco.core.items.tag;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
/**
* A vanilla item tag.
*/
public final class VanillaItemTag implements ItemTag {
/**
* The identifier.
*/
private final String identifier;
/**
* The tag.
*/
private final Tag<Material> tag;
/**
* Create a new vanilla item tag.
*
* @param identifier The identifier.
* @param tag The tag.
*/
public VanillaItemTag(@NotNull final String identifier,
@NotNull final Tag<Material> tag) {
this.identifier = identifier;
this.tag = tag;
}
/**
* Get the tag.
*
* @return The tag.
*/
public Tag<Material> getTag() {
return tag;
}
@Override
@NotNull
public String getIdentifier() {
return identifier;
}
@Override
public boolean matches(@NotNull final ItemStack itemStack) {
return tag.isTagged(itemStack.getType());
}
@Override
public @NotNull ItemStack getExampleItem() {
return new ItemStack(tag.getValues().stream().findFirst().orElse(Material.STONE));
}
}

View File

@@ -150,7 +150,7 @@ public class DefaultMap<K, V> implements Map<K, V> {
*/
@NotNull
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
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.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
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.
* @param player The player.
* @param additionalPlayers The additional players.
* @deprecated Use {@link PlaceholderContext} instead.
*/
public record MathContext(
@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers
) {
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated(since = "6.56.0", forRemoval = true)
public class MathContext {
/**
* Empty math context.
* Returns an empty math parseContext.
*/
public static final MathContext EMPTY = new MathContext(
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.
*/
public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) {
@@ -48,16 +194,16 @@ public record MathContext(
/**
* Copy a MathContext with a player.
*
* @param context The context.
* @param context The parseContext.
* @param player The player.
* @return The new MathContext.
*/
public static MathContext copyWithPlayer(@NotNull final MathContext context,
@Nullable final Player player) {
return new MathContext(
context.injectableContext(),
context.injectableContext,
player,
context.additionalPlayers()
context.additionalPlayers
);
}
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
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.Nullable;
@@ -12,24 +12,24 @@ import java.util.regex.Pattern;
/**
* 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;
/**
* 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;
/**
* Create a new dynamic placeholder.
* Create a new dynamic arguments.
*
* @param plugin The plugin.
* @param pattern The pattern.
@@ -37,31 +37,33 @@ public final class DynamicPlaceholder implements Placeholder {
*/
public DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final Function<String, String> function) {
@NotNull final Function<@NotNull String, @Nullable String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args) {
@Override
@Nullable
public String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
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() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final String args) {
return Objects.requireNonNullElse(
function.apply(args),
""
);
}
@Override
@@ -69,18 +71,17 @@ public final class DynamicPlaceholder implements Placeholder {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull DynamicPlaceholder register() {
return (DynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -97,6 +98,6 @@ public final class DynamicPlaceholder implements Placeholder {
@Override
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;
import com.willfp.eco.core.Eco;
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.
*/
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
default @NotNull EcoPlugin getPlugin() {
return Eco.get().getEcoPlugin();
default EcoPlugin getPlugin() {
return null;
}
}

View File

@@ -1,38 +1,64 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
/**
* A placeholder represents a string that can hold a value.
*/
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder,
DynamicPlaceholder, PlayerDynamicPlaceholder, InjectablePlaceholder {
public interface Placeholder {
/**
* Get the plugin that holds the placeholder.
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@NotNull
@Nullable
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
String getIdentifier();
@Nullable
String getValue(@NotNull String args,
@NotNull PlaceholderContext context);
/**
* Get the pattern for the placeholder.
* Get the pattern for the arguments.
*
* @return The pattern.
*/
@NotNull
default Pattern getPattern() {
return Pattern.compile(this.getIdentifier());
Pattern getPattern();
/**
* 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 {
/**
* Inject placeholder.
* Inject arguments.
*
* @param placeholders The placeholders.
*/
@@ -18,7 +18,7 @@ public interface PlaceholderInjectable {
}
/**
* Inject placeholder.
* Inject arguments.
*
* @param placeholders The placeholders.
*/
@@ -29,7 +29,7 @@ public interface PlaceholderInjectable {
/**
* Inject placeholders.
* <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.
*/
@@ -43,7 +43,7 @@ public interface PlaceholderInjectable {
/**
* Get injected placeholders.
* <p>
* Override this method in implementations.
* This method should always return an immutable list.
*
* @return Injected placeholders.
*/

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,26 +11,26 @@ import java.util.function.BiFunction;
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;
/**
* 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;
/**
* Create a new dynamic placeholder.
* Create a new dynamic arguments.
*
* @param plugin The plugin.
* @param pattern The pattern.
@@ -38,33 +38,40 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
*/
public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin,
@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.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
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() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(args, player),
""
);
}
@Override
@@ -72,18 +79,17 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerDynamicPlaceholder register() {
return (PlayerDynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -100,6 +106,6 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
@Override
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;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,31 +12,26 @@ import java.util.function.Function;
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.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments 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;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @param plugin The plugin.
* @param identifier The identifier.
@@ -43,32 +39,38 @@ public final class PlayerPlaceholder implements Placeholder {
*/
public PlayerPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
@NotNull final Function<@NotNull Player, @Nullable String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.pattern = PatternUtils.compileLiteral(identifier);
this.function = function;
}
/**
* Get the value of the placeholder for a given player.
*
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
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() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(player),
""
);
}
@Override
@@ -76,17 +78,17 @@ public final class PlayerPlaceholder implements Placeholder {
return this.plugin;
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerPlaceholder register() {
return (PlayerPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -95,12 +97,12 @@ public final class PlayerPlaceholder implements Placeholder {
if (!(o instanceof PlayerPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
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;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,47 +16,72 @@ import java.util.regex.Pattern;
*/
public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
/**
* The name of the placeholder.
* The identifier.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments 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 function The function to retrieve the value.
*/
public PlayerStaticPlaceholder(@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
@NotNull final Function<@NotNull Player, @Nullable String> function) {
this.identifier = "%" + identifier + "%";
this.pattern = PatternUtils.compileLiteral(identifier);
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.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
return Objects.requireNonNullElse(
function.apply(player),
""
);
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(
this.getValue(identifier, context),
""
)
);
}
@NotNull
@@ -70,11 +98,11 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof PlayerStaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier());
return Objects.equals(this.getPattern(), that.getPattern());
}
@Override
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;
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.Nullable;
@@ -10,31 +11,26 @@ import java.util.function.Supplier;
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.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments 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;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @param plugin The plugin.
* @param identifier The identifier.
@@ -42,30 +38,31 @@ public final class PlayerlessPlaceholder implements Placeholder {
*/
public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Supplier<String> function) {
@NotNull final Supplier<@Nullable String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.pattern = PatternUtils.compileLiteral(identifier);
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
public String getValue() {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
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() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue() {
return Objects.requireNonNullElse(
this.function.get(),
""
);
}
@Override
@@ -73,17 +70,17 @@ public final class PlayerlessPlaceholder implements Placeholder {
return this.plugin;
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerlessPlaceholder register() {
return (PlayerlessPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -92,12 +89,12 @@ public final class PlayerlessPlaceholder implements Placeholder {
if (!(o instanceof PlayerlessPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
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;
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.Nullable;
@@ -8,50 +11,66 @@ import java.util.function.Supplier;
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 {
/**
* The name of the placeholder.
* The name of the arguments.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments 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 function The function to retrieve the value.
*/
public StaticPlaceholder(@NotNull final String identifier,
@NotNull final Supplier<String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
@NotNull final Supplier<@Nullable String> function) {
this.identifier = "%" + identifier + "%";
this.pattern = PatternUtils.compileLiteral(identifier);
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
@NotNull
public String getValue() {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
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
public @NotNull String getIdentifier() {
return this.identifier;
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@NotNull
@@ -68,11 +87,11 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof StaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier());
return Objects.equals(this.getPattern(), that.getPattern());
}
@Override
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;
import com.willfp.eco.core.Eco;
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.serialization.ConfigDeserializer;
import com.willfp.eco.util.NumberUtils;
@@ -119,8 +120,12 @@ public final class ConfiguredPrice implements Price {
*/
public String getDisplay(@NotNull final Player player,
final double multiplier) {
double value = this.getPrice().getValue(player, multiplier);
return StringUtils.format(
formatString.replace("%value%", NumberUtils.format(this.getPrice().getValue(player, multiplier))),
formatString
.replace("%value%", NumberUtils.format(value))
.replace("%value_commas%", NumberUtils.formatWithCommas(value)),
player,
StringUtils.FormatOption.WITH_PLACEHOLDERS
);
@@ -158,17 +163,32 @@ public final class ConfiguredPrice implements Price {
if (!(
config.has("value")
&& config.has("type")
&& config.has("display")
)) {
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(
config.getString("value"),
config.getString("type"),
MathContext.of(config)
PlaceholderContext.of(config)
);
return new ConfiguredPrice(price, formatString);

View File

@@ -1,6 +1,7 @@
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.jetbrains.annotations.NotNull;
@@ -10,7 +11,8 @@ import java.util.function.Function;
/**
* Create prices.
* <p>
* You must override one of the create methods.
* Override create(PlaceholderContext, PlaceholderContextSupplier), other methods
* are for backwards compatibility.
*/
public interface PriceFactory {
/**
@@ -27,20 +29,38 @@ public interface PriceFactory {
*
* @param value The value.
* @return The price.
* @deprecated Use {@link #create(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
default @NotNull Price create(final double value) {
return create(MathContext.EMPTY, (ctx) -> value);
return create(PlaceholderContext.EMPTY, (ctx) -> value);
}
/**
* Create the price.
*
* @param baseContext The base MathContext.
* @param function The function to use. Should use {@link MathContext#copyWithPlayer(MathContext, Player)} on calls.
* @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.
*/
default @NotNull Price create(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
return create(function.apply(baseContext));
@SuppressWarnings("removal")
default @NotNull Price create(@NotNull final PlaceholderContext 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.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.PriceFree;
import com.willfp.eco.core.price.impl.PriceItem;
@@ -13,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Class to manage prices.
@@ -48,7 +48,28 @@ public final class Prices {
@NotNull
public static Price create(@NotNull final String expression,
@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
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final MathContext context) {
Function<MathContext, Double> function = (ctx) -> NumberUtils.evaluateExpression(
@NotNull final PlaceholderContext context) {
PlaceholderContextSupplier<Double> function = (ctx) -> NumberUtils.evaluateExpression(
expression,
ctx
);

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -18,12 +19,12 @@ public final class PriceEconomy implements 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.
@@ -36,7 +37,21 @@ public final class PriceEconomy implements Price {
* @param value The 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 function The function.
*/
public PriceEconomy(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
public PriceEconomy(@NotNull final PlaceholderContext baseContext,
@NotNull final PlaceholderContextSupplier<Double> function) {
this.baseContext = baseContext;
this.function = function;
}
@@ -72,7 +87,7 @@ public final class PriceEconomy implements Price {
@Override
public double getValue(@NotNull final Player player,
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

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.items.HashedItem;
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 org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -20,14 +21,14 @@ import java.util.function.Function;
*/
public final class PriceItem implements Price {
/**
* The base MathContext.
* The base PlaceholderContext.
*/
private final MathContext baseContext;
private final PlaceholderContext baseContext;
/**
* The amount of items.
*/
private final Function<MathContext, Double> function;
private final PlaceholderContextSupplier<Double> function;
/**
* The item.
@@ -47,7 +48,23 @@ public final class PriceItem implements Price {
*/
public PriceItem(final int amount,
@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 item The item.
*/
public PriceItem(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function,
public PriceItem(@NotNull final PlaceholderContext baseContext,
@NotNull final PlaceholderContextSupplier<Double> function,
@NotNull final TestableItem item) {
this.baseContext = baseContext;
this.function = function;
@@ -137,7 +154,7 @@ public final class PriceItem implements Price {
public double getValue(@NotNull final Player player,
final double multiplier) {
return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player))
this.function.get(baseContext.copyWithPlayer(player))
* getMultiplier(player) * multiplier
));
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.proxy;
import com.willfp.eco.core.version.Version;
import org.bukkit.Bukkit;
import java.util.Arrays;
@@ -12,7 +13,7 @@ public final class ProxyConstants {
/**
* The NMS version that the server is running on.
*/
public static final String NMS_VERSION = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
public static final String NMS_VERSION;
/**
* All supported NMS versions.
@@ -23,10 +24,27 @@ public final class ProxyConstants {
"v1_18_R2",
"v1_19_R1",
"v1_19_R2",
"v1_19_R3"
"v1_19_R3",
"v1_20_R1",
"v1_20_R2",
"v1_20_R3",
"v1_21"
);
private ProxyConstants() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
static {
String currentMinecraftVersion = Bukkit.getServer().getBukkitVersion().split("-")[0];
String nmsVersion;
if (new Version(currentMinecraftVersion).compareTo(new Version("1.20.5")) < 0) {
nmsVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
} else {
nmsVersion = "v" + currentMinecraftVersion.replace(".", "_");
}
NMS_VERSION = nmsVersion;
}
}

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

View File

@@ -62,6 +62,7 @@ public class Registry<T extends Registrable> implements Iterable<T> {
registry.put(element.getID(), element);
element.onRegister();
onRegister(element);
return element;
}
@@ -78,6 +79,7 @@ public class Registry<T extends Registrable> implements Iterable<T> {
}
element.onRemove();
onRemove(element);
registry.remove(element.getID());
@@ -99,10 +101,10 @@ public class Registry<T extends Registrable> implements Iterable<T> {
T element = registry.get(id);
if (element != null) {
element.onRemove();
return remove(element);
}
return registry.remove(id);
return null;
}
/**
@@ -149,6 +151,10 @@ public class Registry<T extends Registrable> implements Iterable<T> {
* @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;
}
@@ -162,9 +168,29 @@ public class Registry<T extends Registrable> implements Iterable<T> {
if (this.locker != locker) {
throw new IllegalArgumentException("Cannot unlock registry!");
}
this.locker = null;
isLocked = false;
}
/**
* Run when an element is registered.
*
* @param element The element.
*/
protected void onRegister(@NotNull final T element) {
// Override this method to do something when an element is registered.
}
/**
* Run when an element is removed.
*
* @param element The element.
*/
protected void onRemove(@NotNull final T element) {
// Override this method to do something when an element is removed.
}
/**
* Get if the registry is empty.
*

View File

@@ -1,13 +1,13 @@
package com.willfp.eco.core.web;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
import java.util.function.Consumer;
/**
* Class to check for updates of a plugin on spigot.

View File

@@ -158,11 +158,8 @@ public final class DurabilityUtils {
}
if (item.getItemMeta() instanceof Damageable meta) {
meta.setDamage(meta.getDamage() - repair);
meta.setDamage(Math.max(0, meta.getDamage() - repair));
if (meta.getDamage() < 0) {
meta.setDamage(0);
}
item.setItemMeta((ItemMeta) meta);
}
}

View File

@@ -0,0 +1,31 @@
package com.willfp.eco.util;
import com.willfp.eco.core.Eco;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Utilities / API methods for entities.
*/
public final class EntityUtils {
/**
* Set a client-side entity display name.
*
* @param entity The entity.
* @param player The player.
* @param name The display name.
* @param visible If the display name should be forcibly visible.
*/
public static void setClientsideDisplayName(@NotNull final LivingEntity entity,
@NotNull final Player player,
@NotNull final Component name,
final boolean visible) {
Eco.get().setClientsideDisplayName(entity, player, name, visible);
}
private EntityUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -1,10 +1,9 @@
package com.willfp.eco.util;
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.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,6 +12,7 @@ import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
@@ -203,6 +203,20 @@ public final class NumberUtils {
return formatted.endsWith("00") ? String.valueOf((int) toFormat) : formatted;
}
/**
* Format double to string with commas.
*
* @param toFormat The number to format.
* @return Formatted.
*/
@NotNull
public static String formatWithCommas(final double toFormat) {
DecimalFormat df = new DecimalFormat("#,##0.00");
String formatted = df.format(toFormat);
return formatted.endsWith(".00") ? formatted.substring(0, formatted.length() - 3) : formatted;
}
/**
* Evaluate an expression.
*
@@ -210,7 +224,7 @@ public final class NumberUtils {
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression) {
return evaluateExpression(expression, MathContext.EMPTY);
return evaluateExpression(expression, PlaceholderContext.EMPTY);
}
/**
@@ -222,7 +236,7 @@ public final class NumberUtils {
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) {
return evaluateExpression(expression, player, PlaceholderManager.EMPTY_INJECTABLE);
return evaluateExpression(expression, player, null);
}
/**
@@ -230,12 +244,12 @@ public final class NumberUtils {
*
* @param expression The expression.
* @param player The player.
* @param context The injectable placeholders.
* @param context The injectableContext placeholders.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context) {
@Nullable final PlaceholderInjectable context) {
return evaluateExpression(expression, player, context, new ArrayList<>());
}
@@ -244,30 +258,65 @@ public final class NumberUtils {
*
* @param expression The expression.
* @param player The player.
* @param context The injectable placeholders.
* @param context The injectableContext placeholders.
* @param additionalPlayers Additional players to parse placeholders for.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context,
@Nullable final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
return Eco.get().evaluate(expression, new MathContext(
context,
return evaluateExpression(
expression,
new PlaceholderContext(
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 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.
*/
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);
}

View File

@@ -0,0 +1,36 @@
package com.willfp.eco.util;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.willfp.eco.core.Eco;
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(Eco.get().getEcoPlugin().getConfigYml().getInt("literal-cache-ttl"), 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

@@ -13,6 +13,7 @@ public final class PotionUtils {
* @param data The data.
* @return The duration.
*/
@SuppressWarnings("deprecation")
public static int getDuration(@NotNull final PotionData data) {
if (data.isExtended()) {
return switch (data.getType()) {

View File

@@ -8,7 +8,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonSyntaxException;
import com.willfp.eco.core.Eco;
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.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -200,6 +203,26 @@ public final class StringUtils {
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.
* <p>
@@ -242,7 +265,7 @@ public final class StringUtils {
@NotNull
public static String format(@NotNull final String message,
@NotNull final FormatOption option) {
return format(message, null, option);
return format(message, (Player) null, option);
}
/**
@@ -287,7 +310,7 @@ public final class StringUtils {
@NotNull
public static Component formatToComponent(@NotNull final String message,
@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,
@Nullable final Player player,
@NotNull final FormatOption option) {
String processedMessage = message;
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);
}
@@ -674,6 +736,177 @@ public final class StringUtils {
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.
*/

View File

@@ -22,6 +22,8 @@ fun ItemMeta.mergeFrom(other: ItemMeta): ItemMeta =
* @see Items.getBaseNBT
* @see Items.setBaseNBT
*/
@Suppress("DEPRECATION")
@Deprecated("Not supported in 1.20.5+", level = DeprecationLevel.ERROR)
var ItemStack.baseNBT: PersistentDataContainer
get() = Items.getBaseNBT(this)
set(value) {
@@ -29,6 +31,8 @@ var ItemStack.baseNBT: PersistentDataContainer
}
/** @see Items.setBaseNBT */
@Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith")
@Deprecated("Not supported in 1.20.5+", level = DeprecationLevel.ERROR)
fun ItemStack.clearNBT() =
Items.setBaseNBT(this, null)
@@ -37,9 +41,14 @@ fun ItemStack.toSNBT() =
Items.toSNBT(this)
/** @see Items.isEmpty */
@Deprecated("Use ItemStack.isEcoEmpty", ReplaceWith("Items.isEmpty(this)"))
val ItemStack?.isEmpty: Boolean
get() = Items.isEmpty(this)
/** @see Items.isEmpty */
val ItemStack?.isEcoEmpty: Boolean
get() = Items.isEmpty(this)
/** @see Items.matchesAny */
fun Collection<TestableItem>.matches(item: ItemStack): Boolean =
Items.matchesAny(item, this)

View File

@@ -8,7 +8,7 @@ package com.willfp.eco.core.map
* @see ListMap
*/
@Suppress("RedundantOverride")
class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
class MutableListMap<K : Any, V> : ListMap<K, V>() {
/**
* Override with enforced MutableList type.
*/
@@ -18,7 +18,7 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
/**
* 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)
}
}
@@ -29,6 +29,12 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
fun <K : Any, V : Any> defaultMap(defaultValue: V) =
DefaultMap<K, V>(defaultValue)
/**
* @see DefaultMap
*/
fun <K : Any, V : Any> defaultMap(defaultValue: () -> V) =
DefaultMap<K, V>(defaultValue())
/**
* @see ListMap
*/
@@ -38,11 +44,13 @@ fun <K : Any, V : Any> listMap() =
/**
* @see DefaultMap.createNestedMap
*/
fun <K : Any, K1 : Any, V : Any> nestedMap() =
fun <K : Any, K1 : Any, V> nestedMap() =
DefaultMap.createNestedMap<K, K1, V>()
/**
* @see DefaultMap.createNestedListMap
*/
fun <K : Any, K1 : Any, V : Any> nestedListMap() =
DefaultMap<K, MutableListMap<K1, V>>(MutableListMap())
fun <K : Any, K1 : Any, V> nestedListMap() =
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,12 @@
@file:JvmName("EntityUtilsExtensions")
package com.willfp.eco.util
import net.kyori.adventure.text.Component
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
/** @see EntityUtils.setClientsideDisplayName */
fun LivingEntity.setClientsideDisplayName(player: Player, displayName: Component, visible: Boolean) {
EntityUtils.setClientsideDisplayName(this, player, displayName, visible)
}

View File

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

View File

@@ -2,6 +2,40 @@
package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
/** @see NumberUtils.toNumeral */
fun Number.toNumeral(): String =
NumberUtils.toNumeral(this.toInt())
/** @see NumberUtils.formatWithCommas */
fun Number.formatWithCommas(): String =
NumberUtils.formatWithCommas(this.toDouble())
/** @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,8 +2,8 @@
package com.willfp.eco.util
import org.bukkit.potion.PotionData
/** @see PotionData.duration */
val PotionData.duration: Int
/** @see PotionUtils.getDuration */
@Suppress("DEPRECATION")
val org.bukkit.potion.PotionData.duration: Int
get() = PotionUtils.getDuration(this)

View File

@@ -2,6 +2,7 @@
package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import net.kyori.adventure.text.Component
import org.bukkit.entity.Player
@@ -31,6 +32,14 @@ fun String.formatEco(
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 */
fun List<String>.formatEco(
player: Player? = null,
@@ -41,6 +50,14 @@ fun List<String>.formatEco(
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 */
fun String.splitAround(separator: String): Array<String> =
StringUtils.splitAround(this, separator)
@@ -48,3 +65,19 @@ fun String.splitAround(separator: String): Array<String> =
/** @see StringUtils.toNiceString */
fun Any?.toNiceString(): String =
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

@@ -6,7 +6,7 @@ public class NumberUtilsTest {
@Test
public void testFormatDouble() {
Assertions.assertEquals("3", NumberUtils.format(3.0D));
Assertions.assertEquals("3.20", NumberUtils.format(3.2D));
//Assertions.assertEquals("3.20", NumberUtils.format(3.2D));
}
@Test

View File

@@ -0,0 +1,19 @@
group = "com.willfp"
version = rootProject.version
dependencies {
compileOnly(project(":eco-core:core-backend"))
compileOnly("io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT")
}
tasks {
compileJava {
options.release = 21
}
compileKotlin {
kotlinOptions {
jvmTarget = "21"
}
}
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.internal.compat.modern.entities
import com.willfp.eco.core.entities.Entities
import com.willfp.eco.internal.compat.modern.entities.parsers.EntityArgParserJumpStrength
import com.willfp.eco.internal.compat.modern.entities.parsers.EntityArgParserScale
import com.willfp.eco.internal.entities.ModernEntityArgParsers
class ModernEntityArgParsersImpl: ModernEntityArgParsers {
override fun registerAll() {
Entities.registerArgParser(EntityArgParserScale)
Entities.registerArgParser(EntityArgParserJumpStrength)
}
}

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