Began reworking enchantment registration

This commit is contained in:
Will FP 2023-12-07 14:38:13 +00:00
parent b41b34a37a
commit 3dceea0b38
34 changed files with 1052 additions and 162 deletions

View File

@ -2,7 +2,7 @@ plugins {
java
`java-library`
`maven-publish`
kotlin("jvm") version "1.7.10"
kotlin("jvm") version "1.9.20"
id("com.github.johnrengelman.shadow") version "8.0.0"
id("com.willfp.libreforge-gradle-plugin") version "1.0.0"
}
@ -40,10 +40,10 @@ allprojects {
}
dependencies {
compileOnly("com.willfp:eco:6.58.0")
compileOnly("com.willfp:eco:6.67.0")
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.1.0")
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.20")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.1.5")
}
java {

View File

@ -1,3 +1,8 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.5.3" apply false
}
group = "com.willfp"
version = rootProject.version

View File

@ -0,0 +1,16 @@
plugins {
id("io.papermc.paperweight.userdev")
}
group = "com.willfp"
version = rootProject.version
dependencies {
paperweight.paperDevBundle("1.20.3-R0.1-SNAPSHOT")
}
tasks {
build {
dependsOn(reobfJar)
}
}

View File

@ -0,0 +1,28 @@
package com.willfp.ecoenchants.proxy.v1_20_R3
import com.willfp.ecoenchants.enchants.EcoEnchant
import com.willfp.ecoenchants.enchants.EcoEnchants
import com.willfp.ecoenchants.vanilla.VanillaEnchantmentData
import net.minecraft.world.item.enchantment.Enchantment
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_20_R3.enchantments.CraftEnchantment
class EcoCraftEnchantment(
target: Enchantment,
key: NamespacedKey,
private val data: VanillaEnchantmentData
) : CraftEnchantment(key, target) {
override fun getMaxLevel(): Int = data.maxLevel ?: super.getMaxLevel()
override fun conflictsWith(other: org.bukkit.enchantments.Enchantment): Boolean {
if (other is EcoEnchant) {
return other.conflictsWith(this)
}
return data.conflicts?.contains(other.key) ?: super.conflictsWith(other)
}
fun register() {
EcoEnchants.register(this)
}
}

View File

@ -0,0 +1,26 @@
package com.willfp.ecoenchants.proxy.v1_20_R3
import com.willfp.ecoenchants.proxy.proxies.EcoCraftEnchantmentManagerProxy
import com.willfp.ecoenchants.proxy.v1_20_R3.EcoCraftEnchantment
import com.willfp.ecoenchants.vanilla.VanillaEnchantmentData
import net.minecraft.core.registries.BuiltInRegistries
import org.bukkit.enchantments.Enchantment
class EcoCraftEnchantmentManager : EcoCraftEnchantmentManagerProxy {
override fun registerNewCraftEnchantment(
enchantment: Enchantment,
data: VanillaEnchantmentData
) {
for (enchant in BuiltInRegistries.ENCHANTMENT) {
val key = org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey.fromMinecraft(
BuiltInRegistries.ENCHANTMENT.getKey(enchant)
)
if (key.key != enchantment.key.key) {
continue
}
EcoCraftEnchantment(enchant, key, data).register()
}
}
}

View File

@ -0,0 +1,73 @@
package com.willfp.ecoenchants.proxy.v1_20_R3
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchant.EcoEnchants
import com.willfp.ecoenchants.enchant.registration.modern.ModernEnchantmentRegistererProxy
import com.willfp.ecoenchants.proxy.v1_20_R3.registration.DelegatedCraftEnchantment
import com.willfp.ecoenchants.proxy.v1_20_R3.registration.VanillaEcoEnchantsEnchantment
import net.minecraft.core.MappedRegistry
import net.minecraft.core.Registry
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_20_R3.CraftRegistry
import org.bukkit.craftbukkit.v1_20_R3.CraftServer
import org.bukkit.craftbukkit.v1_20_R3.enchantments.CraftEnchantment
import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey
import org.bukkit.enchantments.Enchantment
class ModernEnchantmentRegisterer : ModernEnchantmentRegistererProxy {
private val frozenField = MappedRegistry::class.java
.declaredFields
.filter { it.type.isPrimitive }[0]
.apply { isAccessible = true }
@Suppress("UNCHECKED_CAST")
private val registries = CraftServer::class.java
.getDeclaredField("registries")
.apply { isAccessible = true }
.get(Bukkit.getServer())
as HashMap<Class<*>, org.bukkit.Registry<*>>
// Replace bukkit enchantment registry
init {
val server = Bukkit.getServer() as CraftServer
registries[Enchantment::class.java] = CraftRegistry(
Enchantment::class.java,
server.handle.server.registryAccess().registryOrThrow(Registries.ENCHANTMENT)
) { key, registry ->
val enchant = EcoEnchants.getByID(key.key)
if (enchant == null) {
CraftEnchantment(key, registry)
} else {
DelegatedCraftEnchantment(enchant, registry)
}
}
}
override fun register(enchant: EcoEnchant) {
if (BuiltInRegistries.ENCHANTMENT.containsKey(CraftNamespacedKey.toMinecraft(enchant.key))) {
return
}
// Unfreeze registry
frozenField.set(BuiltInRegistries.ENCHANTMENT, false)
Registry.register(
BuiltInRegistries.ENCHANTMENT, enchant.id, VanillaEcoEnchantsEnchantment(
enchant.id
)
)
}
override fun unregister(enchant: EcoEnchant) {
/*
You can't unregister from a minecraft registry, so we simply leave the stale reference there.
This shouldn't cause many issues in production because unregistered enchantments can't be accessed.
*/
}
}

