Compare commits

..

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

139 changed files with 1357 additions and 5063 deletions

View file

@ -12,11 +12,9 @@ on:
branches: [ "v1.x.x", "v2.x.x" ]
pull_request:
branches: [ "v1.x.x", "v2.x.x" ]
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
@ -25,10 +23,6 @@ jobs:
permissions:
contents: write
env:
MODRINTH_VERSIONS: '["1.18.x", "1.19.x", "1.20.x", "1.21.x", "26.1.x", "26.2.x"]'
MODRINTH_PLATFORMS: '["spigot", "paper", "purpur", "folia"]'
steps:
- uses: actions/checkout@v4
- name: Set up JDKs
@ -37,41 +31,21 @@ jobs:
java-version: |
21
distribution: 'temurin'
cache: gradle
- name: Cache Gradle root and wrapper
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-root-${{ runner.os }}-${{ hashFiles('build.gradle*') }}
restore-keys: gradle-root-${{ runner.os }}-
# Setup paperweight cache
- name: Cache paperweight
uses: actions/cache@v3
with:
path: |
./nms/*/.gradle/caches/paperweight
key: paperweight-submodules-${{ runner.os }}-${{ hashFiles('nms/*/build.gradle*') }}
restore-keys: |
paperweight-submodules-${{ runner.os }}-
cache: 'gradle'
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@v4
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Get small commit hash
if: ${{ github.event_name != 'release' && success() }}
run: echo "SMALL_COMMIT_HASH=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV
- name: Build with Gradle Wrapper
run: ./gradlew build --parallel --stacktrace
run: ./gradlew build --parallel
# only submit dependency on push
- name: Generate and submit dependency graph
@ -89,7 +63,7 @@ jobs:
echo "ONLINE_JAR_NAME=$(basename $ONLINE_JAR_PATH)" >> $GITHUB_ENV
echo "OFFLINE_JAR_NAME=$(basename $OFFLINE_JAR_PATH)" >> $GITHUB_ENV
# upload the named jars as artifact
# upload the named jars
- name: Upload online JAR artifact
uses: actions/upload-artifact@v4
with:
@ -105,59 +79,3 @@ jobs:
- name: Summarize tests results
uses: jeantessier/test-summary-action@v1
if: ${{ always() }}
# upload the jar to release
- name: Upload jar to release
if: ${{ github.event_name == 'release' && success() }}
uses: softprops/action-gh-release@v2
with:
files: |
build/libs/${{ env.ONLINE_JAR_NAME }}
build/libs/${{ env.OFFLINE_JAR_NAME }}
- name: Hangar release
if: ${{ (github.event_name != 'release' || github.event_name != 'push') && github.repository_owner == 'alexcrea' && success() }}
env:
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
RELEASE_CHANGELOG: ${{ github.event.release.body }}
run: ./gradlew publishAllPublicationsToHangar --stacktrace
- name: Modrinth publish alpha
if: ${{ github.event_name == 'push' && github.repository_owner == 'alexcrea' && success() }}
uses: cloudnode-pro/modrinth-publish@v2
with:
token: ${{ secrets.MODRINTH_TOKEN }}
project: S75Ueiq9
name: dev-${{ env.SMALL_COMMIT_HASH }}
version: dev-${{ env.SMALL_COMMIT_HASH }}
loaders: ${{ env.MODRINTH_PLATFORMS }}
game-versions: ${{ env.MODRINTH_VERSIONS }}
channel: alpha
files: build/libs/${{ env.ONLINE_JAR_NAME }}
changelog: ${{ github.event.head_commit.message }}
- name: Modrinth publish release
if: ${{ github.event_name == 'release' && github.repository_owner == 'alexcrea' && success() }}
uses: cloudnode-pro/modrinth-publish@v2
with:
token: ${{ secrets.MODRINTH_TOKEN }}
project: S75Ueiq9
name: ${{ github.event.release.name }}
version: ${{ github.event.release.tag_name }}${{ github.event.release.prerelease && '-pre' || '' }}
loaders: ${{ env.MODRINTH_PLATFORMS }}
game-versions: ${{ env.MODRINTH_VERSIONS }}
channel: ${{ github.event.release.prerelease == false && 'release' || 'beta' }}
files: build/libs/${{ env.ONLINE_JAR_NAME }}
changelog: ${{ github.event.release.body }}
- name: Send release note to discord
if: ${{ github.event_name == 'release' && github.repository_owner == 'alexcrea' && success() }}
uses: tsickert/discord-webhook@v7.0.0
with:
webhook-url: ${{ secrets.RELEASE_WEBHOOK_URL }}
content: |
${{ github.event.release.prerelease == false && '<@&1338546156325568642>' || '<@&1352296092989001768>' }}
# New ${{ github.event.release.prerelease && 'beta ' || '' }}version of custom anvil ! <:CustomAnvil:1262550667986342001>([Modrinth](https://modrinth.com/plugin/customanvil), [Hangar](https://hangar.papermc.io/alexcrea/CustomAnvil) and [GitHub](${{ github.event.release.html_url }}) links)
-# note: automated release. spigot is not uploaded yet.
${{ github.event.release.body }}

3
.gitignore vendored
View file

@ -14,9 +14,6 @@
/impl/*/build
/impl/*/.gradle
# run folder
/run/
# other random folders
/htmlReport
/.kotlin/errors

View file

@ -1,9 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Server" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/run/server.jar" />
<option name="PROGRAM_PARAMETERS" value="-nogui" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/run" />
<option name="ALTERNATIVE_JRE_PATH" />
<method v="2" />
</configuration>
</component>

View file

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

View file

