Compare commits

...

110 commits

Author SHA1 Message Date
8447233b1e
Merge remote-tracking branch 'origin/v1.x.x' into v1.x.x 2026-06-22 03:12:11 +02:00
eb2e7b3abb
allow enchanted book for super enchant 2026-06-22 03:12:04 +02:00
7f7f049b7b
Update COMPATIBILITY.md 2026-06-22 01:01:45 +02:00
37e8ca7da9
update excellentenchants 2026-06-20 23:49:03 +02:00
95d3cf3228
update nightexpress 2026-06-20 23:43:01 +02:00
178b372255
paper mns use getter/setter no property access [ci skip] 2026-06-20 14:57:12 +02:00
106cd53b02
mark 26.2.x as supported on modrinth & hangar [ci skip] 2026-06-20 03:28:07 +02:00
950bad2168
versions bump 2026-06-19 20:31:43 +02:00
bc9cbe0b44
fix and simplify xp handling
- Fix xp limit not being respected
- Fix player xp not being check causing error
2026-06-19 20:29:10 +02:00
9d616d2fd0
remove use of legacy currentMinecraftVersionArray 2026-06-17 02:15:34 +02:00
f82ccfa07e
make Version work with experimental 26.1 paper build 2026-06-17 02:15:04 +02:00
cff94a2c5a
better thanks and put names in compatibility note [ci skip] 2026-06-16 04:05:17 +02:00
4b5133c872
markdown issue² 2026-06-16 03:49:32 +02:00
98d359f59f
markdown issue [skip ci] 2026-06-16 03:49:16 +02:00
96754fd260
better readme [skip ci] 2026-06-16 03:48:47 +02:00
b92b762551
overdid \ [skip ci] 2026-06-16 03:33:03 +02:00
c064e4b1e1
forgot \ [skip ci] 2026-06-16 03:32:46 +02:00
fc33b6fbd5
Update README.md 2026-06-16 03:27:10 +02:00
29e08fe29b
fix double space issue [ci skip] 2026-06-16 03:17:53 +02:00
151666fd21
better excellent enchant fake event [skip ci] 2026-06-16 03:10:34 +02:00
593527241a
fix bugged unit repair & version bump 2026-06-16 02:57:41 +02:00
3578322686
Update README.md 2026-06-13 15:22:39 +02:00
12ec4e1f54
update faststats
fix a potential plugin issue with disabling faststat in a certain way and fix potential submission on first run
2026-06-13 15:14:33 +02:00
b0f32fdba2
do not load metrics in test 2026-06-10 17:18:43 +02:00
d82bd9b22c
do not packet manager on test 2026-06-10 17:07:52 +02:00
380b0de92f
Lot of internal change and monetary cost (#116)
Internal changes this big was not intentional but had to do it for
monetary cost

excluding that: 
- add monetary cost, with dependency on rename
dialogue
- also change some a bit rename dialog
2026-06-10 15:35:41 +02:00
d91576b0de
update faststat [skip ci] 2026-06-10 15:30:57 +02:00
2f9d25bfe9
fix fake prepare anvil on modern versions 2026-06-10 15:19:56 +02:00
49b0054eca
move anvil cost to its own class 2026-06-10 14:59:28 +02:00
2efb6e55e2
new treat anvil event 2026-06-10 14:54:13 +02:00
d679cd73f9
remove rename pdc on paper lore append 2026-06-09 14:08:17 +02:00
9f06f708f5
fix superenchant price 2026-06-08 10:28:03 +02:00
31fa3d38b7
minimum version of datapack tester upped to unsure minimum java version 2026-06-06 12:46:32 +02:00
bf4395ba3f
fix multiples issues 2026-06-03 03:21:45 +02:00
2768c0a0dc
custom craft monetary cost fixed 2026-06-03 03:21:44 +02:00
f0d53a6ffa
Rename Dialog (#113)
Add a rename dialog for longer possible rename text
Also has a rename fix
2026-06-02 17:16:28 +02:00
d0078e528d
fix creative price and other small fixes 2026-06-02 16:50:14 +02:00
edceba879f
custom craft logic deduplication 2026-06-02 16:18:16 +02:00
106bc724a1
deduplicate lore edit logic 2026-06-02 14:36:09 +02:00
e6293be1c6
deduplicate unit repair logic 2026-06-02 14:01:49 +02:00
7a705f3bfc
move a lot of function to AnvilMergeLogic.kt 2026-06-02 13:29:26 +02:00
df92b4bf91
update faststat to 0.24.0 2026-06-01 13:49:15 +02:00
bf8144ad06
result work for unity repair and custom craft 2026-05-30 03:53:31 +02:00
2d31a7f5a8
seems to work better 2026-05-29 13:21:43 +02:00
3992ce1662
no price on no result 2026-05-29 02:48:16 +02:00
7aac325c70
hell 2026-05-29 00:39:12 +02:00
171a8cad6d
monetary cost require dialog rename 2026-05-28 20:33:20 +02:00
fb27ad2e55
avoid looping on same name 2026-05-28 20:11:02 +02:00
1b3447d041
monetary cost display 2026-05-28 17:26:57 +02:00
ac9f492125
monetary minimum version & rename impl 2026-05-28 17:26:56 +02:00
1660250ee1
monetary dependency functions 2026-05-28 17:26:56 +02:00
856c1e08bd
add monetary config and generic progress 2026-05-28 17:26:56 +02:00
2c3e43cb84
moved to vault unlocked 2026-05-28 17:26:56 +02:00
d67380da1a
per type xp cost 2026-05-28 17:26:50 +02:00
b18cf1fd59
hook to vault economy api 2026-05-28 17:26:22 +02:00
bf926fb159
add forgoten default 2026-05-23 17:03:17 +02:00
21087b89e0
force exist when fail to load non default config [skip ci] 2026-05-23 16:55:56 +02:00
a392702df2
negative max dialog size as infinity 2026-05-22 23:57:56 +02:00
5265d81176
add pcd keept rename name 2026-05-22 23:57:56 +02:00
91d2cce8cc
dialog max size range 2026-05-22 23:57:56 +02:00
31f9e7e281
dialog rename working good enough 2026-05-22 23:57:53 +02:00
05951d0ace
don't consider content equal if same name as type. don't cover all case sadly 2026-05-22 23:57:14 +02:00
65bf82a239
add rename dialog options 2026-05-22 23:57:09 +02:00
90cc758e88
Per color code permission (#114)
Add ability to have color code restricted by a specific permission for
each color code
2026-05-22 23:48:33 +02:00
d926b5001e
per color code permission 2026-05-22 23:44:20 +02:00
809dc3488b
fix broken isInTag logic 2026-05-21 23:16:12 +02:00
3594cf72af
try catch other plugin's listeners 2026-05-21 23:15:53 +02:00
2070f8fd68
do not track error
faststats update
2026-05-21 22:53:34 +02:00
68f63a8ec7
fix max damage not being checked 2026-05-21 22:32:04 +02:00
c703dc68f9
add compatibility notice 2026-05-17 17:49:09 +02:00
36030c598b
Add compatibility with Item Adder (#112) 2026-05-17 17:35:59 +02:00
a6cdd79750
check the same way as item adder for eco items 2026-05-17 17:33:51 +02:00
440b2b2741
create negative material set for iterator 2026-05-17 17:30:16 +02:00
41a62d810a
get "all material" info from other plugins 2026-05-17 17:30:16 +02:00
459e3351fd
item adder namespace considered for unit merge 2026-05-17 17:30:16 +02:00
e00c5e8b47
clone use item adder on custom items adder item 2026-05-17 17:30:16 +02:00
638df714fd
Rename .java to .kt 2026-05-17 17:30:16 +02:00
190f334656
prepare to use item adder dependency for clone 2026-05-17 17:30:16 +02:00
8141232c46
add superenchant bulk operation 2026-05-17 17:25:39 +02:00
a1984ad5b9
Superenchant compatibility (#111)
Superenchant compatibility & update kotlin to 2.3
2026-05-17 17:15:38 +02:00
f5343440e4
add super enchant compatibility 2026-05-14 06:30:46 +02:00
55f6b94ba9
add super enchant as dep and update kotlin 2026-05-14 06:29:18 +02:00
24db259435
add super enchant as lib 2026-05-14 06:28:50 +02:00
d867ca6c85
forgot paper version 2026-04-23 14:16:48 +02:00
1b86996317
add 26.1 ver to modrinth versions 2026-04-23 12:32:41 +02:00
bce43649bc
fix build error 2026-04-23 12:28:30 +02:00
12bfcb75ce
add more spigot warnings 2026-04-21 15:19:06 +02:00
e30f09120d
Work with eco item (#106)
Use material key instead of material enum

This is made with the goal of making eco item work as independant item.
so item type key now depend on eco item's id 

Known issue: EcoEnchant target group still do not has the eco item id inside it and cannot be handled properly by custom anvil
2026-04-21 15:18:37 +02:00
b42fb42d83
fix weird isAir error ? 2026-04-21 14:20:17 +02:00
daa1c6171f
check section exist for group & conflict 2026-04-12 20:39:31 +02:00
d4e94872d8
fix recipe error 2026-04-10 12:12:11 +02:00
7612eac765
progress on using namespaced key instead of material 2026-04-10 12:12:06 +02:00
TrashyPixl
1a71086327 ci: use native github actions features for environment variables 2026-03-26 11:41:22 +01:00
45fe037a92
[ci skip] version bump 2026-03-25 11:07:39 +01:00
d061bfc6f4
Revert "[ci skip] try better changelog"
This reverts commit f520d5e3db.
2026-03-25 03:52:18 +01:00
889d452466
Merge remote-tracking branch 'origin/v1.x.x' into v1.x.x 2026-03-25 03:31:11 +01:00
7fbf68dff3
[ci skip] add deprecation warning 2026-03-25 03:30:50 +01:00
f520d5e3db
[ci skip] try better changelog 2026-03-25 02:25:47 +01:00
e5167971f4
[ci skip] faststat ver up 2026-03-24 02:47:06 +01:00
3d50e0ec82
bruh 2026-03-24 01:40:46 +01:00
60ebdbf107
fix bad copy paste 2026-03-24 01:40:22 +01:00
f3c6526967
add excellent enchant limit 2026-03-23 23:54:38 +01:00
8ded2ae9c6
Better enchant limit & remove protocolib (#109)
Simplify item level limit code by deleting "default level" and using
enchantment's default level instead
allow max level 0 for "can't use it" and -1 for "default maximum"
made the enchantment limit gui reflect the above change

Also removed force protocolib in config as hopefully should be
unnecessary. It is still in the plugin in case something go wrong, which
seems unlikely as never been necessary as far as I remember
2026-03-23 20:40:52 +01:00
f0d2f07703
int item display better 2026-03-23 20:06:58 +01:00
26469982b2
indicate alias 2026-03-23 18:26:20 +01:00
882e50e449
remove force protocolib in config 2026-03-23 18:04:14 +01:00
c96dd7d308 simplify and extend enchant limit 2026-03-23 17:57:47 +01:00
f59071f504
prioritize paper nms on paper servers 2026-03-09 22:43:57 +01:00
6afe51acca
reduce faststat java major version 2026-03-09 22:16:35 +01:00
6a4c861eab
update faststat 2026-03-09 21:22:49 +01:00
125 changed files with 3325 additions and 1198 deletions

View file

@ -25,6 +25,10 @@ jobs:
permissions:
contents: write
env:
MODRINTH_VERSIONS: '["1.18.x", "1.19.x", "1.20.x", "1.21.x", "26.1.x", "26.2.x"]'
MODRINTH_PLATFORMS: '["spigot", "paper", "purpur", "folia"]'
steps:
- uses: actions/checkout@v4
- name: Set up JDKs
@ -66,19 +70,6 @@ jobs:
if: ${{ github.event_name != 'release' && success() }}
run: echo "SMALL_COMMIT_HASH=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV
- name: Prepare release env variable
if: ${{ github.event_name == 'release' && success() }}
run: |
echo "RELEASE_CHANGELOG<<EOF" >> $GITHUB_ENV
echo "${{ github.event.release.body || '' }}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "IS_GITHUB_PRERELEASE=${{ github.event.release.prerelease }}" >> $GITHUB_ENV
- name: Prepare publish env variable
run: |
echo MODRINTH_VERSIONS='["1.18.x", "1.19.x", "1.20.x", "1.21.x"]' >> $GITHUB_ENV
echo MODRINTH_PLATFORMS='["spigot", "paper", "purpur", "folia"]' >> $GITHUB_ENV
- name: Build with Gradle Wrapper
run: ./gradlew build --parallel --stacktrace
@ -128,6 +119,7 @@ jobs:
if: ${{ (github.event_name != 'release' || github.event_name != 'push') && github.repository_owner == 'alexcrea' && success() }}
env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
RELEASE_CHANGELOG: ${{ github.event.release.body }}
run: ./gradlew publishAllPublicationsToHangar --stacktrace
- name: Modrinth publish alpha
@ -156,7 +148,7 @@ jobs:
game-versions: ${{ env.MODRINTH_VERSIONS }}
channel: ${{ github.event.release.prerelease == false && 'release' || 'beta' }}
files: build/libs/${{ env.ONLINE_JAR_NAME }}
changelog: ${{ env.RELEASE_CHANGELOG }}
changelog: ${{ github.event.release.body }}
- name: Send release note to discord
if: ${{ github.event_name == 'release' && github.repository_owner == 'alexcrea' && success() }}
@ -165,7 +157,7 @@ jobs:
webhook-url: ${{ secrets.RELEASE_WEBHOOK_URL }}
content: |
${{ github.event.release.prerelease == false && '<@&1338546156325568642>' || '<@&1352296092989001768>' }}
# New ${{ github.event.release.prerelease && 'beta' || '' }} version of custom anvil ! <:CustomAnvil:1262550667986342001>([Modrinth](https://modrinth.com/plugin/customanvil), [Hangar](https://hangar.papermc.io/alexcrea/CustomAnvil) and [GitHub](${{ github.event.release.html_url }}) links)
# New ${{ github.event.release.prerelease && 'beta ' || '' }}version of custom anvil ! <:CustomAnvil:1262550667986342001>([Modrinth](https://modrinth.com/plugin/customanvil), [Hangar](https://hangar.papermc.io/alexcrea/CustomAnvil) and [GitHub](${{ github.event.release.html_url }}) links)
-# note: automated release. spigot is not uploaded yet.
${{ env.RELEASE_CHANGELOG }}
${{ github.event.release.body }}

View file

@ -6,44 +6,55 @@ This is cannot be fixed on geyser or my side.
Here is various plugins that had issues with CustomAnvil
where efforts was made for compatibility and should be working right:
some of them are cool I recommend checking them out !
some if not all of them are cool ! I recommend checking them out !
## Supported By CustomAnvil
These plugins have compatibility handled by custom anvil. seek help on custom anvil and do not bother these developers
#### Enchantment plugins
- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/):
Use ExcellentEnchants item type
#### Enchantment Plugins
- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/) by NightExpress:
Use ExcellentEnchants item type \
Also use ExcellentEnchant max enchant limit
- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/):
- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/) by Auxilor:
Need to use /anvilconfigreload or a server restart to add newly added enchantment.
Use EcoEnchant restriction system but new restriction can be added in custom anvil
- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/):
- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/) by Athlaeos:
Support by Custom Anvil but still experimental. Automatic configuration. Plugin is not actively developed anymore
#### Anvil Mechanics
- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
- [SuperEnchants](https://modrinth.com/plugin/superenchants) by Aznos:
Use SuperEnchant restrictions system but new restriction can be added in custom anvil
#### Custom Items Plugins
Custom Items support is considered unstable. If you find issue please report it !
- [EcoItem](https://www.spigotmc.org/resources/30-sale%E2%8F%B3-ecoitems-%E2%AD%95-create-custom-items-%E2%9C%85-weapons-armors-tools-charms-%E2%9C%A8-item-levels-rarities.94601/) by Exanthiax:
May have some issue. but should partially work I hope
- [ItemAdder](https://www.spigotmc.org/resources/%E2%9C%A8itemsadder%E2%AD%90emotes-mobs-items-armors-hud-gui-emojis-blocks-wings-hats-liquids.73355/) by LoneDev:
Need to fix unit item not working completly correctly as in can't have twice same base item
#### Anvil Mechanics Plugins
- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/) by H7KZ
Partially use Custom Anvil maximum XP settings (>= 6.1.5)
- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/)
- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/) by hyperdefined
For bag upgrade and skin via anvil. (version >= 1.31.0)
- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi)
- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi) by ArtillexStudios
For its anvil inventory usage
- [ToolsStats](https://modrinth.com/project/oBZj9E15)
- [ToolsStats](https://modrinth.com/project/oBZj9E15) by Valorless
For token application using anvil
### Known Partially Incompatible
- [UberEnchant](https://modrinth.com/plugin/uberenchant)
- [UberEnchant](https://modrinth.com/plugin/uberenchant) by coltonj96
Anvil handling as they are doing something similar to CustomAnvil.
It is by no mean there faults and I recomend checking them out
It is by no mean there faults and I recommend checking them out especially if custom anvil do not work for your use case !
- [SuperEnchant](https://modrinth.com/plugin/superenchants)
Reported potential incompatibility
- [AdvencedEnchantments](https://ae.advancedplugins.net/)
- [AdvencedEnchantments](https://ae.advancedplugins.net/) by Advanced Plugins
Paid plugin I do not own as I did not get commissioned for support.
may be able to use api but cannot test on my side

View file

@ -5,7 +5,7 @@ Thanks for all the contributors of bukkit, spigot, the paper team and the advent
Thanks JetBrain for making IntelliJ
### Dependencies
Here dependencies are used by custom anvil
These dependencies (or a modified version of) are used by custom anvil
- [IF](https://github.com/stefvanschie/IF) an inventory framework by stefvanschie
- [Mockbukkit](https://github.com/MockBukkit/MockBukkit) for unit testing
- [CentralPortalPlus](https://github.com/lalakii/central-portal-plus) by lalakii
@ -17,23 +17,18 @@ Here dependencies are used by custom anvil
- [ModrinthUpdateChecker](https://github.com/Clickism/ModrinthUpdateChecker) by Clickism and thanks to the modrinth team
### Compatibility
Here is to credits all the author of plugins
It partially repeat the the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
- Big Thanks for H7KZ for [Disenchantment](https://github.com/H7KZ/Disenchantment)
- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/) by Athlaeos
- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/) by Auxilor
- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/) by NightExpress
- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/) by hyperdefined
- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi) by ArtillexStudios
- [ToolsStats](https://modrinth.com/project/oBZj9E15) by Valorless
Thanks to all the cool creator making the minecraft plugin's ecosystem works ! \
See [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md) for details
but especially, Big Thanks for H7KZ maker of [Disenchantment](https://github.com/H7KZ/Disenchantment)
### Special Thanks
Thanks for Microsoft leading me into using a better operating system \
Thanks for all the users trying my plugin for these niche use cases
and for reporting issues and giving ideas !
, reporting issues and giving ideas !
Thanks coltonj96 for [UberEnchant](https://modrinth.com/plugin/uberenchant).
we may be incompatible with the anvil, but I do think it is a good alternative ! \
I wish one day to work on cross compatibiltiy
I wish one day to work on cross compatibiltiy \
* If custom anvil do not work well for you or your use case give it a try ! *

View file

@ -1,8 +1,6 @@
# Custom Anvil
**Custom Anvil** is a plugin that allows server administrators to customize every aspect of the anvil's mechanics.
It is expected to work on 1.18 to 1.21.7 minecraft servers running spigot or paper.
(the plugin support of 1.16.5 to 1.17.1 is experimental and may encounter issues)
### Download Locations:
@ -16,7 +14,7 @@ the plugin can be downloaded on
- Vanilla like default configuration.
- Custom enchantment level limit.
- Custom anvil recipes.
- Custom enchant restrictions (allow unsafe enchantment only for a group of item or create new restriction).
- Custom enchant restrictions (allows unsafe enchantment only for a group of item or create new restriction).
- Custom items of unit repairs (repair damaged with unit of "material", for example the repair of diamond sword by diamonds).
- Custom XP cost for every aspect of the anvil.
- Permissions to bypass level limit or enchantment restriction.
@ -25,8 +23,14 @@ the plugin can be downloaded on
- Gui to configure the plugin in game.
- Support use of color code, hexadecimal color and minimessage for color/decoration
- (Experimental) Folia support (gui do not work)
- (Experimental) Dialog rename (allows longer rename)
- (Experimental) Anvil with monetary cost (using vault) (require dialog rename)
And more !
---
### Permissions:
Note that for most of them you also need to enable feature and in most case enable use of permission for the specfic feature (indicated with `(toggleable)`)
```yml
# Generic and bypass permissions
ca.affected: Player with this permission will be affected by the plugin
@ -44,17 +48,22 @@ ca.config.edit: Allow administrator to edit the plugin's config in game
# -----------------------------------------------------------------------------
# Permissions related to use of color and minimessage
ca.color.code: Allow player to use color code on rename if enabled (toggleable)
ca.color.code.[thecode] (for example ca.color.code.a): Allows usage of only certain color code (toggleable)
ca.color.hex: Allow player to use hexadecimal color on rename if enabled (toggleable)
ca.rename.minimessage: Allow player to use minimessage formating on rename if enabled (toggleable) (only legacy compatible at the time)
ca.rename.minimessage: Allow player to use minimessage formating on rename if enabled (toggleable)
# Permissions related to edition of the lore
ca.lore_edit.book: Allow player to edit lore via book and quil if enabled (toggleable)
ca.lore_edit.paper: Allow player to edit lore via paper if enabled (toggleable)
# Others
ca.rename.dialog: Allow player to use the rename dialog (toggleable)
```
### Commands
run `customanvil help` to get information about available commands (need permissions to use them)
run `/customanvil help` to get information about available commands \
this only show subcommands you have permission for
### Supported Plugins
See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
@ -63,13 +72,10 @@ See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x
One of the configurations allow displaying price about 40 and removing Too Expensive. \
By how the minecraft client work: price above 40 can only be displayed green, even if the player does not own enough experience level.
Minecraft version 1.18 to latest marked as supported do not need any ProtocoLib dependency. \
spigot version 1.18 to 1.21.11 do not need any ProtocoLib dependency. (26.1.0 or above requires it) \
Any recent paper version also are supported for this feature.
But you should wait for update for new version containing new enchantable item or new enchantments.
Other version need ProtocoLib enabled on your server for this feature. \
You can also wait for an update of the plugin to support a newer version.
Please note that 1.16.5 to 1.17.1 are not officially supported. Run at your own risk.
But you should wait for update for new version containing new enchantable item or new enchantments if any of this got added.
Else it is, likely, fine to use the current version you are ussing on a new paper version
### For custom enchantment plugin developers
For information about the API, please refer to [the Wiki](https://github.com/alexcrea/CustomAnvil/wiki) \
@ -100,3 +106,6 @@ Credits and thanks can be seen [here](https://github.com/alexcrea/CustomAnvil/bl
### Known issue:
Most unknown registered enchantments (by unsupported custom enchantment plugin & datapacks) will not have restriction by default. Planned but no eta.
### Do you need help with the plugin, or have any issue or suggestion?
You can ask on the discussion page, create a [GitHub issue](https://github.com/alexcrea/CustomAnvil/issues) or join my [discord](https://discord.gg/KHUNsUfRYJ)

View file

@ -8,7 +8,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.ByteArrayOutputStream
plugins {
kotlin("jvm") version "2.1.0"
kotlin("jvm") version "2.3.0"
java
id("org.jetbrains.dokka").version("1.9.20")
id("com.gradleup.shadow").version("9.3.0")
@ -22,7 +22,7 @@ plugins {
}
group = "xyz.alexcrea"
version = "1.16.1"
version = "1.17.5"
val isDevBuild = System.getenv("SMALL_COMMIT_HASH") != null
val isPreRelease = System.getenv("IS_GITHUB_PRERELEASE") == "true"
@ -37,11 +37,17 @@ repositories {
// ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases")
// for fast stats
// ItemsAdder
maven(url = "https://maven.devs.beer/")
// For fast stats
maven {
name = "thenextlvlReleases"
url = uri("https://repo.thenextlvl.net/releases")
}
// For vault unlocked
maven { url = uri("https://repo.codemc.io/repository/creatorfromhell/") }
}
val reobfNMS = providers.gradleProperty("subprojects.reobfnms")
@ -52,7 +58,7 @@ dependencies {
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
// fast stats
implementation("dev.faststats.metrics:bukkit:0.16.0")
implementation("dev.faststats.metrics:bukkit:0.27.0")
// minimessage
implementation("net.kyori:adventure-text-minimessage:4.25.0")
@ -65,13 +71,17 @@ dependencies {
// EnchantsSquaredRewritten
compileOnly(files("libs/EnchantsSquared.jar"))
// EcoEnchants
compileOnly("com.willfp:EcoEnchants:12.11.1")
// EcoEnchants & item
compileOnly("com.willfp:libreforge:4.79.0:all")
compileOnly("com.willfp:eco:6.74.5")
compileOnly("com.willfp:EcoEnchants:12.11.1")
compileOnly(project(":impl:LegacyEcoEnchant"))
compileOnly("com.willfp:EcoItems:5.66.0")
// ExcellentEnchants
implementation(project(":impl:ExcellentEnchant5_3"))
implementation(project(":impl:ExcellentEnchant5_4"))
compileOnly("su.nightexpress.excellentenchants:Core:5.1.0") {
exclude("org.spigotmc")
}
@ -90,6 +100,15 @@ dependencies {
// AxPlayerWarps
compileOnly(files("libs/AxPlayerWarps-1.10.3.jar"))
// SuperEnchants
compileOnly(files("libs/SuperEnchants-4.6.2-all.jar"))
// ItemsAdder API
compileOnly("dev.lone:api-itemsadder:4.0.10")
// Vault api
compileOnly("net.milkbowl.vault:VaultUnlockedAPI:2.16")
// Include nms
implementation(project(":nms:nms-common"))
implementation(project(":nms:nms-paper"))
@ -149,7 +168,7 @@ allprojects {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_16)
}
}
@ -183,7 +202,7 @@ tasks {
shadowJar {
configureBaseShadow("",
arrayOf(
"org.jetbrains.kotlin:kotlin-stdlib:2.1.0",
"org.jetbrains.kotlin:kotlin-stdlib:2.3.0",
"net.kyori:adventure-text-minimessage:4.25.0",
"net.kyori:adventure-text-serializer-plain:4.25.0",
"net.kyori:adventure-text-serializer-legacy:4.25.0",

View file

@ -77,6 +77,18 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -87,10 +99,23 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
use_of_color_cost: 0
# Default limit to apply to any enchants missing from enchant_limits
# Dialogue rename menu make use of dialog menu to allow bigger rename
# You can also change the maximum size and set it to -1 or less for maximum
#
# Valid values include 1 to 1000
default_limit: 5
# This feature only work on paper 1.21.7 or later
#
# At the moment only english is available for this menu... sorry !
#
# CustomAnvil use "ca.rename.dialog" when permission
enable_dialog_rename: false
dialog_rename_max_size: 256
permission_needed_for_dialog_rename: false
# This allows custom anvil to not "guess" the text used for rename but store it in the item
# It will make item stackable only and only if it had used the same rename text
#
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Override limits for specific enchants
#
@ -98,7 +123,8 @@ default_limit: 5
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 1 - 255 for each enchantment
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -404,16 +430,37 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0

View file

@ -79,6 +79,18 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -89,10 +101,23 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
use_of_color_cost: 0
# Default limit to apply to any enchants missing from enchant_limits
# Dialogue rename menu make use of dialog menu to allow bigger rename
# You can also change the maximum size and set it to -1 or less for maximum
#
# Valid values include 1 to 1000
default_limit: 5
# This feature only work on paper 1.21.7 or later
#
# At the moment only english is available for this menu... sorry !
#
# CustomAnvil use "ca.rename.dialog" when permission
enable_dialog_rename: false
dialog_rename_max_size: 256
permission_needed_for_dialog_rename: false
# This allows custom anvil to not "guess" the text used for rename but store it in the item
# It will make item stackable only and only if it had used the same rename text
#
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Override limits for specific enchants
#
@ -100,7 +125,8 @@ default_limit: 5
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 1 - 255 for each enchantment
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -424,17 +450,38 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.15.5
lowMinecraftVersion: 1.21.11

View file

@ -77,6 +77,18 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -87,10 +99,23 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
use_of_color_cost: 0
# Default limit to apply to any enchants missing from enchant_limits
# Dialogue rename menu make use of dialog menu to allow bigger rename
# You can also change the maximum size and set it to -1 or less for maximum
#
# Valid values include 1 to 1000
default_limit: 5
# This feature only work on paper 1.21.7 or later
#
# At the moment only english is available for this menu... sorry !
#
# CustomAnvil use "ca.rename.dialog" when permission
enable_dialog_rename: false
dialog_rename_max_size: 256
permission_needed_for_dialog_rename: false
# This allows custom anvil to not "guess" the text used for rename but store it in the item
# It will make item stackable only and only if it had used the same rename text
#
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Override limits for specific enchants
#
@ -98,7 +123,8 @@ default_limit: 5
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 1 - 255 for each enchantment
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -416,17 +442,38 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0
lowMinecraftVersion: 1.21.9

View file

@ -77,6 +77,18 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -87,10 +99,23 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
use_of_color_cost: 0
# Default limit to apply to any enchants missing from enchant_limits
# Dialogue rename menu make use of dialog menu to allow bigger rename
# You can also change the maximum size and set it to -1 or less for maximum
#
# Valid values include 1 to 1000
default_limit: 5
# This feature only work on paper 1.21.7 or later
#
# At the moment only english is available for this menu... sorry !
#
# CustomAnvil use "ca.rename.dialog" when permission
enable_dialog_rename: false
dialog_rename_max_size: 256
permission_needed_for_dialog_rename: false
# This allows custom anvil to not "guess" the text used for rename but store it in the item
# It will make item stackable only and only if it had used the same rename text
#
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Override limits for specific enchants
#
@ -98,7 +123,8 @@ default_limit: 5
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 1 - 255 for each enchantment
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -404,16 +430,37 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0

View file

@ -1,5 +1,5 @@
### Default Plugin's Configurations
From 1.18 to 1.20.6 use [1.18 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.18) \
From 1.21 to 1.21.8 use [1.21 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.21)
From 1.21.9 to 1.21.10 use [1.21.9 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.21.9)
From 1.21 to 1.21.8 use [1.21 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.21) \
From 1.21.9 to 1.21.10 use [1.21.9 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.21.9) \
From 1.21.11 use [1.21.11 configurations](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs/1.21.11)

View file

@ -9,5 +9,5 @@ kotlin.daemon.jvmargs=-Xmx8G
subprojects.reobfnms=v1_17R1,v1_18R1,v1_18R2,v1_19R1,v1_19R2,v1_19R3,v1_20R1,v1_20R2,v1_20R3,v1_20R4,v1_21R1,v1_21R2,v1_21R3,v1_21R4,v1_21R5,v1_21R6,v1_21R7
# list of version for hangar release
paperVersion=1.18-1.21.11
paperVersion=1.18-26.2

View file

@ -1,21 +0,0 @@
group = rootProject.group
version = rootProject.version
plugins {
kotlin("jvm") version "2.1.0"
}
repositories {
// ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases")
}
dependencies {
// Spigot api
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
// Excellent Enchant
compileOnly("su.nightexpress.excellentenchants:Core:5.3.0") {
exclude("org.spigotmc")
}
}

View file

@ -0,0 +1,17 @@
group = rootProject.group
version = rootProject.version
plugins {
kotlin("jvm") version "2.3.0"
}
repositories {
// ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases")
}
dependencies {
// Excellent Enchant
compileOnly("su.nightexpress.excellentenchants:Core:5.4.3")
compileOnly("su.nightexpress.nightcore:main:2.16.2")
}

View file

@ -0,0 +1,12 @@
package xyz.alexcrea.cuanvil.dependency.plugins;
import su.nightexpress.excellentenchants.EnchantsAPI;
public class ExcellentEnchant5_4EnchantSettings {
public static int anvilLimit() {
return EnchantsAPI.getEnchantManager().getSettings().getAnvilEnchantsLimit();
}
}

View file

@ -2,7 +2,7 @@ group = rootProject.group
version = rootProject.version
plugins {
kotlin("jvm") version "2.1.0"
kotlin("jvm") version "2.3.0"
}
// Imitate needed class and method to support legacy version of EcoEnchant

Binary file not shown.

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_16)
}
}

View file

@ -0,0 +1,23 @@
package xyz.alexcrea.cuanvil.dialog
import org.bukkit.NamespacedKey
import org.bukkit.entity.HumanEntity
import org.bukkit.event.inventory.PrepareAnvilEvent
interface AnvilRenameDialog {
companion object {
val PCD_KEEP_RENAME_TEXT_KEY = NamespacedKey.fromString("customanvil:last_rename_text")!!
}
fun canSendDialog(): Boolean
fun tryShowDialog(player: HumanEntity, event: PrepareAnvilEvent)
fun closeInventory(player: HumanEntity)
fun currentText(player: HumanEntity): String?
fun isOpenFor(player: HumanEntity): Boolean
}

View file

@ -11,7 +11,7 @@ dependencies {
implementation(project(":nms:nms-common"))
// Used for nms
paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT")
}
repositories {
@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_18)
}
}

View file

@ -24,8 +24,8 @@ class PaperPacketManager : PacketManagerBase(), PacketManager {
sendedAbilities.mayfly = playerAbilities.mayfly
sendedAbilities.instabuild = instantBuild
sendedAbilities.mayBuild = playerAbilities.mayBuild
sendedAbilities.flyingSpeed = playerAbilities.flyingSpeed
sendedAbilities.walkingSpeed = playerAbilities.walkingSpeed
sendedAbilities.setFlyingSpeed(playerAbilities.getFlyingSpeed())
sendedAbilities.setWalkingSpeed(playerAbilities.getWalkingSpeed())
}
val packet = ClientboundPlayerAbilitiesPacket(sendedAbilities)
nmsPlayer.connection.send(packet)

View file

@ -0,0 +1,236 @@
package xyz.alexcrea.cuanvil.dialog
import io.papermc.paper.dialog.Dialog
import io.papermc.paper.registry.data.dialog.ActionButton
import io.papermc.paper.registry.data.dialog.DialogBase
import io.papermc.paper.registry.data.dialog.action.DialogAction
import io.papermc.paper.registry.data.dialog.body.DialogBody
import io.papermc.paper.registry.data.dialog.input.DialogInput
import io.papermc.paper.registry.data.dialog.type.DialogType
import io.papermc.paper.threadedregions.scheduler.ScheduledTask
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.event.ClickCallback
import net.kyori.adventure.text.format.TextColor
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
import net.minecraft.world.inventory.AnvilMenu
import org.bukkit.craftbukkit.event.CraftEventFactory
import org.bukkit.craftbukkit.inventory.CraftInventoryView
import org.bukkit.craftbukkit.inventory.view.CraftAnvilView
import org.bukkit.entity.HumanEntity
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.Plugin
import java.util.*
import java.util.function.BiFunction
import java.util.function.Consumer
import java.util.function.Supplier
@Suppress("UnstableApiUsage")
class AnvilRenameDialogImpl(
val fromFormated: BiFunction<HumanEntity, Component?, String?>,
val keepUserPreviousDialog: Supplier<Boolean>,
val maxLength: Supplier<Int>,
val plugin: Plugin,
) : AnvilRenameDialog {
companion object {
private const val RENAME_TEXT_KEY = "rename"
private const val MAX_WIDTH = 512
private val PLAIN_TEXT_SERIALIZER = PlainTextComponentSerializer.plainText()
// Need to be able to translate it later !
private val USER_FACING_RENAME_TITLE = Component.text("Rename Your Item")
private val USER_FACING_WARNING = Component.text(
"Note that the repair text will appear blank after Confirm\n" +
"But the name will be correctly applied"
)
private val USER_FACING_CONFIRM = Component.text("Confirm").color(TextColor.fromHexString("#40FF40"))
private val USER_FACING_CANCEL = Component.text("Cancel").color(TextColor.fromHexString("#FF4040"))
fun itemDefaultName(item: ItemStack?): String? {
return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName())
}
}
private val lastNames = HashMap<UUID, String>()
private val lastRenames = HashMap<UUID, String>()
private val lastLeftItem = HashMap<UUID, String>()
private val runTaskMap = HashMap<UUID, ScheduledTask>()
// For monetary cost
val hasUiOpen = HashSet<UUID>()
private val containerField = CraftInventoryView::class.java.getDeclaredField("container")
init {
containerField.setAccessible(true)
}
override fun canSendDialog(): Boolean {
return true
}
fun makeDialog(playerID: UUID, initial: String?, callback: Consumer<String?>): Dialog {
val maxLength = this.maxLength.get()
val initialFinal = initial?.take(maxLength)
val baseBuilder = DialogBase.builder(USER_FACING_RENAME_TITLE)
.canCloseWithEscape(true)
.afterAction(DialogBase.DialogAfterAction.CLOSE)
.inputs(
listOf(
DialogInput.text(RENAME_TEXT_KEY, Component.text("Rename text"))
.maxLength(maxLength)
.initial(initialFinal ?: "")
.labelVisible(false)
.width(MAX_WIDTH)
.build(),
),
)
baseBuilder.body(
listOf(
DialogBody.plainMessage(USER_FACING_WARNING, MAX_WIDTH)
)
)
return Dialog.create { builder ->
builder.empty()
.base(baseBuilder.build())
.type(
DialogType.confirmation(
ActionButton.builder(USER_FACING_CONFIRM)
.action(DialogAction.customClick({ response, _ ->
hasUiOpen.remove(playerID)
val text = response.getText(RENAME_TEXT_KEY)!!
callback.accept(text)
}, ClickCallback.Options.builder().build()))
.build(),
ActionButton.builder(USER_FACING_CANCEL)
.action(DialogAction.customClick({ response, _ ->
hasUiOpen.remove(playerID)
}, ClickCallback.Options.builder().build()))
.build(),
)
)
}
}
private fun setResult(player: HumanEntity, view: InventoryView, result: String?) {
val defaultName = itemDefaultName(view.getItem(0))
if (defaultName == result) {
setName(player, view, "", null)
if (defaultName != null) lastNames[player.uniqueId] = defaultName
} else setName(player, view, result, result)
}
private fun setName(player: HumanEntity, view: InventoryView, name: String?, rename: String?) {
val menu = (containerField.get(view) as AnvilMenu)
val isSameName = menu.itemName == name
menu.itemName = rename
if (name == null)
lastNames.remove(player.uniqueId)
else
lastNames[player.uniqueId] = name
if (rename == null)
lastRenames.remove(player.uniqueId)
else
lastRenames[player.uniqueId] = rename
if (!isSameName)
CraftEventFactory.callPrepareResultEvent(menu, 2);
}
private fun nameFromItem(player: HumanEntity, item: ItemStack?): String? {
// Already has text
if (item?.hasItemMeta() != true || !item.itemMeta.hasCustomName())
return PLAIN_TEXT_SERIALIZER.serializeOrNull(item?.effectiveName())
if (keepUserPreviousDialog.get() && item.hasItemMeta()) {
val lastName = item.itemMeta.persistentDataContainer.get(
AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY,
PersistentDataType.STRING
)
if (lastName != null) return lastName
}
return fromFormated.apply(player, item.effectiveName())
}
private fun tryShowDialogScheduled(player: HumanEntity, event: PrepareAnvilEvent) {
val view = event.view
if (view !is CraftAnvilView) return
val renameText = view.renameText
val leftItem = view.getItem(0)
val leftItemStr = leftItem?.toString()
val lastName = lastNames.getOrDefault(player.uniqueId, null)
val lastRename = lastRenames.getOrDefault(player.uniqueId, null)
if (lastLeftItem.getOrDefault(player.uniqueId, null) != leftItemStr) {
if (leftItemStr == null)
lastLeftItem.remove(player.uniqueId)
else lastLeftItem[player.uniqueId] = leftItemStr
setName(player, view, renameText, nameFromItem(player, leftItem))
return
}
if (lastName == renameText || lastRename == renameText)
return
if (renameText?.isBlank() == true || renameText == itemDefaultName(leftItem)) {
setName(player, view, lastName, lastRename)
return
}
val dialog = makeDialog(player.uniqueId, lastRename)
{ result -> setResult(player, view, result) }
player.showDialog(dialog)
hasUiOpen.add(player.uniqueId)
}
// We need to wait for a short time as changing item will change the name BEFORE the item change
// no guaranty both of them came in the same tick too so let's wait 2 tick....
override fun tryShowDialog(player: HumanEntity, event: PrepareAnvilEvent) {
runTaskMap.remove(player.uniqueId)?.cancel()
val task = player.scheduler.runDelayed(
plugin,
{ _ ->
run { tryShowDialogScheduled(player, event) }
},
{},
2
)
if (task == null) return
runTaskMap[player.uniqueId] = task
}
override fun closeInventory(player: HumanEntity) {
lastNames.remove(player.uniqueId)
lastRenames.remove(player.uniqueId)
lastLeftItem.remove(player.uniqueId)
runTaskMap.remove(player.uniqueId)?.cancel()
}
override fun currentText(player: HumanEntity): String? {
return lastNames[player.uniqueId]
}
override fun isOpenFor(player: HumanEntity): Boolean {
return hasUiOpen.contains(player.uniqueId)
}
}

View file

@ -0,0 +1,45 @@
package xyz.alexcrea.cuanvil.util
import io.papermc.paper.threadedregions.scheduler.ScheduledTask
import org.bukkit.entity.HumanEntity
import org.bukkit.inventory.InventoryView
import org.bukkit.plugin.Plugin
import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
import java.util.HashMap
import java.util.UUID
object AnvilTitleUtil {
private val runTaskMap = HashMap<UUID, ScheduledTask>()
private fun actualRename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog) {
runTaskMap.remove(player.uniqueId)
if (view.title == name) return
// We assume rename impl is used
if (anvilDialog.isOpenFor(player)) return
view.title = name
}
// We don't want to rename instantly it is causing issue with rename text
// especially as it can "override" current ui when it is rename ui time but rename ui also need some delay
fun rename(view: InventoryView, name: String, player: HumanEntity, anvilDialog: AnvilRenameDialog, plugin: Plugin) {
runTaskMap.remove(player.uniqueId)?.cancel()
val task = player.scheduler.runDelayed(
plugin,
{ _ ->
run { actualRename(view, name, player, anvilDialog) }
},
{
runTaskMap.remove(player.uniqueId)
},
2
)
if (task == null) return
runTaskMap[player.uniqueId] = task
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_16)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_17)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_17)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_17)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_17)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_17)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_18)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_18)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_18)
}
}

View file

@ -0,0 +1,18 @@
package xyz.alexcrea.cuanvil.util
import org.bukkit.inventory.meta.Damageable
// I LOVE support of old versions and needing to do modules like that
// That truly is my favorite activity
// TODO clean this one of legacy removal branch
object MaxDamageCheckerUtil {
/**
* @return max damage or int max if not set
*/
fun getMaxDamage(meta: Damageable): Int {
if(!meta.hasMaxDamage()) return Integer.MAX_VALUE
return meta.maxDamage
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -18,5 +18,5 @@ for (nmsPart in reobfNMS) {
// compatibility subprojects
include(":impl:LegacyEcoEnchant")
findProject(":impl:LegacyEcoEnchant")?.name = "LegacyEcoEnchant"
include("impl:ExcellentEnchant5_3")
findProject(":impl:ExcellentEnchant5_3")?.name = "ExcellentEnchant5_3"
include("impl:ExcellentEnchant5_4")
findProject(":impl:ExcellentEnchant5_4")?.name = "ExcellentEnchant5_4"

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.api;
import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment;
@ -180,13 +181,13 @@ public class EnchantmentApi {
private static boolean tryWriteDefaultConfig(FileConfiguration defaultConfig, CAEnchantment enchantment, boolean override) {
boolean hasChange = false;
String levelPath = "enchant_limits." + enchantment.getKey();
String levelPath = ConfigOptions.ENCHANT_LIMIT_ROOT + "." + enchantment.getKey();
if(override || !defaultConfig.isSet(levelPath)){
defaultConfig.set(levelPath, enchantment.defaultMaxLevel());
hasChange = true;
}
String basePath = "enchant_values." + enchantment.getKey();
String basePath = ConfigOptions.ENCHANT_VALUES_ROOT + "." + enchantment.getKey();
EnchantmentRarity rarity = enchantment.defaultRarity();
String itemPath = basePath + ".item";

View file

@ -3,6 +3,7 @@ package xyz.alexcrea.cuanvil.api;
import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -123,7 +124,7 @@ public class MaterialGroupApi {
FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
String basePath = group.getName() + ".";
Set<Material> materialSet = group.getNonGroupInheritedMaterials();
Set<NamespacedKey> materialSet = group.getNonGroupInheritedMaterials();
Set<AbstractMaterialGroup> groupSet = group.getGroups();
boolean empty = true;
@ -153,7 +154,7 @@ public class MaterialGroupApi {
FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
String basePath = group.getName() + ".";
EnumSet<Material> materials = group.getMaterials();
Set<NamespacedKey> materials = group.getMaterials();
if (materials.isEmpty()) return false;
@ -163,8 +164,8 @@ public class MaterialGroupApi {
return true;
}
public static List<String> materialSetToStringList(@NotNull Set<Material> materials) {
return materials.stream().map(material -> material.getKey().getKey().toLowerCase()).toList();
public static List<String> materialSetToStringList(@NotNull Set<NamespacedKey> materials) {
return materials.stream().map(NamespacedKey::toString).toList();
}
public static List<String> materialGroupSetToStringList(@NotNull Set<AbstractMaterialGroup> groups) {

View file

@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
* Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
* for this event to be useful.
* <p>
* There is also {@link CATreatAnvilResultEvent} that may be better for some use case.
* There is also {@link CATreatAnvilResult2Event} that may be better for some use case.
*/
public class CAClickResultBypassEvent extends Event implements Cancellable {

View file

@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
* <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p>
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent}
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
* as your use case may be more prone to use theses.
*/
public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable {

View file

@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
* <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p>
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent}
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
* as your use case may be more prone to use theses.
*/
public class CAPreAnvilBypassEvent extends Event implements Cancellable {

View file

@ -0,0 +1,196 @@
package xyz.alexcrea.cuanvil.api.event.listener;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.anvil.AnvilCost;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
/**
* Called after custom anvil processed the click on the result on the anvil inventory.
* This event should be used to modify the result of an anvil use.
* <p>
* You may also want to check {@link CAClickResultBypassEvent},
* {@link CAPreAnvilBypassEvent}
* and {@link CAEarlyPreAnvilBypassEvent} for your use case
* <p>
* A null result will cancel this event
*/
@SuppressWarnings("unused")
public class CATreatAnvilResult2Event extends Event {
private static final HandlerList HANDLERS = new HandlerList();
public static HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
@NotNull
private final InventoryView view;
private final AnvilUseType useType;
@Nullable
private final ItemStack left;
@Nullable
private final ItemStack right;
@Nullable
private ItemStack result;
private final AnvilCost cost;
@ApiStatus.Internal
public CATreatAnvilResult2Event(
@NotNull InventoryView view,
Inventory inv,
AnvilUseType useType,
@Nullable ItemStack result,
AnvilCost cost) {
this.view = view;
this.useType = useType;
this.left = inv.getItem(0); // TODO use view here
this.right = inv.getItem(1);
this.result = result;
this.cost = cost;
}
/**
* Get the bukkit inventory view.
* <p>
* Temporarily marked as internal as it will get changed to anvil view on legacy removal
* so signature will change
*
* @return The inventory view of this event.
*/
@ApiStatus.Internal
public @NotNull InventoryView getView() {
return view;
}
/**
* Get the type of use source of the result.
*
* @return The craft use type.
*/
public AnvilUseType getUseType() {
return useType;
}
/**
* Get the left item of the anvil use
*
* @return the left item
*/
public @Nullable ItemStack getLeftItem() {
return left;
}
/**
* Get the right item of the anvil use
*
* @return the right item
*/
public @Nullable ItemStack getRightItem() {
return right;
}
/**
* Get the current result
* <p>
* note that it will not be null unless another listener previously set it to null.
*
* @return The current result.
*/
public @Nullable ItemStack getResult() {
return result;
}
/**
* Set the current result
* <p>
* note that a null result will cancel this anvil use.
*
* @param result The new result
*/
public void setResult(@Nullable ItemStack result) {
this.result = result;
}
/**
* Get the level cost displayed on the anvil.
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
* </ul>
*
* @return The current cost.
* @deprecated use #{@link #getCost()} instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public int getLevelCost() {
return cost.asXpCost();
}
/**
* Set the level cost displayed on the anvil.
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
* </ul>
*
* @param levelCost The new cost.
* @deprecated use #{@link #getCost()} and set value on this instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public void setLevelCost(int levelCost) {
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
}
/**
* Allow access to the current cost of the event
* Note that modifying this object will change the event resulting cost
*
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
*
* @return the current anvil cost
*/
public AnvilCost getCost() {
return cost;
}
}

View file

@ -6,7 +6,8 @@ import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.util.AnvilUseType;
import xyz.alexcrea.cuanvil.anvil.AnvilCost;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
/**
* Called after custom anvil processed the click on the result on the anvil inventory.
@ -17,8 +18,12 @@ import xyz.alexcrea.cuanvil.util.AnvilUseType;
* and {@link CAEarlyPreAnvilBypassEvent} for your use case
* <p>
* A null result will cancel this pre anvil event
*
* @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event
* use {@link CATreatAnvilResult2Event} instead
*/
@SuppressWarnings("unused")
@Deprecated(forRemoval = true, since = "1.17.0")
public class CATreatAnvilResultEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
@ -40,13 +45,13 @@ public class CATreatAnvilResultEvent extends Event {
@Nullable
private ItemStack result;
private int levelCost;
private final AnvilCost cost;
public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, int levelCost) {
public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, AnvilCost cost) {
this.event = event;
this.useType = useType;
this.result = result;
this.levelCost = levelCost;
this.cost = cost;
}
/**
@ -104,9 +109,11 @@ public class CATreatAnvilResultEvent extends Event {
* </ul>
*
* @return The current cost.
* @deprecated use #{@link #getCost()} instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public int getLevelCost() {
return levelCost;
return cost.asXpCost();
}
/**
@ -124,8 +131,32 @@ public class CATreatAnvilResultEvent extends Event {
* </ul>
*
* @param levelCost The new cost.
* @deprecated use #{@link #getCost()} and set value on this instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public void setLevelCost(int levelCost) {
this.levelCost = levelCost;
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
}
/**
* Allow access to the current cost of the event
* Note that modifying this object will change the event resulting cost
*
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
*
* @return the current anvil cost
*/
public AnvilCost getCost() {
return cost;
}
}

View file

@ -2,7 +2,7 @@ package xyz.alexcrea.cuanvil.config;
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.util.AnvilUseType;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
import java.util.EnumMap;

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@ -11,24 +12,23 @@ public interface AdditionalTestEnchantment {
/**
* Test if the provided enchantments can be compatible with this enchantment. only non-Custom Anvil conflict.
* @param enchantments Immutable map of validated enchantments for the item.
* @param itemMat Material of the tested item.
* @param itemType Material namespaced key of the tested item.
* @return If there is a conflict with the enchantments.
*/
boolean isEnchantConflict(
@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull Material itemMat);
@NotNull NamespacedKey itemType);
/**
* Test if the provided item can be compatible with this enchantment. only non-Custom Anvil conflict.
* @param enchantments Immutable map of validated enchantments for the item.
* @param itemMat Material of the tested item.
* @param itemType Material namespaced key of the tested item.
* @param item Provide a new instance of the used item stack with the partial enchantment applied.
* @return If there is a conflict with the enchantment and the item.
*/
boolean isItemConflict(
@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull Material itemMat,
@NotNull NamespacedKey itemType,
@NotNull ItemStack item);
}

View file

@ -0,0 +1,47 @@
package xyz.alexcrea.cuanvil.enchant.bulk;
import com.maddoxh.superEnchants.items.EnchantApplicator;
import com.maddoxh.superEnchants.items.EnchantReader;
import io.delilaheve.CustomAnvil;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.api.EnchantmentApi;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import java.util.Map;
public class SuperEnchantBulkOperation implements BulkGetEnchantOperation, BulkCleanEnchantOperation {
private Plugin plugin;
public SuperEnchantBulkOperation(Plugin plugin) {
this.plugin = plugin;
}
@Override
public void bulkGet(@NotNull Map<CAEnchantment, Integer> enchantmentMap, @NotNull ItemStack item, @NotNull ItemMeta meta) {
EnchantReader.INSTANCE.readEnchants(item).forEach((ench, level) -> {
var enchantment = EnchantmentApi.getByKey(NamespacedKey.fromString(ench, plugin));
if(enchantment == null) {
CustomAnvil.log("Enchantment " + ench + " not found in custom anvil");
return;
}
enchantmentMap.put(enchantment, level);
}
);
}
@Override
public void bulkClear(@NotNull ItemStack item) {
EnchantApplicator.INSTANCE.clearAllCustomEnchants(item);
}
@Override
public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) {
// item meta is not preferred for enchantment squared clear
}
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
@ -39,7 +40,7 @@ public class CAEEPreV5Enchantment extends CABukkitEnchantment implements Additio
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (!definition.hasConflicts()) return false;
Set<String> conflicts = definition.getConflicts();
@ -52,8 +53,8 @@ public class CAEEPreV5Enchantment extends CABukkitEnchantment implements Additio
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) return false;
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
return !definition.getSupportedItems().is(item);
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
@ -27,7 +28,7 @@ public class CAEEV5Enchantment extends CABukkitEnchantment implements Additional
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (!hasConflicts()) return false;
Set<String> conflicts = getExclusiveSet();
@ -41,10 +42,10 @@ public class CAEEV5Enchantment extends CABukkitEnchantment implements Additional
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) return false;
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
String key = itemMat.getKey().getKey();
String key = itemType.getKey();
ItemSet primary = eeenchantment.getPrimaryItems();
if (primary.getMaterials().contains(key)) return false;

View file

@ -0,0 +1,29 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
import xyz.alexcrea.cuanvil.dependency.plugins.ExcellentEnchant5_4EnchantSettings;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import java.util.Map;
public class CAEEV5_4Enchantment extends CAEEV5Enchantment {
public CAEEV5_4Enchantment(@NotNull CustomEnchantment enchantment) {
super(enchantment);
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemMat) {
if(super.isEnchantConflict(enchantments, itemMat)) return true;
var limit = ExcellentEnchant5_4EnchantSettings.anvilLimit();
var count = enchantments.keySet().stream()
.filter(key -> key instanceof CAEEV5_4Enchantment)
.count();
return count > limit;
}
}

View file

@ -4,6 +4,7 @@ import com.willfp.ecoenchants.enchant.EcoEnchant;
import com.willfp.ecoenchants.target.EnchantmentTarget;
import com.willfp.ecoenchants.type.EnchantmentType;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
@ -23,7 +24,7 @@ public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestE
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (enchantments.isEmpty()) return false;
// Check if there is only self
@ -61,9 +62,9 @@ public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestE
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull Material itemMat,
@NotNull NamespacedKey itemType,
@NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
return false;
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@ -24,12 +25,12 @@ public class CAIncompatibleAllEnchant extends CABukkitEnchantment implements Add
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
return !enchantments.isEmpty() && !(enchantments.size() == 1 && enchantments.containsKey(this));
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
return false;
}
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.EnchantmentData;
@ -22,7 +23,7 @@ public class CALegacyEEEnchantment extends CABukkitEnchantment implements Additi
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (!eeenchantment.hasConflicts()) return false;
Set<String> conflicts = eeenchantment.getConflicts();
@ -35,8 +36,8 @@ public class CALegacyEEEnchantment extends CABukkitEnchantment implements Additi
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) return false;
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
return !eeenchantment.getSupportedItems().is(item);
}

View file

@ -4,12 +4,14 @@ import com.willfp.ecoenchants.enchantments.EcoEnchant;
import com.willfp.ecoenchants.enchantments.meta.EnchantmentTarget;
import com.willfp.ecoenchants.enchantments.meta.EnchantmentType;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.Map;
@ -23,7 +25,7 @@ public class CALegacyEcoEnchant extends CABukkitEnchantment implements Additiona
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (enchantments.isEmpty()) return false;
EnchantmentType type = this.ecoEnchant.getType();
@ -48,14 +50,15 @@ public class CALegacyEcoEnchant extends CABukkitEnchantment implements Additiona
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull Material itemMat,
@NotNull NamespacedKey itemType,
@NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
return false;
}
var mat = MaterialUtil.INSTANCE.getMatFromKey(itemType);
for (EnchantmentTarget target : this.ecoEnchant.getTargets()) {
if (target.getMaterials().contains(itemMat)) {
if (target.getMaterials().contains(mat)) {
return false;
}
}

View file

@ -0,0 +1,76 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import com.maddoxh.superEnchants.enchants.CustomEnchant;
import com.maddoxh.superEnchants.enchants.EnchantManager;
import com.maddoxh.superEnchants.items.EnchantApplicator;
import com.maddoxh.superEnchants.items.EnchantReader;
import com.maddoxh.superEnchants.util.ConflictChecker;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentBase;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import java.util.HashMap;
import java.util.Map;
public class CASuperEnchantEnchantment extends CAEnchantmentBase implements AdditionalTestEnchantment {
private @NotNull CustomEnchant enchant;
private @NotNull EnchantManager enchantManager;
public CASuperEnchantEnchantment(@NotNull CustomEnchant enchant, @NotNull Plugin plugin, @NotNull EnchantManager enchantManager) {
super(NamespacedKey.fromString(enchant.getId(), plugin), EnchantmentRarity.COMMON, enchant.getMaxLevel());
this.enchant = enchant;
this.enchantManager = enchantManager;
}
@Override
public int getLevel(@NotNull ItemStack item, @NotNull ItemMeta meta) {
return EnchantReader.INSTANCE.getEnchantLevel(item, enchant.getId());
}
@Override
public boolean isEnchantmentPresent(@NotNull ItemStack item, @NotNull ItemMeta meta) {
return EnchantReader.INSTANCE.hasEnchant(item, enchant.getId());
}
@Override
public void addEnchantmentUnsafe(@NotNull ItemStack item, int level) {
EnchantApplicator.INSTANCE.applyEnchant(item, enchant.getId(), level);
}
@Override
public void removeFrom(@NotNull ItemStack item) {
EnchantApplicator.INSTANCE.removeEnchant(item, enchant.getId());
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
var idMap = new HashMap<String, Integer>();
enchantments.forEach((enchant, level) -> {
if(!(enchant instanceof CASuperEnchantEnchantment superEnch)) return;
idMap.put(superEnch.enchant.getId(), level);
});
return ConflictChecker.INSTANCE.hasConflict(
idMap,
enchant.getId(),
enchantManager
) != null;
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if(Material.ENCHANTED_BOOK.equals(item.getType())) return false;
return !enchant.canApplyTo(item.getType());
}
}

View file

@ -1,34 +1,35 @@
package xyz.alexcrea.cuanvil.gui.config;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.*;
public interface SelectMaterialContainer {
EnumSet<Material> getSelectedMaterials();
Set<NamespacedKey> getSelectedMaterials();
boolean setSelectedMaterials(EnumSet<Material> materials);
boolean setSelectedMaterials(Set<NamespacedKey> materials);
EnumSet<Material> illegalMaterials();
Set<NamespacedKey> illegalMaterials();
static List<String> getMaterialLore(SelectMaterialContainer container, String containerType, String action){
// Prepare material lore
ArrayList<String> groupLore = new ArrayList<>();
groupLore.add("§7Allow you to select a list of §ematerials §7that this " + containerType + " should " + action);
Set<Material> materialSet = container.getSelectedMaterials();
Set<NamespacedKey> materialSet = container.getSelectedMaterials();
if (materialSet.isEmpty()) {
groupLore.add("§7There is no "+action+"d material for this "+containerType+".");
} else {
groupLore.add("§7List of "+action+"d materials for this "+containerType+":");
Iterator<Material> materialIterator = materialSet.iterator();
Iterator<NamespacedKey> materialIterator = materialSet.iterator();
boolean greaterThanMax = materialSet.size() > 5;
int maxindex = (greaterThanMax ? 4 : materialSet.size());
for (int i = 0; i < maxindex; i++) {
// format string like "- Stone Sword"
String formattedName = CasedStringUtil.snakeToUpperSpacedCase(materialIterator.next().name().toLowerCase());
String formattedName = CasedStringUtil.snakeToUpperSpacedCase(materialIterator.next().getKey().toLowerCase());
groupLore.add("§7- §e" + formattedName);
}

View file

@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
@ -52,7 +53,7 @@ public class SelectItemTypeGui extends AbstractAskGui {
event.setCancelled(true);
ItemStack cursor = event.getWhoClicked().getItemOnCursor();
if(cursor.getType().isAir()) return;
if(MaterialUtil.INSTANCE.isAir(cursor)) return;
ItemStack finalItem;
if(materialOnly){

View file

@ -14,6 +14,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil;
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui;
@ -283,7 +284,7 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
if(!this.packetManager.getCanSetInstantBuild()){
lore.add("");
lore.add("§4/!\\§cCaution§4/!\\ §cYou need ProtocoLib installed and working or a newer version of this plugin for this to work.");
lore.add("§4/!\\§cCaution§4/!\\ §cYou need ProtocoLib installed and working or a paper server.");
lore.add("§cCurrently ProtocoLib is not detected.");
}

View file

@ -17,7 +17,7 @@ import java.util.Locale;
*/
public class EnchantLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsGui.IntSettingFactory> {
private static final String SECTION_NAME = "enchant_limits";
private static final String SECTION_NAME = ConfigOptions.ENCHANT_LIMIT_ROOT;
private static EnchantLimitConfigGui INSTANCE = null;
@ -41,18 +41,34 @@ public class EnchantLimitConfigGui extends AbstractEnchantConfigGui<IntSettingsG
String key = enchant.getKey().toString().toLowerCase(Locale.ROOT);
String prettyKey = CasedStringUtil.snakeToUpperSpacedCase(key.replace(":", "_"));
var defaultValue = enchant.defaultMaxLevel();
return new IntSettingsGui.IntSettingFactory(prettyKey + " Limit", this,
SECTION_NAME + '.' + key, ConfigHolder.DEFAULT_CONFIG,
Collections.singletonList(
"§7Maximum applied level of " + prettyKey
),
0, 255,
enchant.defaultMaxLevel(),
-1, 255, -1,
1, 5, 10, 50, 100){
@Override
public int getConfiguredValue() {
return ConfigOptions.INSTANCE.enchantLimit(enchant);
var value = ConfigOptions.INSTANCE.rawEnchantLimit(enchant);
return Math.min(value, ConfigOptions.ENCHANT_LIMIT);
}
@Override
public String valueDisplayName(IntSettingsGui.ValueDisplayType type, int value) {
if(value < 0) {
return switch (type) {
case CURRENT -> "Default (" + defaultValue + ")";
case RESET -> String.valueOf(defaultValue);
default -> "Default";
};
}
else return super.valueDisplayName(type, value);
}
};
}

View file

@ -5,6 +5,7 @@ import com.github.stefvanschie.inventoryframework.pane.PatternPane;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
@ -325,19 +326,19 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
// ----------------------------
@Override
public EnumSet<Material> getSelectedMaterials() {
public Set<NamespacedKey> getSelectedMaterials() {
return this.group.getNonGroupInheritedMaterials();
}
@Override
public boolean setSelectedMaterials(EnumSet<Material> materials) {
public boolean setSelectedMaterials(Set<NamespacedKey> materials) {
this.group.setNonGroupInheritedMaterials(materials);
// Write to file configuration
String[] groupNames = new String[materials.size()];
int index = 0;
for (Material otherGroup : materials) {
groupNames[index++] = otherGroup.name().toLowerCase();
for (NamespacedKey otherGroup : materials) {
groupNames[index++] = otherGroup.getKey().toLowerCase();
}
ConfigHolder.ITEM_GROUP_HOLDER.getConfig().set(this.group.getName()+"."+ItemGroupManager.MATERIAL_LIST_PATH, groupNames);
@ -353,8 +354,8 @@ public class GroupConfigSubSettingGui extends MappedToListSubSettingGui implemen
}
@Override
public EnumSet<Material> illegalMaterials() {
return EnumSet.of(Material.AIR);
public Set<NamespacedKey> illegalMaterials() {
return Set.of(Material.AIR.getKey());
}
// ----------------------------

View file

@ -72,7 +72,8 @@ public class IntSettingsGui extends AbstractSettingGui {
assert meta != null;
meta.setDisplayName("§eReset to default value");
meta.setLore(Collections.singletonList("§7Default value is §e" + holder.defaultVal));
meta.setLore(Collections.singletonList("§7Default value is §e" +
holder.valueDisplayName(ValueDisplayType.RESET, holder.defaultVal)));
item.setItemMeta(meta);
returnToDefault = new GuiItem(item, event -> {
event.setCancelled(true);
@ -86,41 +87,23 @@ public class IntSettingsGui extends AbstractSettingGui {
* Update item using the setting value to match the new value.
*/
protected void updateValueDisplay() {
PatternPane pane = getPane();
// minus item
GuiItem minusItem;
if (now > holder.min) {
int planned = Math.max(holder.min, now - step);
ItemStack item = new ItemStack(Material.RED_TERRACOTTA);
ItemMeta meta = item.getItemMeta();
assert meta != null;
meta.setDisplayName("§e" + now + " §f-> §e" + planned + " §r(§c-" + (now - planned) + "§r)");
meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
item.setItemMeta(meta);
minusItem = new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
minusItem = valueEditItem(Material.RED_TERRACOTTA, ValueDisplayType.REMOVE, planned);
} else {
minusItem = GuiGlobalItems.backgroundItem(Material.BARRIER);
}
pane.bindItem('-', minusItem);
//plus item
// may do a function to generalise ?
GuiItem plusItem;
if (now < holder.max) {
int planned = Math.min(holder.max, now + step);
ItemStack item = new ItemStack(Material.GREEN_TERRACOTTA);
ItemMeta meta = item.getItemMeta();
assert meta != null;
meta.setDisplayName("§e" + now + " §f-> §e" + planned + " §r(§a+" + (planned - now) + "§r)");
meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
item.setItemMeta(meta);
plusItem = new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
plusItem = valueEditItem(Material.GREEN_TERRACOTTA, ValueDisplayType.ADD, planned);
} else {
plusItem = GuiGlobalItems.backgroundItem(Material.BARRIER);
}
@ -131,7 +114,7 @@ public class IntSettingsGui extends AbstractSettingGui {
ItemMeta resultMeta = resultPaper.getItemMeta();
assert resultMeta != null;
resultMeta.setDisplayName("§fValue: §e" + now);
resultMeta.setDisplayName("§fValue: §e" + holder.valueDisplayName(ValueDisplayType.CURRENT, now));
resultMeta.setLore(holder.displayLore);
resultPaper.setItemMeta(resultMeta);
@ -149,7 +132,21 @@ public class IntSettingsGui extends AbstractSettingGui {
}
pane.bindItem('D', returnToDefault);
}
private GuiItem valueEditItem(Material mat, ValueDisplayType type, int planned) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
assert meta != null;
var nowDisplay = holder.valueDisplayName(type, now);
var plannedDisplay = holder.valueDisplayName(type, planned);
var deltaDisplay = holder.deltaDisplay(type, now, planned);
meta.setDisplayName("§e" + nowDisplay + " §f-> §e" + plannedDisplay + " §r(§c" + deltaDisplay + "§r)");
meta.setLore(Collections.singletonList(AbstractSettingGui.CLICK_LORE));
item.setItemMeta(meta);
return new GuiItem(item, updateNowConsumer(planned), CustomAnvil.instance);
}
/**
@ -389,6 +386,23 @@ public class IntSettingsGui extends AbstractSettingGui {
return getItem(itemMat, CasedStringUtil.detectToUpperSpacedCase(configPath));
}
protected String valueDisplayName(ValueDisplayType type, int value) {
return String.valueOf(value);
}
protected String deltaDisplay(ValueDisplayType type, int now, int planned) {
var delta = planned - now;
if(delta < 0) return "§c" + delta;
else return "§a+" + delta;
}
}
public enum ValueDisplayType {
ADD,
CURRENT,
REMOVE,
RESET,
}
}

View file

@ -5,6 +5,7 @@ import com.github.stefvanschie.inventoryframework.gui.type.util.Gui;
import com.github.stefvanschie.inventoryframework.pane.util.Pattern;
import io.delilaheve.CustomAnvil;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemFlag;
@ -18,18 +19,19 @@ import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.*;
import java.util.function.Consumer;
public class MaterialSelectSettingGui extends MappedElementListConfigGui<Material, GuiItem> {
public class MaterialSelectSettingGui extends MappedElementListConfigGui<NamespacedKey, GuiItem> {
private final SelectMaterialContainer selector;
private final Gui backGui;
private boolean instantRemove;
private final List<Material> defaultMaterials;
private final EnumSet<Material> illegalMaterials;
private final List<NamespacedKey> defaultMaterials;
private final Set<NamespacedKey> illegalMaterials;
private final int defaultMaterialHash;
private int nowMaterialHash;
@ -161,8 +163,7 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
// Save setting
EnumSet<Material> result = EnumSet.noneOf(Material.class);
result.addAll(this.elementGuiMap.keySet());
Set<NamespacedKey> result = new HashSet<>(this.elementGuiMap.keySet());
if(!this.selector.setSelectedMaterials(result)){
player.sendMessage("§cSomething went wrong while saving the change of value.");
@ -185,8 +186,8 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
ItemStack cursor = player.getItemOnCursor();
// Test if cursor material allowed
Material cursorMat = cursor.getType();
if(cursorMat.isAir()) return;
NamespacedKey cursorMat = MaterialUtil.INSTANCE.getCustomType(cursor);
if(MaterialUtil.INSTANCE.isAir(cursorMat)) return;
if(this.illegalMaterials.contains(cursorMat)) return;
// Update gui only if item did not exist before.
@ -201,12 +202,12 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
}
@Override
protected ItemStack createItemForGeneric(Material material) {
ItemStack item = new ItemStack(material);
protected ItemStack createItemForGeneric(NamespacedKey material) {
ItemStack item = new ItemStack(Objects.requireNonNull(MaterialUtil.INSTANCE.getMatFromKey(material)));
ItemMeta meta = item.getItemMeta();
if(meta == null) return item;
meta.setDisplayName("§a" + CasedStringUtil.snakeToUpperSpacedCase(material.name().toLowerCase()));
meta.setDisplayName("§a" + CasedStringUtil.snakeToUpperSpacedCase(material.getKey().toLowerCase()));
meta.setLore(Collections.singletonList("§7Click here to remove this material from the list"));
meta.addItemFlags(ItemFlag.values());
@ -216,22 +217,22 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
}
@Override
protected Collection<Material> getEveryDisplayableInstanceOfGeneric() {
protected Collection<NamespacedKey> getEveryDisplayableInstanceOfGeneric() {
return this.defaultMaterials;
}
@Override
protected void updateElement(Material material, GuiItem element) {
protected void updateElement(NamespacedKey material, GuiItem element) {
// Nothing happen here I think
}
@Override
protected GuiItem newElementRequested(Material material, GuiItem newItem) {
protected GuiItem newElementRequested(NamespacedKey material, GuiItem newItem) {
newItem.setAction(event -> {
if(this.instantRemove){
removeMaterial(material);
}else {
String materialName = CasedStringUtil.snakeToUpperSpacedCase(material.name().toLowerCase());
String materialName = CasedStringUtil.snakeToUpperSpacedCase(material.getKey().toLowerCase());
// Create and show confirm remove gui.
ConfirmActionGui confirmGui = new ConfirmActionGui(
@ -250,7 +251,7 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
return newItem;
}
private void removeMaterial(Material material) {
private void removeMaterial(NamespacedKey material) {
if(this.elementGuiMap.containsKey(material)){
this.nowMaterialHash ^= material.hashCode();
setSaveItem();
@ -260,18 +261,18 @@ public class MaterialSelectSettingGui extends MappedElementListConfigGui<Materia
}
@Override
protected GuiItem findItemFromElement(Material generic, GuiItem element) {
protected GuiItem findItemFromElement(NamespacedKey generic, GuiItem element) {
return element;
}
@Override
protected GuiItem findGuiItemForRemoval(Material generic, GuiItem element) {
protected GuiItem findGuiItemForRemoval(NamespacedKey generic, GuiItem element) {
return element;
}
private static int hashFromMaterialList(List<Material> materialList){
private static int hashFromMaterialList(List<NamespacedKey> materialList){
int defaultMaterialHash = 0;
for (Material material : materialList) {
for (NamespacedKey material : materialList) {
defaultMaterialHash ^= material.hashCode();
}
return defaultMaterialHash;

View file

@ -11,11 +11,11 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
import xyz.alexcrea.cuanvil.gui.config.global.BasicConfigGui;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.util.AnvilUseType;
import java.util.ArrayList;
import java.util.EnumMap;

View file

@ -5,6 +5,7 @@ import io.delilaheve.util.ConfigOptions;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.util.MetricType;
import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil;
import xyz.alexcrea.cuanvil.util.config.LoreEditType;
@ -18,6 +19,9 @@ public class PluginSetDefault {
int nbSet = 0;
nbSet += trySetDefault(config, METRIC_TYPE, MetricType.AUTO.getValue());
nbSet += trySetDefault(config, METRIC_COLLECT_ERROR, true);
nbSet += trySetDefault(config, CAP_ANVIL_COST, DEFAULT_CAP_ANVIL_COST);
nbSet += trySetDefault(config, MAX_ANVIL_COST, DEFAULT_MAX_ANVIL_COST);
nbSet += trySetDefault(config, REMOVE_ANVIL_COST_LIMIT, DEFAULT_REMOVE_ANVIL_COST_LIMIT);
@ -30,7 +34,7 @@ public class PluginSetDefault {
nbSet += trySetDefault(config, ALLOW_HEXADECIMAL_COLOR, DEFAULT_ALLOW_HEXADECIMAL_COLOR);
nbSet += trySetDefault(config, PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR);
nbSet += trySetDefault(config, USE_OF_COLOR_COST, DEFAULT_USE_OF_COLOR_COST);
nbSet += trySetDefault(config, DEFAULT_LIMIT_PATH, DEFAULT_ENCHANT_LIMIT);
nbSet += trySetDefault(config, PER_COLOR_CODE_PERMISSION, DEFAULT_PER_COLOR_CODE_PERMISSION);
// Lore Edit defaults
for (@NotNull LoreEditType value : LoreEditType.values()) {
@ -57,6 +61,11 @@ public class PluginSetDefault {
nbSet += trySetDefault(config, PAPER_EDIT_ORDER, DEFAULT_PAPER_EDIT_ORDER);
nbSet += trySetDefault(config, DIALOG_RENAME_ENABLED, DEFAULT_DIALOG_RENAME_ENABLED);
nbSet += trySetDefault(config, DIALOG_MAX_SIZE, DEFAULT_DIALOG_MAX_SIZE);
nbSet += trySetDefault(config, DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION);
nbSet += trySetDefault(config, DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT);
if (nbSet > 0) {
CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values.");
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);

View file

@ -77,6 +77,12 @@ public class UpdateHandler {
if (hadUpdate) {
CustomAnvil.instance.getLogger().info("Updating Done !");
}
if(current.major() == 1 && current.minor() < 21) {
var logger = CustomAnvil.instance.getLogger();
logger.warning("Your are running an old version of minecraft (lower than 1.21)");
logger.warning("Custom Anvil will stop supporting this version on the first of july 2026");
}
}
private static void finishConfiguration(@Nonnull String newVersion, @Nonnull Set<ConfigHolder> toSave) {

View file

@ -15,22 +15,6 @@ public class UpdateUtils {
return Version.fromString(versionString);
}
@Deprecated
public static int[] currentMinecraftVersionArray() {
String versionString = Bukkit.getServer().getBukkitVersion().split("-")[0];
return UpdateUtils.readVersionFromString(versionString);
}
public static int[] readVersionFromString(String versionString) {
String[] partialVersion = versionString.split("\\.");
int[] versionParts = new int[]{0, 0, 0};
for (int i = 0; i < Math.min(3, partialVersion.length); i++) {
versionParts[i] = Integer.parseInt(partialVersion[i]);
}
return versionParts;
}
public static void addToStringList(FileConfiguration config, String path, String... toAdd) {
List<String> groups = new ArrayList<>(config.getStringList(path));
groups.addAll(Arrays.asList(toAdd));

View file

@ -21,7 +21,11 @@ public record Version(int major, int minor, int patch) {
int[] versionParts = new int[]{0, 0, 0};
for (int i = 0; i < Math.min(3, partialVersion.length); i++) {
try {
versionParts[i] = Integer.parseInt(partialVersion[i]);
} catch (NumberFormatException e) {
break;
}
}
return new Version(versionParts[0], versionParts[1], versionParts[2]);
}

View file

@ -1,6 +1,7 @@
package xyz.alexcrea.cuanvil.update.plugin;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
@ -11,6 +12,7 @@ import xyz.alexcrea.cuanvil.group.AbstractMaterialGroup;
import xyz.alexcrea.cuanvil.group.IncludeGroup;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@ -69,7 +71,12 @@ public class PUpdate_1_11_0 {
// Create new group
IncludeGroup group = new IncludeGroup(toolset);
group.addAll(toolMats);
NamespacedKey[] keys = new NamespacedKey[toolMats.length];
for (int i = 0; i < toolMats.length; i++) {
keys[i] = toolMats[i].getKey();
}
group.addAll(keys);
MaterialGroupApi.addMaterialGroup(group, true);
@ -77,8 +84,8 @@ public class PUpdate_1_11_0 {
if (tools == null) return;
if (!(tools instanceof IncludeGroup include)) return;
List<Material> mats = List.of(toolMats);
Set<Material> matSet = include.getNonGroupInheritedMaterials();
List<NamespacedKey> mats = List.of(keys);
Set<NamespacedKey> matSet = include.getNonGroupInheritedMaterials();
if (!matSet.containsAll(mats)) return;
mats.forEach(matSet::remove);

View file

@ -2,10 +2,10 @@ package xyz.alexcrea.cuanvil.update.plugin;
import io.delilaheve.util.ConfigOptions;
import org.bukkit.configuration.file.FileConfiguration;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.config.WorkPenaltyType;
import xyz.alexcrea.cuanvil.gui.config.settings.WorkPenaltyTypeSettingGui;
import xyz.alexcrea.cuanvil.util.AnvilUseType;
import javax.annotation.Nonnull;
import java.util.EnumMap;

View file

@ -11,6 +11,8 @@ import xyz.alexcrea.cuanvil.command.EditConfigExecutor
import xyz.alexcrea.cuanvil.command.ReloadExecutor
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
@ -194,6 +196,10 @@ open class CustomAnvil : JavaPlugin() {
if(!isPaper) {
logger.warning("It seems you are using spigot")
logger.warning("Please take notice that spigot is less supported than paper and derivatives")
if(MinecraftVersionUtil.isTooNewForSpigot) {
logger.warning("If replace too expensive is not working this is likely because of spigot")
logger.warning("As native nms is not supported for spigot starting 26.1")
}
}
val loader = if(isPaper) "paper" else "spigot"
@ -205,7 +211,6 @@ open class CustomAnvil : JavaPlugin() {
.setFeatured(featured)
.setOnError {
logger.log(Level.WARNING, "error trying to fetch latest update", it)
MetricsUtil.trackError(it)
}
.checkVersion { latestVer: String? ->
CustomAnvil.latestVer = latestVer
@ -236,7 +241,8 @@ open class CustomAnvil : JavaPlugin() {
// Load config
if (!ConfigHolder.loadNonDefaultConfig()) {
logger.log(Level.SEVERE,"could not load non default config.")
logger.log(Level.SEVERE,"Plugin has an issue while trying to load non default config... exiting...")
server.pluginManager.disablePlugin(this)
return
}
@ -254,9 +260,11 @@ open class CustomAnvil : JavaPlugin() {
MainConfigGui.getInstance().init(DependencyManager.packetManager)
GuiSharedConstant.loadConstants()
// Prepare economy if possible
EconomyManager.setupEconomy(this)
// Finally, re add default we may be missing
PluginSetDefault.reAddMissingDefault()
}
fun reloadResource(

View file

@ -2,14 +2,17 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil
import io.delilaheve.util.EnchantmentUtil.enchantmentName
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.config.WorkPenaltyType
import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.util.AnvilUseType
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
import java.math.BigDecimal
import java.util.*
/**
@ -45,6 +48,8 @@ object ConfigOptions {
const val PERMISSION_NEEDED_FOR_COLOR = "permission_needed_for_color"
const val USE_OF_COLOR_COST = "use_of_color_cost"
const val PER_COLOR_CODE_PERMISSION = "per_color_code_permission"
// Work penalty config
const val WORK_PENALTY_ROOT = "work_penalty"
const val WORK_PENALTY_INCREASE = "shared_increase"
@ -57,15 +62,25 @@ object ConfigOptions {
const val ENCHANT_COUNT_LIMIT_DEFAULT = "$ENCHANT_COUNT_LIMIT_ROOT.default"
const val ENCHANT_COUNT_LIMIT_ITEMS = "$ENCHANT_COUNT_LIMIT_ROOT.items"
const val DEFAULT_LIMIT_PATH = "default_limit"
const val ENCHANT_LIMIT_ROOT = "enchant_limits"
const val ENCHANT_VALUES_ROOT = "enchant_values"
// Dialog menu rename
const val DIALOG_RENAME_ENABLED = "enable_dialog_rename"
const val DIALOG_MAX_SIZE = "dialog_rename_max_size"
const val DIALOG_RENAME_USE_PERMISSION = "permission_needed_for_dialog_rename"
const val DIALOG_KEEP_USER_TEXT = "dialog_rename_keep_user_text"
// Others
const val DISABLE_MERGE_OVER_ROOT = "disable-merge-over"
const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments"
// Monetary configs
const val MONETARY_USAGE_ROOT = "monetary_cost"
const val SHOULD_USE_MONEY = "$MONETARY_USAGE_ROOT.enabled"
const val MONEY_CURRENCY = "$MONETARY_USAGE_ROOT.currency"
const val MONETARY_MULTIPLIER_ROOT = "$MONETARY_USAGE_ROOT.multipliers"
// Keys for specific enchantment values
private const val KEY_BOOK = "book"
@ -102,12 +117,23 @@ object ConfigOptions {
const val DEFAULT_PERMISSION_NEEDED_FOR_COLOR = true
const val DEFAULT_USE_OF_COLOR_COST = 0
const val DEFAULT_ENCHANT_LIMIT = 5
const val DEFAULT_PER_COLOR_CODE_PERMISSION = false
// Monetary configs
const val DEFAULT_SHOULD_USE_MONEY = false
const val DEFAULT_MONEY_CURRENCY = "default"
const val DEFAULT_MONEY_MULTIPLIER = 1.0
// Debug flag
private const val DEFAULT_DEBUG_LOG = false
private const val DEFAULT_VERBOSE_DEBUG_LOG = false
// Dialog menu rename
const val DEFAULT_DIALOG_RENAME_ENABLED = false
const val DEFAULT_DIALOG_MAX_SIZE = 256
const val DEFAULT_DIALOG_RENAME_USE_PERMISSION = false
const val DEFAULT_DIALOG_KEEP_USER_TEXT = true
// -------------
// Config Ranges
// -------------
@ -132,9 +158,11 @@ object ConfigOptions {
@JvmField
val USE_OF_COLOR_COST_RANGE = 0..1000
// Valid range for an enchantment limit
@JvmField
val ENCHANT_LIMIT_RANGE = 1..255
val DIALOG_MAX_SIZE_RANGE = 0..Int.MAX_VALUE
// Valid range for an enchantment limit
const val ENCHANT_LIMIT = 255
// Valid range for an enchantment count limit
@JvmField
@ -150,6 +178,11 @@ object ConfigOptions {
// Default max before merge disabled (negative mean enabled)
const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1
// -----------
// Permissions
// -----------
private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog"
// -------------
// Get methods
// -------------
@ -302,6 +335,16 @@ object ConfigOptions {
.getBoolean(PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR)
}
/**
* Should each color code require a permission
*/
val usePerColorCodePermission: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(PER_COLOR_CODE_PERMISSION, DEFAULT_PER_COLOR_CODE_PERMISSION)
}
/**
* How many xp should use of color should cost
*/
@ -348,22 +391,12 @@ object ConfigOptions {
return WorkPenaltyPart(penaltyIncrease, penaltyAdditive, exclusivePenaltyIncrease, exclusivePenaltyAdditive)
}
/**
* Default enchantment limit
*/
private val defaultEnchantLimit: Int
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getInt(DEFAULT_LIMIT_PATH, DEFAULT_ENCHANT_LIMIT)
}
/**
* Get material enchantment count limit
*
* @return the current enchantment limit. -1 if none
*/
fun getEnchantCountLimit(type: Material): Int? {
fun getEnchantCountLimit(type: NamespacedKey): Int? {
val limit = materialEnchantCountLimit(type)
if(limit != null) return limit
@ -377,8 +410,8 @@ object ConfigOptions {
*
* @return The current enchantment limit. -1 if none
*/
private fun materialEnchantCountLimit(type: Material): Int? {
val path = "$ENCHANT_COUNT_LIMIT_ITEMS.${type.key.key.lowercase()}"
private fun materialEnchantCountLimit(type: NamespacedKey): Int? {
val path = "$ENCHANT_COUNT_LIMIT_ITEMS.${type.key.lowercase()}"
if(!ConfigHolder.DEFAULT_CONFIG.config.isInt(path))
return null
@ -418,46 +451,90 @@ object ConfigOptions {
.getBoolean(VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG)
}
/**
* Is the dialog menu for rename enabled
*/
val doRenameDialog: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(DIALOG_RENAME_ENABLED, DEFAULT_DIALOG_RENAME_ENABLED)
}
/**
* Do the dialog menu require permission
*/
val doRenameDialogUsePermission: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION)
}
fun canUseDialogRename(player: HumanEntity): Boolean {
if(!doRenameDialog || !AnvilRenameDialogUtil.anvilRenameDialog.canSendDialog()) return false
if(doRenameDialogUsePermission && !player.hasPermission(RENAME_DIALOG_PERMISSION)) return false
return true
}
/**
* Do the dialog menu require permission
*/
val renameDialogMaxSize: Int
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getInt(DIALOG_MAX_SIZE, DEFAULT_DIALOG_MAX_SIZE)
.takeIf { it in DIALOG_MAX_SIZE_RANGE }
?: Int.MAX_VALUE
}
/**
* Should the text used for rename should be kept in the item's pdc
*/
val shouldKeepRenameText: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT)
}
/**
* Get the given [enchantment]'s limit
*/
fun enchantLimit(enchantment: CAEnchantment): Int {
val limit = rawEnchantLimit(enchantment)
if(limit >= 0) return limit.coerceAtMost(ENCHANT_LIMIT)
// get default
return enchantment.defaultMaxLevel()
}
/**
* Get the given [enchantment]'s limit
*/
fun rawEnchantLimit(enchantment: CAEnchantment): Int {
// Test namespace
var limit = enchantLimit(enchantment.key.toString())
if (limit != null) return limit
if (limit >= 0) return limit
// Test legacy (name only)
limit = enchantLimit(enchantment.enchantmentName)
if (limit != null) return limit
if (limit >= 0) return limit
// get default (and test old legacy if present)
return getDefaultLevel(enchantment.enchantmentName)
// Default to negative
return -1
}
/**
* Get the given [enchantmentName]'s limit
*/
private fun enchantLimit(enchantmentName: String): Int? {
private fun enchantLimit(enchantmentName: String): Int {
val path = "${ENCHANT_LIMIT_ROOT}.$enchantmentName"
return CustomAnvil.instance
.config
.getInt(path, ENCHANT_LIMIT_RANGE.first - 1)
.takeIf { it in ENCHANT_LIMIT_RANGE }
}
/**
* Get default value if enchantment do not exist on config
*/
private fun getDefaultLevel(
enchantmentName: String, // compatibility with 1.20.5. TODO better update system
): Int {
if (enchantmentName == "sweeping_edge") {
val limit = enchantLimit("sweeping")
if (limit != null) return limit
}
return defaultEnchantLimit
return CustomAnvil.instance.config
.getInt(path, -1)
}
/**
@ -529,20 +606,20 @@ object ConfigOptions {
fun maxBeforeMergeDisabled(enchantment: CAEnchantment): Int {
val key = enchantment.key.toString()
var value = maxBeforeMergeDisabled(key)
if (value != null) return value
if (value >= 0) return value
// Legacy name
val legacy = enchantment.enchantmentName
value = maxBeforeMergeDisabled(legacy)
if (value != null) return value
if (value >= 0) return value
if (key == "minecraft:sweeping_edge") {
value = maxBeforeMergeDisabled("minecraft:sweeping")
if (value != null) return value
if (value >= 0) return value
// legacy name of legacy enchantment name
value = maxBeforeMergeDisabled("sweeping")
if (value != null) return value
if (value >= 0) return value
}
return DEFAULT_MAX_BEFORE_MERGE_DISABLED
@ -552,14 +629,13 @@ object ConfigOptions {
* Get the given [enchantmentName]'s level before merge is disabled
* a negative value would mean never disabled
*/
private fun maxBeforeMergeDisabled(enchantmentName: String): Int? {
private fun maxBeforeMergeDisabled(enchantmentName: String): Int {
// find if set
val path = "${DISABLE_MERGE_OVER_ROOT}.$enchantmentName"
return CustomAnvil.instance
.config
.getInt(path, ENCHANT_LIMIT_RANGE.min() - 1)
.takeIf { it in ENCHANT_LIMIT_RANGE }
.getInt(path, -1)
}
fun isImmutable(key: NamespacedKey): Boolean {
@ -575,4 +651,29 @@ object ConfigOptions {
return false
}
/*
* Monetary configs (only for 1.21.6+)
* Also require dialog rename
*/
fun shouldUseMoney(player: HumanEntity): Boolean {
return EconomyManager.economy?.initialized() == true &&
canUseDialogRename(player) &&
ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(SHOULD_USE_MONEY, DEFAULT_SHOULD_USE_MONEY)
}
val usedCurrency: String
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getString(MONEY_CURRENCY, DEFAULT_MONEY_CURRENCY)!!
}
fun getMonetaryMultiplier(type: String): BigDecimal {
return BigDecimal(ConfigHolder.DEFAULT_CONFIG
.config
.getDouble("$MONETARY_MULTIPLIER_ROOT.$type", DEFAULT_MONEY_MULTIPLIER))
}
}

View file

@ -6,6 +6,7 @@ import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.group.ConflictType
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import kotlin.math.max
import kotlin.math.min
@ -34,7 +35,7 @@ object EnchantmentUtil {
val bypassFuse = player.hasPermission(CustomAnvil.bypassFusePermission)
val bypassLevel = player.hasPermission(CustomAnvil.bypassLevelPermission)
var maxEnchantCount = ConfigOptions.getEnchantCountLimit(item.type)
var maxEnchantCount = ConfigOptions.getEnchantCountLimit(item.customType)
if(maxEnchantCount == null || maxEnchantCount < 0) maxEnchantCount = Int.MAX_VALUE
val allowed = other.filter { (enchantment, _) -> enchantment.isAllowed(player) }

View file

@ -4,6 +4,9 @@ import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.update.UpdateUtils
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import xyz.alexcrea.cuanvil.util.MaxDamageCheckerUtil
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@ -35,6 +38,13 @@ object ItemUtil {
}
private fun maxDamage(damageable: Damageable): Int {
val ver = UpdateUtils.currentMinecraftVersion()
if(ver.major <= 1 && ver.minor <= 20 && ver.patch < 5) return Integer.MAX_VALUE
return MaxDamageCheckerUtil.getMaxDamage(damageable)
}
/**
* Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values
@ -54,7 +64,9 @@ object ItemUtil {
val secondDurability = durability - secondDamage
val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, durability)
it.damage = durability - newDurability
val maxDamage = maxDamage(it)
it.damage = min(durability - newDurability, maxDamage)
itemMeta = it
return true
}
@ -90,5 +102,5 @@ object ItemUtil {
*/
fun ItemStack.canMergeWith(
other: ItemStack?
) = (other != null) && (type == other.type || (other.isEnchantedBook()))
) = (other != null) && (customType == other.customType || (other.isEnchantedBook()))
}

View file

@ -0,0 +1,72 @@
package xyz.alexcrea.cuanvil.anvil
import io.delilaheve.util.ConfigOptions
import java.math.BigDecimal
import kotlin.math.min
import io.delilaheve.util.ConfigOptions.getMonetaryMultiplier as moneyMultiplier
open class AnvilCost {
private val isAlone: Boolean
var valid = true // Get set as invalid if cost can be satisfied
var isMonetary = false
var generic = 0
var enchantment = 0
var repair = 0
var rename = 0
var lore = 0
var illegalPenalty = 0
var workPenalty = 0
var recipe = 0
constructor(generic: Int) {
this.generic = generic
isAlone = true
}
constructor() {
isAlone = false
}
fun asXpCost(): Int {
return generic + enchantment + repair + rename + lore + illegalPenalty + workPenalty + recipe
}
fun filteredXpCost(ignoreRules: Boolean = false): Int {
val original = asXpCost()
// Test repair cost limit
return if (
!ignoreRules &&
!ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost
) {
min(original, ConfigOptions.maxAnvilCost)
} else {
original
}
}
open fun asMonetaryCost(): BigDecimal {
// multiply by per use type multipliers
return BigDecimal(generic)
.add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
.add(BigDecimal(repair).multiply(moneyMultiplier("repair")))
.add(BigDecimal(rename).multiply(moneyMultiplier("rename")))
.add(BigDecimal(lore).multiply(moneyMultiplier("lore_edit")))
.add(BigDecimal(enchantment).multiply(moneyMultiplier("enchantment")))
.add(BigDecimal(illegalPenalty).multiply(moneyMultiplier("work_penalty")))
.add(BigDecimal(workPenalty).multiply(moneyMultiplier("work_penalty")))
.add(BigDecimal(recipe).multiply(moneyMultiplier("recipe")))
.multiply(moneyMultiplier("global"))
}
}
class CustomCraftCost(val rawCost: Int): AnvilCost() {
override fun asMonetaryCost(): BigDecimal {
return BigDecimal(rawCost)
.multiply(moneyMultiplier("global"))
}
}

View file

@ -0,0 +1,331 @@
package xyz.alexcrea.cuanvil.anvil
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.ChatColor
import org.bukkit.Material
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.persistence.PersistentDataType
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.CasedStringUtil
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir
import xyz.alexcrea.cuanvil.util.MiniMessageUtil
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import xyz.alexcrea.cuanvil.util.anvil.AnvilColorUtil
import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.config.LoreEditType
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
object AnvilMergeLogic {
open class AnvilResult {
companion object {
val EMPTY = AnvilResult(null, AnvilCost())
}
val item: ItemStack?
val cost: AnvilCost
val ignoreXpRules: Boolean
constructor(item: ItemStack?, cost: AnvilCost, ignoreXpRules: Boolean = false) {
this.item = item
this.cost = cost
this.ignoreXpRules = ignoreXpRules
}
fun isEmpty(): Boolean {
return item == null
}
}
class UnitRepairResult : AnvilResult {
companion object {
val EMPTY = UnitRepairResult(null, AnvilCost(), 0)
}
val repairAmount: Int
constructor(item: ItemStack?, cost: AnvilCost, repairAmount: Int) : super(item, cost) {
this.repairAmount = repairAmount
}
}
class CustomCraftResult : AnvilResult {
companion object {
val EMPTY = CustomCraftResult(null, CustomCraftCost(0), 0, null)
}
val customCraftCost: CustomCraftCost
val amount: Int
val recipe: AnvilCustomRecipe?
constructor(
item: ItemStack?, cost: CustomCraftCost,
amount: Int, recipe: AnvilCustomRecipe?
) : super(item, cost, true) {
this.customCraftCost = cost
this.amount = amount
this.recipe = recipe
}
}
class LoreEditResult : AnvilResult {
companion object {
val EMPTY = LoreEditResult(null, AnvilCost(), LoreEditType.APPEND_PAPER)
}
val type: LoreEditType
constructor(item: ItemStack?, cost: AnvilCost, type: LoreEditType) : super(item, cost) {
this.type = type
}
}
fun doRenaming(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory,
player: Player, first: ItemStack
): AnvilResult {
val resultItem = DependencyManager.cloneItem(player, first)
val cost = AnvilCost()
cost.rename = handleRename(resultItem, inventory, player)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("no right item, But input is same as output")
return AnvilResult.EMPTY
}
cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
val result =
DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost)
return AnvilResult(result, cost)
}
private fun processDialogPCD(meta: ItemMeta, player: HumanEntity) {
val text = AnvilRenameDialogUtil.anvilRenameDialog.currentText(player)
return processPCD(meta, player, text)
}
fun processPCD(meta: ItemMeta, player: HumanEntity, text: String?) {
val keepDialog = ConfigOptions.canUseDialogRename(player) && ConfigOptions.shouldKeepRenameText
val pdc = meta.persistentDataContainer
if (!keepDialog)
pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY)
else {
if (text == null || text.isBlank())
pdc.remove(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY)
else pdc.set(AnvilRenameDialog.PCD_KEEP_RENAME_TEXT_KEY, PersistentDataType.STRING, text)
}
}
private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int {
// Can be null
var renameText = ChatColor.stripColor(inventory.renameText)
var sumCost = 0
var useColor = false
if (ConfigOptions.renameColorPossible && renameText != null) {
val component = AnvilColorUtil.handleColor(
renameText,
AnvilColorUtil.renamePermission(player)
)
if (component != null) {
renameText = MiniMessageUtil.legacy_mm.serialize(component)
sumCost += ConfigOptions.useOfColorCost
useColor = true
}
}
// Rename item and add renaming cost
resultItem.itemMeta?.let {
val hasDisplayName = it.hasDisplayName()
val displayName = if (!hasDisplayName) null
else if (useColor) it.displayName
else ChatColor.stripColor(it.displayName)
if (!displayName.contentEquals(renameText) && !(displayName == null &&
renameText == "" ||
//TODO on recent paper check effective name instead
renameText == CasedStringUtil.snakeToUpperSpacedCase(resultItem.type.name.lowercase())
)
) {
it.setDisplayName(renameText)
processDialogPCD(it, player)
resultItem.itemMeta = it
sumCost += ConfigOptions.itemRenameCost
}
return sumCost
}
return 0
}
fun doMerge(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory,
player: Player,
first: ItemStack, second: ItemStack
): AnvilResult {
val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player)
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
val resultItem = DependencyManager.cloneItem(player, first)
val cost = AnvilCost()
if (hasChanged) {
resultItem.setEnchantmentsUnsafe(newEnchants)
// Calculate enchantment cost
AnvilXpUtil.getRightValues(second, resultItem, cost)
}
// Calculate repair cost
if (!first.isEnchantedBook() && !second.isEnchantedBook()) {
// we only need to be concerned with repair when neither item is a book
val repaired = resultItem.repairFrom(first, second)
cost.repair = if (repaired) ConfigOptions.itemRepairCost else 0
hasChanged = hasChanged || repaired
}
// Test/stop if nothing changed.
if (!hasChanged) {
CustomAnvil.log("Mergeable with second, But input is same as output")
return AnvilResult.EMPTY
}
// As calculatePenalty edit result, we need to calculate penalty after checking equality
cost.workPenalty = AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE)
// Calculate rename cost
cost.rename = handleRename(resultItem, inventory, player)
val result =
DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost)
return AnvilResult(result, cost)
}
private fun isIdentical(
firstEnchants: MutableMap<CAEnchantment, Int>,
resultEnchants: MutableMap<CAEnchantment, Int>
): Boolean {
if (firstEnchants.size != resultEnchants.size) return false
for (entry in resultEnchants) {
if (firstEnchants.getOrDefault(entry.key, entry.value - 1) != entry.value) return false
}
return true
}
// return true if a custom recipe exist with these ingredients
fun testCustomRecipe(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory,
player: Player,
first: ItemStack, second: ItemStack?
): CustomCraftResult {
val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
if (recipe == null) return CustomCraftResult.EMPTY
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
val resultItem: ItemStack = DependencyManager.cloneItem(player, recipe.resultItem!!)
resultItem.amount *= amount
// Maybe add an option on custom craft to ignore/not ignore penalty ??
val xpCost = recipe.determineCost(amount, first, resultItem)
val cost = CustomCraftCost(xpCost)
// This is for displayed cost
cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
else AnvilXpUtil.calculateLevelForXp(xpCost)
val result =
DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost)
return CustomCraftResult(result, cost, amount, recipe)
}
fun testUnitRepair(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory,
player: Player,
first: ItemStack, second: ItemStack
): UnitRepairResult {
val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY
return testUnitRepair(view, inventory, player, first, second, unitRepairAmount)
}
fun testUnitRepair(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory,
player: Player,
first: ItemStack, second: ItemStack,
unitRepairAmount: Double
): UnitRepairResult {
val resultItem = DependencyManager.cloneItem(player, first)
val cost = AnvilCost()
cost.rename = handleRename(resultItem, inventory, player)
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
if (repairAmount > 0)
cost.repair = repairAmount * ConfigOptions.unitRepairCost
// We do not care about right item penalty for unit repair
cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.UNIT_REPAIR)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("unit repair, But input is same as output")
return UnitRepairResult.EMPTY
}
val result =
DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost)
return UnitRepairResult(result, cost, repairAmount)
}
fun testLoreEdit(
player: Player,
first: ItemStack, second: ItemStack
): LoreEditResult {
val type = second.type
val result = if (Material.WRITABLE_BOOK == type)
AnvilLoreEditUtil.tryLoreEditByBook(player, first, second)
else if (Material.PAPER == type)
AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second)
else LoreEditResult.EMPTY
if (result.isEmpty()) return result
if (result.item!!.isAir || first == result.item) {
CustomAnvil.log("lore edit, But input is same as output")
return LoreEditResult.EMPTY
}
return result
}
}

View file

@ -1,60 +1,60 @@
package xyz.alexcrea.cuanvil.util
package xyz.alexcrea.cuanvil.anvil
import org.bukkit.Material
import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart
import xyz.alexcrea.cuanvil.util.config.LoreEditType
import xyz.alexcrea.cuanvil.config.WorkPenaltyType
import xyz.alexcrea.cuanvil.util.anvil.AnvilUseTypeUtil
enum class AnvilUseType(
val typeName: String, val path: String,
val defaultPenalty: WorkPenaltyPart,
val defaultPenalty: WorkPenaltyType.WorkPenaltyPart,
val displayName: String, val displayMat: Material
) {
RENAME_ONLY(
"rename_only",
WorkPenaltyPart(false, true),
WorkPenaltyType.WorkPenaltyPart(false, true),
"Rename Only", Material.NAME_TAG
),
MERGE(
"merge",
WorkPenaltyPart(true, true),
WorkPenaltyType.WorkPenaltyPart(true, true),
"Merge", Material.ANVIL
),
UNIT_REPAIR(
"unit_repair",
WorkPenaltyPart(true, true),
WorkPenaltyType.WorkPenaltyPart(true, true),
"Unit Repair", Material.DIAMOND
),
CUSTOM_CRAFT(
"custom_craft",
WorkPenaltyPart(false, false),
WorkPenaltyType.WorkPenaltyPart(false, false),
"Custom Craft", Material.CRAFTING_TABLE
),
LORE_EDIT_BOOK_APPEND(
"lore_edit_book_append", "lore_edit.book_and_quil.append",
WorkPenaltyPart(false, false),
WorkPenaltyType.WorkPenaltyPart(false, false),
"Book Add", Material.WRITABLE_BOOK
),
LORE_EDIT_BOOK_REMOVE(
"lore_edit_book_remove", "lore_edit.book_and_quil.remove",
WorkPenaltyPart(false, false),
WorkPenaltyType.WorkPenaltyPart(false, false),
"Book Remove", Material.WRITABLE_BOOK
),
LORE_EDIT_PAPER_APPEND(
"lore_edit_paper_append", "lore_edit.paper.append_line",
WorkPenaltyPart(false, false),
WorkPenaltyType.WorkPenaltyPart(false, false),
"Paper Add", Material.WRITABLE_BOOK
),
LORE_EDIT_PAPER_REMOVE(
"lore_edit_paper_remove", "lore_edit.paper.remove_line",
WorkPenaltyPart(false, false),
WorkPenaltyType.WorkPenaltyPart(false, false),
"Paper Remove", Material.WRITABLE_BOOK
),
;
constructor(
typeName: String,
defaultPenalty: WorkPenaltyPart,
defaultPenalty: WorkPenaltyType.WorkPenaltyPart,
displayName: String, displayMat: Material
) :
this(

View file

@ -16,7 +16,7 @@ abstract class CASubCommand: CommandExecutor {
): Boolean {
if(!alreadySaid){
sender.sendMessage(ChatColor.RED.toString() +
"Please not that this command will be replaced as a subcommand of `/customanvil`")
"Please not that this command will be replaced as a subcommand of `/customanvil` or `/ca`")
alreadySaid = true
}

View file

@ -26,7 +26,6 @@ import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.packet.NoPacketManager
import xyz.alexcrea.cuanvil.dependency.packet.ProtocoLibWrapper
import xyz.alexcrea.cuanvil.dependency.packet.versions.PaperPacketManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.util.MetricsUtil

View file

@ -1,22 +1,28 @@
package xyz.alexcrea.cuanvil.dependency
import com.maddoxh.superEnchants.SuperEnchants
import io.delilaheve.CustomAnvil
import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CAPreAnvilBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency
import xyz.alexcrea.cuanvil.dependency.gui.GenericExternGuiTester
import xyz.alexcrea.cuanvil.dependency.gui.GuiTesterSelector
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.dependency.packet.PacketManagerSelector
import xyz.alexcrea.cuanvil.dependency.plugins.*
@ -26,7 +32,6 @@ import xyz.alexcrea.cuanvil.dependency.scheduler.TaskScheduler
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT
import xyz.alexcrea.cuanvil.util.AnvilUseType
import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError
import java.util.logging.Level
@ -34,7 +39,7 @@ object DependencyManager {
lateinit var scheduler: TaskScheduler
lateinit var packetManager: PacketManager
var externGuiTester: GenericExternGuiTester? = null
var externGuiTester: GenericExternGuiTester = GenericExternGuiTester()
var enchantmentSquaredCompatibility: EnchantmentSquaredDependency? = null
var ecoEnchantCompatibility: EcoEnchantDependency? = null
@ -45,6 +50,8 @@ object DependencyManager {
var axPlayerWarpsCompatibility: AxPlayerWarpsDependency? = null
var itemsAdderCompatibility: ItemsAdderDependency? = null
val genericDependencies = ArrayList<GenericPluginDependency>()
fun loadDependency() {
@ -60,7 +67,6 @@ object DependencyManager {
// Packet Manager
val forceProtocolib = ConfigHolder.DEFAULT_CONFIG.config.getBoolean("force_protocolib", false)
packetManager = PacketManagerSelector.selectPacketManager(forceProtocolib)
externGuiTester = GuiTesterSelector.selectGuiTester
// Enchantment Squared dependency
if (pluginManager.isPluginEnabled("EnchantsSquared")) {
@ -97,6 +103,12 @@ object DependencyManager {
axPlayerWarpsCompatibility = AxPlayerWarpsDependency()
}
if (pluginManager.isPluginEnabled("ItemsAdder")){
val dependency = ItemsAdderDependency(pluginManager.getPlugin("ItemsAdder")!!)
itemsAdderCompatibility = dependency
genericDependencies.add(dependency)
}
// "Generic" dependencies
if (pluginManager.isPluginEnabled("ToolStats"))
genericDependencies.add(ToolStatsDependency(pluginManager.getPlugin("ToolStats")!!))
@ -104,6 +116,12 @@ object DependencyManager {
if (pluginManager.isPluginEnabled("ItemsAdder"))
genericDependencies.add(GenericPluginDependency(pluginManager.getPlugin("ItemsAdder")!!))
if (pluginManager.isPluginEnabled("SuperEnchants")){
val compatibility = SuperEnchantDependency(pluginManager.getPlugin("SuperEnchants")!! as SuperEnchants)
if(compatibility.registerEnchantments())
genericDependencies.add(compatibility)
}
for (dependency in genericDependencies)
dependency.redirectListeners()
@ -131,12 +149,7 @@ object DependencyManager {
ecoEnchantCompatibility?.handleConfigReload()
}
// Return true if should bypass (either by a dependency or error)
// called before immutability test
fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
try {
return earlyUnsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) {
private fun logException(target: CommandSender, e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
@ -144,14 +157,27 @@ object DependencyManager {
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
// Finally, warn the player
target.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
}
private fun logExceptionAndClear(target: CommandSender, inventory: Inventory, e: Exception) {
// Just in case to avoid illegal items
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
logException(target, e)
}
// Return true if should bypass (either by a dependency or error)
// called before immutability test
fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
try {
return earlyUnsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) {
logExceptionAndClear(event.view.player, event.inventory, e)
return true
}
}
@ -164,7 +190,7 @@ object DependencyManager {
var bypass = bypassEvent.isCancelled
// Test if the inventory is a gui(version specific)
if (!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true
if (!bypass && externGuiTester.testIfGui(event.view)) bypass = true
// Test if in an ax player warp rating gui
if (!bypass && (axPlayerWarpsCompatibility?.testIfGui(player) == true)) bypass = true
@ -173,30 +199,16 @@ object DependencyManager {
}
// Return true if should bypass (either by a dependency or error)
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean {
try {
return unsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
logExceptionAndClear(event.view.player, event.inventory, e)
return true
}
}
private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
private fun unsafeTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: Player): Boolean {
// Run the event
val bypassEvent = CAPreAnvilBypassEvent(event)
Bukkit.getPluginManager().callEvent(bypassEvent)
@ -221,36 +233,24 @@ object DependencyManager {
// Return null if there was an issue
fun tryTreatAnvilResult(
event: PrepareAnvilEvent,
view: InventoryView,
inventory: Inventory, // TODO REMOVE, use view instead on legacy removal
player: HumanEntity,
result: ItemStack,
useType: AnvilUseType,
cost: Int
): CATreatAnvilResultEvent? {
val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost)
cost: AnvilCost
): ItemStack? {
val treatEvent = CATreatAnvilResult2Event(view, inventory, useType, result, cost)
try {
unsafeTryTreatAnvilResult(treatEvent)
return treatEvent;
return treatEvent.result
} catch (e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
logExceptionAndClear(player, inventory, e)
return null
}
}
private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResultEvent) {
private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResult2Event) {
Bukkit.getPluginManager().callEvent(event)
excellentEnchantsCompatibility?.treatAnvilResult(event)
@ -261,21 +261,7 @@ object DependencyManager {
try {
return unsafeTryClickAnvilResultBypass(event, inventory)
} catch (e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.whoClicked.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
logExceptionAndClear(event.view.player, event.inventory, e)
return true
}
}
@ -301,7 +287,7 @@ object DependencyManager {
}
// Test if the inventory is a gui(version specific)
if (!bypass && (externGuiTester?.testIfGui(event.view) == true)) bypass = true
if (!bypass && externGuiTester.testIfGui(event.view)) bypass = true
// Test if in an ax player warp rating gui
if (!bypass && (axPlayerWarpsCompatibility?.testIfGui(event.view.player) == true)) bypass = true
@ -309,6 +295,23 @@ object DependencyManager {
return bypass
}
// Clone item and use plugin specific clone if needed
fun cloneItem(player: HumanEntity, item: ItemStack): ItemStack {
try {
return unsafeCloneItem(item)
} catch (e: Exception) {
logException(player, e)
return item.clone()
}
}
private fun unsafeCloneItem(item: ItemStack): ItemStack {
val cloned = itemsAdderCompatibility?.tryClone(item)
if(cloned != null) return cloned
return item.clone()
}
fun stripLore(item: ItemStack): MutableList<Component?> {
val dummy = item.clone()

View file

@ -6,29 +6,29 @@ object MinecraftVersionUtil {
val craftbukkitVersion: String?
get() {
val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts[0] != 1) return null
val version = UpdateUtils.currentMinecraftVersion()
if (version.major != 1) return null
return when (versionParts[1]) {
17 -> when (versionParts[2]) {
return when (version.minor) {
17 -> when (version.patch) {
0, 1 -> "1_17R1"
else -> null
}
18 -> when (versionParts[2]) {
18 -> when (version.patch) {
0, 1 -> "1_18R1"
2 -> "1_18R2"
else -> null
}
19 -> when (versionParts[2]) {
19 -> when (version.patch) {
0, 1, 2 -> "1_19R1"
3 -> "1_19R2"
4 -> "1_19R3"
else -> null
}
20 -> when (versionParts[2]) {
20 -> when (version.patch) {
0, 1 -> "1_20R1"
2 -> "1_20R2"
3, 4 -> "1_20R3"
@ -36,7 +36,7 @@ object MinecraftVersionUtil {
else -> null
}
21 -> when (versionParts[2]) {
21 -> when (version.patch) {
0, 1 -> "1_21R1"
2, 3 -> "1_21R2"
4 -> "1_21R3"
@ -51,4 +51,8 @@ object MinecraftVersionUtil {
}
}
val isTooNewForSpigot: Boolean get() {
return UpdateUtils.currentMinecraftVersion().major != 1
}
}

View file

@ -17,7 +17,7 @@ import xyz.alexcrea.cuanvil.update.Version
import java.io.InputStreamReader
object DataPackDependency {
private val START_DETECT_VERSION = Version(1, 19, 0)
private val START_DETECT_VERSION = Version(1, 20, 5)
/**
* Map of the latest CustomAnvil update related to the pack
@ -145,7 +145,7 @@ object DataPackDependency {
CustomAnvil.instance.logger.warning("Could not find material $name for item group $groupName")
continue
}
group.addToPolicy(mat)
group.addToPolicy(mat.key)
}
for (name in section.getStringList("groups")) {
val otherGroup = MaterialGroupApi.getGroup(name)

View file

@ -0,0 +1,32 @@
package xyz.alexcrea.cuanvil.dependency.economy
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.math.BigDecimal
interface EconomyManager {
companion object {
var economy: EconomyManager? = null
fun setupEconomy(plugin: Plugin) {
if (plugin.server.pluginManager.getPlugin("Vault") == null)
return
if (UnlockedEconomyManager.unlockedAvailable())
economy = UnlockedEconomyManager(plugin)
if (economy == null || !economy!!.initialized())
economy = VaultEconomyManager(plugin)
}
}
fun initialized(): Boolean
// We assume "initialized" got checked before these function get called
fun has(player: Player, money: BigDecimal): Boolean
fun remove(player: Player, money: BigDecimal): Boolean
fun format(money: BigDecimal): String;
}

View file

@ -0,0 +1,74 @@
package xyz.alexcrea.cuanvil.dependency.economy
import io.delilaheve.util.ConfigOptions
import net.milkbowl.vault2.economy.Economy
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.math.BigDecimal
class UnlockedEconomyManager : EconomyManager {
val plugin: String
val economy: Economy?
companion object {
fun unlockedAvailable(): Boolean {
try {
Class.forName("net.milkbowl.vault2.economy.Economy")
return true
} catch (_: ClassNotFoundException) {
return false
}
}
}
constructor(plugin: Plugin) {
this.plugin = plugin.name
val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java)
economy = rsp?.getProvider()
}
override fun initialized(): Boolean {
return economy != null
}
private fun currency(): String {
val configured = ConfigOptions.usedCurrency
return if ("default".equals(configured, true))
economy!!.getDefaultCurrency(plugin)
else configured
}
override fun has(player: Player, money: BigDecimal): Boolean {
if (money.signum() <= 0) return true
return economy!!.has(
plugin,
player.uniqueId,
player.world.name,
currency(),
money
)
}
override fun remove(player: Player, money: BigDecimal): Boolean {
if (money.signum() <= 0) return true
return economy!!.withdraw(
plugin,
player.uniqueId,
player.world.name,
currency(),
money
)
.transactionSuccess()
}
override fun format(money: BigDecimal): String {
return economy!!.format(plugin, money, currency())
}
}

View file

@ -0,0 +1,37 @@
package xyz.alexcrea.cuanvil.dependency.economy
import net.milkbowl.vault.economy.Economy
import org.bukkit.entity.Player
import org.bukkit.plugin.Plugin
import java.math.BigDecimal
class VaultEconomyManager : EconomyManager {
val economy: Economy?
constructor(plugin: Plugin) {
val rsp = plugin.server.servicesManager.getRegistration(Economy::class.java)
economy = rsp?.getProvider()
}
override fun initialized(): Boolean {
return economy != null
}
override fun has(player: Player, money: BigDecimal): Boolean {
if (money.signum() <= 0) return true
return economy!!.has(player, money.toDouble())
}
override fun remove(player: Player, money: BigDecimal): Boolean {
if (money.signum() <= 0) return true
return economy!!.withdrawPlayer(player, money.toDouble()).transactionSuccess()
}
override fun format(money: BigDecimal): String {
return economy!!.format(money.toDouble())
}
}

View file

@ -36,6 +36,11 @@ class GenericExternGuiTester {
getHandleMethod = clazz.getMethod(HANDLE_METHOD_NAME)
}
fun isInTest(): Boolean {
if(!testExist) testClassExist()
return inTesting
}
fun testClassExist() {
testExist = true
@ -61,8 +66,7 @@ class GenericExternGuiTester {
// Try if were in another plugin anvil inventory
fun testIfGui(inventory: InventoryView): Boolean {
// In case we are in a test environment
if(!testExist) testClassExist()
if(inTesting) return false
if(isInTest()) return false
val clazz = getContainerClass(inventory) ?: return false

View file

@ -1,15 +0,0 @@
package xyz.alexcrea.cuanvil.dependency.gui
import xyz.alexcrea.cuanvil.update.UpdateUtils
object GuiTesterSelector {
val selectGuiTester: GenericExternGuiTester?
get() {
val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts[0] != 1) return null
return GenericExternGuiTester()
}
}

View file

@ -1,21 +1,31 @@
package xyz.alexcrea.cuanvil.dependency.packet
import org.bukkit.Bukkit
import su.nightexpress.nightcore.bridge.paper.PaperBridge
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
import xyz.alexcrea.cuanvil.dependency.packet.versions.*
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.update.UpdateUtils
object PacketManagerSelector {
private const val PAPER_CRAFT_PLAYER_CLASS = "org.bukkit.craftbukkit.entity.CraftPlayer"
fun selectPacketManager(forceProtocolib: Boolean): PacketManager {
// Try to find version
if(DependencyManager.externGuiTester.isInTest())
return NoPacketManager()
return if (forceProtocolib)
protocolibIfPresent
else
reobfPacketManager ?:
if(PlatformUtil.isPaper) PaperPacketManager()
else protocolibIfPresent
else {
try {
Class.forName(PAPER_CRAFT_PLAYER_CLASS)
return PaperPacketManager()
} catch (_: ClassNotFoundException) {
return reobfPacketManager ?: protocolibIfPresent
}
}
}
private val protocolibIfPresent: PacketManager
@ -28,8 +38,8 @@ object PacketManagerSelector {
// Reobfuscated packet manager for spigot or paper as it remap
private val reobfPacketManager: PacketManagerBase?
get() {
val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts[0] != 1) return null
val versionParts = UpdateUtils.currentMinecraftVersion()
if (versionParts.major != 1) return null
try {
val clazz = Class.forName("xyz.alexcrea.cuanvil.dependency.packet.versions." +
@ -37,7 +47,7 @@ object PacketManagerSelector {
val manager = clazz.getConstructor().newInstance()
return manager as PacketManagerBase
} catch (e: ClassNotFoundException) {
} catch (_: ClassNotFoundException) {
return null
}
}

View file

@ -8,15 +8,16 @@ import com.jankominek.disenchantment.events.ShatterEvent
import com.jankominek.disenchantment.listeners.DisenchantClickListener
import com.jankominek.disenchantment.listeners.ShatterClickListener
import io.delilaheve.CustomAnvil
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.MetricsUtil.trackError
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
import java.util.logging.Level
import kotlin.reflect.KClass
@ -50,7 +51,7 @@ class DisenchantmentDependency {
InventoryClickEvent.getHandlerList().unregister(listener)
}
fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean {
val previousResult = event.result
event.result = null
@ -58,14 +59,14 @@ class DisenchantmentDependency {
DisenchantEvent.onEvent(event)
if (event.result != null) {
CustomAnvil.log("Detected pre anvil item extract bypass.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
return true
}
ShatterEvent.onEvent(event)
if (event.result != null) {
CustomAnvil.log("Detected pre anvil split enchant bypass.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
return true
}

View file

@ -0,0 +1,38 @@
package xyz.alexcrea.cuanvil.dependency.plugins
import com.willfp.ecoitems.items.EcoItem
import com.willfp.ecoitems.items.EcoItems
import com.willfp.ecoitems.items.ecoItem
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.inventory.ItemStack
object EcoItemDependencyUtil {
fun ecoItemNamespace(item: ItemStack): NamespacedKey? {
val ecoi = item.ecoItem ?: return null
return ecoi.id
}
fun ecoItemFromKey(key: NamespacedKey): EcoItem? {
return EcoItems.getByID(key.toString())
}
fun ecoItemMaterialFromKey(key: NamespacedKey): Material? {
val ecoi = ecoItemFromKey(key) ?: return null
return ecoi.itemStack.type
}
fun newEcoItemstack(key: NamespacedKey): ItemStack? {
val ecoi = ecoItemFromKey(key) ?: return null
return ecoi.itemStack
}
fun getItems(): List<NamespacedKey> {
return EcoItems.values().map { item -> item.id }
}
}

View file

@ -102,15 +102,15 @@ class EnchantmentSquaredDependency(private val enchantmentSquaredPlugin: Plugin)
private fun writeMissingGroups(){
// Write group that do not exist on custom anvil.
val shield = IncludeGroup("shield")
shield.addToPolicy(Material.SHIELD)
shield.addToPolicy(Material.SHIELD.key)
MaterialGroupApi.addMaterialGroup(shield)
val elytra = IncludeGroup("elytra")
elytra.addToPolicy(Material.ELYTRA)
elytra.addToPolicy(Material.ELYTRA.key)
MaterialGroupApi.addMaterialGroup(elytra)
val trinkets = IncludeGroup("trinkets")
trinkets.addToPolicy(Material.ROTTEN_FLESH)
trinkets.addToPolicy(Material.ROTTEN_FLESH.key)
MaterialGroupApi.addMaterialGroup(trinkets)
}

View file

@ -8,10 +8,12 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CALegacyEEEnchantment
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import su.nightexpress.excellentenchants.api.EnchantRegistry as V5EnchantRegistry
import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant as LegacyCurseOfFragilityEnchant
@ -25,6 +27,7 @@ import su.nightexpress.excellentenchants.registry.EnchantRegistry as PreV5Enchan
class ExcellentEnchantsDependency {
enum class ListenerVersion(val classPath: String) {
V5_4("su.nightexpress.excellentenchants.enchantment.EnchantSettings"),
V5_3("su.nightexpress.excellentenchants.enchantment.EnchantRegistry"),
V5("su.nightexpress.excellentenchants.manager.listener.AnvilListener"),
PRE_V5("su.nightexpress.excellentenchants.enchantment.listener.AnvilListener"),
@ -70,14 +73,14 @@ class ExcellentEnchantsDependency {
// As excellent enchants is loaded before custom anvil and register enchantment to registry, we need to unregister old "vanilla" enchant.
when (listenerVersion) {
ListenerVersion.V5_3 -> {
ListenerVersion.V5_4 -> {
for (enchantment in ExcellentEnchant5_3Registry.getRegistered()) {
EnchantmentApi.unregisterEnchantment(enchantment.bukkitEnchantment.key)
EnchantmentApi.registerEnchantment(CAEEV5Enchantment(enchantment))
EnchantmentApi.registerEnchantment(CAEEV5_4Enchantment(enchantment))
}
}
ListenerVersion.V5 -> {
ListenerVersion.V5, ListenerVersion.V5_3 -> {
for (enchantment in V5EnchantRegistry.getRegistered()) {
EnchantmentApi.unregisterEnchantment(enchantment.bukkitEnchantment.key)
EnchantmentApi.registerEnchantment(CAEEV5Enchantment(enchantment))
@ -115,6 +118,8 @@ class ExcellentEnchantsDependency {
private lateinit var handleRechargeMethod: Method
private lateinit var handleCombineMethod: Method
private val prepareAnvilConstructor = PrepareAnvilEvent::class.java.constructors.first() as Constructor<PrepareAnvilEvent>
fun redirectListeners() {
val toUnregister = ArrayList<RegisteredListener>()
// get required PrepareAnvilEvent listener
@ -130,7 +135,8 @@ class ExcellentEnchantsDependency {
when (listenerVersion) {
ListenerVersion.V5,
ListenerVersion.V5_3
ListenerVersion.V5_3,
ListenerVersion.V5_4,
-> {
if (listener is V5AnvilListener) {
this.v5AnvilListener = listener
@ -165,6 +171,7 @@ class ExcellentEnchantsDependency {
when (listenerVersion) {
ListenerVersion.V5_3,
ListenerVersion.V5,
ListenerVersion.V5_4,
-> this.usedAnvilListener = v5AnvilListener!!
ListenerVersion.PRE_V5 -> this.usedAnvilListener = preV5AnvilListener!!
ListenerVersion.LEGACY -> this.usedAnvilListener = legacyAnvilListener!!
@ -214,21 +221,24 @@ class ExcellentEnchantsDependency {
return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean
}
fun treatAnvilResult(event: CATreatAnvilResultEvent) {
val result = event.result
if (result == null) return
fun treatAnvilResult(event: CATreatAnvilResult2Event) {
val result = event.result ?: return
val first: ItemStack = treatInput(event.event.inventory.getItem(0))
val second: ItemStack = treatInput(event.event.inventory.getItem(1))
val first: ItemStack = treatInput(event.leftItem)
val second: ItemStack = treatInput(event.rightItem)
val fakeEvent = prepareAnvilConstructor.newInstance(event.view, result)
handleCombineMethod.invoke(this.usedAnvilListener, event.event, first, second, result)
handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result)
event.result = fakeEvent.result
}
fun testAnvilResult(event: InventoryClickEvent): Any {
if (event.inventory.getItem(2) != null) {
when (listenerVersion) {
ListenerVersion.V5,
ListenerVersion.V5_3
ListenerVersion.V5_3,
ListenerVersion.V5_4,
-> v5AnvilListener!!.onClickAnvil(event)
ListenerVersion.PRE_V5 -> preV5AnvilListener!!.onClickAnvil(event)
ListenerVersion.LEGACY -> legacyAnvilListener!!.onClickAnvil(event)

View file

@ -5,7 +5,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.plugin.Plugin
import org.bukkit.plugin.RegisteredListener
open class GenericPluginDependency(protected val plugin: Plugin) {
open class GenericPluginDependency(protected open val plugin: Plugin, private val testPrepare: Boolean = true) {
private val preAnvil = ArrayList<RegisteredListener>()
private val postAnvil = ArrayList<RegisteredListener>()
@ -40,11 +40,19 @@ open class GenericPluginDependency(protected val plugin: Plugin) {
}
open fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean {
if(!testPrepare) return false
val previousResult = event.result
event.result = null
for (registeredListener in preAnvil) {
// We do not want error from another plugin to be our fault
try {
registeredListener.callEvent(event)
} catch (e: Exception) {
e.printStackTrace()
}
if (event.result != null) return true
}
@ -53,8 +61,15 @@ open class GenericPluginDependency(protected val plugin: Plugin) {
}
open fun testAnvilResult(event: InventoryClickEvent): Boolean {
if(!testPrepare) return false
for (registeredListener in postAnvil) {
// We do not want error from another plugin to be our fault
try {
registeredListener.callEvent(event)
} catch (e: Exception) {
e.printStackTrace()
}
if (event.inventory.getItem(2) == null) return true
}

View file

@ -1,7 +1,7 @@
package xyz.alexcrea.cuanvil.dependency.plugins
import io.delilaheve.CustomAnvil
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
@ -9,8 +9,9 @@ import org.bukkit.plugin.RegisteredListener
import valorless.havenbags.HavenBags
import valorless.havenbags.features.BagSkin
import valorless.havenbags.features.BagUpgrade
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
class HavenBagsDependency {
@ -45,7 +46,7 @@ class HavenBagsDependency {
}
fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
fun testPrepareAnvil(event: PrepareAnvilEvent, player: Player): Boolean {
val previousResult = event.result
event.result = null
@ -53,14 +54,14 @@ class HavenBagsDependency {
bagSkin.onPrepareAnvil(event)
if (event.result != null) {
CustomAnvil.log("Detected pre anvil heaven bag anvil skin.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
return true
}
bagUpgrade.onPrepareAnvil(event)
if (event.result != null) {
CustomAnvil.log("Detected pre anvil heaven bag anvil upgrade.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost)
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, AnvilCost(event.inventory.repairCost))
return true
}

View file

@ -0,0 +1,42 @@
package xyz.alexcrea.cuanvil.dependency.plugins
import dev.lone.itemsadder.api.CustomStack
import dev.lone.itemsadder.api.ItemsAdder
import org.bukkit.NamespacedKey
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.Plugin
class ItemsAdderDependency(plugin: Plugin) : GenericPluginDependency(plugin) {
var isLoaded: Boolean = false
get() {
if (field) return true
// We can't be sure the event is registered before being triggered so we need to use this function
field = ItemsAdder.areItemsLoaded()
return field
}
fun tryClone(item: ItemStack): ItemStack? {
if(!isLoaded) return null
val customItem = CustomStack.byItemStack(item) ?: return null
return CustomStack.getInstance(customItem.namespacedID)?.itemStack
}
fun fromKey(key: NamespacedKey): ItemStack? {
if(!isLoaded) return null
return CustomStack.getInstance(key.toString())?.itemStack
}
fun getKey(item: ItemStack) : NamespacedKey? {
if(!isLoaded) return null
val customItem = CustomStack.byItemStack(item) ?: return null
return NamespacedKey.fromString(customItem.namespacedID)
}
fun idsCount(): Set<String> {
return CustomStack.getNamespacedIdsInRegistry()
}
}

View file

@ -0,0 +1,91 @@
package xyz.alexcrea.cuanvil.dependency.plugins
import com.maddoxh.superEnchants.SuperEnchants
import com.maddoxh.superEnchants.enchants.EnchantManager
import com.maddoxh.superEnchants.listeners.AnvilListener
import io.delilaheve.CustomAnvil
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.enchant.bulk.SuperEnchantBulkOperation
import xyz.alexcrea.cuanvil.enchant.wrapped.CASuperEnchantEnchantment
import java.util.logging.Level
class SuperEnchantDependency(override val plugin: SuperEnchants): GenericPluginDependency(plugin, false) {
lateinit var enchManager: EnchantManager
val enchantments = ArrayList<CASuperEnchantEnchantment>()
fun registerEnchantments(): Boolean{
CustomAnvil.instance.logger.info("Preparing Super Enchant compatibility...")
val field = SuperEnchants::class.java.getDeclaredField("enchantManager")
if(field == null) {
CustomAnvil.instance.logger.log(Level.SEVERE, "Failed to initialize Super Enchant compatibility")
return false
}
field.setAccessible(true)
val bulkOpperations = SuperEnchantBulkOperation(plugin)
EnchantmentApi.addBulkGet(bulkOpperations)
EnchantmentApi.addBulkClean(bulkOpperations)
enchManager = field.get(plugin) as EnchantManager
overrideReloadCommand()
reload()
return true
}
fun reload() {
for (enchantment in enchantments) {
EnchantmentApi.unregisterEnchantment(enchantment)
}
enchantments.clear()
// Register enchantments
for (enchant in enchManager.getAll()) {
val enchantment = CASuperEnchantEnchantment(enchant, plugin, enchManager)
enchantments.add(enchantment)
EnchantmentApi.registerEnchantment(enchantment)
}
}
private fun overrideReloadCommand() {
val reload = CustomAnvil.instance.getCommand("sereload")
reload?.setExecutor(ReloadInterceptor(reload.executor))
}
inner class ReloadInterceptor(val other: CommandExecutor): CommandExecutor {
override fun onCommand(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String?>
): Boolean {
val result = other.onCommand(sender, command, label, args)
CustomAnvil.log("Detected SuperEnchant reload")
reload()
return result
}
}
override fun fillPostAnvil(postAnvil: ArrayList<RegisteredListener>, preAnvil: ArrayList<RegisteredListener>) {
for (registeredListener in InventoryClickEvent.getHandlerList().registeredListeners) {
if (registeredListener.listener.javaClass != AnvilListener::class.java) continue
postAnvil.add(registeredListener)
}
}
}

View file

@ -1,7 +1,8 @@
package xyz.alexcrea.cuanvil.group
import org.bukkit.Material
import java.util.*
import org.bukkit.NamespacedKey
import xyz.alexcrea.cuanvil.util.MaterialUtil
abstract class AbstractMaterialGroup(private val name: String) {
protected val includedMaterial by lazy { createDefaultSet() }
@ -9,12 +10,12 @@ abstract class AbstractMaterialGroup(private val name: String) {
/**
* Get the group default set
*/
protected abstract fun createDefaultSet(): EnumSet<Material>
protected abstract fun createDefaultSet(): MutableSet<NamespacedKey>
/**
* Get if a material is allowed following the group policy
*/
open fun contain(mat: Material): Boolean {
open fun contain(mat: NamespacedKey): Boolean {
return mat in getMaterials()
}
@ -27,13 +28,13 @@ abstract class AbstractMaterialGroup(private val name: String) {
* Push a material to this group to follow this group policy
* @return this instance.
*/
abstract fun addToPolicy(mat: Material): AbstractMaterialGroup
abstract fun addToPolicy(type: NamespacedKey): AbstractMaterialGroup
/**
* Push a list of material to this group to follow this group policy
* @return this instance.
*/
fun addAll(vararg materials: Material): AbstractMaterialGroup {
fun addAll(vararg materials: NamespacedKey): AbstractMaterialGroup {
for (material in materials) {
addToPolicy(material)
}
@ -60,19 +61,19 @@ abstract class AbstractMaterialGroup(private val name: String) {
/**
* Get the group contained material as a set
*/
abstract fun getMaterials(): EnumSet<Material>
abstract fun getMaterials(): Set<NamespacedKey>
/**
* Get the group non-inherited material as a set
*/
open fun getNonGroupInheritedMaterials(): EnumSet<Material> {
open fun getNonGroupInheritedMaterials(): Set<NamespacedKey> {
return includedMaterial
}
/**
* Get the group non-inherited material as a set
*/
open fun setNonGroupInheritedMaterials(materials: EnumSet<Material>) {
open fun setNonGroupInheritedMaterials(materials: Set<NamespacedKey>) {
this.includedMaterial.clear()
this.includedMaterial.addAll(materials)
}
@ -102,8 +103,9 @@ abstract class AbstractMaterialGroup(private val name: String) {
// Test inner material
val matIterator = includedMaterial.iterator()
while (matIterator.hasNext()) {
val material = matIterator.next()
if (material.isAir) continue
val key = matIterator.next()
val material = MaterialUtil.getMatFromKey(key)
if (material == null || material.isAir) continue
return material
}
// Test included group representative material

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