View File

@ -0,0 +1,11 @@
package com.willfp.ecoenchants.proxy.v1_20_R3
import com.willfp.ecoenchants.proxy.proxies.OpenInventoryProxy
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer
import org.bukkit.entity.Player
class OpenInventory : OpenInventoryProxy {
override fun getOpenInventory(player: Player): Any {
return (player as CraftPlayer).handle.containerMenu
}
}

View File

@ -0,0 +1,115 @@
package com.willfp.ecoenchants.proxy.v1_20_R3.registration
import com.willfp.eco.util.StringUtils
import com.willfp.ecoenchants.display.getFormattedName
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchants.wrap
import io.papermc.paper.enchantments.EnchantmentRarity
import net.kyori.adventure.text.Component
import net.minecraft.world.item.enchantment.Enchantment
import org.bukkit.craftbukkit.v1_20_R3.enchantments.CraftEnchantment
import org.bukkit.enchantments.EnchantmentTarget
import org.bukkit.entity.EntityCategory
import org.bukkit.inventory.EquipmentSlot
import org.bukkit.inventory.ItemStack
import java.util.Objects
class DelegatedCraftEnchantment(
private val enchant: EcoEnchant,
nmsEnchantment: Enchantment
) : CraftEnchantment(enchant.key, nmsEnchantment), EcoEnchant by enchant {
override fun canEnchantItem(item: ItemStack): Boolean {
return enchant.canEnchantItem(item)
}
override fun conflictsWith(other: org.bukkit.enchantments.Enchantment): Boolean {
return enchant.conflictsWith(other)
}
override fun translationKey(): String {
return "ecoenchants:enchantment.$id"
}
@Deprecated(
message = "getName is a legacy Spigot API",
replaceWith = ReplaceWith("this.displayName(level)")
)
override fun getName(): String = this.id.uppercase()
override fun getMaxLevel(): Int {
return maxLevel
}
override fun getStartLevel(): Int {
return 1
}
@Deprecated(
message = "getItemTargets is an incompatible Spigot API",
replaceWith = ReplaceWith("this.targets")
)
override fun getItemTarget(): EnchantmentTarget = EnchantmentTarget.ALL
@Deprecated(
message = "Treasure enchantments do not exist in EcoEnchants",
replaceWith = ReplaceWith("this.isEnchantable")
)
override fun isTreasure(): Boolean = !isEnchantable
@Deprecated(
message = "Use EnchantmentType instead",
replaceWith = ReplaceWith("type.id")
)
override fun isCursed(): Boolean {
return false
}
override fun displayName(level: Int): Component {
return StringUtils.toComponent(this.wrap().getFormattedName(level))
}
override fun isTradeable(): Boolean {
return isTradeable
}
override fun isDiscoverable(): Boolean {
return isDiscoverable
}
override fun getMinModifiedCost(level: Int): Int {
return Int.MAX_VALUE
}
override fun getMaxModifiedCost(level: Int): Int {
return Int.MAX_VALUE
}
@Deprecated(
message = "EcoEnchants uses a custom system for enchantment rarity",
replaceWith = ReplaceWith("this.enchantRarity")
)
override fun getRarity(): EnchantmentRarity {
return EnchantmentRarity.RARE
}
@Deprecated(
message = "EcoEnchants do not have damage increase, this method is for sharpness/boa/smite",
replaceWith = ReplaceWith("0.0f")
)
override fun getDamageIncrease(level: Int, entityCategory: EntityCategory): Float = 0.0f
@Deprecated(
message = "getActiveSlots is an incompatible Paper API",
replaceWith = ReplaceWith("this.slots")
)
override fun getActiveSlots() = emptySet<EquipmentSlot>()
override fun equals(other: Any?): Boolean {
return other is DelegatedCraftEnchantment &&
other.key == this.key
}
override fun hashCode(): Int {
return Objects.hash(this.key)
}
}

View File