@ -1,34 +0,0 @@
Thanks **DelilahEve** for making [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants). \
CustomAnvil was initially a fork of Unsafe Enchants where I wanted to add more and more and here we are now !
Thanks for all the contributors of bukkit, spigot, the paper team and the adventure API developers \
Thanks JetBrain for making IntelliJ
### Dependencies
These dependencies (or a modified version of) are used by custom anvil
- [IF](https://github.com/stefvanschie/IF) an inventory framework by stefvanschie
- [Mockbukkit](https://github.com/MockBukkit/MockBukkit) for unit testing
- [CentralPortalPlus](https://github.com/lalakii/central-portal-plus) by lalakii
- [test-summary-action](https://github.com/jeantessier/test-summary-action) by jeantessier
- [modrinth-publish](https://github.com/cloudnode-pro/modrinth-publish) by Zefir
- [discord-webhook](https://github.com/tsickert/discord-webhook) by tsickert
- Thanks [bstats](https://bstats.org/) for keeping me motivated
- And [FastStats](https://faststats.dev/) alternative to bstats in beta test
- [ModrinthUpdateChecker](https://github.com/Clickism/ModrinthUpdateChecker) by Clickism and thanks to the modrinth team
### Compatibility
Thanks to all the cool creator making the minecraft plugin's ecosystem works ! \
See [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md) for details
but especially, Big Thanks for H7KZ maker of [Disenchantment](https://github.com/H7KZ/Disenchantment)
### Special Thanks
Thanks for all the users trying my plugin for these niche use cases
, reporting issues and giving ideas !
Thanks coltonj96 for [UberEnchant](https://modrinth.com/plugin/uberenchant).
we may be incompatible with the anvil, but I do think it is a good alternative ! \
I wish one day to work on cross compatibiltiy \
* If custom anvil do not work well for you or your use case give it a try ! *

View file

@ -1,11 +1,18 @@
# Custom Anvil
**Custom Anvil** is a plugin that allows server administrators to customize every aspect of the anvil's mechanics.
It is expected to work on 1.18 to 1.21.7 minecraft servers running spigot or paper.
(the plugin support of 1.16.5 to 1.17.1 is experimental and may encounter issues)
**Custom Anvil** was previously named **Unsafe Enchants+**.
It was renamed because it now affects every anvil aspect and not only unsafe enchants\
**Custom Anvil** is based on [Unsafe Enchants](https://github.com/DelilahEve/UnsafeEnchants) by DelilahEve.
### Download Locations:
the plugin can be downloaded on
[Modrinth](https://modrinth.com/plugin/customanvil),
[Spigot](https://www.spigotmc.org/resources/custom-anvil.114884),
[modrinth](https://modrinth.com/plugin/customanvil),
[Hangar](https://hangar.papermc.io/alexcrea/CustomAnvil)
or here [on GitHub](https://github.com/alexcrea/CustomAnvil/releases/latest)
@ -14,7 +21,7 @@ the plugin can be downloaded on
- Vanilla like default configuration.
- Custom enchantment level limit.
- 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 XP cost for every aspect of the anvil.
- Permissions to bypass level limit or enchantment restriction.
@ -23,63 +30,75 @@ the plugin can be downloaded on
- Gui to configure the plugin in game.
- Support use of color code, hexadecimal color and minimessage for color/decoration
- (Experimental) Folia support (gui do not work)
- (Experimental) Dialog rename (allows longer rename)
- (Experimental) Anvil with monetary cost (using vault) (require dialog rename)
And more !
---
### Permissions:
Note that for most of them you also need to enable feature and in most case enable use of permission for the specfic feature (indicated with `(toggleable)`)
```yml
# Generic and bypass permissions
ca.affected: Player with this permission will be affected by the plugin
ca.bypass.fuse: Allow player to combine every enchantments to every item (no custom limit)
ca.bypass.level: Allow player to bypass every level limit (no custom limit)
# Command permissions
ca.command.reload: Allow administrator to reload the plugin's configs
ca.command.diagnostic: Allow adminastator to diagnistic some simple problem with the plugin
ca.config.edit: Allow administrator to edit the plugin's config in game
# -----------------------------------------------------------------------------
# Bellow permissions also require some config change to allow usage of features
# Usage of these permission is toggleable in basic config gui or config.yml
# -----------------------------------------------------------------------------
# usage of these permission is toggleable in basic config gui or config.yml
# Permissions related to use of color and minimessage
ca.color.code: Allow player to use color code on rename if enabled (toggleable)
ca.color.code.[thecode] (for example ca.color.code.a): Allows usage of only certain color code (toggleable)
ca.color.hex: Allow player to use hexadecimal color on rename if enabled (toggleable)
ca.rename.minimessage: Allow player to use minimessage formating on rename if enabled (toggleable)
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
ca.lore_edit.book: Allow player to edit lore via book and quil if enabled (toggleable)
ca.lore_edit.paper: Allow player to edit lore via paper if enabled (toggleable)
# Others
ca.rename.dialog: Allow player to use the rename dialog (toggleable)
```
### Commands
run `/customanvil help` to get information about available commands \
this only show subcommands you have permission for
```yml
anvilconfigreload or carl: Reload every config of this plugin
customanvilconfig or configanvil: open a menu for administrator to edit plugin's config in game
```
### Supported Plugins
See the [Compatibility list](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/COMPATIBILITY.md)
Custom Anvil can be compatible with some custom enchantments and anvil mechanics plugins.
Here is a list of supported custom enchantment plugins with support status:
- [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.
- [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/):
Support by Custom Anvil but still experimental. Need to use /anvilconfigreload or a server restart to add newly added enchantment.
Use EcoEnchant restriction system by default.
- [ExcellentEnchants](https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/):
Support by Custom Anvil but still experimental. Use ExcellentEnchants item type.
- [Superenchants](https://modrinth.com/plugin/superenchants)
support by Superenchants. Use CustomAnvil to combine enchantment in anvil in survival.
Here is a list of supported anvil mechanic plugins with support status:
- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
support by Custom Anvil but still experimental. Mostly use Custom Anvil basic XP settings. (version >= 6.1.5)
- [HavenBags](https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/)
support by Custom Anvil. Not really enchantment related but CustomAnvil should not impact bag upgrade and skin via anvil. (version >= 1.31.0)
If you like Custom Anvil to support a specific plugin (custom enchant or anvil mechanic).
You can ask, but please note implementing compatibility will be considered
as low priority as I work for the plugin on my free time for free.
### Overriding 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.
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.
But you should wait for update for new version containing new enchantable item or new enchantments if any of this got added.
Else it is, likely, fine to use the current version you are ussing on a new paper version
But you should wait for update for new version containing new enchantable item or new enchantments.
Other version need ProtocoLib enabled on your server for this feature. \
You can also wait for an update of the plugin to support a newer version.
Please note that 1.16.5 to 1.17.1 are not officially supported. Run at your own risk.
### For custom enchantment plugin developers
For information about the API, please refer to [the Wiki](https://github.com/alexcrea/CustomAnvil/wiki) \
(Please note that the wiki is currently incomplete)
(Please note that the wiki is currently incomplete)
---
@ -87,25 +106,12 @@ For information about the API, please refer to [the Wiki](https://github.com/ale
see [Here](https://github.com/alexcrea/CustomAnvil/tree/master/defaultconfigs)
---
### Metric And Telemetry
Custom anvil [use bstat](https://bstats.org/plugin/bukkit/Unsafe%20Enchants%20Plus/20923)
and [faststats](https://faststats.dev/project/customanvil/minecraft-plugin) for metric and error reporting.
You can select specific telemetry or disable all in config.yml. \
You can also [disable bstat](https://bstats.org/getting-started) and [faststats](https://faststats.dev/info) in their /plugin folder if you like too.
faststats is in beta testing please report me or them any error you encounter
### Credits and Thanks
Credits and thanks can be seen [here](https://github.com/alexcrea/CustomAnvil/blob/v1.x.x/CREDITS.md)
Custom anvil [use bstat](https://bstats.org/plugin/bukkit/Unsafe%20Enchants%20Plus/20923) for metric. You can [disable it](https://bstats.org/getting-started) if you like.
### Planned:
- Better Folia support (make gui work. fix some dirty handled parts)
- Get restriction on unknown enchantments (planned for V2)
- Get restriction on unknown enchantments
- More features for custom anvil craft
### Known issue:
Most unknown registered enchantments (by unsupported custom enchantment plugin & datapacks) will not have restriction by default. Planned but no eta.
### Do you need help with the plugin, or have any issue or suggestion?
You can ask on the discussion page, create a [GitHub issue](https://github.com/alexcrea/CustomAnvil/issues) or join my [discord](https://discord.gg/KHUNsUfRYJ)

View file

@ -2,13 +2,10 @@ import cn.lalaki.pub.BaseCentralPortalPlusExtension
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import groovy.util.Node
import groovy.util.NodeList
import io.papermc.hangarpublishplugin.model.HangarPublication
import io.papermc.hangarpublishplugin.model.Platforms
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.ByteArrayOutputStream
plugins {
kotlin("jvm") version "2.3.0"
kotlin("jvm") version "2.1.0"
java
id("org.jetbrains.dokka").version("1.9.20")
id("com.gradleup.shadow").version("9.3.0")
@ -18,17 +15,13 @@ plugins {
id("cn.lalaki.central").version("1.2.8")
// Paper
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" apply false
id("io.papermc.hangar-publish-plugin") version "0.1.2"
}
group = "xyz.alexcrea"
version = "1.17.5"
val isDevBuild = System.getenv("SMALL_COMMIT_HASH") != null
val isPreRelease = System.getenv("IS_GITHUB_PRERELEASE") == "true"
version = "1.15.8"
val effectiveVersion = "$version" +
(if (isDevBuild) "-dev-${System.getenv("SMALL_COMMIT_HASH")!!}" else "")
(if (System.getenv("SMALL_COMMIT_HASH") != null) "-dev-${System.getenv("SMALL_COMMIT_HASH")!!}" else "")
repositories {
// EcoEnchants
@ -36,18 +29,6 @@ repositories {
// ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases")
// ItemsAdder
maven(url = "https://maven.devs.beer/")
// For fast stats
maven {
name = "thenextlvlReleases"
url = uri("https://repo.thenextlvl.net/releases")
}
// For vault unlocked
maven { url = uri("https://repo.codemc.io/repository/creatorfromhell/") }
}
val reobfNMS = providers.gradleProperty("subprojects.reobfnms")
@ -57,9 +38,6 @@ dependencies {
// Spigot api
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
// fast stats
implementation("dev.faststats.metrics:bukkit:0.27.0")
// minimessage
implementation("net.kyori:adventure-text-minimessage:4.25.0")
@ -71,17 +49,13 @@ dependencies {
// EnchantsSquaredRewritten
compileOnly(files("libs/EnchantsSquared.jar"))
// EcoEnchants & item
compileOnly("com.willfp:libreforge:4.79.0:all")
compileOnly("com.willfp:eco:6.74.5")
// EcoEnchants
compileOnly("com.willfp:EcoEnchants:12.11.1")
compileOnly("com.willfp:eco:6.74.5")
compileOnly(project(":impl:LegacyEcoEnchant"))
compileOnly("com.willfp:EcoItems:5.66.0")
// ExcellentEnchants
implementation(project(":impl:ExcellentEnchant5_4"))
implementation(project(":impl:ExcellentEnchant5_3"))
compileOnly("su.nightexpress.excellentenchants:Core:5.1.0") {
exclude("org.spigotmc")
}
@ -100,15 +74,6 @@ dependencies {
// AxPlayerWarps
compileOnly(files("libs/AxPlayerWarps-1.10.3.jar"))
// SuperEnchants
compileOnly(files("libs/SuperEnchants-4.6.2-all.jar"))
// ItemsAdder API
compileOnly("dev.lone:api-itemsadder:4.0.10")
// Vault api
compileOnly("net.milkbowl.vault:VaultUnlockedAPI:2.16")
// Include nms
implementation(project(":nms:nms-common"))
implementation(project(":nms:nms-paper"))
@ -160,7 +125,7 @@ allprojects {
// Set target version
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility =
"16" // We aim for java 16 for minecraft 1.16.5. even if it not really supported by custom anvil.
"16" // We aim for java 16 for minecraft 1.16.5. even if it not really suported by custom anvil.
targetCompatibility = "16"
options.encoding = "UTF-8"
@ -168,29 +133,34 @@ allprojects {
kotlin {
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)
}
}
}
tasks {
fun ShadowJar.configureBaseShadow(suffix: String, libraries: Array<String>) {
val processedSuffix = if(suffix.isEmpty()) "" else "-$suffix"
val name = "${rootProject.name}-${effectiveVersion}${processedSuffix}.jar"
// Online jar (use of libraries)
shadowJar {
// No suffix for this jar
val name = "${rootProject.name}-${effectiveVersion}.jar"
archiveFileName.set(name)
// Shadow necessary dependency
relocate("com.github.stefvanschie.inventoryframework", "xyz.alexcrea.cuanvil.inventoryframework")
relocate("dev.faststats", "xyz.alexcrea.cuanvil.faststats")
// Exclude kotlin std and its annotation
exclude("**/kotlin-stdlib*.jar")
exclude("**/annotations*.jar")
// Shadow necessary dependency
relocate("com.github.stefvanschie.inventoryframework", "xyz.alexcrea.inventoryframework")
// Replace version and example fields in plugin.yml
filesMatching("plugin.yml") {
expand(
"version" to effectiveVersion + processedSuffix,
"libraries" to libraries.joinToString(transform = { "\"$it\"" }),
"version" to effectiveVersion,
"libraries" to " \"org.jetbrains.kotlin:kotlin-stdlib:2.1.0\" " +
", \"net.kyori:adventure-platform-bukkit:4.4.1\""
)
}
@ -198,28 +168,36 @@ tasks {
dependsOn(processResources)
}
// Online jar (use of libraries)
shadowJar {
configureBaseShadow("",
arrayOf(
"org.jetbrains.kotlin:kotlin-stdlib:2.3.0",
"net.kyori:adventure-text-minimessage:4.25.0",
"net.kyori:adventure-text-serializer-plain:4.25.0",
"net.kyori:adventure-text-serializer-legacy:4.25.0",
))
// Offline jar (include kotlin std in the final jar fine)
val offlineJar by // Shadow necessary dependency
registering(
// Exclude kotlin std, annotations and adventure api
exclude("*kotlin/**")
exclude("**/annotations/**")
exclude("net/kyori/**")
// Include all project other dependencies
ShadowJar
// Add custom anvil compiled
::class, fun ShadowJar.() {
val name = "${rootProject.name}-${effectiveVersion}-offline.jar"
archiveFileName.set(name)
// Shadow necessary dependency
relocate("com.github.stefvanschie.inventoryframework", "xyz.alexcrea.inventoryframework")
filesMatching("plugin.yml") {
expand(
"version" to "$effectiveVersion-offline",
"libraries" to ""
)
}
val offlineJar by registering(ShadowJar::class) {
configureBaseShadow("offline", emptyArray())
// Include all project other dependencies
from(project.configurations.runtimeClasspath)
// Add custom anvil compiled
from(sourceSets.main.get().output)
configurations = listOf(project.configurations.runtimeClasspath.get())
}
dependsOn(processResources)
})
// Make the online and offline jar on build
named("build") {
@ -355,101 +333,3 @@ publishing {
}
}
}
// hangar publish
fun executeGitCommand(vararg command: String): String {
val byteOut = ByteArrayOutputStream()
exec {
commandLine = listOf("git", *command)
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
}
fun latestCommitMessage(): String {
return executeGitCommand("log", "-1", "--pretty=%B")
}
fun changelog(isOnline: Boolean): String {
var changelog = if(isDevBuild) latestCommitMessage()
else System.getenv("RELEASE_CHANGELOG")
if(!isOnline) {
changelog = "This is an offline version of the plugin. \\\n" +
"This mean that this plugin libraries are shaded into this plugin \\\n" +
"You likely want to use the normal version of this plugin\n\n" + changelog
}
if(changelog == null || changelog.isEmpty()) {
changelog = "empty changelog"
}
return changelog
}
hangarPublish {
fun HangarPublication.configure(isOnline: Boolean, devChannel: String, releaseChannel: String) {
var versionName = effectiveVersion
if(isPreRelease) versionName+= "-pre"
if(!isOnline) versionName+= "-offline"
version.set(versionName)
channel.set(if (isDevBuild || isPreRelease) devChannel else releaseChannel)
changelog.set(changelog(isOnline))
id.set("CustomAnvil")
apiKey.set(System.getenv("HANGAR_API_TOKEN"))
platforms {
register(Platforms.PAPER) {
// Set the JAR file to upload
var task = if(isOnline) tasks.shadowJar
else tasks.named<ShadowJar>("offlineJar")
jar.set(task.flatMap { it.archiveFile })
// Set platform versions from gradle.properties file
val versions: List<String> = (property("paperVersion") as String)
.split(",")
.map { it.trim() }
platformVersions.set(versions)
dependencies {
hangar("ProtocolLib") {
required.set(false)
}
url("Disenchantment", "https://modrinth.com/plugin/disenchantment") {
required.set(false)
}
url("ToolStats", "https://modrinth.com/plugin/toolstats") {
required.set(false)
}
url("HavenBags", "https://www.spigotmc.org/resources/havenbags-shulker-like-player-bound-bags-1-17-1-21-4.110420/") {
required.set(false)
}
url("EcoEnchants", "https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/") {
required.set(false)
}
hangar("EnchantsSquared") {
required.set(false)
}
url("ExcellentEnchants", "https://www.spigotmc.org/resources/excellentenchants-%E2%AD%90-75-vanilla-like-enchantments.61693/") {
required.set(false)
}
}
}
}
}
publications.register("plugin") {
configure(true, "DevSnapshot", "Release")
}
publications.register("offline") {
configure(false, "OfflineSnapshot", "OfflineRelease")
}
}

View file

@ -3,19 +3,6 @@
# You can still manually edit here if you like to. but if you do, don't forget to /anvilconfigreload after you changes !
#
# What service of metric should custom anvil use
# Custom anvil collect generic information like server minecraft version, type, etc...
# It can also collect error information if error is happening (currently faststats only)
# It can also be disabled
# Please refer to README for public metric link
# Possible options: auto, bstat, faststats, disabled (auto by default)
metric_type: auto
# Allow to report errors made caused by this plugin (only for faststats)
# This allows me to fix potentials issue that I'm not aware of
# Accept true or false (true by default)
metric_collect_errors: true
# All anvil cost will be capped to limit_repair_value if enabled.
#
# In other words:
@ -77,18 +64,6 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,23 +74,10 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
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
# Default limit to apply to any enchants missing from enchant_limits
#
# 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
# Valid values include 1 to 1000
default_limit: 5
# Override limits for specific enchants
#
@ -123,8 +85,7 @@ dialog_rename_keep_user_text: true
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
# Valid range of 1 - 255 for each enchantment
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -306,7 +267,7 @@ enchant_values:
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla on default configuration)
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
#minecraft:unbreaking: 2
@ -430,37 +391,16 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0

View file

@ -3,19 +3,6 @@
# You can still manually edit here if you like to. but if you do, don't forget to /anvilconfigreload after you changes !
#
# What service of metric should custom anvil use
# Custom anvil collect generic information like server minecraft version, type, etc...
# It can also collect error information if error is happening (currently faststats only)
# It can also be disabled
# Please refer to README for public metric link
# Possible options: auto, bstat, faststats, disabled (auto by default)
metric_type: auto
# Allow to report errors made caused by this plugin (only for faststats)
# This allows me to fix potentials issue that I'm not aware of
# Accept true or false (true by default)
metric_collect_errors: true
# All anvil cost will be capped to limit_repair_value if enabled.
#
# In other words:
@ -79,18 +66,6 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -101,23 +76,10 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
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
# Default limit to apply to any enchants missing from enchant_limits
#
# 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
# Valid values include 1 to 1000
default_limit: 5
# Override limits for specific enchants
#
@ -125,8 +87,7 @@ dialog_rename_keep_user_text: true
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
# Valid range of 1 - 255 for each enchantment
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -324,7 +285,7 @@ enchant_values:
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla on default configuration)
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
# minecraft:unbreaking: 2
@ -450,38 +411,17 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.15.5
lowMinecraftVersion: 1.21.11

View file

@ -3,19 +3,6 @@
# You can still manually edit here if you like to. but if you do, don't forget to /anvilconfigreload after you changes !
#
# What service of metric should custom anvil use
# Custom anvil collect generic information like server minecraft version, type, etc...
# It can also collect error information if error is happening (currently faststats only)
# It can also be disabled
# Please refer to README for public metric link
# Possible options: auto, bstat, faststats, disabled (auto by default)
metric_type: auto
# Allow to report errors made caused by this plugin (only for faststats)
# This allows me to fix potentials issue that I'm not aware of
# Accept true or false (true by default)
metric_collect_errors: true
# All anvil cost will be capped to limit_repair_value if enabled.
#
# In other words:
@ -77,18 +64,6 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,23 +74,10 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
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
# Default limit to apply to any enchants missing from enchant_limits
#
# 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
# Valid values include 1 to 1000
default_limit: 5
# Override limits for specific enchants
#
@ -123,8 +85,7 @@ dialog_rename_keep_user_text: true
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
# Valid range of 1 - 255 for each enchantment
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -318,7 +279,7 @@ enchant_values:
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla on default configuration)
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
# minecraft:unbreaking: 2
@ -442,38 +403,17 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0
lowMinecraftVersion: 1.21.9

View file

@ -3,19 +3,6 @@
# You can still manually edit here if you like to. but if you do, don't forget to /anvilconfigreload after you changes !
#
# What service of metric should custom anvil use
# Custom anvil collect generic information like server minecraft version, type, etc...
# It can also collect error information if error is happening (currently faststats only)
# It can also be disabled
# Please refer to README for public metric link
# Possible options: auto, bstat, faststats, disabled (auto by default)
metric_type: auto
# Allow to report errors made caused by this plugin (only for faststats)
# This allows me to fix potentials issue that I'm not aware of
# Accept true or false (true by default)
metric_collect_errors: true
# All anvil cost will be capped to limit_repair_value if enabled.
#
# In other words:
@ -77,18 +64,6 @@ allow_color_code: false
allow_hexadecimal_color: false
allow_minimessage: false
# This enables restricting color code for player having specific permission
# It requires allow_color_code enabled for... obvious reasons
#
# For example: if player want to use "&aHello" it will be required that the player has
# the permission "ca.color.code.a" as he used the color code "a"
# In general permission to give to the player is "ca.color.code.[code]"
# where [code] is the color code you wish to allow the player
#
# It is kinda of useless when minimessage is supported as players would be able to bypass
# that using the equivalent minimessage tag
per_color_code_permission: false
# Toggle if color should only be applicable if the player a certain permission.
#
# permission are "ca.color.code" for use of color code and "ca.color.hex" for use of hexadecimal color.
@ -99,23 +74,10 @@ permission_needed_for_color: true
# Valid values include 0 to 1000.
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
# Default limit to apply to any enchants missing from enchant_limits
#
# 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
# Valid values include 1 to 1000
default_limit: 5
# Override limits for specific enchants
#
@ -123,8 +85,7 @@ dialog_rename_keep_user_text: true
#
# Overrides provided default from aqua_affinity to depth_strider won't change effect with extra levels
#
# Valid range of 0 - 255 for each enchantment
# -1 mean keep default
# Valid range of 1 - 255 for each enchantment
enchant_limits:
minecraft:aqua_affinity: 1
minecraft:binding_curse: 1
@ -306,7 +267,7 @@ enchant_values:
# Even if disable-merge-over of unbreaking is set to 2
# -1 mean enchantment merge for this enchantment is not disabled. default to -1 if absent.
disable-merge-over:
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla on default configuration)
# Sharpness is set to -1. it equivalent to it not being set to anything (and work as vanilla)
minecraft:sharpness: -1
# If uncommented. 2 unbreaking II book would not give an unbreaking III book. but unbreaking III book can still be applied
# minecraft:unbreaking: 2
@ -430,37 +391,16 @@ lore_edit:
allow_hexadecimal_color: false
allow_minimessage: true
# Allow to replace the xp cost by a monetary cost
# If enabled it will not be bound to the experience level limits
#
# It also requires to enable dialog rename (set "enable_dialog_rename: false" a bit higher)
# If dialog rename permission is enabled and player do not have the permission merge will fall back to vanilla xp cost
#
# If you are using custom craft I recommend using Linear Xp Cost with Exact Linear Xp as normal Xp Cost will act "weird"
# But Linear Xp will act as 1$ time global multiplier. In other word: like you expect
#
# As this feature require dialog rename, it can only be enabled starting with paper 1.21.6 and later
monetary_cost:
enabled: false
# If using vault unlocked this allow to specify what currency should be used for anvil usage
# default being the default currency
currency: default
# multiply the anvil cost by a value to allow to have price a big bigger than like 40
multipliers:
# global multipliers. all usage type will be multiplied by this value
global: 1.0
# usage specific type. it will only apply for specific xp "reason"
enchantment: 1.0 # related to enchantments level
repair: 1.0 # for repairing via unit repair (per unit)
rename: 1.0 # for renaming the item
lore_edit: 1.0 # for changing the lore of the item (only if lore edit is enabled)
work_penalty: 1.0 # for work penalty (aka use penalty)
recipe: 1.0 # for custom anvil recipe cost
# Whether to show debug logging
debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# In case something when wrong with CustomAnvil packet manager.
# If you see "missing class exception" or similar you may test this.
# If enabled and Protocolib absent or disabled "Replace to expensive" will not work.
# ProtocoLib may also be used if the server is in an "unsupported" version even if this option is disabled.
force_protocolib: false
configVersion: 1.11.0

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

View file

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

View file

@ -0,0 +1,16 @@
package xyz.alexcrea.cuanvil.dependency.gui
import org.bukkit.inventory.InventoryView
interface ExternGuiTester {
fun getContainerClass(view: InventoryView): Class<Any>?
fun testIfGui(inventory: InventoryView): Boolean {
// container class only allow default bukkit craft view or test class
val clazz = getContainerClass(inventory)
return clazz == null
}
}

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"))
// Used for nms
paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT")
}
repositories {
@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -24,8 +24,8 @@ class PaperPacketManager : PacketManagerBase(), PacketManager {
sendedAbilities.mayfly = playerAbilities.mayfly
sendedAbilities.instabuild = instantBuild
sendedAbilities.mayBuild = playerAbilities.mayBuild
sendedAbilities.setFlyingSpeed(playerAbilities.getFlyingSpeed())
sendedAbilities.setWalkingSpeed(playerAbilities.getWalkingSpeed())
sendedAbilities.flyingSpeed = playerAbilities.flyingSpeed
sendedAbilities.walkingSpeed = playerAbilities.walkingSpeed
}
val packet = ClientboundPlayerAbilitiesPacket(sendedAbilities)
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 {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

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 {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -29,7 +29,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

@ -28,7 +28,7 @@ tasks.withType<JavaCompile>().configureEach {
kotlin {
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)
}
}

View file

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

View file

@ -13,7 +13,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
//TODO add conflict after level
/**
* A Builder for material conflict.
*/

View file

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

View file

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

View file

@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
* Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
* for this event to be useful.
* <p>
* There is also {@link 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 {

View file

@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
* <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p>
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link 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.
*/
public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable {

View file

@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
* <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p>
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link 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.
*/
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.anvil.AnvilCost;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
import xyz.alexcrea.cuanvil.util.AnvilUseType;
/**
* 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
* <p>
* A null result will cancel this pre anvil event
*
* @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event
* use {@link CATreatAnvilResult2Event} instead
*/
@SuppressWarnings("unused")
@Deprecated(forRemoval = true, since = "1.17.0")
public class CATreatAnvilResultEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
@ -45,13 +40,13 @@ public class CATreatAnvilResultEvent extends Event {
@Nullable
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.useType = useType;
this.result = result;
this.cost = cost;
this.levelCost = levelCost;
}
/**
@ -109,11 +104,9 @@ public class CATreatAnvilResultEvent extends Event {
* </ul>
*
* @return The current cost.
* @deprecated use #{@link #getCost()} instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public int getLevelCost() {
return cost.asXpCost();
return levelCost;
}
/**
@ -131,32 +124,8 @@ public class CATreatAnvilResultEvent extends Event {
* </ul>
*
* @param levelCost The new cost.
* @deprecated use #{@link #getCost()} and set value on this instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public void setLevelCost(int levelCost) {
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

@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.group.EnchantConflictManager;
import xyz.alexcrea.cuanvil.group.ItemGroupManager;
import xyz.alexcrea.cuanvil.recipe.CustomAnvilRecipeManager;
import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.io.File;
import java.io.IOException;
@ -146,7 +145,6 @@ public abstract class ConfigHolder {
sufficientSuccess = true;
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not copy backup saving config " + base.getName(), e);
MetricsUtil.INSTANCE.trackError(e);
}
}
// save last backup
@ -277,7 +275,6 @@ public abstract class ConfigHolder {
this.deletedConfigFile.createNewFile();
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not create " + this.deletedConfigFile.getPath(), e);
MetricsUtil.INSTANCE.trackError(e);
}
loadDeletedListFile(false);
@ -315,7 +312,6 @@ public abstract class ConfigHolder {
this.deletedListConfig.save(this.deletedConfigFile);
} catch (IOException e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not save " + this.deletedConfigFile.getPath(), e);
MetricsUtil.INSTANCE.trackError(e);
return false;
}

View file

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

View file

@ -1,7 +1,6 @@
package xyz.alexcrea.cuanvil.enchant;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
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.
* @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.
*/
boolean isEnchantConflict(
@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.
* @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.
* @return If there is a conflict with the enchantment and the item.
*/
boolean isItemConflict(
@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull NamespacedKey itemType,
@NotNull Material itemMat,
@NotNull ItemStack item);
}

View file

@ -10,7 +10,6 @@ import xyz.alexcrea.cuanvil.enchant.bulk.BukkitEnchantBulkOperation;
import xyz.alexcrea.cuanvil.enchant.bulk.BulkCleanEnchantOperation;
import xyz.alexcrea.cuanvil.enchant.bulk.BulkGetEnchantOperation;
import xyz.alexcrea.cuanvil.enchant.wrapped.CABukkitEnchantment;
import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.util.*;
import java.util.logging.Level;
@ -86,13 +85,11 @@ public class CAEnchantmentRegistry {
return false;
}
var error = new IllegalStateException("enchantment " + enchantment.getKey() + " was already registered");
CustomAnvil.instance.getLogger().log(Level.WARNING,
"Duplicate distinct registered enchantment. This should NOT happen any time.\n" +
"If you are a custom anvil developer: Maybe custom anvil detected your enchantment as a bukkit enchantment. " +
"you should maybe remove enchantment with the same key before registering yours",
error);
MetricsUtil.INSTANCE.trackError(error);
new IllegalStateException("enchantment " + enchantment.getKey() + " was already registered"));
return false;
}

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

View file

@ -1,51 +1,48 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;
import su.nightexpress.excellentenchants.api.item.ItemSet;
import su.nightexpress.excellentenchants.api.wrapper.EnchantDefinition;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
public class CAEEV5Enchantment extends CABukkitEnchantment implements AdditionalTestEnchantment {
@NotNull CustomEnchantment eeenchantment;
@NotNull Object definition;
@NotNull EnchantDefinition definition;
public CAEEV5Enchantment(@NotNull CustomEnchantment enchantment) {
super(enchantment.getBukkitEnchantment(), EnchantmentRarity.getRarity(getAnvilCost(enchantment)));
super(enchantment.getBukkitEnchantment(), EnchantmentRarity.getRarity(enchantment.getDefinition().getAnvilCost()));
this.eeenchantment = enchantment;
this.definition = getDefinition(enchantment);
this.definition = enchantment.getDefinition();
}
@Override
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType) {
if (!hasConflicts()) return false;
public boolean isEnchantConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat) {
if (!definition.hasConflicts()) return false;
Set<String> conflicts = getExclusiveSet();
Set<String> conflicts = definition.getExclusiveSet();
for (CAEnchantment caEnchantment : enchantments.keySet()) {
if (conflicts.contains(caEnchantment.getName())) return true;
if (conflicts.contains(caEnchantment.getKey().toString())) return true;
}
return false;
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) return false;
String key = itemType.getKey();
String key = itemMat.getKey().getKey();
ItemSet primary = eeenchantment.getPrimaryItems();
if (primary.getMaterials().contains(key)) return false;
@ -55,74 +52,4 @@ public class CAEEV5Enchantment extends CABukkitEnchantment implements Additional
return true;
}
private static final Method getDefinitonMethod;
private static final Method getAnvilCostMethod;
private static final Method hasConflictsMethod;
private static final Method getExclusiveSetMethod;
static {
var enchClazz = CustomEnchantment.class;
try {
getDefinitonMethod = enchClazz.getDeclaredMethod("getDefinition");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
Class<?> definitionClazz;
try {
definitionClazz = Class.forName("su.nightexpress.excellentenchants.api.EnchantDefinition");
} catch (ClassNotFoundException e) {
try {
definitionClazz = Class.forName("su.nightexpress.excellentenchants.api.wrapper.EnchantDefinition");
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
// Now definition methods
try {
getAnvilCostMethod = definitionClazz.getDeclaredMethod("getAnvilCost");
hasConflictsMethod = definitionClazz.getDeclaredMethod("hasConflicts");
getExclusiveSetMethod = definitionClazz.getDeclaredMethod("getExclusiveSet");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private static Object getDefinition(CustomEnchantment enchantment) {
try {
return getDefinitonMethod.invoke(enchantment);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static int getAnvilCost(CustomEnchantment enchantment) {
try {
return (int) getAnvilCostMethod.invoke(getDefinition(enchantment));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private boolean hasConflicts() {
try {
return (boolean) hasConflictsMethod.invoke(definition);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private Set<String> getExclusiveSet() {
try {
return (Set<String>) getExclusiveSetMethod.invoke(definition);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}

View file

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

View file

@ -4,7 +4,6 @@ import com.willfp.ecoenchants.enchant.EcoEnchant;
import com.willfp.ecoenchants.target.EnchantmentTarget;
import com.willfp.ecoenchants.type.EnchantmentType;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
@ -24,13 +23,9 @@ public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestE
}
@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;
// Check if there is only self
if (enchantments.size() == 1 && this.equals(enchantments.keySet().stream().findFirst().get()))
return false;
if (this.ecoEnchant.getConflictsWithEverything()) {
return true;
}
@ -62,9 +57,9 @@ public class CAEcoEnchant extends CABukkitEnchantment implements AdditionalTestE
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull NamespacedKey itemType,
@NotNull Material itemMat,
@NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) {
return false;
}

View file

@ -1,7 +1,6 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@ -25,12 +24,12 @@ public class CAIncompatibleAllEnchant extends CABukkitEnchantment implements Add
@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));
}
@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;
}
}

View file

@ -1,7 +1,6 @@
package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.EnchantmentData;
@ -23,7 +22,7 @@ public class CALegacyEEEnchantment extends CABukkitEnchantment implements Additi
}
@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;
Set<String> conflicts = eeenchantment.getConflicts();
@ -36,8 +35,8 @@ public class CALegacyEEEnchantment extends CABukkitEnchantment implements Additi
}
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull NamespacedKey itemType, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) return false;
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments, @NotNull Material itemMat, @NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) return false;
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.EnchantmentType;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
import xyz.alexcrea.cuanvil.enchant.EnchantmentRarity;
import xyz.alexcrea.cuanvil.util.MaterialUtil;
import java.util.Map;
@ -25,7 +23,7 @@ public class CALegacyEcoEnchant extends CABukkitEnchantment implements Additiona
}
@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;
EnchantmentType type = this.ecoEnchant.getType();
@ -50,15 +48,14 @@ public class CALegacyEcoEnchant extends CABukkitEnchantment implements Additiona
@Override
public boolean isItemConflict(@NotNull Map<CAEnchantment, Integer> enchantments,
@NotNull NamespacedKey itemType,
@NotNull Material itemMat,
@NotNull ItemStack item) {
if (Material.ENCHANTED_BOOK.getKey().equals(itemType)) {
if (Material.ENCHANTED_BOOK.equals(itemMat)) {
return false;
}
var mat = MaterialUtil.INSTANCE.getMatFromKey(itemType);
for (EnchantmentTarget target : this.ecoEnchant.getTargets()) {
if (target.getMaterials().contains(mat)) {
if (target.getMaterials().contains(itemMat)) {
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;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import java.util.*;
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){
// Prepare material lore
ArrayList<String> groupLore = new ArrayList<>();
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()) {
groupLore.add("§7There is no "+action+"d material for this "+containerType+".");
} else {
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;
int maxindex = (greaterThanMax ? 4 : materialSet.size());
for (int i = 0; i < maxindex; i++) {
// 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);
}

View file

@ -11,7 +11,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.util.Arrays;
import java.util.function.Supplier;
@ -42,7 +41,6 @@ public class ConfirmActionGui extends AbstractAskGui {
success = onConfirm.get();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "Could not process confirmation supplier.", e);
MetricsUtil.INSTANCE.trackError(e);
success = false;
}

View file

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

View file

@ -14,7 +14,6 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil;
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager;
import xyz.alexcrea.cuanvil.gui.ValueUpdatableGui;
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui;
@ -284,7 +283,7 @@ public class BasicConfigGui extends ChestGui implements ValueUpdatableGui {
if(!this.packetManager.getCanSetInstantBuild()){
lore.add("");
lore.add("§4/!\\§cCaution§4/!\\ §cYou need ProtocoLib installed and working or a 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.");
}

View file

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

View file

@ -25,7 +25,6 @@ import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions;
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalItems;
import xyz.alexcrea.cuanvil.gui.util.GuiSharedConstant;
import xyz.alexcrea.cuanvil.util.CasedStringUtil;
import xyz.alexcrea.cuanvil.util.MetricsUtil;
import java.util.*;
import java.util.function.Supplier;
@ -265,7 +264,6 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
updateGuiValues();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "An error occurred while updating enchants for " + this.enchantConflict, e);
MetricsUtil.INSTANCE.trackError(e);
}
// Save file configuration to disk
@ -310,7 +308,6 @@ public class EnchantConflictSubSettingGui extends MappedToListSubSettingGui impl
updateGuiValues();
} catch (Exception e) {
CustomAnvil.instance.getLogger().log(Level.WARNING, "An error occurred while updating group for " + this.enchantConflict, e);
MetricsUtil.INSTANCE.trackError(e);
}
// Save file configuration to disk

View file

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

View file

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

View file

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

View file

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

View file

@ -1,214 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2025 Clickism
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package xyz.alexcrea.cuanvil.update;
import com.google.gson.*;
import org.jetbrains.annotations.Nullable;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Utility class to check for newer versions of a project hosted on Modrinth.
*/
public class ModrinthUpdateChecker {
private static final String API_URL = "https://api.modrinth.com/v2/project/{id}/version";
private final String projectId;
private final String loader;
@Nullable
private final String minecraftVersion;
@Nullable
private Boolean featured = null;
@Nullable
public Consumer<Exception> onError = null;
@Nullable
public Function<String, String> getRawVersion = ModrinthUpdateChecker::getRawVersion;
/**
* Create a new update checker for the given project.
* This will check the latest version for the given loader and any minecraft version.
*
* @param projectId the project ID
* @param loader the loader
*/
public ModrinthUpdateChecker(String projectId, String loader) {
this(projectId, loader, null);
}
/**
* Create a new update checker for the given project.
* This will check the latest version for the given loader and minecraft version.
*
* @param projectId the project ID
* @param loader the loader
* @param minecraftVersion the minecraft version, or null for any version
*/
public ModrinthUpdateChecker(String projectId, String loader, @Nullable String minecraftVersion) {
this.projectId = projectId;
this.loader = loader;
this.minecraftVersion = minecraftVersion;
}
/**
* Check the latest version of the project for the given loader and minecraft version
* and call the consumer with it.
*
* @param consumer the consumer
*/
public void checkVersion(Consumer<String> consumer) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(prepareURI())
.GET()
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAcceptAsync(response -> {
if (response.statusCode() != 200) {
if(onError != null)
onError.accept(new RuntimeException("wrong response status code: " + response.statusCode()));
return;
}
JsonArray versionsArray = JsonParser.parseString(response.body()).getAsJsonArray();
String latestVersion = getLatestVersion(versionsArray);
if (latestVersion == null) {
if(onError != null)
onError.accept(new RuntimeException("latest version is null"));
return;
}
consumer.accept(latestVersion);
});
} catch (Exception e) {
if(onError != null) onError.accept(e);
}
}
/**
* Get the latest compatible version from the versions array.
*
* @param versions the versions array
* @return the latest compatible version
*/
@Nullable
protected String getLatestVersion(JsonArray versions) {
return versions.asList().stream().findFirst()
.map(JsonElement::getAsJsonObject)
.map(version -> version.get("version_number").getAsString())
.map(getRawVersion != null ? getRawVersion : (v -> v))
.orElse(null);
}
/**
* Gets the raw version from a version string.
* i.E: "fabric-1.2+1.17.1" -> "1.2"
*
* @param version the version string
* @return the raw version string
*/
public static String getRawVersion(String version) {
if (version.isEmpty()) return version;
version = version.replaceAll("^\\D+", "");
String[] split = version.split("\\+");
return split[0];
}
/**
* Prepare this request uri based on current parameters.
* @return the request uri
*/
private URI prepareURI() {
var url = new StringBuilder(API_URL.replace("{id}", projectId));
var parameters = prepareParameters();
String[] paramArray = new String[parameters.size()];
int i = 0;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
paramArray[i++] = entry.getKey() + '=' + entry.getValue();
}
url.append('?').append(String.join("&", paramArray));
return URI.create(url.toString());
}
/**
* Get the parameters for the version request.
*
* @return a map of key-value map of the request parameters
*/
private Map<String, String> prepareParameters(){
var parameters = new HashMap<String, String>();
parameters.put("loaders", List.of(loader).toString());
if(minecraftVersion != null) parameters.put("game_versions", List.of(minecraftVersion).toString());
if(featured != null) parameters.put("featured", featured.toString());
parameters.put("include_changelog", "false");
return parameters;
}
/**
* Only get featured or non-featured versions.
* Null represent no filter.
* @param featured should be restricted to featured version ? default null if not called
* @return this
*/
public ModrinthUpdateChecker setFeatured(@Nullable Boolean featured) {
this.featured = featured;
return this;
}
/**
* Function called on error calling the api.
* @param onError What should happen on error
* @return this
*/
public ModrinthUpdateChecker setOnError(@Nullable Consumer<Exception> onError) {
this.onError = onError;
return this;
}
/**
* Set the function to get raw version from the modrinth version.
* If null provided raw version will act as in the identity function.
* @param getRawVersion The function transforming modrinth version to raw version
* @return this
*/
public ModrinthUpdateChecker setGetRawVersion(@Nullable Function<String, String> getRawVersion) {
this.getRawVersion = getRawVersion;
return this;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,13 +6,10 @@ import org.bukkit.configuration.file.YamlConfiguration
import org.bukkit.plugin.java.JavaPlugin
import xyz.alexcrea.cuanvil.api.event.CAConfigReadyEvent
import xyz.alexcrea.cuanvil.api.event.CAEnchantRegistryReadyEvent
import xyz.alexcrea.cuanvil.command.CustomAnvilCommand
import xyz.alexcrea.cuanvil.command.EditConfigExecutor
import xyz.alexcrea.cuanvil.command.ReloadExecutor
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.MinecraftVersionUtil
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
@ -21,10 +18,9 @@ import xyz.alexcrea.cuanvil.listener.AnvilCloseListener
import xyz.alexcrea.cuanvil.listener.AnvilResultListener
import xyz.alexcrea.cuanvil.listener.ChatEventListener
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.update.ModrinthUpdateChecker
import xyz.alexcrea.cuanvil.update.PluginSetDefault
import xyz.alexcrea.cuanvil.update.UpdateHandler
import xyz.alexcrea.cuanvil.util.MetricsUtil
import xyz.alexcrea.cuanvil.util.Metrics
import java.io.File
import java.io.FileReader
import java.util.logging.Level
@ -35,8 +31,8 @@ import java.util.logging.Level
open class CustomAnvil : JavaPlugin() {
companion object {
// pluginIDS
private const val modrinthPluginID = "S75Ueiq9"
// bstats plugin id
private const val bstatsPluginId = 20923
// Permission string required to use the plugin's features
const val affectedByPluginPermission = "ca.affected"
@ -50,13 +46,9 @@ open class CustomAnvil : JavaPlugin() {
// Permission string required to reload the config
const val commandReloadPermission = "ca.command.reload"
// Permission string required to get diagnostic data
const val diagnosticPermission = "ca.command.diagnostic"
// Permission string required to edit the plugin's config
const val editConfigPermission = "ca.config.edit"
// Command Name to reload the config
const val commandReloadName = "anvilconfigreload"
@ -69,8 +61,6 @@ open class CustomAnvil : JavaPlugin() {
// Chat message listener
lateinit var chatListener: ChatEventListener
var latestVer: String? = null
/**
* Logging handler
*/
@ -91,99 +81,12 @@ open class CustomAnvil : JavaPlugin() {
}
// stop plugin if we do not force a dirty start (true by default)
// Return true if start was stopped
private fun tryDirtyStart(): Boolean {
if(!ConfigHolder.DEFAULT_CONFIG.config.getBoolean("dirty_start", false)) {
Bukkit.getPluginManager().disablePlugin(this)
return true
}
return false
}
// stop plugin if we force a safe start (false by default)
// Return true if start was stopped
private fun trySafeStart(): Boolean {
if(ConfigHolder.DEFAULT_CONFIG.config.getBoolean("safe_start", false)) {
Bukkit.getPluginManager().disablePlugin(this)
return true
}
return false
}
/**
* Setup plugin for use
*/
override fun onEnable() {
instance = this
try {
legacyCheck()
} catch (e: Exception) {
logger.log(Level.SEVERE, "error trying to check for legacy system", e)
MetricsUtil.trackError(e)
if(trySafeStart()) return
}
// Add commands
try {
prepareCommand()
} catch (e: Exception) {
logger.log(Level.SEVERE, "error trying to register commands", e)
MetricsUtil.trackError(e)
if(trySafeStart()) return
}
// Load default configuration
try {
if(!ConfigHolder.loadDefaultConfig())
throw RuntimeException("Error loading configuration file")
} catch (e: Exception) {
logger.log(Level.SEVERE, "error occurred loading default configuration", e)
MetricsUtil.trackError(e)
if(tryDirtyStart()) return
}
// Load dependency
try {
DependencyManager.loadDependency()
} catch (e: Exception) {
logger.log(Level.SEVERE, "error loading dependency compatibility", e)
MetricsUtil.trackError(e)
if(tryDirtyStart()) return
}
// Register listeners
try {
registerListeners()
} catch (e: Exception) {
logger.log(Level.SEVERE, "error registering listeners", e)
MetricsUtil.trackError(e)
if(tryDirtyStart()) return
}
// Load metrics
MetricsUtil.loadMetrics(this)
// Load other thing later.
// It is so other dependent plugins can implement there event listener before we fire them.
DependencyManager.scheduler.scheduleGlobally(this) { loadEnchantmentSystemDirty() }
}
override fun onDisable() {
MetricsUtil.shutdownMetrics()
}
private fun loadEnchantmentSystemDirty() {
try {
loadEnchantmentSystem()
} catch (e: Exception) {
logger.log(Level.SEVERE, "error initializing enchantment system", e)
MetricsUtil.trackError(e)
tryDirtyStart()
}
}
private fun legacyCheck() {
// Disable old plugin name if exist
val potentialPlugin = Bukkit.getPluginManager().getPlugin("UnsafeEnchantsPlus")
if (potentialPlugin != null) {
@ -192,43 +95,38 @@ open class CustomAnvil : JavaPlugin() {
logger.warning("Please note CustomAnvil is a more recent version of UnsafeEnchantsPlus")
}
val isPaper = PlatformUtil.isPaper
if(!isPaper) {
if(!PlatformUtil.isPaper) {
logger.warning("It seems you are using spigot")
logger.warning("Please take notice that spigot is less supported than paper and derivatives")
if(MinecraftVersionUtil.isTooNewForSpigot) {
logger.warning("If replace too expensive is not working this is likely because of spigot")
logger.warning("As native nms is not supported for spigot starting 26.1")
}
}
val loader = if(isPaper) "paper" else "spigot"
// Add commands
prepareCommand()
val version = description.version
val featured = if(version.contains("dev")) null else true
ModrinthUpdateChecker(modrinthPluginID, loader, null)
.setFeatured(featured)
.setOnError {
logger.log(Level.WARNING, "error trying to fetch latest update", it)
}
.checkVersion { latestVer: String? ->
CustomAnvil.latestVer = latestVer
if(latestVer == null || version.contains(latestVer)) return@checkVersion
logger.warning("An update may be available: $latestVer")
}
}
private fun registerListeners() {
// Register chat listener
// Load chat listener
chatListener = ChatEventListener()
server.pluginManager.registerEvents(chatListener, this)
// Load default configuration
if (!ConfigHolder.loadDefaultConfig()) {
logger.log(Level.SEVERE,"could not load default config.")
return
}
// Load dependency
DependencyManager.loadDependency()
// Register anvil events
server.pluginManager.registerEvents(PrepareAnvilListener(), this)
server.pluginManager.registerEvents(AnvilResultListener(), this)
server.pluginManager.registerEvents(AnvilCloseListener(DependencyManager.packetManager), this)
// Load metrics
Metrics(this, bstatsPluginId)
// Load other thing later.
// It is so other dependent plugins can implement there event listener before we fire them.
DependencyManager.scheduler.scheduleGlobally(this, {loadEnchantmentSystem()})
}
private fun loadEnchantmentSystem(){
@ -241,8 +139,7 @@ open class CustomAnvil : JavaPlugin() {
// Load config
if (!ConfigHolder.loadNonDefaultConfig()) {
logger.log(Level.SEVERE,"Plugin has an issue while trying to load non default config... exiting...")
server.pluginManager.disablePlugin(this)
logger.log(Level.SEVERE,"could not load non default config.")
return
}
@ -260,11 +157,9 @@ open class CustomAnvil : JavaPlugin() {
MainConfigGui.getInstance().init(DependencyManager.packetManager)
GuiSharedConstant.loadConstants()
// Prepare economy if possible
EconomyManager.setupEconomy(this)
// Finally, re add default we may be missing
PluginSetDefault.reAddMissingDefault()
}
fun reloadResource(
@ -316,8 +211,6 @@ open class CustomAnvil : JavaPlugin() {
command = getCommand(commandConfigName)
command?.setExecutor(EditConfigExecutor())
CustomAnvilCommand(this)
}
}

View file

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

View file

@ -4,9 +4,9 @@ import io.delilaheve.CustomAnvil
import org.bukkit.entity.HumanEntity
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.group.ConflictType
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import kotlin.math.max
import kotlin.math.min
@ -31,77 +31,24 @@ object EnchantmentUtil {
) = mutableMapOf<CAEnchantment, Int>().apply {
putAll(this@combineWith)
CustomAnvil.verboseLog("Testing merge")
val bypassFuse = player.hasPermission(CustomAnvil.bypassFusePermission)
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
val allowed = other.filter { (enchantment, _) -> enchantment.isAllowed(player) }
val new = allowed.filter{ (enchantment, _) -> !containsKey(enchantment)}
val old = allowed.filter{ (enchantment, _) -> containsKey(enchantment)}
other.forEach { (enchantment, level) ->
if(!enchantment.isAllowed(player)) return@forEach
fun maxLevel(enchantment: CAEnchantment): Int {
val max = if (bypassLevel) { 255 }
// Get max level or 255 if player can bypass
val maxLevel = if (bypassLevel) { 255 }
else { ConfigOptions.enchantLimit(enchantment) }
CustomAnvil.verboseLog("Max level of ${enchantment.key} is $maxLevel (bypassLevel is $bypassLevel)")
CustomAnvil.verboseLog("Max level of ${enchantment.key} is $max (bypassLevel is $bypassLevel)")
return max
}
old.forEach { (enchantment, level) ->
// Get max level or 255 if player can bypass
val maxLevel = maxLevel(enchantment)
val cappedLevel = min(level, maxLevel)
val oldLevel = this[enchantment]!! // <- should not be null. (enchantment already in result list)
// ... and they're not the same level
if (oldLevel != cappedLevel) {
// apply the greater of the two or left one if right is above max
this[enchantment] = max(oldLevel, cappedLevel)
}
// ... and they're the same level
else {
// We test if it is allowed to merge at this level
if(!bypassLevel){
val maxBeforeDisabled = ConfigOptions.maxBeforeMergeDisabled(enchantment)
if((maxBeforeDisabled > 0) && (oldLevel >= maxBeforeDisabled)) {
CustomAnvil.verboseLog(
"Reached max merge before disable for ${enchantment.key}: $oldLevel/$maxBeforeDisabled)")
return@forEach
}
}
// Now we increase the enchantment level by 1
var newLevel = oldLevel + 1
newLevel = max(min(newLevel, maxLevel), oldLevel)
this[enchantment] = newLevel
}
if(bypassFuse){
CustomAnvil.verboseLog("Bypassed conflict check for ${enchantment.key}")
} else {
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager
.isConflicting(this, item, enchantment)
// ... and they are conflicting
if(conflictType != ConflictType.NO_CONFLICT){
CustomAnvil.verboseLog(
"Enchantment already in result list, and they are conflicting (${enchantment.key}, conflict: $conflictType)")
this[enchantment] = oldLevel
return@forEach
}
}
}
// Try to add new now
new.forEach { (enchantment, level) ->
// Get max level or 255 if player can bypass
val maxLevel = maxLevel(enchantment)
val cappedLevel = min(level, maxLevel)
// Enchantment not yet in result list
if (!containsKey(enchantment)) {
// Do not allow new enchantment if above maximum
if(this.size >= maxEnchantCount) return@forEach
@ -121,7 +68,50 @@ object EnchantmentUtil {
}
}
// Enchantment already in result list
else {
val oldLevel = this[enchantment]!! // <- should not be null. (enchantment already in result list)
if(bypassFuse){
CustomAnvil.verboseLog("Bypassed conflict check for ${enchantment.key}")
} else {
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager
.isConflicting(this, item, enchantment)
// ... and they are conflicting
if(conflictType != ConflictType.NO_CONFLICT){
CustomAnvil.verboseLog(
"Enchantment already in result list, and they are conflicting (${enchantment.key}, conflict: $conflictType)")
return@forEach
}
}
// ... and they're not the same level
if (oldLevel != cappedLevel) {
// apply the greater of the two or left one if right is above max
this[enchantment] = max(oldLevel, cappedLevel)
}
// ... and they're the same level
else {
// We test if it is allowed to merge at this level
if(!bypassLevel){
val maxBeforeDisabled = ConfigOptions.maxBeforeMergeDisabled(enchantment)
if((maxBeforeDisabled > 0) && (oldLevel >= maxBeforeDisabled)) {
CustomAnvil.verboseLog(
"Reached max merge before disable for ${enchantment.key}: $oldLevel/$maxBeforeDisabled)")
return@forEach
}
}
// Now we increase the enchantment level by 1
var newLevel = oldLevel + 1
newLevel = max(min(newLevel, maxLevel), oldLevel)
this[enchantment] = newLevel
}
}
}
}
}

View file

@ -4,9 +4,6 @@ import org.bukkit.Material.ENCHANTED_BOOK
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.update.UpdateUtils
import xyz.alexcrea.cuanvil.util.MaterialUtil.customType
import xyz.alexcrea.cuanvil.util.MaxDamageCheckerUtil
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@ -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
* [first] and [second] item's durability values
@ -64,9 +54,7 @@ object ItemUtil {
val secondDurability = durability - secondDamage
val combinedDurability = firstDurability + secondDurability
val newDurability = min(combinedDurability, durability)
val maxDamage = maxDamage(it)
it.damage = min(durability - newDurability, maxDamage)
it.damage = durability - newDurability
itemMeta = it
return true
}
@ -102,5 +90,5 @@ object ItemUtil {
*/
fun ItemStack.canMergeWith(
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,46 +0,0 @@
package xyz.alexcrea.cuanvil.command
import org.bukkit.ChatColor
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
abstract class CASubCommand: CommandExecutor {
private var alreadySaid = false;
override fun onCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): Boolean {
if(!alreadySaid){
sender.sendMessage(ChatColor.RED.toString() +
"Please not that this command will be replaced as a subcommand of `/customanvil` or `/ca`")
alreadySaid = true
}
return executeCommand(sender, cmd, cmdstr, args)
}
abstract fun executeCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>): Boolean
open fun allowed(sender: CommandSender): Boolean {
return true
}
open fun tabCompleter(
sender: CommandSender,
args: Array<out String>,
list: MutableList<String>) {
}
open fun description(): String {
return "no description"
}
}

View file

@ -1,96 +0,0 @@
package xyz.alexcrea.cuanvil.command
import com.google.common.collect.ImmutableMap
import io.delilaheve.CustomAnvil
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import xyz.alexcrea.cuanvil.util.MetricsUtil
import java.util.ArrayList
class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
// Name of the generic command
companion object {
private const val genericCommandName = "customanvil"
}
private val editConfigCommand = EditConfigExecutor()
private val helpCommand = HelpExecutor()
private val commands = ImmutableMap.of(
"gui", editConfigCommand,
"reload", ReloadExecutor(),
"diagnostic", DiagnosticExecutor(),
"help", helpCommand,
)
init {
val self = plugin.getCommand(genericCommandName)!!
self.setExecutor(this)
self.tabCompleter = this
helpCommand.commands = commands
}
override fun onCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): Boolean {
// Find sub command to execute based on the provided command name
val subcmd: CASubCommand?
val newargs: Array<out String>
if(args.isEmpty()) {
subcmd = editConfigCommand
newargs = args
}else {
subcmd = commands[args[0].lowercase()]
newargs = args.copyOfRange(1, args.size)
}
if(subcmd == null || !subcmd.allowed(sender)) {
sender.sendMessage("Invalid subcommand. run `$cmdstr help` to see available commands")
return true
}
try {
return subcmd.executeCommand(sender, cmd, cmdstr, newargs)
} catch (e: Throwable) {
MetricsUtil.trackError(e)
sender.sendMessage("§cError running this command")
return false
}
}
override fun onTabComplete(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): MutableList<String> {
val result = ArrayList<String>()
if(args.size < 2) {
for ((key, cmd) in commands) {
if(!cmd.allowed(sender)) continue
result.add(key)
}
} else {
val subcmd = commands[args[0].lowercase()]
if(subcmd != null) {
val newArgs = args.copyOfRange(1, args.size)
if(!subcmd.allowed(sender)) return result
subcmd.tabCompleter(sender, newArgs, result)
}
}
//assumed all provided tab completed string are lowercase
return result.stream()
.filter { it.startsWith(args[args.size - 1]) }
.sorted()
.toList()
}
}

View file

@ -1,347 +0,0 @@
package xyz.alexcrea.cuanvil.command
import com.github.stefvanschie.inventoryframework.inventoryview.interface_.InventoryViewUtil
import io.delilaheve.CustomAnvil
import net.md_5.bungee.api.chat.ClickEvent
import net.md_5.bungee.api.chat.HoverEvent
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.api.chat.hover.content.Text
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Material
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryType
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.EnchantmentStorageMeta
import org.bukkit.plugin.Plugin
import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.packet.NoPacketManager
import xyz.alexcrea.cuanvil.dependency.packet.ProtocoLibWrapper
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener
import xyz.alexcrea.cuanvil.util.MetricsUtil
import java.util.*
import java.util.stream.Collectors
class DiagnosticExecutor: CASubCommand() {
companion object{
private const val NO_DIAG_PERM = "You do not have permission to diagnostic this server"
fun fetchNMSType(): String {
val packetManager = DependencyManager.packetManager
val packetManagerClass = packetManager.javaClass
val className = packetManagerClass.name
val result = if(className.contains("PaperPacket")) {
"Paper"
} else {
when (packetManagerClass) {
ProtocoLibWrapper::class.java -> "Protocolib"
NoPacketManager::class.java -> "None"
else -> "Version Specific"
}
}
return "$result ${if(packetManager.canSetInstantBuild) '✅' else '❌'}"
}
}
enum class DiagParams(val value: String) {
OS_PRIVACY("os_privacy"),
PLUGIN_PRIVACY("plugin_privacy"),
NO_MERGE_TEST("no_merge_test"),
FULL_ENCHANTMENT_DATA("full_enchantment_data"),
INCLUDE_LAST_ERROR("include_last_error"),
}
private fun fetchParameters(args: Array<out String>): EnumSet<DiagParams> {
val result = EnumSet.noneOf(DiagParams::class.java)
val argSet = HashSet<String>()
for (string in args) {
argSet.add(string.lowercase())
}
for (param in DiagParams.entries) {
if(argSet.contains(param.value))
result.add(param)
}
return result
}
override fun tabCompleter(
sender: CommandSender,
args: Array<out String>,
list: MutableList<String>) {
if(!allowed(sender)) return
val map = fetchParameters(args)
for (param in DiagParams.entries) {
if(!map.contains(param))
list.add(param.value)
}
}
override fun executeCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): Boolean {
if (!allowed(sender)) {
sender.sendMessage(NO_DIAG_PERM)
return false
}
val stb = StringBuilder("```\n")
val params = fetchParameters(args)
var hasError = false
try {
diagnostic(sender, stb, params)
} catch(e: Throwable){
stb.append("\n\nError happened trying to get diagnostic data:\n")
.append(e.message).append("\n")
.append(e.stackTrace.joinToString("\n"))
hasError = true
e.printStackTrace()
}
stb.append("\n```")
if (sender is HumanEntity) {
if(hasError)
sender.spigot().sendMessage(TextComponent(ChatColor.RED.toString() + "There was an error running the diagnostic"))
val message = TextComponent(ChatColor.GREEN.toString() + "Click to copy diagnostic data")
message.clickEvent = ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, stb.toString())
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, Text("§7Click to copy"))
sender.spigot().sendMessage(message);
} else {
sender.sendMessage(stb.toString());
}
return true
}
override fun allowed(sender: CommandSender): Boolean {
return sender.hasPermission(CustomAnvil.diagnosticPermission)
}
fun diagnostic(sender: CommandSender, stb: StringBuilder, params: Set<DiagParams>){
stb.append("Server Info\n")
val version = CustomAnvil.instance.description.version
stb.append("\nPlugin Version: ").append(version)
if(version.contains("dev")) stb.append(" (alpha)")
stb.append("\nLatest Update: ").append(CustomAnvil.latestVer)
stb.append("\nServer Version: ").append(Bukkit.getVersion()).append(" (").append(Bukkit.getName()).append(')')
stb.append("\nPlugin Enabled: ").append(if(CustomAnvil.instance.isEnabled) "Yes" else "No")
stb.append("\nNMS type: ").append(fetchNMSType())
if(!params.contains(DiagParams.OS_PRIVACY)) {
stb.append("\nJava Version: ").append(System.getProperty("java.version"))
stb.append("\nOS: ").append(System.getProperty("os.name")).append(" ")
.append(System.getProperty("os.version"))
.append(System.getProperty("os.arch"))
}
stb.append("\nHad detect error: ").append(if(MetricsUtil.lastError != null) "Yes" else "No")
if(!params.contains(DiagParams.PLUGIN_PRIVACY)) {
pluginListDiag(sender, stb)
}
prepareAnvilListeners(stb)
if(!params.contains(DiagParams.NO_MERGE_TEST)){
if(sender is Player) testMerge(sender, stb)
}
stb.append("\n\nEnchantments data:")
partialEnchantmentData(stb)
if(params.contains(DiagParams.FULL_ENCHANTMENT_DATA)){
fullEnchantmentData(stb)
}
if(params.contains(DiagParams.INCLUDE_LAST_ERROR)){
includeLastError(stb)
}
}
private fun testMerge(player: Player, stb: StringBuilder) {
val sword = ItemStack(Material.DIAMOND_SWORD)
val damagedSword = sword.clone()
val enchantedSword = sword.clone()
val enchantedBook = ItemStack(Material.ENCHANTED_BOOK)
val unitForRepair = ItemStack(Material.DIAMOND)
var meta = damagedSword.itemMeta
(meta as Damageable).damage = 5
damagedSword.itemMeta = meta
meta = enchantedSword.itemMeta
meta!!.addEnchant(Enchantment.DAMAGE_ALL, 1, true)
enchantedSword.itemMeta = meta
meta = enchantedBook.itemMeta
(meta as EnchantmentStorageMeta).addStoredEnchant(Enchantment.DAMAGE_ALL, 1, true)
enchantedBook.itemMeta = meta
stb.append("\n\nItem to Item repair:")
simulateAnvil(player, stb, damagedSword, damagedSword, sword)
stb.append("\n\nUnit repair:")
simulateAnvil(player, stb, damagedSword, unitForRepair, sword)
stb.append("\n\nEnchanting an item:")
simulateAnvil(player, stb, sword, enchantedBook, enchantedSword)
}
private val Plugin.pluginNameDisplay: String
get() {
return this.name + " v" + this.description.version
}
override fun description(): String {
return "Basic diagnostic of this plugin"
}
private fun pluginListDiag(sender: CommandSender, stb: StringBuilder) {
val enabledPlugins: MutableList<Plugin?> = ArrayList<Plugin?>()
val disabledPlugins: MutableList<Plugin?> = ArrayList<Plugin?>()
for (plugin in Bukkit.getPluginManager().plugins) {
if (plugin.isEnabled) {
enabledPlugins.add(plugin)
} else {
disabledPlugins.add(plugin)
}
}
stb.append("\nEnabled Plugins: ").append(
enabledPlugins.stream()
.map { plugin -> plugin!!.pluginNameDisplay }
.reduce { a: String?, b: String? -> "$a, $b" }.orElse("None")
)
stb.append("\nDisabled Plugins: ").append(
disabledPlugins.stream()
.map { plugin -> plugin!!.pluginNameDisplay }
.reduce { a: String?, b: String? -> "$a, $b" }.orElse("None")
)
}
fun prepareAnvilListeners(stb: StringBuilder) {
val eventListeners: MutableSet<Plugin?> = Arrays
.stream(
PrepareAnvilEvent
.getHandlerList()
.getRegisteredListeners()
)
.map { obj: RegisteredListener? -> obj!!.plugin }
.collect(Collectors.toSet())
eventListeners.remove(CustomAnvil.instance)
stb.append("\nPrepare Anvil Listeners: ").append(
if (eventListeners.isEmpty()) "None" else eventListeners.stream()
.map { plugin -> plugin!!.pluginNameDisplay }
.reduce { a: String?, b: String? -> "$a, $b" }.orElse("None")
)
}
fun simulateAnvil(player: Player, stb: StringBuilder, left: ItemStack?, right: ItemStack?, result: ItemStack?) {
var invView: InventoryView
var event: PrepareAnvilEvent
try {
val fakeInv = Bukkit.createInventory(player, InventoryType.ANVIL)
invView = player.openInventory(fakeInv)!!
event = PrepareAnvilEvent(invView, result)
} catch (e: Throwable) {
// Help
val menuTypeClazz = Class.forName("org.bukkit.inventory.MenuType")
val anvilTypeField = menuTypeClazz.getField("ANVIL")
val anvilType = anvilTypeField.get(null)
val createMethod = anvilType.javaClass.getMethod("create", HumanEntity::class.java)
invView = createMethod.invoke(anvilType, player) as InventoryView
player.openInventory(invView)
val anvilViewClass = Class.forName("org.bukkit.inventory.view.AnvilView")
val constructor = PrepareAnvilEvent::class.java.getConstructor(anvilViewClass, ItemStack::class.java)
event = constructor.newInstance(invView, result)
}
val fakeInv = InventoryViewUtil.getInstance().getTopInventory(invView) as AnvilInventory
fakeInv.setItem(0, left)
fakeInv.setItem(1, right)
val xp = fakeInv.repairCost
val maxXp = fakeInv.maximumRepairCost
val mergeResult = fakeInv.getItem(2)
stb.append("\n${if(result == mergeResult) "E" else "Une"}xpected Result")
PrepareAnvilListener().anvilCombineCheck(event)
// Now we check if item and xp same
stb.append("\nXP/Max XP: ")
.append(if(fakeInv.repairCost == xp) "Correct" else "Incorrect")
.append("/")
.append(if(fakeInv.maximumRepairCost == maxXp) "Correct" else "Incorrect")
.append(" (${fakeInv.repairCost} $xp|${fakeInv.maximumRepairCost} $maxXp)")
.append("\nMerge result: ")
.append(if(fakeInv.getItem(2) == mergeResult) "Correct" else "Incorrect")
PrepareAnvilListener.IS_EMPTY_TEST = true
Bukkit.getPluginManager().callEvent(event)
stb.append("\nNull result test: ")
.append(if(event.result == null) "Correct" else "Incorrect")
fakeInv.setItem(0, null)
fakeInv.setItem(1, null)
fakeInv.setItem(2, null)
player.closeInventory()
}
private fun fullEnchantmentData(stb: StringBuilder) {
for (enchantment in CAEnchantmentRegistry.getInstance().values()) {
stb.append("\n- ").append(enchantment.key.toString())
.append(" ").append(enchantment.name)
.append(" ").append(enchantment.defaultMaxLevel())
}
}
private fun partialEnchantmentData(stb: StringBuilder) {
val map = HashMap<String, Int>()
for (enchant in CAEnchantmentRegistry.getInstance().values()) {
map[enchant.key.namespace] = map.getOrDefault(enchant.key.namespace, 0) + 1
}
stb.append("\nNamespaces: ${
map.entries.stream()
.map { (key, value) -> "$key ($value)" }
.reduce { a, b -> "$a, $b" }.get()
}")
}
private fun includeLastError(stb: StringBuilder) {
val e = MetricsUtil.lastError ?: return
stb.append("\n\nLast stack trace: ${e.stackTraceToString()}")
}
}

View file

@ -2,21 +2,17 @@ package xyz.alexcrea.cuanvil.command
import io.delilaheve.CustomAnvil
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity
import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui
import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions
class EditConfigExecutor: CASubCommand() {
class EditConfigExecutor : CommandExecutor {
override fun executeCommand(sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>): Boolean {
if (sender !is HumanEntity) return false
if (!allowed(sender)) {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (!sender.hasPermission(CustomAnvil.editConfigPermission)) {
sender.sendMessage(GuiGlobalActions.NO_EDIT_PERM)
return false
}
@ -29,17 +25,10 @@ class EditConfigExecutor: CASubCommand() {
return false
}
if (sender !is HumanEntity) return false
MainConfigGui.getInstance().show(sender)
return true
}
override fun allowed(sender: CommandSender): Boolean {
return sender.hasPermission(CustomAnvil.editConfigPermission)
}
override fun description(): String {
return "Gui to edit the plugin's config"
}
}

View file

@ -1,32 +0,0 @@
package xyz.alexcrea.cuanvil.command
import com.google.common.collect.ImmutableMap
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
class HelpExecutor: CASubCommand() {
lateinit var commands: ImmutableMap<String, CASubCommand>
override fun executeCommand(sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>): Boolean {
val stb = StringBuilder("List of available commands:")
for ((key, cmd) in commands) {
if(!cmd.allowed(sender)) continue
stb.append("\n- $key: ").append(cmd.description())
}
sender.sendMessage(stb.toString())
return true
}
override fun description(): String {
return "Help command"
}
}

View file

@ -11,13 +11,9 @@ import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.gui.config.global.*
import xyz.alexcrea.cuanvil.update.UpdateHandler
class ReloadExecutor : CASubCommand() {
override fun executeCommand(sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>): Boolean {
if (!allowed(sender)) {
class ReloadExecutor : CommandExecutor {
override fun onCommand(sender: CommandSender, cmd: Command, cmdstr: String, args: Array<out String>): Boolean {
if (!sender.hasPermission(CustomAnvil.commandReloadPermission)) {
sender.sendMessage("§cYou do not have permission to reload the config")
return false
}
@ -35,14 +31,6 @@ class ReloadExecutor : CASubCommand() {
return commandSuccess
}
override fun allowed(sender: CommandSender): Boolean {
return sender.hasPermission(CustomAnvil.commandReloadPermission)
}
override fun description(): String {
return "Reload the configuration of this plugin"
}
/**
* Execute the command, return true if success or false otherwise
*/

View file

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

View file

@ -6,29 +6,29 @@ object MinecraftVersionUtil {
val craftbukkitVersion: String?
get() {
val version = UpdateUtils.currentMinecraftVersion()
if (version.major != 1) return null
val versionParts = UpdateUtils.currentMinecraftVersionArray()
if (versionParts[0] != 1) return null
return when (version.minor) {
17 -> when (version.patch) {
return when (versionParts[1]) {
17 -> when (versionParts[2]) {
0, 1 -> "1_17R1"
else -> null
}
18 -> when (version.patch) {
18 -> when (versionParts[2]) {
0, 1 -> "1_18R1"
2 -> "1_18R2"
else -> null
}
19 -> when (version.patch) {
19 -> when (versionParts[2]) {
0, 1, 2 -> "1_19R1"
3 -> "1_19R2"
4 -> "1_19R3"
else -> null
}
20 -> when (version.patch) {
20 -> when (versionParts[2]) {
0, 1 -> "1_20R1"
2 -> "1_20R2"
3, 4 -> "1_20R3"
@ -36,7 +36,7 @@ object MinecraftVersionUtil {
else -> null
}
21 -> when (version.patch) {
21 -> when (versionParts[2]) {
0, 1 -> "1_21R1"
2, 3 -> "1_21R2"
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
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
@ -145,7 +145,7 @@ object DataPackDependency {
CustomAnvil.instance.logger.warning("Could not find material $name for item group $groupName")
continue
}
group.addToPolicy(mat.key)
group.addToPolicy(mat)
}
for (name in section.getStringList("groups")) {
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())
}
}

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