Compare commits

..

No commits in common. "v1.x.x" and "v1.16.2" have entirely different histories.

115 changed files with 1047 additions and 3162 deletions

View file

@ -25,10 +25,6 @@ jobs:
permissions: permissions:
contents: write 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up JDKs - name: Set up JDKs
@ -70,6 +66,19 @@ jobs:
if: ${{ github.event_name != 'release' && success() }} if: ${{ github.event_name != 'release' && success() }}
run: echo "SMALL_COMMIT_HASH=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV 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 - name: Build with Gradle Wrapper
run: ./gradlew build --parallel --stacktrace run: ./gradlew build --parallel --stacktrace
@ -119,7 +128,6 @@ jobs:
if: ${{ (github.event_name != 'release' || github.event_name != 'push') && github.repository_owner == 'alexcrea' && success() }} if: ${{ (github.event_name != 'release' || github.event_name != 'push') && github.repository_owner == 'alexcrea' && success() }}
env: env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }} HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
RELEASE_CHANGELOG: ${{ github.event.release.body }}
run: ./gradlew publishAllPublicationsToHangar --stacktrace run: ./gradlew publishAllPublicationsToHangar --stacktrace
- name: Modrinth publish alpha - name: Modrinth publish alpha
@ -148,7 +156,7 @@ jobs:
game-versions: ${{ env.MODRINTH_VERSIONS }} game-versions: ${{ env.MODRINTH_VERSIONS }}
channel: ${{ github.event.release.prerelease == false && 'release' || 'beta' }} channel: ${{ github.event.release.prerelease == false && 'release' || 'beta' }}
files: build/libs/${{ env.ONLINE_JAR_NAME }} files: build/libs/${{ env.ONLINE_JAR_NAME }}
changelog: ${{ github.event.release.body }} changelog: ${{ env.RELEASE_CHANGELOG }}
- name: Send release note to discord - name: Send release note to discord
if: ${{ github.event_name == 'release' && github.repository_owner == 'alexcrea' && success() }} if: ${{ github.event_name == 'release' && github.repository_owner == 'alexcrea' && success() }}
@ -160,4 +168,4 @@ jobs:
# 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. -# note: automated release. spigot is not uploaded yet.
${{ github.event.release.body }} ${{ env.RELEASE_CHANGELOG }}

View file