@ -0,0 +1,80 @@
package com.willfp.ecoenchants.proxy.v1_20_R3.registration
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchant.EcoEnchants
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.MobType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.Enchantment
import net.minecraft.world.item.enchantment.EnchantmentCategory
import org.bukkit.craftbukkit.v1_20_R3.enchantments.CraftEnchantment
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack
class VanillaEcoEnchantsEnchantment(
private val id: String
) : Enchantment(
Rarity.VERY_RARE,
EnchantmentCategory.VANISHABLE,
EquipmentSlot.values()
) {
private val enchant: EcoEnchant
get() = EcoEnchants[id] ?: throw IllegalStateException("Enchantment $id not found")
override fun canEnchant(stack: ItemStack): Boolean {
val item = CraftItemStack.asCraftMirror(stack)
return enchant.canEnchantItem(item)
}
override fun doPostAttack(user: LivingEntity, target: Entity, level: Int) {
// Do nothing
}
override fun doPostHurt(user: LivingEntity, attacker: Entity, level: Int) {
// Do nothing
}
override fun getDamageBonus(level: Int, group: MobType): Float {
return 0f
}
override fun getDamageProtection(level: Int, source: DamageSource): Int {
return 0
}
override fun getMinLevel(): Int {
return 1
}
override fun getMaxLevel(): Int {
return enchant.maxLevel
}
override fun isCurse(): Boolean {
return false
}
override fun isDiscoverable(): Boolean {
return false
}
override fun isTradeable(): Boolean {
return false
}
override fun isTreasureOnly(): Boolean {
return true
}
override fun checkCompatibility(other: Enchantment): Boolean {
val bukkit = CraftEnchantment.minecraftToBukkit(other)
return !enchant.conflictsWith(bukkit)
}
override fun getSlotItems(entity: LivingEntity): MutableMap<EquipmentSlot, ItemStack> {
return mutableMapOf()
}
}

View File

@ -13,6 +13,7 @@ import com.willfp.ecoenchants.config.VanillaEnchantsYml
import com.willfp.ecoenchants.display.DisplayCache
import com.willfp.ecoenchants.display.EnchantDisplay
import com.willfp.ecoenchants.display.EnchantSorter
import com.willfp.ecoenchants.enchant.registration.EnchantmentRegisterer
import com.willfp.ecoenchants.enchants.EcoEnchantLevel
import com.willfp.ecoenchants.enchants.EcoEnchants
import com.willfp.ecoenchants.enchants.EnchantGUI
@ -47,6 +48,8 @@ class EcoEnchantsPlugin : LibreforgePlugin() {
var isLoaded = false
private set
val enchantmentRegisterer: EnchantmentRegisterer = TODO()
init {
instance = this
}

View File

@ -0,0 +1,82 @@
package com.willfp.ecoenchants.enchant
import com.willfp.eco.core.registry.KRegistrable
import com.willfp.ecoenchants.target.EnchantmentTarget
import com.willfp.libreforge.conditions.ConditionList
import com.willfp.libreforge.slot.SlotType
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
interface EcoEnchant : KRegistrable, EcoEnchantLike {
/**
* The key.
*/
val key: NamespacedKey
/**
* The enchantment.
*/
override val enchantment: Enchantment
get() = this as Enchantment
/**
* The max enchantment level.
*/
val maxLevel: Int
/**
* The enchantment targets.
*/
val targets: Set<EnchantmentTarget>
/**
* The enchantment slots.
*/
val slots: Set<SlotType>
get() = targets.map { it.slot }.toSet()
/**
* The conditions to use the enchantment.
*/
val conditions: ConditionList
/**
* If the enchantment is enchantable.
*/
val isEnchantable: Boolean
/**
* If the enchantment is tradeable.
*/
val isTradeable: Boolean
/**
* If the enchantment is discoverable.
*/
val isDiscoverable: Boolean
/**
* Get a certain [level].
*/
fun getLevel(level: Int): EcoEnchantLevel
/**
* Get if this enchantment conflicts with [other], only checking one way.
*/
fun conflictsWithDirectly(other: Enchantment): Boolean
/**
* Get if this enchantment conflicts with [other].
*/
fun conflictsWith(other: Enchantment): Boolean {
if (this.conflictsWithDirectly(other)) {
return true
}
if (other is EcoEnchant) {
return other.conflictsWithDirectly(this as Enchantment)
}
return false
}
}

View File

@ -0,0 +1,42 @@
package com.willfp.ecoenchants.enchant
import com.willfp.eco.core.EcoPlugin
import com.willfp.libreforge.Holder
import com.willfp.libreforge.conditions.ConditionList
import com.willfp.libreforge.effects.EffectList
import java.util.Objects
class EcoEnchantLevel(
val enchant: EcoEnchant,
val level: Int,
override val effects: EffectList,
override val conditions: ConditionList,
plugin: EcoPlugin
) : Holder {
override val id = plugin.createNamespacedKey("${enchant.id}_$level")
override fun equals(other: Any?): Boolean {
if (other !is EcoEnchantLevel) {
return false
}
return this.id == other.id
}
override fun toString(): String {
return id.toString()
}
override fun hashCode(): Int {
return Objects.hash(this.id)
}
}
data class FoundEcoEnchantLevel(
val level: EcoEnchantLevel,
val activeLevel: Int
): Holder {
override val effects = level.effects
override val conditions = level.conditions
override val id = level.id
}

View File

@ -0,0 +1,130 @@
package com.willfp.ecoenchants.enchant
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.fast.fast
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.PlaceholderInjectable
import com.willfp.eco.core.placeholder.StaticPlaceholder
import com.willfp.eco.core.placeholder.context.placeholderContext
import com.willfp.eco.util.NumberUtils
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchants.DescriptionPlaceholder
import com.willfp.ecoenchants.enchants.wrap
import com.willfp.ecoenchants.mechanics.infiniteIfNegative
import com.willfp.ecoenchants.rarity.EnchantmentRarity
import com.willfp.ecoenchants.type.EnchantmentType
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
interface EcoEnchantLike {
/**
* The config.
*/
val config: Config
/**
* The plugin.
*/
val plugin: EcoEnchantsPlugin
/**
* The bukkit enchantment.
*/
val enchantment: Enchantment
/**
* The raw display name.
*/
val rawDisplayName: String
/**
* The enchantment type.
*/
val type: EnchantmentType
/**
* The enchantment rarity.
*/
val rarity: EnchantmentRarity
/**
* Get if this enchantment can be applied to [item].
*/
fun canEnchantItem(item: ItemStack): Boolean {
if (
item.fast().getEnchants(true).keys
.map { it.wrap() }
.count { it.type == this.type } >= this.type.limit
) {
return false
}
if (item.fast().getEnchants(true).any { (enchant, _) -> enchant.conflictsWithDeep(this.enchantment) }) {
return false
}
if (item.fast().getEnchants(true).size >= plugin.configYml.getInt("anvil.enchant-limit").infiniteIfNegative()) {
return false
}
if (item.type == Material.ENCHANTED_BOOK) {
return true
}
return enchantment.canEnchantItem(item)
}
/**
* Get the raw description for the enchantment.
*/
fun getRawDescription(level: Int, player: Player?): String {
// Fetch custom placeholders other than %placeholder%
val uncompiledPlaceholders = config.getSubsection("placeholders").getKeys(false).associateWith {
config.getString("placeholders.$it")
}.toMutableMap()
// Add %placeholder% placeholder in
uncompiledPlaceholders["placeholder"] = config.getString("placeholder")
// Evaluate each placeholder
val placeholders = uncompiledPlaceholders.map { (id, expr) ->
DescriptionPlaceholder(
id,
NumberUtils.evaluateExpression(
expr,
placeholderContext(
player = player,
injectable = object : PlaceholderInjectable {
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
return listOf(
StaticPlaceholder(
"level",
) { level.toString() }
)
}
override fun addInjectablePlaceholder(p0: MutableIterable<InjectablePlaceholder>) {
// Do nothing
}
override fun clearInjectedPlaceholders() {
// Do nothing
}
}
)
)
)
}
// Apply placeholders to description
val rawDescription = config.getString("description")
var description = rawDescription
for (placeholder in placeholders) {
description = description.replace("%${placeholder.id}%", NumberUtils.format(placeholder.value))
}
return description
}
}

View File

@ -0,0 +1,80 @@
package com.willfp.ecoenchants.enchant
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.impl.LibreforgeEcoEnchant
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentPermanenceCurse
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentRepairing
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentReplenish
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentSoulbound
import com.willfp.ecoenchants.enchants.sendPrompts
import com.willfp.ecoenchants.integrations.EnchantRegistrations
import com.willfp.ecoenchants.rarity.EnchantmentRarities
import com.willfp.ecoenchants.target.EnchantmentTargets
import com.willfp.ecoenchants.type.EnchantmentTypes
import com.willfp.libreforge.loader.LibreforgePlugin
import com.willfp.libreforge.loader.configs.RegistrableCategory
@Suppress("UNUSED")
object EcoEnchants : RegistrableCategory<EcoEnchant>("enchant", "enchants") {
override fun clear(plugin: LibreforgePlugin) {
plugin as EcoEnchantsPlugin
for (enchant in registry.values()) {
plugin.enchantmentRegisterer.unregister(enchant)
EnchantRegistrations.removeEnchant(enchant)
}
registry.clear()
}
override fun beforeReload(plugin: LibreforgePlugin) {
plugin as EcoEnchantsPlugin
EnchantmentRarities.update(plugin)
EnchantmentTargets.update(plugin)
EnchantmentTypes.update(plugin)
}
override fun afterReload(plugin: LibreforgePlugin) {
plugin as EcoEnchantsPlugin
sendPrompts(plugin)
registerHardcodedEnchantments(plugin)
}
override fun acceptConfig(plugin: LibreforgePlugin, id: String, config: Config) {
plugin as EcoEnchantsPlugin
val enchant = createEnchantment(plugin, id, config) ?: return
registry.register(enchant)
plugin.enchantmentRegisterer.register(enchant)
EnchantRegistrations.registerEnchantments()
}
private fun createEnchantment(plugin: LibreforgePlugin, id: String, config: Config): EcoEnchant? {
plugin as EcoEnchantsPlugin
if (config.has("effects")) {
// Libreforge
return LibreforgeEcoEnchant(
id,
config,
plugin
)
} else {
TODO()
}
}
/** Register the hardcoded enchantments. */
private fun registerHardcodedEnchantments(
plugin: EcoEnchantsPlugin
) {
EnchantmentPermanenceCurse(plugin)
EnchantmentRepairing(plugin)
EnchantmentReplenish(plugin)
EnchantmentSoulbound(plugin)
}
}