@ -6,55 +6,44 @@ This is cannot be fixed on geyser or my side.
Here is various plugins that had issues with CustomAnvil Here is various plugins that had issues with CustomAnvil
where efforts was made for compatibility and should be working right: where efforts was made for compatibility and should be working right:
some if not all of them are cool ! I recommend checking them out ! some of them are cool I recommend checking them out !
## Supported By CustomAnvil ## Supported By CustomAnvil
These plugins have compatibility handled by custom anvil. seek help on custom anvil and do not bother these developers These plugins have compatibility handled by custom anvil. seek help on custom anvil and do not bother these developers
#### Enchantment Plugins #### Enchantment plugins
- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/) by NightExpress: - [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/):
Use ExcellentEnchants item type \ 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/) by Auxilor: - [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/):
Need to use /anvilconfigreload or a server restart to add newly added enchantment. 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 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/) by Athlaeos: - [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/):
Support by Custom Anvil but still experimental. Automatic configuration. Plugin is not actively developed anymore Support by Custom Anvil but still experimental. Automatic configuration. Plugin is not actively developed anymore
- [SuperEnchants](https://modrinth.com/plugin/superenchants) by Aznos: #### Anvil Mechanics
Use SuperEnchant restrictions system but new restriction can be added in custom anvil - [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
#### 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) 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/) by hyperdefined - [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/)
For bag upgrade and skin via anvil. (version >= 1.31.0) For bag upgrade and skin via anvil. (version >= 1.31.0)
- [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi) by ArtillexStudios - [AxPlayerWarp](https://modrinth.com/project/QDJHDKvi)
For its anvil inventory usage For its anvil inventory usage
- [ToolsStats](https://modrinth.com/project/oBZj9E15) by Valorless - [ToolsStats](https://modrinth.com/project/oBZj9E15)
For token application using anvil For token application using anvil
### Known Partially Incompatible ### Known Partially Incompatible
- [UberEnchant](https://modrinth.com/plugin/uberenchant) by coltonj96 - [UberEnchant](https://modrinth.com/plugin/uberenchant)
Anvil handling as they are doing something similar to CustomAnvil. Anvil handling as they are doing something similar to CustomAnvil.
It is by no mean there faults and I recommend checking them out especially if custom anvil do not work for your use case ! It is by no mean there faults and I recomend checking them out
- [AdvencedEnchantments](https://ae.advancedplugins.net/) by Advanced Plugins - [SuperEnchant](https://modrinth.com/plugin/superenchants)
Reported potential incompatibility
- [AdvencedEnchantments](https://ae.advancedplugins.net/)
Paid plugin I do not own as I did not get commissioned for support. 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 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 Thanks JetBrain for making IntelliJ
### Dependencies ### Dependencies
These dependencies (or a modified version of) are used by custom anvil Here dependencies are used by custom anvil
- [IF](https://github.com/stefvanschie/IF) an inventory framework by stefvanschie - [IF](https://github.com/stefvanschie/IF) an inventory framework by stefvanschie
- [Mockbukkit](https://github.com/MockBukkit/MockBukkit) for unit testing - [Mockbukkit](https://github.com/MockBukkit/MockBukkit) for unit testing
- [CentralPortalPlus](https://github.com/lalakii/central-portal-plus) by lalakii - [CentralPortalPlus](https://github.com/lalakii/central-portal-plus) by lalakii
@ -17,18 +17,23 @@ These dependencies (or a modified version of) are used by custom anvil
- [ModrinthUpdateChecker](https://github.com/Clickism/ModrinthUpdateChecker) by Clickism and thanks to the modrinth team - [ModrinthUpdateChecker](https://github.com/Clickism/ModrinthUpdateChecker) by Clickism and thanks to the modrinth team
### Compatibility ### Compatibility
Thanks to all the cool creator making the minecraft plugin's ecosystem works ! \ Here is to credits all the author of plugins
See [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md) for details 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)
but especially, Big Thanks for H7KZ maker of [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
### Special Thanks ### 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 Thanks for all the users trying my plugin for these niche use cases
, reporting issues and giving ideas ! and for reporting issues and giving ideas !
Thanks coltonj96 for [UberEnchant](https://modrinth.com/plugin/uberenchant). 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 ! \ 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,6 +1,8 @@
# Custom Anvil # Custom Anvil
**Custom Anvil** is a plugin that allows server administrators to customize every aspect of the anvil's mechanics. **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: ### Download Locations:
@ -14,7 +16,7 @@ the plugin can be downloaded on
- Vanilla like default configuration. - Vanilla like default configuration.
- Custom enchantment level limit. - Custom enchantment level limit.
- Custom anvil recipes. - Custom anvil recipes.
- Custom enchant restrictions (allows unsafe enchantment only for a group of item or create new restriction). - Custom enchant restrictions (allow 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 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. - Custom XP cost for every aspect of the anvil.
- Permissions to bypass level limit or enchantment restriction. - Permissions to bypass level limit or enchantment restriction.
@ -23,14 +25,8 @@ the plugin can be downloaded on
- Gui to configure the plugin in game. - Gui to configure the plugin in game.
- Support use of color code, hexadecimal color and minimessage for color/decoration - Support use of color code, hexadecimal color and minimessage for color/decoration
- (Experimental) Folia support (gui do not work) - (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: ### 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 ```yml
# Generic and bypass permissions # Generic and bypass permissions
ca.affected: Player with this permission will be affected by the plugin ca.affected: Player with this permission will be affected by the plugin
@ -48,22 +44,17 @@ ca.config.edit: Allow administrator to edit the plugin's config in game
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Permissions related to use of color and minimessage # 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: 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.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) ca.rename.minimessage: Allow player to use minimessage formating on rename if enabled (toggleable) (only legacy compatible at the time)
# Permissions related to edition of the lore # 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.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) 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 ### Commands
run `/customanvil help` to get information about available commands \ run `customanvil help` to get information about available commands (need permissions to use them)
this only show subcommands you have permission for
### Supported Plugins ### Supported Plugins
See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md) See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
@ -72,10 +63,13 @@ 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. \ 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. By how the minecraft client work: price above 40 can only be displayed green, even if the player does not own enough experience level.
spigot version 1.18 to 1.21.11 do not need any ProtocoLib dependency. (26.1.0 or above requires it) \ Minecraft version 1.18 to latest marked as supported do not need any ProtocoLib dependency. \
Any recent paper version also are supported for this feature. 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 if any of this got added. But you should wait for update for new version containing new enchantable item or new enchantments.
Else it is, likely, fine to use the current version you are ussing on a new paper version 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.
### For custom enchantment plugin developers ### For custom enchantment plugin developers
For information about the API, please refer to [the Wiki](https://github.com/alexcrea/CustomAnvil/wiki) \ For information about the API, please refer to [the Wiki](https://github.com/alexcrea/CustomAnvil/wiki) \
@ -106,6 +100,3 @@ Credits and thanks can be seen [here](https://github.com/alexcrea/CustomAnvil/bl
### Known issue: ### Known issue:
Most unknown registered enchantments (by unsupported custom enchantment plugin & datapacks) will not have restriction by default. Planned but no eta. 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 import java.io.ByteArrayOutputStream
plugins { plugins {
kotlin("jvm") version "2.3.0" kotlin("jvm") version "2.1.0"
java java
id("org.jetbrains.dokka").version("1.9.20") id("org.jetbrains.dokka").version("1.9.20")
id("com.gradleup.shadow").version("9.3.0") id("com.gradleup.shadow").version("9.3.0")
@ -22,7 +22,7 @@ plugins {
} }
group = "xyz.alexcrea" group = "xyz.alexcrea"
version = "1.17.5" version = "1.16.2"
val isDevBuild = System.getenv("SMALL_COMMIT_HASH") != null val isDevBuild = System.getenv("SMALL_COMMIT_HASH") != null
val isPreRelease = System.getenv("IS_GITHUB_PRERELEASE") == "true" val isPreRelease = System.getenv("IS_GITHUB_PRERELEASE") == "true"
@ -37,17 +37,11 @@ repositories {
// ExcellentEnchants // ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases") maven(url = "https://repo.nightexpressdev.com/releases")
// ItemsAdder // for fast stats
maven(url = "https://maven.devs.beer/")
// For fast stats
maven { maven {
name = "thenextlvlReleases" name = "thenextlvlReleases"
url = uri("https://repo.thenextlvl.net/releases") 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") val reobfNMS = providers.gradleProperty("subprojects.reobfnms")
@ -58,7 +52,7 @@ dependencies {
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
// fast stats // fast stats
implementation("dev.faststats.metrics:bukkit:0.27.0") implementation("dev.faststats.metrics:bukkit:0.19.0")
// minimessage // minimessage
implementation("net.kyori:adventure-text-minimessage:4.25.0") implementation("net.kyori:adventure-text-minimessage:4.25.0")
@ -71,15 +65,11 @@ dependencies {
// EnchantsSquaredRewritten // EnchantsSquaredRewritten
compileOnly(files("libs/EnchantsSquared.jar")) compileOnly(files("libs/EnchantsSquared.jar"))
// EcoEnchants & item // EcoEnchants
compileOnly("com.willfp:libreforge:4.79.0:all")
compileOnly("com.willfp:eco:6.74.5")
compileOnly("com.willfp:EcoEnchants:12.11.1") compileOnly("com.willfp:EcoEnchants:12.11.1")
compileOnly("com.willfp:eco:6.74.5")
compileOnly(project(":impl:LegacyEcoEnchant")) compileOnly(project(":impl:LegacyEcoEnchant"))
compileOnly("com.willfp:EcoItems:5.66.0")
// ExcellentEnchants // ExcellentEnchants
implementation(project(":impl:ExcellentEnchant5_4")) implementation(project(":impl:ExcellentEnchant5_4"))
compileOnly("su.nightexpress.excellentenchants:Core:5.1.0") { compileOnly("su.nightexpress.excellentenchants:Core:5.1.0") {
@ -100,15 +90,6 @@ dependencies {
// AxPlayerWarps // AxPlayerWarps
compileOnly(files("libs/AxPlayerWarps-1.10.3.jar")) 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 // Include nms
implementation(project(":nms:nms-common")) implementation(project(":nms:nms-common"))
implementation(project(":nms:nms-paper")) implementation(project(":nms:nms-paper"))
@ -168,7 +149,7 @@ allprojects {
kotlin { kotlin {
compilerOptions { compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2) apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_16) jvmTarget.set(JvmTarget.JVM_16)
} }
} }
@ -202,7 +183,7 @@ tasks {
shadowJar { shadowJar {
configureBaseShadow("", configureBaseShadow("",
arrayOf( arrayOf(
"org.jetbrains.kotlin:kotlin-stdlib:2.3.0", "org.jetbrains.kotlin:kotlin-stdlib:2.1.0",
"net.kyori:adventure-text-minimessage:4.25.0", "net.kyori:adventure-text-minimessage:4.25.0",
"net.kyori:adventure-text-serializer-plain:4.25.0", "net.kyori:adventure-text-serializer-plain:4.25.0",
"net.kyori:adventure-text-serializer-legacy:4.25.0", "net.kyori:adventure-text-serializer-legacy:4.25.0",

View file

@ -77,18 +77,6 @@ allow_color_code: false
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: 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. # 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. # permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,24 +87,6 @@ permission_needed_for_color: true
# Valid values include 0 to 1000. # Valid values include 0 to 1000.
use_of_color_cost: 0 use_of_color_cost: 0
# 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
#
# 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # Enchantments not listed here will use the value of default_limit
@ -430,33 +400,6 @@ lore_edit:
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: true 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 # Whether to show debug logging
debug_log: false debug_log: false

View file

@ -79,18 +79,6 @@ allow_color_code: false
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: 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. # 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. # permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -101,24 +89,6 @@ permission_needed_for_color: true
# Valid values include 0 to 1000. # Valid values include 0 to 1000.
use_of_color_cost: 0 use_of_color_cost: 0
# 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
#
# 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # Enchantments not listed here will use the value of default_limit
@ -450,33 +420,6 @@ lore_edit:
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: true 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 # Whether to show debug logging
debug_log: false debug_log: false

View file

@ -77,18 +77,6 @@ allow_color_code: false
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: 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. # 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. # permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,24 +87,6 @@ permission_needed_for_color: true
# Valid values include 0 to 1000. # Valid values include 0 to 1000.
use_of_color_cost: 0 use_of_color_cost: 0
# 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
#
# 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # Enchantments not listed here will use the value of default_limit
@ -442,33 +412,6 @@ lore_edit:
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: true 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 # Whether to show debug logging
debug_log: false debug_log: false

View file

@ -77,18 +77,6 @@ allow_color_code: false
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: 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. # 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. # permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,24 +87,6 @@ permission_needed_for_color: true
# Valid values include 0 to 1000. # Valid values include 0 to 1000.
use_of_color_cost: 0 use_of_color_cost: 0
# 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
#
# 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # Enchantments not listed here will use the value of default_limit
@ -430,33 +400,6 @@ lore_edit:
allow_hexadecimal_color: false allow_hexadecimal_color: false
allow_minimessage: true 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 # Whether to show debug logging
debug_log: false debug_log: false

View file

@ -1,5 +1,5 @@
### Default Plugin's Configurations ### 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.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 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.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) 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 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 # list of version for hangar release
paperVersion=1.18-26.2 paperVersion=1.18-1.21.11

View file

@ -2,7 +2,7 @@ group = rootProject.group
version = rootProject.version version = rootProject.version
plugins { plugins {
kotlin("jvm") version "2.3.0" kotlin("jvm") version "2.1.0"
} }
repositories { repositories {
@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// Excellent Enchant // Excellent Enchant
compileOnly("su.nightexpress.excellentenchants:Core:5.4.3") compileOnly("su.nightexpress.excellentenchants:Core:5.4.1")
compileOnly("su.nightexpress.nightcore:main:2.16.2") compileOnly("su.nightexpress.nightcore:main:2.14.1")
} }

View file

@ -2,7 +2,7 @@ group = rootProject.group
version = rootProject.version version = rootProject.version
plugins { plugins {
kotlin("jvm") version "2.3.0" kotlin("jvm") version "2.1.0"
} }
// Imitate needed class and method to support legacy version of EcoEnchant // 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 { kotlin {
compilerOptions { compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2) apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_16) jvmTarget.set(JvmTarget.JVM_16)
} }
} }

View file

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

View file

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

View file

@ -1,236 +0,0 @@
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

@ -1,45 +0,0 @@
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 { kotlin {
compilerOptions { compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2) apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_16) jvmTarget.set(JvmTarget.JVM_16)
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,18 +0,0 @@
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 { kotlin {
compilerOptions { compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_2) apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_21) jvmTarget.set(JvmTarget.JVM_21)
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.api;
import io.delilaheve.CustomAnvil; import io.delilaheve.CustomAnvil;
import io.delilaheve.util.ConfigOptions; import io.delilaheve.util.ConfigOptions;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -124,7 +123,7 @@ public class MaterialGroupApi {
FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig(); FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
String basePath = group.getName() + "."; String basePath = group.getName() + ".";
Set<NamespacedKey> materialSet = group.getNonGroupInheritedMaterials(); Set<Material> materialSet = group.getNonGroupInheritedMaterials();
Set<AbstractMaterialGroup> groupSet = group.getGroups(); Set<AbstractMaterialGroup> groupSet = group.getGroups();
boolean empty = true; boolean empty = true;
@ -154,7 +153,7 @@ public class MaterialGroupApi {
FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig(); FileConfiguration config = ConfigHolder.ITEM_GROUP_HOLDER.getConfig();
String basePath = group.getName() + "."; String basePath = group.getName() + ".";
Set<NamespacedKey> materials = group.getMaterials(); EnumSet<Material> materials = group.getMaterials();
if (materials.isEmpty()) return false; if (materials.isEmpty()) return false;
@ -164,8 +163,8 @@ public class MaterialGroupApi {
return true; return true;
} }
public static List<String> materialSetToStringList(@NotNull Set<NamespacedKey> materials) { public static List<String> materialSetToStringList(@NotNull Set<Material> materials) {
return materials.stream().map(NamespacedKey::toString).toList(); return materials.stream().map(material -> material.getKey().getKey().toLowerCase()).toList();
} }
public static List<String> materialGroupSetToStringList(@NotNull Set<AbstractMaterialGroup> groups) { 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} * Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
* for this event to be useful. * for this event to be useful.
* <p> * <p>
* There is also {@link CATreatAnvilResult2Event} that may be better for some use case. * There is also {@link CATreatAnvilResultEvent} that may be better for some use case.
*/ */
public class CAClickResultBypassEvent extends Event implements Cancellable { public class CAClickResultBypassEvent extends Event implements Cancellable {

View file

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

View file

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

View file

@ -1,196 +0,0 @@
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,8 +6,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.anvil.AnvilCost; import xyz.alexcrea.cuanvil.util.AnvilUseType;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
/** /**
* Called after custom anvil processed the click on the result on the anvil inventory. * Called after custom anvil processed the click on the result on the anvil inventory.
@ -18,12 +17,8 @@ import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
* and {@link CAEarlyPreAnvilBypassEvent} for your use case * and {@link CAEarlyPreAnvilBypassEvent} for your use case
* <p> * <p>
* A null result will cancel this pre anvil event * 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") @SuppressWarnings("unused")
@Deprecated(forRemoval = true, since = "1.17.0")
public class CATreatAnvilResultEvent extends Event { public class CATreatAnvilResultEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();
@ -45,13 +40,13 @@ public class CATreatAnvilResultEvent extends Event {
@Nullable @Nullable
private ItemStack result; private ItemStack result;
private final AnvilCost cost; private int levelCost;
public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, AnvilCost cost) { public CATreatAnvilResultEvent(@NotNull PrepareAnvilEvent event, AnvilUseType useType, @Nullable ItemStack result, int levelCost) {
this.event = event; this.event = event;
this.useType = useType; this.useType = useType;
this.result = result; this.result = result;
this.cost = cost; this.levelCost = levelCost;
} }
/** /**
@ -109,11 +104,9 @@ public class CATreatAnvilResultEvent extends Event {
* </ul> * </ul>
* *
* @return The current cost. * @return The current cost.
* @deprecated use #{@link #getCost()} instead
*/ */
@Deprecated(forRemoval = true, since = "1.17.0")
public int getLevelCost() { public int getLevelCost() {
return cost.asXpCost(); return levelCost;
} }
/** /**
@ -131,32 +124,8 @@ public class CATreatAnvilResultEvent extends Event {
* </ul> * </ul>
* *
* @param levelCost The new cost. * @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) { public void setLevelCost(int levelCost) {
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost()); this.levelCost = levelCost;
} }
/**
* 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 com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType; import xyz.alexcrea.cuanvil.util.AnvilUseType;
import java.util.EnumMap; import java.util.EnumMap;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,6 @@ import io.delilaheve.util.ConfigOptions;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.config.ConfigHolder; 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.LoreEditConfigUtil;
import xyz.alexcrea.cuanvil.util.config.LoreEditType; import xyz.alexcrea.cuanvil.util.config.LoreEditType;
@ -19,9 +18,6 @@ public class PluginSetDefault {
int nbSet = 0; 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, CAP_ANVIL_COST, DEFAULT_CAP_ANVIL_COST);
nbSet += trySetDefault(config, MAX_ANVIL_COST, DEFAULT_MAX_ANVIL_COST); nbSet += trySetDefault(config, MAX_ANVIL_COST, DEFAULT_MAX_ANVIL_COST);
nbSet += trySetDefault(config, REMOVE_ANVIL_COST_LIMIT, DEFAULT_REMOVE_ANVIL_COST_LIMIT); nbSet += trySetDefault(config, REMOVE_ANVIL_COST_LIMIT, DEFAULT_REMOVE_ANVIL_COST_LIMIT);
@ -34,7 +30,6 @@ public class PluginSetDefault {
nbSet += trySetDefault(config, ALLOW_HEXADECIMAL_COLOR, DEFAULT_ALLOW_HEXADECIMAL_COLOR); 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, PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR);
nbSet += trySetDefault(config, USE_OF_COLOR_COST, DEFAULT_USE_OF_COLOR_COST); nbSet += trySetDefault(config, USE_OF_COLOR_COST, DEFAULT_USE_OF_COLOR_COST);
nbSet += trySetDefault(config, PER_COLOR_CODE_PERMISSION, DEFAULT_PER_COLOR_CODE_PERMISSION);
// Lore Edit defaults // Lore Edit defaults
for (@NotNull LoreEditType value : LoreEditType.values()) { for (@NotNull LoreEditType value : LoreEditType.values()) {
@ -61,11 +56,6 @@ public class PluginSetDefault {
nbSet += trySetDefault(config, PAPER_EDIT_ORDER, DEFAULT_PAPER_EDIT_ORDER); 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) { if (nbSet > 0) {
CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values."); CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values.");
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true); ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);

View file

@ -15,6 +15,22 @@ public class UpdateUtils {
return Version.fromString(versionString); 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) { public static void addToStringList(FileConfiguration config, String path, String... toAdd) {
List<String> groups = new ArrayList<>(config.getStringList(path)); List<String> groups = new ArrayList<>(config.getStringList(path));
groups.addAll(Arrays.asList(toAdd)); groups.addAll(Arrays.asList(toAdd));

View file

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

View file

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

View file

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

View file

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

View file

@ -2,17 +2,14 @@ package io.delilaheve.util
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.EnchantmentUtil.enchantmentName
import org.bukkit.Material
import org.bukkit.NamespacedKey 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.ConfigHolder
import xyz.alexcrea.cuanvil.config.WorkPenaltyType import xyz.alexcrea.cuanvil.config.WorkPenaltyType
import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart import xyz.alexcrea.cuanvil.config.WorkPenaltyType.WorkPenaltyPart
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import xyz.alexcrea.cuanvil.util.AnvilUseType
import java.math.BigDecimal
import java.util.* import java.util.*
/** /**
@ -48,8 +45,6 @@ object ConfigOptions {
const val PERMISSION_NEEDED_FOR_COLOR = "permission_needed_for_color" const val PERMISSION_NEEDED_FOR_COLOR = "permission_needed_for_color"
const val USE_OF_COLOR_COST = "use_of_color_cost" const val USE_OF_COLOR_COST = "use_of_color_cost"
const val PER_COLOR_CODE_PERMISSION = "per_color_code_permission"
// Work penalty config // Work penalty config
const val WORK_PENALTY_ROOT = "work_penalty" const val WORK_PENALTY_ROOT = "work_penalty"
const val WORK_PENALTY_INCREASE = "shared_increase" const val WORK_PENALTY_INCREASE = "shared_increase"
@ -65,22 +60,10 @@ object ConfigOptions {
const val ENCHANT_LIMIT_ROOT = "enchant_limits" const val ENCHANT_LIMIT_ROOT = "enchant_limits"
const val ENCHANT_VALUES_ROOT = "enchant_values" 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 DISABLE_MERGE_OVER_ROOT = "disable-merge-over"
const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments" 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 // Keys for specific enchantment values
private const val KEY_BOOK = "book" private const val KEY_BOOK = "book"
@ -117,23 +100,10 @@ object ConfigOptions {
const val DEFAULT_PERMISSION_NEEDED_FOR_COLOR = true const val DEFAULT_PERMISSION_NEEDED_FOR_COLOR = true
const val DEFAULT_USE_OF_COLOR_COST = 0 const val DEFAULT_USE_OF_COLOR_COST = 0
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 // Debug flag
private const val DEFAULT_DEBUG_LOG = false private const val DEFAULT_DEBUG_LOG = false
private const val DEFAULT_VERBOSE_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 // Config Ranges
// ------------- // -------------
@ -158,9 +128,6 @@ object ConfigOptions {
@JvmField @JvmField
val USE_OF_COLOR_COST_RANGE = 0..1000 val USE_OF_COLOR_COST_RANGE = 0..1000
@JvmField
val DIALOG_MAX_SIZE_RANGE = 0..Int.MAX_VALUE
// Valid range for an enchantment limit // Valid range for an enchantment limit
const val ENCHANT_LIMIT = 255 const val ENCHANT_LIMIT = 255
@ -178,11 +145,6 @@ object ConfigOptions {
// Default max before merge disabled (negative mean enabled) // Default max before merge disabled (negative mean enabled)
const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1 const val DEFAULT_MAX_BEFORE_MERGE_DISABLED = -1
// -----------
// Permissions
// -----------
private const val RENAME_DIALOG_PERMISSION = "ca.rename.dialog"
// ------------- // -------------
// Get methods // Get methods
// ------------- // -------------
@ -335,16 +297,6 @@ object ConfigOptions {
.getBoolean(PERMISSION_NEEDED_FOR_COLOR, DEFAULT_PERMISSION_NEEDED_FOR_COLOR) .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 * How many xp should use of color should cost
*/ */
@ -396,7 +348,7 @@ object ConfigOptions {
* *
* @return the current enchantment limit. -1 if none * @return the current enchantment limit. -1 if none
*/ */
fun getEnchantCountLimit(type: NamespacedKey): Int? { fun getEnchantCountLimit(type: Material): Int? {
val limit = materialEnchantCountLimit(type) val limit = materialEnchantCountLimit(type)
if(limit != null) return limit if(limit != null) return limit
@ -410,8 +362,8 @@ object ConfigOptions {
* *
* @return The current enchantment limit. -1 if none * @return The current enchantment limit. -1 if none
*/ */
private fun materialEnchantCountLimit(type: NamespacedKey): Int? { private fun materialEnchantCountLimit(type: Material): Int? {
val path = "$ENCHANT_COUNT_LIMIT_ITEMS.${type.key.lowercase()}" val path = "$ENCHANT_COUNT_LIMIT_ITEMS.${type.key.key.lowercase()}"
if(!ConfigHolder.DEFAULT_CONFIG.config.isInt(path)) if(!ConfigHolder.DEFAULT_CONFIG.config.isInt(path))
return null return null
@ -451,55 +403,6 @@ object ConfigOptions {
.getBoolean(VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG) .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 * Get the given [enchantment]'s limit
*/ */
@ -651,29 +554,4 @@ object ConfigOptions {
return false 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,7 +6,6 @@ import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.group.ConflictType import xyz.alexcrea.cuanvil.group.ConflictType
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -35,7 +34,7 @@ object EnchantmentUtil {
val bypassFuse = player.hasPermission(CustomAnvil.bypassFusePermission) val bypassFuse = player.hasPermission(CustomAnvil.bypassFusePermission)
val bypassLevel = player.hasPermission(CustomAnvil.bypassLevelPermission) val bypassLevel = player.hasPermission(CustomAnvil.bypassLevelPermission)
var maxEnchantCount = ConfigOptions.getEnchantCountLimit(item.customType) var maxEnchantCount = ConfigOptions.getEnchantCountLimit(item.type)
if(maxEnchantCount == null || maxEnchantCount < 0) maxEnchantCount = Int.MAX_VALUE if(maxEnchantCount == null || maxEnchantCount < 0) maxEnchantCount = Int.MAX_VALUE
val allowed = other.filter { (enchantment, _) -> enchantment.isAllowed(player) } val allowed = other.filter { (enchantment, _) -> enchantment.isAllowed(player) }

View file

@ -4,9 +4,6 @@ import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable import org.bukkit.inventory.meta.Damageable
import xyz.alexcrea.cuanvil.enchant.CAEnchantment 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.ceil
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -38,13 +35,6 @@ 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 * Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values * [first] and [second] item's durability values
@ -64,9 +54,7 @@ object ItemUtil {
val secondDurability = durability - secondDamage val secondDurability = durability - secondDamage
val combinedDurability = firstDurability + secondDurability val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, durability) val newDurability = min(combinedDurability, durability)
it.damage = durability - newDurability
val maxDamage = maxDamage(it)
it.damage = min(durability - newDurability, maxDamage)
itemMeta = it itemMeta = it
return true return true
} }
@ -102,5 +90,5 @@ object ItemUtil {
*/ */
fun ItemStack.canMergeWith( fun ItemStack.canMergeWith(
other: ItemStack? other: ItemStack?
) = (other != null) && (customType == other.customType || (other.isEnchantedBook())) ) = (other != null) && (type == other.type || (other.isEnchantedBook()))
} }

View file

@ -1,72 +0,0 @@
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

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

View file

@ -6,29 +6,29 @@ object MinecraftVersionUtil {
val craftbukkitVersion: String? val craftbukkitVersion: String?
get() { get() {
val version = UpdateUtils.currentMinecraftVersion() val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (version.major != 1) return null if (versionParts[0] != 1) return null
return when (version.minor) { return when (versionParts[1]) {
17 -> when (version.patch) { 17 -> when (versionParts[2]) {
0, 1 -> "1_17R1" 0, 1 -> "1_17R1"
else -> null else -> null
} }
18 -> when (version.patch) { 18 -> when (versionParts[2]) {
0, 1 -> "1_18R1" 0, 1 -> "1_18R1"
2 -> "1_18R2" 2 -> "1_18R2"
else -> null else -> null
} }
19 -> when (version.patch) { 19 -> when (versionParts[2]) {
0, 1, 2 -> "1_19R1" 0, 1, 2 -> "1_19R1"
3 -> "1_19R2" 3 -> "1_19R2"
4 -> "1_19R3" 4 -> "1_19R3"
else -> null else -> null
} }
20 -> when (version.patch) { 20 -> when (versionParts[2]) {
0, 1 -> "1_20R1" 0, 1 -> "1_20R1"
2 -> "1_20R2" 2 -> "1_20R2"
3, 4 -> "1_20R3" 3, 4 -> "1_20R3"
@ -36,7 +36,7 @@ object MinecraftVersionUtil {
else -> null else -> null
} }
21 -> when (version.patch) { 21 -> when (versionParts[2]) {
0, 1 -> "1_21R1" 0, 1 -> "1_21R1"
2, 3 -> "1_21R2" 2, 3 -> "1_21R2"
4 -> "1_21R3" 4 -> "1_21R3"
@ -51,8 +51,4 @@ 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 import java.io.InputStreamReader
object DataPackDependency { object DataPackDependency {
private val START_DETECT_VERSION = Version(1, 20, 5) private val START_DETECT_VERSION = Version(1, 19, 0)
/** /**
* Map of the latest CustomAnvil update related to the pack * 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") CustomAnvil.instance.logger.warning("Could not find material $name for item group $groupName")
continue continue
} }
group.addToPolicy(mat.key) group.addToPolicy(mat)
} }
for (name in section.getStringList("groups")) { for (name in section.getStringList("groups")) {
val otherGroup = MaterialGroupApi.getGroup(name) val otherGroup = MaterialGroupApi.getGroup(name)

View file

@ -1,32 +0,0 @@
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

@ -1,74 +0,0 @@
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

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

View file

@ -0,0 +1,15 @@
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,7 +1,6 @@
package xyz.alexcrea.cuanvil.dependency.packet package xyz.alexcrea.cuanvil.dependency.packet
import org.bukkit.Bukkit import org.bukkit.Bukkit
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
import xyz.alexcrea.cuanvil.dependency.packet.versions.* import xyz.alexcrea.cuanvil.dependency.packet.versions.*
import xyz.alexcrea.cuanvil.update.UpdateUtils import xyz.alexcrea.cuanvil.update.UpdateUtils
@ -12,9 +11,6 @@ object PacketManagerSelector {
fun selectPacketManager(forceProtocolib: Boolean): PacketManager { fun selectPacketManager(forceProtocolib: Boolean): PacketManager {
// Try to find version // Try to find version
if(DependencyManager.externGuiTester.isInTest())
return NoPacketManager()
return if (forceProtocolib) return if (forceProtocolib)
protocolibIfPresent protocolibIfPresent
else { else {
@ -38,8 +34,8 @@ object PacketManagerSelector {
// Reobfuscated packet manager for spigot or paper as it remap // Reobfuscated packet manager for spigot or paper as it remap
private val reobfPacketManager: PacketManagerBase? private val reobfPacketManager: PacketManagerBase?
get() { get() {
val versionParts = UpdateUtils.currentMinecraftVersion() val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts.major != 1) return null if (versionParts[0] != 1) return null
try { try {
val clazz = Class.forName("xyz.alexcrea.cuanvil.dependency.packet.versions." + val clazz = Class.forName("xyz.alexcrea.cuanvil.dependency.packet.versions." +
@ -47,7 +43,7 @@ object PacketManagerSelector {
val manager = clazz.getConstructor().newInstance() val manager = clazz.getConstructor().newInstance()
return manager as PacketManagerBase return manager as PacketManagerBase
} catch (_: ClassNotFoundException) { } catch (e: ClassNotFoundException) {
return null return null
} }
} }

View file

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

View file

@ -1,38 +0,0 @@
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(){ private fun writeMissingGroups(){
// Write group that do not exist on custom anvil. // Write group that do not exist on custom anvil.
val shield = IncludeGroup("shield") val shield = IncludeGroup("shield")
shield.addToPolicy(Material.SHIELD.key) shield.addToPolicy(Material.SHIELD)
MaterialGroupApi.addMaterialGroup(shield) MaterialGroupApi.addMaterialGroup(shield)
val elytra = IncludeGroup("elytra") val elytra = IncludeGroup("elytra")
elytra.addToPolicy(Material.ELYTRA.key) elytra.addToPolicy(Material.ELYTRA)
MaterialGroupApi.addMaterialGroup(elytra) MaterialGroupApi.addMaterialGroup(elytra)
val trinkets = IncludeGroup("trinkets") val trinkets = IncludeGroup("trinkets")
trinkets.addToPolicy(Material.ROTTEN_FLESH.key) trinkets.addToPolicy(Material.ROTTEN_FLESH)
MaterialGroupApi.addMaterialGroup(trinkets) MaterialGroupApi.addMaterialGroup(trinkets)
} }

View file

@ -8,12 +8,11 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.api.EnchantmentApi import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CALegacyEEEnchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CALegacyEEEnchantment
import java.lang.reflect.Constructor
import java.lang.reflect.Method import java.lang.reflect.Method
import su.nightexpress.excellentenchants.api.EnchantRegistry as V5EnchantRegistry import su.nightexpress.excellentenchants.api.EnchantRegistry as V5EnchantRegistry
import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant as LegacyCurseOfFragilityEnchant import su.nightexpress.excellentenchants.enchantment.impl.universal.CurseOfFragilityEnchant as LegacyCurseOfFragilityEnchant
@ -118,8 +117,6 @@ class ExcellentEnchantsDependency {
private lateinit var handleRechargeMethod: Method private lateinit var handleRechargeMethod: Method
private lateinit var handleCombineMethod: Method private lateinit var handleCombineMethod: Method
private val prepareAnvilConstructor = PrepareAnvilEvent::class.java.constructors.first() as Constructor<PrepareAnvilEvent>
fun redirectListeners() { fun redirectListeners() {
val toUnregister = ArrayList<RegisteredListener>() val toUnregister = ArrayList<RegisteredListener>()
// get required PrepareAnvilEvent listener // get required PrepareAnvilEvent listener
@ -221,16 +218,14 @@ class ExcellentEnchantsDependency {
return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean
} }
fun treatAnvilResult(event: CATreatAnvilResult2Event) { fun treatAnvilResult(event: CATreatAnvilResultEvent) {
val result = event.result ?: return val result = event.result
if (result == null) return
val first: ItemStack = treatInput(event.leftItem) val first: ItemStack = treatInput(event.event.inventory.getItem(0))
val second: ItemStack = treatInput(event.rightItem) val second: ItemStack = treatInput(event.event.inventory.getItem(1))
val fakeEvent = prepareAnvilConstructor.newInstance(event.view, result)
handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result) handleCombineMethod.invoke(this.usedAnvilListener, event.event, first, second, result)
event.result = fakeEvent.result
} }
fun testAnvilResult(event: InventoryClickEvent): Any { fun testAnvilResult(event: InventoryClickEvent): Any {

View file

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

View file

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

View file

@ -1,42 +0,0 @@
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

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

View file

@ -2,7 +2,6 @@ package xyz.alexcrea.cuanvil.group
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.NamespacedKey
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
class EnchantConflictGroup( class EnchantConflictGroup(
@ -54,7 +53,7 @@ class EnchantConflictGroup(
return canBypassByBeforeLevel(enchants) || canBypassByAfterLevel(enchants) return canBypassByBeforeLevel(enchants) || canBypassByAfterLevel(enchants)
} }
fun allowed(enchants: Map<CAEnchantment, Int>, mat: NamespacedKey): Boolean { fun allowed(enchants: Map<CAEnchantment, Int>, mat: Material): Boolean {
if (enchantments.size < minBeforeBlock) { if (enchantments.size < minBeforeBlock) {
CustomAnvil.verboseLog("Conflicting bc of to many enchantments") CustomAnvil.verboseLog("Conflicting bc of to many enchantments")
return true return true

View file

@ -8,7 +8,6 @@ import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import java.util.* import java.util.*
import kotlin.collections.set import kotlin.collections.set
@ -49,11 +48,6 @@ class EnchantConflictManager {
lateinit var conflictList: ArrayList<EnchantConflictGroup> lateinit var conflictList: ArrayList<EnchantConflictGroup>
private fun warnBadKey(key: String) {
CustomAnvil.instance.logger.warning("Invalid key $key for conflict: is not a conflict")
}
// Read and prepare all conflict // Read and prepare all conflict
fun prepareConflicts(config: ConfigurationSection, itemManager: ItemGroupManager) { fun prepareConflicts(config: ConfigurationSection, itemManager: ItemGroupManager) {
conflictList = ArrayList() conflictList = ArrayList()
@ -65,11 +59,7 @@ class EnchantConflictManager {
val keys = config.getKeys(false) val keys = config.getKeys(false)
for (key in keys) { for (key in keys) {
val section = config.getConfigurationSection(key) val section = config.getConfigurationSection(key)!!
if(section == null) {
warnBadKey(key)
continue
}
val conflict = createConflict(section, itemManager, key) val conflict = createConflict(section, itemManager, key)
addConflict(conflict) addConflict(conflict)
@ -221,8 +211,8 @@ class EnchantConflictManager {
item: ItemStack, item: ItemStack,
newEnchant: CAEnchantment newEnchant: CAEnchantment
): ConflictType { ): ConflictType {
val type = item.customType val mat = item.type
CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${type}") CustomAnvil.verboseLog("Testing conflict for ${newEnchant.key} on ${mat.key}")
val conflictList = newEnchant.conflicts val conflictList = newEnchant.conflicts
var result = ConflictType.NO_CONFLICT var result = ConflictType.NO_CONFLICT
@ -233,7 +223,7 @@ class EnchantConflictManager {
continue continue
} }
val allowed = conflict.allowed(appliedEnchants, type) val allowed = conflict.allowed(appliedEnchants, mat)
CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ") CustomAnvil.verboseLog("Was against $conflict and conflicting: ${!allowed} ")
if (!allowed) { if (!allowed) {
if (conflict.getEnchants().size <= 1) { if (conflict.getEnchants().size <= 1) {
@ -249,7 +239,7 @@ class EnchantConflictManager {
val immutableEnchants = Collections.unmodifiableMap(appliedEnchants) val immutableEnchants = Collections.unmodifiableMap(appliedEnchants)
for (appliedEnchant in appliedEnchants.keys) { for (appliedEnchant in appliedEnchants.keys) {
if (appliedEnchant is AdditionalTestEnchantment) { if (appliedEnchant is AdditionalTestEnchantment) {
val doConflict = appliedEnchant.isEnchantConflict(immutableEnchants, type) val doConflict = appliedEnchant.isEnchantConflict(immutableEnchants, mat)
if (doConflict) { if (doConflict) {
CustomAnvil.verboseLog("Big conflict by additional test, stopping") CustomAnvil.verboseLog("Big conflict by additional test, stopping")
return ConflictType.ENCHANTMENT_CONFLICT return ConflictType.ENCHANTMENT_CONFLICT
@ -261,7 +251,7 @@ class EnchantConflictManager {
if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) { if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) {
val partialItem = createPartialResult(item, immutableEnchants) val partialItem = createPartialResult(item, immutableEnchants)
if (newEnchant.isItemConflict(immutableEnchants, type, partialItem)) { if (newEnchant.isItemConflict(immutableEnchants, mat, partialItem)) {
return ConflictType.ITEM_CONFLICT return ConflictType.ITEM_CONFLICT
} }

View file

@ -1,12 +1,11 @@
package xyz.alexcrea.cuanvil.group package xyz.alexcrea.cuanvil.group
import org.bukkit.NamespacedKey import org.bukkit.Material
import java.util.* import java.util.*
class ExcludeGroup(name: String) : AbstractMaterialGroup(name) { class ExcludeGroup(name: String) : AbstractMaterialGroup(name) {
override fun createDefaultSet(): EnumSet<Material> {
override fun createDefaultSet(): MutableSet<NamespacedKey> { return EnumSet.allOf(Material::class.java)
return NegativeMaterialSet()
} }
private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet() private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet()
@ -21,9 +20,9 @@ class ExcludeGroup(name: String) : AbstractMaterialGroup(name) {
return false return false
} }
override fun addToPolicy(type: NamespacedKey): ExcludeGroup { override fun addToPolicy(mat: Material): ExcludeGroup {
includedMaterial.remove(type) includedMaterial.remove(mat)
groupItems.remove(type) groupItems.remove(mat)
return this return this
} }
@ -61,7 +60,7 @@ class ExcludeGroup(name: String) : AbstractMaterialGroup(name) {
} }
} }
override fun getMaterials(): MutableSet<NamespacedKey> { override fun getMaterials(): EnumSet<Material> {
return groupItems return groupItems
} }

View file

@ -1,12 +1,11 @@
package xyz.alexcrea.cuanvil.group package xyz.alexcrea.cuanvil.group
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.NamespacedKey
import java.util.* import java.util.*
class IncludeGroup(name: String) : AbstractMaterialGroup(name) { class IncludeGroup(name: String) : AbstractMaterialGroup(name) {
override fun createDefaultSet(): MutableSet<NamespacedKey> { override fun createDefaultSet(): EnumSet<Material> {
return HashSet() return EnumSet.noneOf(Material::class.java)
} }
private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet() private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet()
@ -21,9 +20,9 @@ class IncludeGroup(name: String) : AbstractMaterialGroup(name) {
return false return false
} }
override fun addToPolicy(type: NamespacedKey): IncludeGroup { override fun addToPolicy(mat: Material): IncludeGroup {
includedMaterial.add(type) includedMaterial.add(mat)
groupItems.add(type) groupItems.add(mat)
return this return this
} }
@ -48,7 +47,7 @@ class IncludeGroup(name: String) : AbstractMaterialGroup(name) {
} }
} }
override fun setNonGroupInheritedMaterials(materials: Set<NamespacedKey>) { override fun setNonGroupInheritedMaterials(materials: EnumSet<Material>) {
super.setNonGroupInheritedMaterials(materials) super.setNonGroupInheritedMaterials(materials)
updateMaterials() updateMaterials()
@ -67,7 +66,7 @@ class IncludeGroup(name: String) : AbstractMaterialGroup(name) {
} }
} }
override fun getMaterials(): MutableSet<NamespacedKey> { override fun getMaterials(): EnumSet<Material> {
return groupItems return groupItems
} }

View file

@ -31,8 +31,6 @@ class ItemGroupManager {
for (key in keys) { for (key in keys) {
if (groupMap.containsKey(key)) if (groupMap.containsKey(key))
continue continue
if (!config.isConfigurationSection(key))
continue
createGroup(config, keys, key) createGroup(config, keys, key)
} }
} }
@ -53,7 +51,6 @@ class ItemGroupManager {
key: String key: String
): AbstractMaterialGroup { ): AbstractMaterialGroup {
val groupSection = config.getConfigurationSection(key)!! val groupSection = config.getConfigurationSection(key)!!
val groupType = groupSection.getString(GROUP_TYPE_PATH, null) val groupType = groupSection.getString(GROUP_TYPE_PATH, null)
// Create Material group according to the group type // Create Material group according to the group type
@ -94,7 +91,7 @@ class ItemGroupManager {
} }
continue continue
} }
group.addToPolicy(material.key) group.addToPolicy(material)
} }
// Read group to include in this group policy. // Read group to include in this group policy.
@ -108,13 +105,11 @@ class ItemGroupManager {
continue continue
} }
// Get other group or create it if not yet created // Get other group or create it if not yet created
val otherGroup = val otherGroup = if (!groupMap.containsKey(groupName)) {
if (!groupMap.containsKey(groupName)) {
if(!config.isConfigurationSection(groupName)) continue
createGroup(config, keys, groupName) createGroup(config, keys, groupName)
} else {
groupMap[groupName]!!
} }
else groupMap[groupName]!!
// Avoid self reference or it will create an infinite loop // Avoid self reference or it will create an infinite loop
if (otherGroup.isReferencing(group)) { if (otherGroup.isReferencing(group)) {
CustomAnvil.instance.logger.warning( CustomAnvil.instance.logger.warning(

View file

@ -1,22 +0,0 @@
package xyz.alexcrea.cuanvil.group
import org.bukkit.NamespacedKey
import xyz.alexcrea.cuanvil.util.MaterialUtil
import xyz.alexcrea.cuanvil.util.NegativeSet
class NegativeMaterialSet: NegativeSet<NamespacedKey>() {
override fun iterator(): MutableIterator<NamespacedKey> {
val materials = MaterialUtil.getMaterials()
materials.removeIf { negate.contains(it) }
return materials.iterator()
}
override fun isEmpty(): Boolean {
return negate.size >= MaterialUtil.getMaterialCount()
}
override val size get() = MaterialUtil.getMaterialCount() - negate.size
}

View file

@ -7,7 +7,6 @@ import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil
class AnvilCloseListener(private val packetManager: PacketManager) : Listener { class AnvilCloseListener(private val packetManager: PacketManager) : Listener {
@ -19,7 +18,6 @@ class AnvilCloseListener(private val packetManager: PacketManager) : Listener {
packetManager.setInstantBuild(player, false) packetManager.setInstantBuild(player, false)
} }
AnvilRenameDialogUtil.anvilRenameDialog.closeInventory(player)
} }
} }

View file

@ -3,6 +3,7 @@ package xyz.alexcrea.cuanvil.listener
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.ItemUtil.canMergeWith import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Player import org.bukkit.entity.Player
@ -15,25 +16,22 @@ import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.BookMeta import org.bukkit.inventory.meta.BookMeta
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.CustomCraftResult
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.UnitRepairResult
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.AnvilLoreEditUtil
import xyz.alexcrea.cuanvil.util.AnvilUseType
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.CustomRecipeUtil import xyz.alexcrea.cuanvil.util.CustomRecipeUtil
import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.MiniMessageUtil
import xyz.alexcrea.cuanvil.util.anvil.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil
import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil
import xyz.alexcrea.cuanvil.util.config.LoreEditType import xyz.alexcrea.cuanvil.util.config.LoreEditType
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import kotlin.math.min import kotlin.math.min
@ -52,7 +50,6 @@ class AnvilResultListener : Listener {
fun anvilExtractionCheck(event: InventoryClickEvent) { fun anvilExtractionCheck(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return val player = event.whoClicked as? Player ?: return
val inventory = event.inventory as? AnvilInventory ?: return val inventory = event.inventory as? AnvilInventory ?: return
val view = event.view
if (event.rawSlot != ANVIL_OUTPUT_SLOT) { if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return return
@ -67,104 +64,84 @@ class AnvilResultListener : Listener {
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT) val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
// Deny by default. allow if working
event.result = Event.Result.DENY
if (GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) { if (GameMode.CREATIVE != player.gameMode && inventory.repairCost >= inventory.maximumRepairCost) {
event.result = Event.Result.DENY
return return
} }
// Test custom recipe // Test custom recipe
val customRecipeResult = AnvilMergeLogic.testCustomRecipe(view, inventory, player, leftItem, rightItem) val recipe = CustomRecipeUtil.getCustomRecipe(leftItem, rightItem)
if (!customRecipeResult.isEmpty()) { if (recipe != null) {
event.result = Event.Result.ALLOW
onCustomCraft( onCustomCraft(
event, player, inventory, event, recipe, player,
leftItem, rightItem, customRecipeResult leftItem, rightItem, output, inventory
) )
return return
} }
// Do not continue if there was no change // Do not continue if there was no change
if ((output == inventory.getItem(ANVIL_INPUT_LEFT))) { if ((output == inventory.getItem(ANVIL_INPUT_LEFT))) {
event.result = Event.Result.DENY
return return
} }
// Rename // Rename
if (rightItem == null) { if (rightItem == null) {
val result = AnvilMergeLogic.doRenaming(view, inventory, player, leftItem) event.result = Event.Result.ALLOW
if (result.isEmpty()) return
extractAnvilResult(
event, player, inventory,
null, 0,
null, 0,
result
)
return return
} }
// Merge // Merge
val canMerge = leftItem.canMergeWith(rightItem) val canMerge = leftItem.canMergeWith(rightItem)
if (canMerge) { if (canMerge) {
val result = AnvilMergeLogic.doMerge(view, inventory, player, leftItem, rightItem) event.result = Event.Result.ALLOW
extractAnvilResult(
event, player, inventory,
null, 0,
null, 0,
result
)
return return
} }
// Unit repair // Unit repair
val unitRepairResult = AnvilMergeLogic.testUnitRepair( val unitRepairResult = leftItem.getRepair(rightItem)
view, inventory, player, if (unitRepairResult != null) {
leftItem, rightItem
)
if (!unitRepairResult.isEmpty()) {
onUnitRepairExtract( onUnitRepairExtract(
rightItem, event, player, inventory, leftItem, rightItem, output,
unitRepairResult unitRepairResult, event, player, inventory
) )
return return
} }
// For lore edit // For lore edit
val loreResult = AnvilMergeLogic.testLoreEdit(player, leftItem, rightItem) if (handleBookLoreEdit(event, inventory, player, leftItem, rightItem, output)) {
if (!loreResult.isEmpty()) { return
if (loreResult.type.isBook) } else if (handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, output)) {
handleBookLoreEdit(event, inventory, player, leftItem, rightItem, loreResult)
else
handlePaperLoreEdit(event, inventory, player, leftItem, rightItem, loreResult)
return return
} }
// Else there was no working situation somehow so we deny
event.result = Event.Result.DENY
} }
private fun onCustomCraft( private fun onCustomCraft(
event: InventoryClickEvent, event: InventoryClickEvent,
recipe: AnvilCustomRecipe,
player: Player, player: Player,
inventory: AnvilInventory,
leftItem: ItemStack, leftItem: ItemStack,
rightItem: ItemStack?, rightItem: ItemStack?,
result: CustomCraftResult, output: ItemStack,
inventory: AnvilInventory
) { ) {
val recipe = result.recipe!! event.result = Event.Result.DENY
val rawCost = result.customCraftCost.rawCost
if (recipe.leftItem == null) return // in case it changed
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
val xpCost = recipe.determineCost(amount, leftItem, output)
val finalCost = val finalCost =
if (recipe.removeExactLinearXp) rawCost if (recipe.removeExactLinearXp) xpCost
else AnvilXpUtil.calculateLevelForXp(rawCost) else AnvilXpUtil.calculateLevelForXp(xpCost)
CustomAnvil.log(
"gamemode: ${player.gameMode != GameMode.CREATIVE}, " +
"cost: $finalCost, level: ${player.level}, " +
"result: ${player.totalExperience < finalCost} ${player.level < finalCost}"
)
CustomAnvil.log("gamemode: ${player.gameMode != GameMode.CREATIVE}, cost: $finalCost, level: ${player.level}, result: ${player.totalExperience < finalCost} ${player.level < finalCost}")
if (player.gameMode != GameMode.CREATIVE) { if (player.gameMode != GameMode.CREATIVE) {
if (ConfigOptions.shouldUseMoney(player)) { if (recipe.removeExactLinearXp) {
result.cost.isMonetary = true
if (!EconomyManager.economy!!.has(player, result.cost.asMonetaryCost())) return
} else if (recipe.removeExactLinearXp) {
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level) val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
val totalXp = levelXp + player.exp * delta val totalXp = levelXp + player.exp * delta
@ -181,31 +158,31 @@ class AnvilResultListener : Listener {
if (event.click != ClickType.MIDDLE && if (event.click != ClickType.MIDDLE &&
!handleCustomCraftClick( !handleCustomCraftClick(
event, event,
recipe,
inventory, inventory,
player, player,
leftItem, leftItem,
rightItem, rightItem,
result amount,
finalCost,
recipe.removeExactLinearXp
) )
) return ) return
// Finally, we add the item to the player // Finally, we add the item to the player
if (slotDestination.type == SlotType.CURSOR) { if (slotDestination.type == SlotType.CURSOR) {
player.setItemOnCursor(result.item) player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY } else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, result.item) player.inventory.setItem(slotDestination.slot, output)
} }
} }
private fun handleCustomCraftClick( private fun handleCustomCraftClick(
event: InventoryClickEvent, event: InventoryClickEvent, recipe: AnvilCustomRecipe,
inventory: AnvilInventory, player: Player, inventory: AnvilInventory, player: Player,
leftItem: ItemStack, rightItem: ItemStack?, leftItem: ItemStack, rightItem: ItemStack?,
result: CustomCraftResult amount: Int, xpCost: Int, linearCost: Boolean = false
): Boolean { ): Boolean {
val amount = result.amount
val recipe = result.recipe!!
// We remove what should be removed // We remove what should be removed
if (rightItem != null) { if (rightItem != null) {
if (recipe.rightItem == null) return false// in case it changed if (recipe.rightItem == null) return false// in case it changed
@ -217,7 +194,25 @@ class AnvilResultListener : Listener {
leftItem.amount -= amount * recipe.leftItem!!.amount leftItem.amount -= amount * recipe.leftItem!!.amount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem) inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
removeCustomCraftCost(player, result) if (player.gameMode != GameMode.CREATIVE) {
if (linearCost) {
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
var totalXp = levelXp + player.exp * delta
totalXp -= xpCost
val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt())
val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel)
val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp
val xp = (totalXp - newLevelXp) / newDelta
player.level = newLevel
player.exp = xp / newDelta
} else {
player.level -= xpCost
}
}
// Then we try to find the new values for the anvil // Then we try to find the new values for the anvil
val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem) val newAmount = CustomRecipeUtil.getCustomRecipeAmount(recipe, leftItem, rightItem)
@ -241,53 +236,6 @@ class AnvilResultListener : Listener {
return true return true
} }
private fun removeCustomCraftCost(player: Player, result: CustomCraftResult) {
if (player.gameMode == GameMode.CREATIVE) return
val rawCost = result.customCraftCost.rawCost
if (result.cost.isMonetary) {
EconomyManager.economy!!.remove(player, result.cost.asMonetaryCost())
return
}
if (result.recipe!!.removeExactLinearXp) {
val levelXp = AnvilXpUtil.calculateXpForLevel(player.level)
val delta = AnvilXpUtil.calculateXpForLevel(player.level + 1) - levelXp
var totalXp = levelXp + player.exp * delta
totalXp -= rawCost
val newLevel = AnvilXpUtil.calculateLevelForXp(totalXp.toInt())
val newLevelXp = AnvilXpUtil.calculateXpForLevel(newLevel)
val newDelta = AnvilXpUtil.calculateXpForLevel(newLevel + 1) - newLevelXp
val xp = (totalXp - newLevelXp) / newDelta
player.level = newLevel
player.exp = xp / newDelta
} else {
player.level -= AnvilXpUtil.calculateLevelForXp(rawCost)
}
}
private fun tryRemoveCost(player: Player, result: AnvilResult): Boolean {
if (player.gameMode == GameMode.CREATIVE) return true
val cost = result.cost
if (cost.isMonetary) {
val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost())
if (!result) return false
} else {
val xpCost = cost.filteredXpCost()
if (xpCost > AnvilXpUtil.maximumXpCost(result.ignoreXpRules)) return false
if (player.level < xpCost) return false
player.level -= xpCost
}
return true
}
private fun extractAnvilResult( private fun extractAnvilResult(
event: InventoryClickEvent, event: InventoryClickEvent,
player: Player, player: Player,
@ -296,17 +244,15 @@ class AnvilResultListener : Listener {
leftRemoveCount: Int, leftRemoveCount: Int,
rightItem: ItemStack?, rightItem: ItemStack?,
rightRemoveCount: Int, rightRemoveCount: Int,
result: AnvilResult output: ItemStack,
repairCost: Int,
): Boolean { ): Boolean {
if (result.isEmpty()) return false
// To avoid vanilla, we cancel the event // To avoid vanilla, we cancel the event
event.result = Event.Result.DENY event.result = Event.Result.DENY
event.isCancelled = true event.isCancelled = true
val cost = result.cost
processCost(inventory, player, cost) // Assumed if player do not have enough xp then it returned MIN_VALUE
if (!cost.valid && player.gameMode != GameMode.CREATIVE) return false if (repairCost == Int.MIN_VALUE) return false
// Where should we get the item // Where should we get the item
val slotDestination = getActionSlot(event, player) val slotDestination = getActionSlot(event, player)
@ -314,8 +260,6 @@ class AnvilResultListener : Listener {
// If not creative middle click... // If not creative middle click...
if (event.click != ClickType.MIDDLE) { if (event.click != ClickType.MIDDLE) {
if (!tryRemoveCost(player, result)) return false
// We remove what should be removed // We remove what should be removed
if (leftItem != null) leftItem.amount -= leftRemoveCount if (leftItem != null) leftItem.amount -= leftRemoveCount
inventory.setItem(ANVIL_INPUT_LEFT, leftItem) inventory.setItem(ANVIL_INPUT_LEFT, leftItem)
@ -324,58 +268,99 @@ class AnvilResultListener : Listener {
inventory.setItem(ANVIL_INPUT_RIGHT, rightItem) inventory.setItem(ANVIL_INPUT_RIGHT, rightItem)
inventory.setItem(ANVIL_OUTPUT_SLOT, null) inventory.setItem(ANVIL_OUTPUT_SLOT, null)
player.level -= repairCost
} }
// Finally, we add the item to the player // Finally, we add the item to the player
if (SlotType.CURSOR == slotDestination.type) { if (SlotType.CURSOR == slotDestination.type) {
player.setItemOnCursor(result.item) player.setItemOnCursor(output)
} else {// We assume SlotType == SlotType.INVENTORY } else {// We assume SlotType == SlotType.INVENTORY
player.inventory.setItem(slotDestination.slot, result.item) player.inventory.setItem(slotDestination.slot, output)
} }
// TODO probably anvil damage & sound here ?? // TODO probably anvil damage & sound here ??
return true return true
} }
private fun processCost(inventory: AnvilInventory, player: Player, cost: AnvilCost) { private fun onUnitRepairExtract(
var sum = cost.repair leftItem: ItemStack,
rightItem: ItemStack,
output: ItemStack,
unitRepairResult: Double,
event: InventoryClickEvent,
player: Player,
inventory: AnvilInventory
) {
val resultCopy = leftItem.clone()
val resultAmount = resultCopy.unitRepair(
rightItem.amount, unitRepairResult
)
// Get repair cost
val repairCost = getUnitRepairCost(inventory, player, leftItem, output, resultCopy, resultAmount)
// And then we give the item manually
extractAnvilResult(
event, player, inventory,
null, 0,
rightItem, resultAmount,
resultCopy, repairCost
)
}
private fun getUnitRepairCost(
inventory: AnvilInventory, player: Player,
leftItem: ItemStack, output: ItemStack,
resultCopy: ItemStack, resultAmount: Int
): Int {
if (player.gameMode == GameMode.CREATIVE) return 0
var repairCost = 0
// Get repairCost
leftItem.itemMeta?.let { leftMeta ->
val leftName = leftMeta.displayName
output.itemMeta?.let {
// Rename cost
if (!leftName.contentEquals(it.displayName)) {
repairCost += ConfigOptions.itemRenameCost
// Color cost
if (it.displayName.contains('§')) {
repairCost += ConfigOptions.useOfColorCost
}
}
}
}
repairCost += AnvilXpUtil.calculatePenalty(leftItem, null, resultCopy, AnvilUseType.UNIT_REPAIR)
repairCost += resultAmount * ConfigOptions.unitRepairCost
if ( if (
!ConfigOptions.doRemoveCostLimit && !ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost ConfigOptions.doCapCost
) { ) {
val final = min(sum, ConfigOptions.maxAnvilCost) repairCost = min(repairCost, ConfigOptions.maxAnvilCost)
cost.generic += (final - sum)
sum = final
} }
if (ConfigOptions.shouldUseMoney(player)) { if ((inventory.maximumRepairCost <= repairCost)
cost.isMonetary = true || (player.level < repairCost)
if (!EconomyManager.economy!!.has(player, cost.asMonetaryCost())) ) return Int.MIN_VALUE
cost.valid = false
} else { return repairCost
if ((inventory.maximumRepairCost <= sum)
|| (player.level < sum)
) cost.valid = false
}
} }
private fun onUnitRepairExtract( private fun getFromLoreEditXpCost(
rightItem: ItemStack, xpCost: AtomicInteger,
event: InventoryClickEvent,
player: Player, player: Player,
inventory: AnvilInventory, inventory: AnvilInventory,
result: UnitRepairResult, ): Int {
) { if (GameMode.CREATIVE == player.gameMode) return 0
// We give the item manually
extractAnvilResult( val repairCost = xpCost.get()
event, player, inventory, return if ((inventory.maximumRepairCost <= repairCost)
null, 0, || (player.level < repairCost)
rightItem, result.repairAmount, ) Int.MIN_VALUE
result else repairCost
)
} }
private fun handleBookLoreEdit( private fun handleBookLoreEdit(
@ -384,22 +369,16 @@ class AnvilResultListener : Listener {
player: Player, player: Player,
leftItem: ItemStack, leftItem: ItemStack,
rightItem: ItemStack, rightItem: ItemStack,
result: LoreEditResult output: ItemStack,
) { ): Boolean {
if (result.type.isAppend) if (Material.WRITABLE_BOOK != rightItem.type) return false
handleBookLoreAppend(event, inventory, player, rightItem, result) val bookMeta = rightItem.itemMeta as BookMeta? ?: return false
else
handleBookLoreRemove(event, inventory, player, leftItem, rightItem, result)
}
private fun handleBookLoreAppend( val editType = AnvilLoreEditUtil.bookLoreEditIsAppend(leftItem, rightItem) ?: return false
event: InventoryClickEvent,
inventory: AnvilInventory, val xpCost = AtomicInteger()
player: Player, if (editType) {
rightItem: ItemStack, if (output != AnvilLoreEditUtil.handleLoreAppendByBook(player, leftItem, bookMeta, xpCost)) return false
result: LoreEditResult
) {
val bookMeta = rightItem.itemMeta as BookMeta? ?: return
// Remove pages to book // Remove pages to book
val clearedBook: ItemStack? val clearedBook: ItemStack?
@ -411,27 +390,18 @@ class AnvilResultListener : Listener {
clearedBook.itemMeta = bookMeta clearedBook.itemMeta = bookMeta
} }
extractAnvilResult( return extractAnvilResult(
event, player, inventory, event, player, inventory,
null, 0, null, 0,
clearedBook, 0, clearedBook, 0,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} } else {
if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, xpCost)) return false
private fun handleBookLoreRemove(
event: InventoryClickEvent,
inventory: AnvilInventory,
player: Player,
leftItem: ItemStack,
rightItem: ItemStack,
result: LoreEditResult
) {
val bookMeta = rightItem.itemMeta as BookMeta? ?: return
// fill book meta // fill book meta
val lore = DependencyManager.stripLore(leftItem) val lore = DependencyManager.stripLore(leftItem)
if (lore.isEmpty()) return if (lore.isEmpty()) return false
val rightCopy: ItemStack? val rightCopy: ItemStack?
if (LoreEditType.REMOVE_BOOK.doConsume) { if (LoreEditType.REMOVE_BOOK.doConsume) {
@ -456,13 +426,14 @@ class AnvilResultListener : Listener {
rightCopy.itemMeta = bookMeta rightCopy.itemMeta = bookMeta
} }
extractAnvilResult( return extractAnvilResult(
event, player, inventory, event, player, inventory,
null, 0, null, 0,
rightCopy, 0, rightCopy, 0,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} }
}
private fun handlePaperLoreEdit( private fun handlePaperLoreEdit(
event: InventoryClickEvent, event: InventoryClickEvent,
@ -470,23 +441,16 @@ class AnvilResultListener : Listener {
player: Player, player: Player,
leftItem: ItemStack, leftItem: ItemStack,
rightItem: ItemStack, rightItem: ItemStack,
result: LoreEditResult output: ItemStack,
) { ): Boolean {
if (result.type.isAppend) if (Material.PAPER != rightItem.type) return false
handlePaperLoreAppend(event, inventory, player, rightItem, result) val paperMeta = rightItem.itemMeta ?: return false
else
handlePaperLoreRemove(event, inventory, player, leftItem, rightItem, result)
}
private fun handlePaperLoreAppend( val editTypeIsAppend = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false
event: InventoryClickEvent,
inventory: AnvilInventory,
player: Player,
rightItem: ItemStack,
result: LoreEditResult
) {
val paperMeta = rightItem.itemMeta ?: return
val xpCost = AtomicInteger()
if (editTypeIsAppend) {
if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, xpCost)) return false
val paperCopy: ItemStack? val paperCopy: ItemStack?
if (LoreEditType.APPEND_PAPER.doConsume) { if (LoreEditType.APPEND_PAPER.doConsume) {
@ -496,43 +460,31 @@ class AnvilResultListener : Listener {
paperCopy = rightItem.clone() paperCopy = rightItem.clone()
paperCopy.amount = 1 paperCopy.amount = 1
paperMeta.setComponentDisplayName(null) paperMeta.setComponentDisplayName(null)
// Remove pcd name
AnvilMergeLogic.processPCD(paperMeta, player, null)
paperCopy.itemMeta = paperMeta paperCopy.itemMeta = paperMeta
} }
if (rightItem.amount > 1) { return if (rightItem.amount > 1) {
extractAnvilResult( extractAnvilResult(
event, player, inventory, event, player, inventory,
paperCopy, 0, paperCopy, 0,
rightItem, 1, rightItem, 1,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} else { } else {
extractAnvilResult( extractAnvilResult(
event, player, inventory, event, player, inventory,
null, 0, null, 0,
paperCopy, 0, paperCopy, 0,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} }
} } else {
if (output != AnvilLoreEditUtil.handleLoreRemoveByPaper(player, leftItem, xpCost)) return false
private fun handlePaperLoreRemove(
event: InventoryClickEvent,
inventory: AnvilInventory,
player: Player,
leftItem: ItemStack,
rightItem: ItemStack,
result: LoreEditResult
) {
val leftMeta = leftItem.itemMeta val leftMeta = leftItem.itemMeta
if (leftMeta == null || !leftMeta.hasLore()) return if (leftMeta == null || !leftMeta.hasLore()) return false
val lore = DependencyManager.stripLore(leftItem) val lore = DependencyManager.stripLore(leftItem)
if (lore.isEmpty()) return if (lore.isEmpty()) return false
// Create result item // Create result item
val rightClone: ItemStack? val rightClone: ItemStack?
@ -550,28 +502,30 @@ class AnvilResultListener : Listener {
rightClone = rightItem.clone() rightClone = rightItem.clone()
rightClone.amount = 1 rightClone.amount = 1
val resultMeta = rightClone.itemMeta ?: return val resultMeta = rightClone.itemMeta ?: return false
resultMeta.setComponentDisplayName(ref.get()) resultMeta.setComponentDisplayName(ref.get())
rightClone.itemMeta = resultMeta rightClone.itemMeta = resultMeta
} }
if (rightItem.amount > 1) { return if (rightItem.amount > 1) {
extractAnvilResult( extractAnvilResult(
event, player, inventory, event, player, inventory,
rightClone, 0, rightClone, 0,
rightItem, 1, rightItem, 1,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} else { } else {
extractAnvilResult( extractAnvilResult(
event, player, inventory, event, player, inventory,
null, 0, null, 0,
rightClone, 0, rightClone, 0,
result output, getFromLoreEditXpCost(xpCost, player, inventory)
) )
} }
} }
}
/** /**
* Get the destination slot or "NO_SLOT" slot container if there is no slot available * Get the destination slot or "NO_SLOT" slot container if there is no slot available
*/ */

View file

@ -3,29 +3,29 @@ package xyz.alexcrea.cuanvil.listener
import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil import com.github.stefvanschie.inventoryframework.util.InventoryViewUtil
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.canMergeWith import io.delilaheve.util.ItemUtil.canMergeWith
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.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.EnchantmentStorageMeta
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doMerge
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.doRenaming
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testCustomRecipe
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testLoreEdit
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.testUnitRepair
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.*
import xyz.alexcrea.cuanvil.util.dialog.AnvilRenameDialogUtil import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import java.util.concurrent.atomic.AtomicInteger
/** /**
* Listener for anvil events * Listener for anvil events
@ -42,32 +42,32 @@ class PrepareAnvilListener : Listener {
var IS_EMPTY_TEST = false var IS_EMPTY_TEST = false
} }
private fun ItemStack?.isAir(): Boolean {
return this == null || this.type.isAir || this.amount == 0
}
/** /**
* Event handler logic for when an anvil contains items to be combined * Event handler logic for when an anvil contains items to be combined
*/ */
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun anvilCombineCheck(event: PrepareAnvilEvent) { fun anvilCombineCheck(event: PrepareAnvilEvent) {
val view = event.view // Should find player
val player: HumanEntity = InventoryViewUtil.getInstance().getPlayer(event.view)
val inventory = event.inventory val inventory = event.inventory
val player = InventoryViewUtil.getInstance().getPlayer(view)
if(player !is Player) return
tryRenameDialog(player, event)
// Test if custom anvil is bypassed before immutability test // Test if custom anvil is bypassed before immutability test
if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) { if (DependencyManager.earlyTryEventPreAnvilBypass(event, player)) {
// even if we got bypassed we still want to set price // even if we got bypassed we still want to set price
AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost)) AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost)
return return
} }
val first = inventory.getItem(ANVIL_INPUT_LEFT) val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT) val second = inventory.getItem(ANVIL_INPUT_RIGHT)
if(IS_EMPTY_TEST) { if(IS_EMPTY_TEST) {
event.result = null
IS_EMPTY_TEST = false IS_EMPTY_TEST = false
applyResult(event, player, AnvilResult.EMPTY)
return return
} }
@ -79,71 +79,49 @@ class PrepareAnvilListener : Listener {
if (isImmutable(first) || isImmutable(second)) { if (isImmutable(first) || isImmutable(second)) {
CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable") CustomAnvil.verboseLog("Skipping anvil process as one of the two item is immutable")
applyResult(event, player, AnvilResult.EMPTY) event.result = null
return return
} }
// Test if the event should bypass custom anvil. // Test if the event should bypass custom anvil.
if (DependencyManager.tryEventPreAnvilBypass(event, player)) { if (DependencyManager.tryEventPreAnvilBypass(event, player)) {
// even if we got bypassed we still want to set price // even if we got bypassed we still want to set price
AnvilXpUtil.setAnvilInvCost(inventory, view, player, AnvilCost(event.inventory.repairCost)) AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, event.inventory.repairCost)
return return
} }
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val result = getResult(view, inventory, player, first, second)
applyResult(event, player, result)
}
fun getResult(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory,
player: Player,
first: ItemStack?, second: ItemStack?) : AnvilResult
{
if(first == null)
return AnvilResult.EMPTY
// Test custom recipe // Test custom recipe
var result: AnvilResult = testCustomRecipe(view, inventory, player, first, second) if (testCustomRecipe(event, inventory, player, first, second)) return
if (!result.isEmpty())
return result
// Test rename lonely item // Test rename lonely item
val shouldTryRename = second.isAir val isAir = second.isAir()
CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename") CustomAnvil.verboseLog("checking air in main logic: $isAir")
if (shouldTryRename) if (isAir) {
return doRenaming(view, inventory, player, first) doRenaming(event, inventory, player, first)
return
// Test for merge
if (first.canMergeWith(second!!))
return doMerge(view, inventory, player, first, second)
// Test for unit repair
result = testUnitRepair(view, inventory, player, first, second)
if (!result.isEmpty())
return result
// Test for lore edit
result = testLoreEdit(player, first, second)
if (!result.isEmpty())
return result
return AnvilResult.EMPTY
} }
private fun tryRenameDialog( // Test for merge
player: HumanEntity, if (first.canMergeWith(second!!)) {
event: PrepareAnvilEvent doMerge(event, inventory, player, first, second)
) { return
if(!ConfigOptions.canUseDialogRename(player)) return }
// Test for unit repair
if (testUnitRepair(event, inventory, player, first, second)) return
// Test for lore edit
if (testLoreEdit(event, inventory, player, first, second)) return
CustomAnvil.log("no anvil fuse type found")
event.result = null
AnvilRenameDialogUtil.anvilRenameDialog.tryShowDialog(player, event)
} }
private fun isImmutable(item: ItemStack?): Boolean { private fun isImmutable(item: ItemStack?): Boolean {
if (item.isAir) return false if (item.isAir()) return false
val meta = item!!.itemMeta val meta = item!!.itemMeta
return meta != null && return meta != null &&
@ -168,14 +146,219 @@ class PrepareAnvilListener : Listener {
return false return false
} }
private fun applyResult(event: PrepareAnvilEvent, player: Player, result: AnvilResult) { // return true if a custom recipe exist with these ingredients
event.result = result.item private fun testCustomRecipe(
event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity,
first: ItemStack, second: ItemStack?
): Boolean {
val recipe = CustomRecipeUtil.getCustomRecipe(first, second)
CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}")
if (recipe == null) return false
if(result.item == null) { val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
AnvilXpUtil.onNoResult(player, event.view)
val resultItem: ItemStack = recipe.resultItem!!.clone()
resultItem.amount *= amount
// Maybe add an option on custom craft to ignore/not ignore penalty ??
val xpCost = recipe.determineCost(amount, first, resultItem)
val levelCost =
if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
else AnvilXpUtil.calculateLevelForXp(xpCost)
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.CUSTOM_CRAFT, levelCost)
if (finalResult == null) return false
event.result = finalResult.result
if (finalResult.result.isAir()) return false
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost, true)
return true
}
private fun doRenaming(
event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, first: ItemStack
) {
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
// Test/stop if nothing changed.
if (first == resultItem) {
CustomAnvil.log("no right item, But input is same as output")
event.result = null
return return
} }
AnvilXpUtil.setAnvilInvCost(event.inventory, event.view, player, result.cost, result.ignoreXpRules)
anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.RENAME_ONLY, anvilCost)
if (finalResult == null) return
event.result = finalResult.result
if (finalResult.result.isAir()) return
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
} }
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, player,
ConfigOptions.permissionNeededForColor,
ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, ConfigOptions.allowMinimessage,
AnvilColorUtil.ColorUseType.RENAME
)
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 == "")) {
it.setDisplayName(renameText)
resultItem.itemMeta = it
sumCost += ConfigOptions.itemRenameCost
}
return sumCost
}
return 0
}
private fun doMerge(
event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity,
first: ItemStack, second: ItemStack
) {
val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player)
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
val resultItem = first.clone()
var anvilCost = 0
if(hasChanged){
resultItem.setEnchantmentsUnsafe(newEnchants)
// Calculate enchantment cost
anvilCost+= AnvilXpUtil.getRightValues(second, resultItem)
}
// 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)
anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0
hasChanged = hasChanged || repaired
}
// Test/stop if nothing changed.
if (!hasChanged) {
CustomAnvil.log("Mergable with second, But input is same as output")
event.result = null
return
}
// As calculatePenalty edit result, we need to calculate penalty after checking equality
anvilCost += AnvilXpUtil.calculatePenalty(first, second, resultItem, AnvilUseType.MERGE)
// Calculate rename cost
anvilCost += handleRename(resultItem, inventory, player)
// Finally, we set result
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.MERGE, anvilCost)
if (finalResult == null) return
event.result = finalResult.result
if (finalResult.result.isAir()) return
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
}
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 there is a valid unit repair with these ingredients
private fun testUnitRepair(
event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
first: ItemStack, second: ItemStack
): Boolean {
val unitRepairAmount = first.getRepair(second) ?: return false
val resultItem = first.clone()
var anvilCost = handleRename(resultItem, inventory, player)
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)
if (repairAmount > 0) {
anvilCost += repairAmount * ConfigOptions.unitRepairCost
}
// We do not care about right item penalty for unit repair
anvilCost += 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")
event.result = null
return true
}
val finalResult = DependencyManager.tryTreatAnvilResult(event, resultItem, AnvilUseType.UNIT_REPAIR, anvilCost)
if (finalResult == null) return false
event.result = finalResult.result
if (finalResult.result.isAir()) return false
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, finalResult.levelCost)
return true
}
private fun testLoreEdit(
event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity,
first: ItemStack, second: ItemStack
): Boolean {
val type = second.type
var result: ItemStack? = null
val xpCost = AtomicInteger()
if (Material.WRITABLE_BOOK == type) {
result = AnvilLoreEditUtil.tryLoreEditByBook(player, first, second, xpCost)
} else if (Material.PAPER == type) {
result = AnvilLoreEditUtil.tryLoreEditByPaper(player, first, second, xpCost)
}
if (result.isAir() || first == result) {
CustomAnvil.log("lore edit, But input is same as output")
event.result = null
return false
}
event.result = result
AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, xpCost.get())
return true
}
} }

View file

@ -3,11 +3,10 @@ package xyz.alexcrea.cuanvil.recipe
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant
import xyz.alexcrea.cuanvil.util.MaterialUtil.isAir import xyz.alexcrea.cuanvil.util.AnvilUseType
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil import xyz.alexcrea.cuanvil.util.AnvilXpUtil
class AnvilCustomRecipe( class AnvilCustomRecipe(
val name: String, val name: String,
@ -81,9 +80,11 @@ class AnvilCustomRecipe(
} }
fun validate(): Boolean { fun validate(): Boolean {
return !leftItem.isAir && return (leftItem != null) && !(leftItem!!.type.isAir) && (leftItem!!.amount > 0) &&
(rightItem == null || !resultItem.isAir) && //(rightItem != null) && !(rightItem!!.type.isAir) && (rightItem!!.amount > 0) &&
!resultItem.isAir ((rightItem == null) || (!(rightItem!!.type.isAir) && (rightItem!!.amount > 0))) &&
(resultItem != null) && !(resultItem!!.type.isAir) && (resultItem!!.amount > 0)
} }
fun saveToFile(writeFile: Boolean, doBackup: Boolean) { fun saveToFile(writeFile: Boolean, doBackup: Boolean) {
@ -161,7 +162,7 @@ class AnvilCustomRecipe(
CustomAnvil.verboseLog("Testing $name $leftItem") CustomAnvil.verboseLog("Testing $name $leftItem")
// We assume this function can be call only if leftItem != null // We assume this function can be call only if leftItem != null
// Test if valid // Test is valid
if (!validate()) return false if (!validate()) return false
val leftSimilar = leftItem!!.isSimilar(item1) val leftSimilar = leftItem!!.isSimilar(item1)

View file

@ -1,11 +1,10 @@
package xyz.alexcrea.cuanvil.util.anvil package xyz.alexcrea.cuanvil.util
import io.delilaheve.util.ConfigOptions
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.permissions.Permissible import org.bukkit.permissions.Permissible
import xyz.alexcrea.cuanvil.util.MiniMessageUtil
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.text.indexOf
object AnvilColorUtil { object AnvilColorUtil {
private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string
@ -14,8 +13,7 @@ object AnvilColorUtil {
class ColorPermissions( class ColorPermissions(
val canUseColorCode: Boolean, val canUseColorCode: Boolean,
val canUseHexColor: Boolean, val canUseHexColor: Boolean,
val canUseMinimessage: Boolean, val canUseMinimessage: Boolean
val permissible: Permissible, // source of the permission. tried to avoid needing it but meh
) { ) {
fun allowed(): Boolean { fun allowed(): Boolean {
return canUseColorCode || canUseHexColor || canUseMinimessage return canUseColorCode || canUseHexColor || canUseMinimessage
@ -38,8 +36,7 @@ object AnvilColorUtil {
return ColorPermissions( return ColorPermissions(
canUseColorCode = false, canUseColorCode = false,
canUseHexColor = false, canUseHexColor = false,
canUseMinimessage = false, canUseMinimessage = false
player,
) )
val canUseColorCode = val canUseColorCode =
@ -57,14 +54,26 @@ object AnvilColorUtil {
useType.hexColorPerm useType.hexColorPerm
)) ))
return ColorPermissions(canUseColorCode, canUseHexColor, canUseMinimessage, player) return ColorPermissions(canUseColorCode, canUseHexColor, canUseMinimessage)
} }
fun renamePermission(player: Permissible): ColorPermissions { /**
return calculatePermissions(player, * Color a string depending on allowed color type, color use type and player permissions
ConfigOptions.permissionNeededForColor, * @return colored component or null if nothing has been colored
ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, ConfigOptions.allowMinimessage, */
ColorUseType.RENAME) fun handleColor(
textToColorText: String,
player: Permissible,
usePermission: Boolean,
allowColorCode: Boolean,
allowHexadecimalColor: Boolean,
allowMinimessage: Boolean,
useType: ColorUseType
): Component? {
val permission = calculatePermissions(player, usePermission,
allowColorCode, allowHexadecimalColor, allowMinimessage,
useType)
return handleColor(textToColorText, permission)
} }
/** /**
@ -73,8 +82,7 @@ object AnvilColorUtil {
*/ */
fun handleColor( fun handleColor(
textToColorText: String, textToColorText: String,
permission: ColorPermissions, permission: ColorPermissions
): Component? { ): Component? {
if(!permission.allowed()) return null if(!permission.allowed()) return null
@ -85,12 +93,7 @@ object AnvilColorUtil {
var nbReplacement = replaceAll(textToColor, "&", "§", 2) var nbReplacement = replaceAll(textToColor, "&", "§", 2)
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2) nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
if (nbReplacement > 0) { if (nbReplacement > 0) useColor = true
useColor = true
if (ConfigOptions.usePerColorCodePermission)
filterPermissibleColorCode(textToColor, permission.permissible)
}
} }
if (permission.canUseHexColor) { if (permission.canUseHexColor) {
@ -118,21 +121,6 @@ object AnvilColorUtil {
else null else null
} }
private fun filterPermissibleColorCode(textToColor: StringBuilder, player: Permissible) {
var index = 0
while (true) {
index = textToColor.indexOf('§', index)
if (index == -1 || index == textToColor.length - 1) return
val next = textToColor[index + 1]
// check permission for this color
if(!player.hasPermission("ca.color.code.$next"))
textToColor.replace(index, index + 1, "&")
index++
}
}
/** /**
* Best effort to revert a component to the smallest allowed string * Best effort to revert a component to the smallest allowed string
* that would result in it getting closest as possible to handleColor * that would result in it getting closest as possible to handleColor
@ -259,7 +247,7 @@ object AnvilColorUtil {
if (rightIndex == -1 || (newleftIndex != -1 && newleftIndex < rightIndex)) return false if (rightIndex == -1 || (newleftIndex != -1 && newleftIndex < rightIndex)) return false
// Then finally we use minimessage to check for tag // Then finally we use minimessage to check for tag
val expectedTag = builder.substring(leftIndex, newleftIndex + index + 1) val expectedTag = builder.substring(leftIndex, newleftIndex + 1)
val notag = MiniMessageUtil.mm.stripTags(expectedTag) val notag = MiniMessageUtil.mm.stripTags(expectedTag)
return notag != expectedTag return notag != expectedTag

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