View File

@ -0,0 +1,23 @@
package com.willfp.ecoenchants.enchant
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.impl.VanillaEcoEnchantLike
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
private val ecoEnchantLikes = mutableMapOf<NamespacedKey, EcoEnchantLike>()
fun Enchantment.wrap(): EcoEnchantLike {
if (this is EcoEnchant) {
return this
}
return ecoEnchantLikes.getOrPut(this.key) {
VanillaEcoEnchantLike(this, EcoEnchantsPlugin.instance)
}
}
fun Enchantment.conflictsWithDeep(other: Enchantment): Boolean {
return this.conflictsWith(other) || other.conflictsWith(this)
}

View File

@ -0,0 +1,74 @@
package com.willfp.ecoenchants.enchant.impl
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchant.EcoEnchantLevel
import com.willfp.ecoenchants.rarity.EnchantmentRarities
import com.willfp.ecoenchants.rarity.EnchantmentRarity
import com.willfp.ecoenchants.target.EnchantmentTargets
import com.willfp.ecoenchants.type.EnchantmentType
import com.willfp.ecoenchants.type.EnchantmentTypes
import com.willfp.libreforge.ViolationContext
import com.willfp.libreforge.conditions.Conditions
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
abstract class EcoEnchantBase(
final override val id: String,
final override val plugin: EcoEnchantsPlugin,
) : EcoEnchant {
protected val context = ViolationContext(plugin, "enchantment $id")
final override val config by lazy { loadConfig() }
private val levels = mutableMapOf<Int, EcoEnchantLevel>()
private val conflictIds = config.getStrings("conflicts").toSet()
override val key = NamespacedKey.minecraft(id)
override val rawDisplayName = config.getString("display-name")
override val maxLevel = config.getInt("max-level")
override val targets = config.getStrings("targets")
.mapNotNull { EnchantmentTargets[it] }
.toSet()
override val type: EnchantmentType = config.getString("type")
.let { EnchantmentTypes[it] }
?: EnchantmentTypes.values().first()
override val rarity: EnchantmentRarity = config.getString("rarity")
.let { EnchantmentRarities[it] }
?: EnchantmentRarities.values().first()
override val conditions = Conditions.compile(
config.getSubsections("conditions"),
context.with("conditions")
)
override val isEnchantable = config.getBool("enchantable")
override val isTradeable = config.getBool("tradeable")
override val isDiscoverable = config.getBool("discoverable")
/**
* Load the config for this enchant.
*/
protected abstract fun loadConfig(): Config
override fun getLevel(level: Int): EcoEnchantLevel {
return levels.getOrPut(level) {
createLevel(level)
}
}
protected abstract fun createLevel(level: Int): EcoEnchantLevel
override fun conflictsWithDirectly(other: Enchantment): Boolean {
return other.key.key in conflictIds
}
}

View File

@ -0,0 +1,25 @@
package com.willfp.ecoenchants.enchant.impl
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.readConfig
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.EcoEnchantLevel
import com.willfp.libreforge.effects.emptyEffectList
import java.io.File
abstract class HardcodedEcoEnchant(
id: String,
plugin: EcoEnchantsPlugin
) : EcoEnchantBase(id, plugin) {
final override fun loadConfig(): Config {
return File(plugin.dataFolder, "enchants")
.walk()
.firstOrNull { file -> file.nameWithoutExtension == id }
.readConfig(ConfigType.YAML)
}
override fun createLevel(level: Int): EcoEnchantLevel {
return EcoEnchantLevel(this, level, emptyEffectList(), conditions, plugin)
}
}

View File

@ -0,0 +1,26 @@
package com.willfp.ecoenchants.enchant.impl
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.EcoEnchantLevel
import com.willfp.libreforge.SilentViolationContext
import com.willfp.libreforge.effects.Effects
class LibreforgeEcoEnchant(
id: String,
private val _config: Config,
plugin: EcoEnchantsPlugin
) : EcoEnchantBase(id, plugin) {
private val effects = Effects.compile(
config.getSubsections("effects"),
if (plugin.isLoaded) context.with("effects") else SilentViolationContext
)
override fun loadConfig(): Config {
return _config
}
override fun createLevel(level: Int): EcoEnchantLevel {
return EcoEnchantLevel(this, level, effects, conditions, plugin)
}
}

View File

@ -0,0 +1,39 @@
package com.willfp.ecoenchants.enchant.impl
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.EcoEnchantLike
import com.willfp.ecoenchants.rarity.EnchantmentRarities
import com.willfp.ecoenchants.rarity.EnchantmentRarity
import com.willfp.ecoenchants.type.EnchantmentType
import com.willfp.ecoenchants.type.EnchantmentTypes
import org.bukkit.enchantments.Enchantment
import java.util.Objects
class VanillaEcoEnchantLike(
override val enchantment: Enchantment,
override val plugin: EcoEnchantsPlugin
) : EcoEnchantLike {
override val config = plugin.vanillaEnchantsYml.getSubsection(enchantment.key.key)
override val type: EnchantmentType =
EnchantmentTypes[plugin.vanillaEnchantsYml.getString("${enchantment.key.key}.type")]
?: EnchantmentTypes.values().first()
override val rarity: EnchantmentRarity =
EnchantmentRarities[plugin.vanillaEnchantsYml.getString("${enchantment.key.key}.rarity")]
?: EnchantmentRarities.values().first()
override val rawDisplayName = plugin.vanillaEnchantsYml.getString("${enchantment.key.key}.name")
override fun equals(other: Any?): Boolean {
if (other !is VanillaEcoEnchantLike) {
return false
}
return this.enchantment == other.enchantment
}
override fun hashCode(): Int {
return Objects.hash(this.enchantment)
}
}

View File

@ -0,0 +1,13 @@
package com.willfp.ecoenchants.enchant.impl.hardcoded
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchant.impl.HardcodedEcoEnchant
class EnchantmentPermanenceCurse(
plugin: EcoEnchantsPlugin
) : HardcodedEcoEnchant(
"permanence_curse",
plugin
) {
}

View File

@ -1,4 +1,4 @@
package com.willfp.ecoenchants.enchants.impl
package com.willfp.ecoenchants.enchant.impl.hardcoded
import com.willfp.eco.util.DurabilityUtils
import com.willfp.ecoenchants.EcoEnchantsPlugin

View File

@ -1,4 +1,4 @@
package com.willfp.ecoenchants.enchants.impl
package com.willfp.ecoenchants.enchant.impl.hardcoded
import com.willfp.eco.core.EcoPlugin
import com.willfp.ecoenchants.EcoEnchantsPlugin

View File

@ -1,4 +1,4 @@
package com.willfp.ecoenchants.enchants.impl
package com.willfp.ecoenchants.enchant.impl.hardcoded
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.Prerequisite

View File

@ -0,0 +1,9 @@
package com.willfp.ecoenchants.enchant.registration
import com.willfp.ecoenchants.enchant.EcoEnchant
interface EnchantmentRegisterer {
fun register(enchant: EcoEnchant)
fun unregister(enchant: EcoEnchant)
}

View File

@ -0,0 +1,87 @@
package com.willfp.ecoenchants.enchant.registration.legacy
import com.willfp.eco.util.StringUtils
import com.willfp.ecoenchants.display.getFormattedName
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchants.wrap
import io.papermc.paper.enchantments.EnchantmentRarity
import net.kyori.adventure.text.Component
import org.bukkit.enchantments.Enchantment
import org.bukkit.enchantments.EnchantmentTarget
import org.bukkit.entity.EntityCategory
import org.bukkit.inventory.EquipmentSlot
@Suppress("DEPRECATION")
class LegacyDelegatedEnchantment(
private val enchant: EcoEnchant
) : Enchantment(enchant.key), EcoEnchant by enchant {
override fun translationKey(): String {
return "ecoenchants:enchantment.$id"
}
@Deprecated(
message = "getName is a legacy Spigot API",
replaceWith = ReplaceWith("this.displayName(level)")
)
override fun getName(): String = this.id.uppercase()
override fun getMaxLevel(): Int {
return maxLevel
}
override fun getStartLevel(): Int {
return 1
}
@Deprecated(
message = "getItemTargets is an incompatible Spigot API",
replaceWith = ReplaceWith("this.targets")
)
override fun getItemTarget(): EnchantmentTarget = EnchantmentTarget.ALL
@Deprecated(
message = "Treasure enchantments do not exist in EcoEnchants",
replaceWith = ReplaceWith("this.isEnchantable")
)
override fun isTreasure(): Boolean = !isEnchantable
@Deprecated(
message = "Use EnchantmentType instead",
replaceWith = ReplaceWith("type.id")
)
override fun isCursed(): Boolean {
return false
}
override fun displayName(level: Int): Component {
return StringUtils.toComponent(this.wrap().getFormattedName(level))
}
override fun isTradeable(): Boolean {
return isTradeable
}
override fun isDiscoverable(): Boolean {
return isDiscoverable
}
@Deprecated(
message = "EcoEnchants uses a custom system for enchantment rarity",
replaceWith = ReplaceWith("this.enchantRarity")
)
override fun getRarity(): EnchantmentRarity {
return EnchantmentRarity.RARE
}
@Deprecated(
message = "EcoEnchants do not have damage increase, this method is for sharpness/boa/smite",
replaceWith = ReplaceWith("0.0f")
)
override fun getDamageIncrease(level: Int, entityCategory: EntityCategory): Float = 0.0f
@Deprecated(
message = "getActiveSlots is an incompatible Paper API",
replaceWith = ReplaceWith("this.slots")
)
override fun getActiveSlots() = emptySet<EquipmentSlot>()
}

View File

@ -0,0 +1,33 @@
package com.willfp.ecoenchants.enchant.registration.legacy
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchant.registration.EnchantmentRegisterer
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
@Suppress("UNCHECKED_CAST")
object LegacyEnchantmentRegisterer : EnchantmentRegisterer {
override fun register(enchant: EcoEnchant) {
Enchantment::class.java.getDeclaredField("acceptingNew")
.apply {
isAccessible = true
set(null, true)
}
Enchantment.registerEnchantment(LegacyDelegatedEnchantment(enchant))
}
override fun unregister(enchant: EcoEnchant) {
Enchantment::class.java.getDeclaredField("byKey")
.apply {
isAccessible = true
(get(null) as MutableMap<NamespacedKey, Enchantment>).apply { remove(enchant.key) }
}
Enchantment::class.java.getDeclaredField("byName")
.apply {
isAccessible = true
(get(null) as MutableMap<String, Enchantment>).apply { remove(enchant.id.uppercase()) }
}
}
}

View File

@ -0,0 +1,6 @@
package com.willfp.ecoenchants.enchant.registration.modern
import com.willfp.ecoenchants.enchant.registration.EnchantmentRegisterer
interface ModernEnchantmentRegistererProxy : EnchantmentRegisterer {
}

View File

@ -9,6 +9,7 @@ import com.willfp.eco.core.fast.fast
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.templates.SimpleInjectablePlaceholder
import com.willfp.eco.core.registry.KRegistrable
import com.willfp.eco.util.StringUtils
import com.willfp.eco.util.containsIgnoreCase
import com.willfp.ecoenchants.EcoEnchantsPlugin
@ -41,10 +42,10 @@ import java.util.Objects
@Suppress("DEPRECATION")
abstract class EcoEnchant(
val id: String,
override val id: String,
configProvider: (EcoEnchant) -> Config,
protected val plugin: EcoEnchantsPlugin
) : Enchantment(NamespacedKey.minecraft(id)), EcoEnchantLike {
) : Enchantment(NamespacedKey.minecraft(id)), EcoEnchantLike, KRegistrable {
final override val config by lazy { configProvider(this) }
override val enchant by lazy { this }

View File

@ -33,59 +33,6 @@ interface EcoEnchantLike {
// Includes all extra logic not found in vanilla canEnchantItem
fun canEnchantItem(item: ItemStack): Boolean
// Method body goes here
fun getUnformattedDescription(level: Int, player: Player? = null): String {
// Fetch custom placeholders other than %placeholder%
val uncompiledPlaceholders = config.getSubsection("placeholders").getKeys(false).associateWith {
config.getString("placeholders.$it")
}.toMutableMap()
// Add %placeholder% placeholder in
uncompiledPlaceholders["placeholder"] = config.getString("placeholder")
// Evaluate each placeholder
val placeholders = uncompiledPlaceholders.map { (id, expr) ->
DescriptionPlaceholder(
id,
NumberUtils.evaluateExpression(
expr,
placeholderContext(
player = player,
injectable = object : PlaceholderInjectable {
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
return listOf(
StaticPlaceholder(
"level",
) { level.toString() }
)
}
override fun addInjectablePlaceholder(p0: MutableIterable<InjectablePlaceholder>) {
// Do nothing
}
override fun clearInjectedPlaceholders() {
// Do nothing
}
}
)
)
)
}
// Apply placeholders to description
val rawDescription = config.getString("description")
var description = rawDescription
for (placeholder in placeholders) {
description = description.replace("%${placeholder.id}%", NumberUtils.format(placeholder.value))
}
return description
}
// Java backwards compatibility
fun getUnformattedDescription(level: Int): String {
return getUnformattedDescription(level, null)

View File

@ -1,90 +1,22 @@
package com.willfp.ecoenchants.enchants
import com.google.common.collect.HashBiMap
import com.google.common.collect.ImmutableSet
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchants.impl.EnchantmentPermanenceCurse
import com.willfp.ecoenchants.enchants.impl.EnchantmentRepairing
import com.willfp.ecoenchants.enchants.impl.EnchantmentReplenish
import com.willfp.ecoenchants.enchants.impl.EnchantmentSoulbound
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentPermanenceCurse
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentRepairing
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentReplenish
import com.willfp.ecoenchants.enchant.impl.hardcoded.EnchantmentSoulbound
import com.willfp.ecoenchants.integrations.EnchantRegistrations
import com.willfp.ecoenchants.rarity.EnchantmentRarities
import com.willfp.ecoenchants.target.EnchantmentTargets
import com.willfp.ecoenchants.type.EnchantmentTypes
import com.willfp.libreforge.loader.LibreforgePlugin
import com.willfp.libreforge.loader.configs.ConfigCategory
import org.bukkit.ChatColor
import com.willfp.libreforge.loader.configs.RegistrableCategory
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
@Suppress("UNUSED")
object EcoEnchants : ConfigCategory("enchant", "enchants") {
private val BY_KEY = HashBiMap.create<String, EcoEnchant>()
private val BY_NAME = HashBiMap.create<String, EcoEnchant>()
override val shouldPreload = true
/**
* Get all registered [EcoEnchant]s.
*
* @return A list of all [EcoEnchant]s.
*/
@JvmStatic
fun values(): Set<EcoEnchant> {
return ImmutableSet.copyOf(BY_KEY.values)
}
/**
* Get [String]s for all registered [EcoEnchant]s.
*
* @return A list of all [EcoEnchant]s.
*/
@JvmStatic
fun keySet(): Set<String> {
return ImmutableSet.copyOf(BY_KEY.keys)
}
/**
* Get [EcoEnchant] matching id.
*
* @param id The id to search for.
* @return The matching [EcoEnchant], or null if not found.
*/
@JvmStatic
fun getByID(id: String?): EcoEnchant? {
return if (id == null) {
null
} else BY_KEY[id]
}
/**
* Get [EcoEnchant] matching key.
*
* @param key The key to search for.
* @return The matching [EcoEnchant], or null if not found.
*/
@JvmStatic
fun getByKey(key: NamespacedKey?): EcoEnchant? {
return if (key == null) {
null
} else getByID(key.key)
}
/**
* Get [EcoEnchant] matching name.
*
* @param name The name to search for.
* @return The matching [EcoEnchant], or null if not found.
*/
@JvmStatic
fun getByName(name: String?): EcoEnchant? {
return if (name == null) {
null
} else BY_NAME[name]
}
object EcoEnchants : RegistrableCategory<EcoEnchant>("enchant", "enchants") {
override fun clear(plugin: LibreforgePlugin) {
for (enchant in values()) {
removeEnchant(enchant)
@ -135,8 +67,7 @@ object EcoEnchants : ConfigCategory("enchant", "enchants") {
@Suppress("UNCHECKED_CAST", "DEPRECATION")
fun removeEnchant(enchant: Enchantment) {
if (enchant is EcoEnchant) {
BY_KEY.remove(enchant.id)
BY_NAME.remove(ChatColor.stripColor(enchant.displayName))
registry.remove(enchant.id)
EnchantRegistrations.removeEnchant(enchant)
}
@ -163,9 +94,6 @@ object EcoEnchants : ConfigCategory("enchant", "enchants") {
*/
internal fun addNewEnchant(enchant: EcoEnchant) {
register(enchant)
BY_KEY[enchant.id] = enchant
BY_NAME[ChatColor.stripColor(enchant.displayName)] = enchant
}
/**

View File

@ -1,12 +0,0 @@
package com.willfp.ecoenchants.enchants.impl
import com.willfp.ecoenchants.EcoEnchantsPlugin
import com.willfp.ecoenchants.enchants.EcoEnchant
class EnchantmentPermanenceCurse(
plugin: EcoEnchantsPlugin
) : EcoEnchant(
"permanence_curse",
plugin,
force = false
)

View File

@ -1,8 +1,7 @@
package com.willfp.ecoenchants.integrations
import com.willfp.eco.core.integrations.Integration
import com.willfp.ecoenchants.enchants.EcoEnchant
import org.jetbrains.annotations.NotNull
import com.willfp.ecoenchants.enchant.EcoEnchant
interface EnchantRegistrationIntegration : Integration {

View File

@ -1,10 +1,9 @@
package com.willfp.ecoenchants.integrations.plugins
import com.earth2me.essentials.Enchantments
import com.willfp.ecoenchants.enchants.EcoEnchant
import com.willfp.ecoenchants.enchants.EcoEnchants
import com.willfp.ecoenchants.enchant.EcoEnchant
import com.willfp.ecoenchants.enchant.EcoEnchants
import com.willfp.ecoenchants.integrations.EnchantRegistrationIntegration
import org.bukkit.NamespacedKey
import org.bukkit.enchantments.Enchantment
@Suppress("UNCHECKED_CAST")
@ -18,8 +17,8 @@ class EssentialsIntegration: EnchantRegistrationIntegration {
.apply {
isAccessible = true
(get(null) as MutableMap<String, Enchantment>).apply {
put(enchantment.id, enchantment)
put(enchantment.id.replace("_",""), enchantment)
put(enchantment.id, enchantment.enchantment)
put(enchantment.id.replace("_", ""), enchantment.enchantment)
}
}
}

View File

@ -3,6 +3,7 @@ pluginManagement {
gradlePluginPortal()
mavenLocal()
maven("https://repo.auxilor.io/repository/maven-public/")
maven("https://repo.papermc.io/repository/maven-public/")
}
}
@ -21,3 +22,4 @@ include(":eco-core:core-nms:v1_19_R2")
include(":eco-core:core-nms:v1_19_R3")
include(":eco-core:core-nms:v1_20_R1")
include(":eco-core:core-nms:v1_20_R2")
include(":eco-core:core-nms:v1_20_R3")