Skip to content

Commit

Permalink
feat: add all new 1.20.6 item-properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed May 11, 2024
1 parent 9d00e2f commit fff4f47
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.mineinabyss.idofront.serialization

import com.mineinabyss.idofront.time.inWholeTicks
import com.mineinabyss.idofront.time.ticks
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.components.FoodComponent
import org.bukkit.inventory.meta.components.FoodComponent.FoodEffect
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit

@Serializable
@SerialName("FoodComponent")
private class FoodComponentSurrogate(
val nutrition: Int,
val saturation: Float,
val eatSeconds: Float,
val canAlwaysEat: Boolean,
val effects: List<FoodEffectWrapper>
) {
init {
require(nutrition >= 0) { "FoodComponent must have a positive nutrition" }
require(saturation >= 0) { "FoodComponent must have a positive saturation" }
require(eatSeconds >= 0) { "FoodComponent must have a positive eatSeconds" }
require(effects.all { it.probability in 0.0..1.0 }) { "FoodEffect-probability must be between 0.0..1.0" }
}
}

object FoodComponentSerializer : KSerializer<FoodComponent> {
override val descriptor: SerialDescriptor = FoodComponentSurrogate.serializer().descriptor
override fun serialize(encoder: Encoder, value: FoodComponent) {
val surrogate = FoodComponentSurrogate(
value.nutrition,
value.saturation,
value.eatSeconds,
value.canAlwaysEat(),
value.effects.map { FoodEffectWrapper(it.effect, it.probability) }
)
encoder.encodeSerializableValue(FoodComponentSurrogate.serializer(), surrogate)
}

override fun deserialize(decoder: Decoder): FoodComponent {
return ItemStack(Material.PAPER).itemMeta.food.apply {
val surrogate = decoder.decodeSerializableValue(FoodComponentSurrogate.serializer())
nutrition = surrogate.nutrition
saturation = surrogate.saturation
setCanAlwaysEat(surrogate.canAlwaysEat)
eatSeconds = surrogate.eatSeconds
surrogate.effects.forEach { addEffect(it.effect, it.probability) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mineinabyss.idofront.serialization

import kotlinx.serialization.Serializable
import org.bukkit.potion.PotionEffect

@Serializable
class FoodEffectWrapper(val effect: @Serializable(PotionEffectSerializer::class) PotionEffect, val probability: Float)
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

package com.mineinabyss.idofront.serialization

import com.google.common.collect.HashMultimap
import com.mineinabyss.idofront.di.DI
import com.mineinabyss.idofront.messaging.idofrontLogger
import com.mineinabyss.idofront.messaging.logWarn
import com.mineinabyss.idofront.plugin.Plugins
import dev.lone.itemsadder.api.CustomStack
import io.lumine.mythiccrucible.MythicCrucible
Expand All @@ -18,14 +16,14 @@ import kotlinx.serialization.UseSerializers
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.*
import org.bukkit.attribute.Attribute
import org.bukkit.attribute.AttributeModifier
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemRarity
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.KnowledgeBookMeta
import org.bukkit.inventory.meta.LeatherArmorMeta
import org.bukkit.inventory.meta.PotionMeta
import org.bukkit.inventory.meta.components.FoodComponent
import org.bukkit.potion.PotionType
import java.util.*

Expand All @@ -45,18 +43,29 @@ data class BaseSerializableItemStack(
@EncodeDefault(NEVER) val type: @Serializable(with = MaterialByNameSerializer::class) Material? = null,
@EncodeDefault(NEVER) val amount: Int? = null,
@EncodeDefault(NEVER) val customModelData: Int? = null,
@EncodeDefault(NEVER) val displayName: Component? = null,
@EncodeDefault(NEVER) val itemName: Component? = null,
// This is private as we only want to use itemName in configs
@EncodeDefault(NEVER) private val displayName: Component? = null,
@EncodeDefault(NEVER) val lore: List<Component>? = null,
@EncodeDefault(NEVER) val unbreakable: Boolean? = null,
@EncodeDefault(NEVER) val damage: Int? = null,
@EncodeDefault(NEVER) val durability: Int? = null,
@EncodeDefault(NEVER) val prefab: String? = null,
@EncodeDefault(NEVER) val enchantments: List<SerializableEnchantment>? = null,
@EncodeDefault(NEVER) val itemFlags: List<ItemFlag>? = null,
@EncodeDefault(NEVER) val attributeModifiers: List<SerializableAttribute>? = null,
@EncodeDefault(NEVER) val potionType: @Serializable(with = PotionTypeSerializer::class) PotionType? = null,
@EncodeDefault(NEVER) val knowledgeBookRecipes: List<String>? = null,
@EncodeDefault(NEVER) val color: @Serializable(with = ColorSerializer::class) Color? = null,
@EncodeDefault(NEVER) val food: @Serializable(with = FoodComponentSerializer::class) FoodComponent? = null,
@EncodeDefault(NEVER) val hideTooltips: Boolean? = null,
@EncodeDefault(NEVER) val isFireResistant: Boolean? = null,
@EncodeDefault(NEVER) val enchantmentGlintOverride: Boolean? = null,
@EncodeDefault(NEVER) val maxStackSize: Int? = null,
@EncodeDefault(NEVER) val rarity: ItemRarity? = null,
@EncodeDefault(NEVER) val tag: String? = null,

// Third-party plugins
@EncodeDefault(NEVER) val crucibleItem: String? = null,
@EncodeDefault(NEVER) val oraxenItem: String? = null,
@EncodeDefault(NEVER) val itemsadderItem: String? = null,
Expand All @@ -76,7 +85,7 @@ data class BaseSerializableItemStack(
crucibleItem?.let { id ->
if (Plugins.isEnabled<MythicCrucible>()) {
MythicCrucible.core().itemManager.getItemStack(id)?.let {
applyTo.type = it.type
applyTo.withType(it.type)
applyTo.itemMeta = it.itemMeta
} ?: idofrontLogger.w("No Crucible item found with id $id")
} else {
Expand All @@ -88,7 +97,7 @@ data class BaseSerializableItemStack(
oraxenItem?.let { id ->
if (Plugins.isEnabled<OraxenPlugin>()) {
OraxenItems.getItemById(id)?.build()?.let {
applyTo.type = it.type
applyTo.withType(it.type)
applyTo.itemMeta = it.itemMeta
} ?: idofrontLogger.w("No Oraxen item found with id $id")
} else {
Expand All @@ -100,7 +109,7 @@ data class BaseSerializableItemStack(
itemsadderItem?.let { id ->
if (Plugins.isEnabled("ItemsAdder")) {
CustomStack.getInstance(id)?.itemStack?.let {
applyTo.type = it.type
applyTo.withType(it.type)
applyTo.itemMeta = it.itemMeta
} ?: idofrontLogger.w("No ItemsAdder item found with id $id")
} else {
Expand All @@ -115,37 +124,46 @@ data class BaseSerializableItemStack(

// Modify item
amount?.takeIf { Properties.AMOUNT !in ignoreProperties }?.let { applyTo.amount = it }
type?.takeIf { Properties.TYPE !in ignoreProperties }?.let { applyTo.type = it }
type?.takeIf { Properties.TYPE !in ignoreProperties }?.let { applyTo.withType(it) }

// Modify meta
val meta = applyTo.itemMeta ?: return applyTo
customModelData?.takeIf { Properties.CUSTOM_MODEL_DATA !in ignoreProperties }
?.let { meta.setCustomModelData(it) }
itemName?.takeIf { Properties.ITEM_NAME !in ignoreProperties }
?.let { meta.itemName(it) }
displayName?.takeIf { Properties.DISPLAY_NAME !in ignoreProperties }
?.let { meta.displayName(it.removeItalics()) }
?.let { meta.displayName(it) }
unbreakable?.takeIf { Properties.UNBREAKABLE !in ignoreProperties }
?.let { meta.isUnbreakable = it }
lore?.takeIf { Properties.LORE !in ignoreProperties }
?.let { meta.lore(it.map { line -> line.removeItalics() }) }
if (meta is Damageable && Properties.DAMAGE !in ignoreProperties) this@BaseSerializableItemStack.damage?.let {
meta.damage = it
damage?.takeIf { meta is Damageable && Properties.DAMAGE !in ignoreProperties }?.let {
(meta as Damageable).damage = it
}
durability?.takeIf { meta is Damageable && Properties.DURABILITY !in ignoreProperties }?.let {
(meta as Damageable).setMaxDamage(it)
}
if (itemFlags?.isNotEmpty() == true && Properties.ITEM_FLAGS !in ignoreProperties) meta.addItemFlags(*itemFlags.toTypedArray())
if (color != null && Properties.COLOR !in ignoreProperties) (meta as? PotionMeta)?.setColor(color)
?: (meta as? LeatherArmorMeta)?.setColor(color)
if (potionType != null && Properties.POTION_TYPE !in ignoreProperties) (meta as? PotionMeta)?.basePotionType =
potionType
if (enchantments != null && Properties.ENCHANTMENTS !in ignoreProperties) {
if (color != null && Properties.COLOR !in ignoreProperties)
(meta as? PotionMeta)?.setColor(color) ?: (meta as? LeatherArmorMeta)?.setColor(color)
if (potionType != null && Properties.POTION_TYPE !in ignoreProperties)
(meta as? PotionMeta)?.basePotionType = potionType
if (enchantments != null && Properties.ENCHANTMENTS !in ignoreProperties)
enchantments.forEach { meta.addEnchant(it.enchant, it.level, true) }
}
if (knowledgeBookRecipes != null && Properties.KNOWLEDGE_BOOK_RECIPES !in ignoreProperties) {
if (knowledgeBookRecipes != null && Properties.KNOWLEDGE_BOOK_RECIPES !in ignoreProperties)
(meta as? KnowledgeBookMeta)?.recipes = knowledgeBookRecipes.map { it.getSubRecipeIDs() }.flatten()
}
if (attributeModifiers != null && Properties.ATTRIBUTE_MODIFIERS !in ignoreProperties) {
val newAttributeModifiers = HashMultimap.create<Attribute?, AttributeModifier?>()
attributeModifiers.forEach { newAttributeModifiers.put(it.attribute, it.modifier) }
meta.attributeModifiers = newAttributeModifiers
}
if (attributeModifiers != null && Properties.ATTRIBUTE_MODIFIERS !in ignoreProperties)
attributeModifiers.forEach { meta.addAttributeModifier(it.attribute, it.modifier) }

enchantmentGlintOverride?.takeIf { Properties.ENCHANTMENT_GLINT_OVERRIDE !in ignoreProperties }?.let { meta.setEnchantmentGlintOverride(it) }
if (Properties.FOOD !in ignoreProperties) meta.setFood(food)
if (Properties.ENCHANTMENT_GLINT_OVERRIDE !in ignoreProperties) meta.setEnchantmentGlintOverride(enchantmentGlintOverride)
if (Properties.MAX_STACK_SIZE !in ignoreProperties) meta.setMaxStackSize(maxStackSize)
rarity?.takeIf { Properties.ITEM_RARITY !in ignoreProperties }?.let { meta.setRarity(it) }
isFireResistant?.takeIf { Properties.FIRE_RESISTANT !in ignoreProperties }?.let { meta.isFireResistant = it }
hideTooltips?.takeIf { Properties.HIDE_TOOLTIPS !in ignoreProperties }?.let { meta.isHideTooltip = it }

applyTo.itemMeta = meta
return applyTo
}
Expand All @@ -158,17 +176,25 @@ data class BaseSerializableItemStack(
TYPE,
AMOUNT,
CUSTOM_MODEL_DATA,
ITEM_NAME,
DISPLAY_NAME,
LORE,
UNBREAKABLE,
DAMAGE,
DURABILITY,
PREFAB,
ENCHANTMENTS,
ITEM_FLAGS,
ATTRIBUTE_MODIFIERS,
POTION_TYPE,
KNOWLEDGE_BOOK_RECIPES,
COLOR,
FOOD,
HIDE_TOOLTIPS,
FIRE_RESISTANT,
ENCHANTMENT_GLINT_OVERRIDE,
MAX_STACK_SIZE,
ITEM_RARITY,
}

/** @return whether applying this [SerializableItemStack] to [item] would keep [item] identical. */
Expand All @@ -193,18 +219,27 @@ fun ItemStack.toSerializable(): SerializableItemStack = with(itemMeta) {
SerializableItemStack(
type = type,
amount = amount.takeIf { it != 1 },
customModelData = if (this.hasCustomModelData()) this.customModelData else null,
displayName = if (this.hasDisplayName()) this.displayName() else null,
unbreakable = this?.isUnbreakable.takeIf { it != null && it },
customModelData = if (hasCustomModelData()) customModelData else null,
itemName = if (hasItemName()) itemName() else null,
displayName = if (hasDisplayName()) displayName() else null,
unbreakable = isUnbreakable.takeIf { it },
lore = if (this.hasLore()) this.lore() else null,
damage = (this as? Damageable)?.takeIf { it.hasDamage() }?.damage,
durability = (this as? Damageable)?.takeIf { it.hasMaxDamage() }?.maxDamage,
enchantments = enchants.map { SerializableEnchantment(it.key, it.value) }.takeIf { it.isNotEmpty() },
knowledgeBookRecipes = ((this as? KnowledgeBookMeta)?.recipes?.map { it.getItemPrefabFromRecipe() }?.flatten()
?: emptyList()).takeIf { it.isNotEmpty() },
itemFlags = (this?.itemFlags?.toList() ?: listOf()).takeIf { it.isNotEmpty() },
attributeModifiers = attributeList.takeIf { it.isNotEmpty() },
potionType = (this as? PotionMeta)?.basePotionType,
color = (this as? PotionMeta)?.color ?: (this as? LeatherArmorMeta)?.color
color = (this as? PotionMeta)?.color ?: (this as? LeatherArmorMeta)?.color,
food = food.takeIf { hasFood() },
enchantmentGlintOverride = enchantmentGlintOverride.takeIf { hasEnchantmentGlintOverride() },
maxStackSize = maxStackSize.takeIf { hasMaxStackSize() },
rarity = rarity.takeIf { hasRarity() },
hideTooltips = isHideTooltip,
isFireResistant = isFireResistant.takeIf { it },

) //TODO perhaps this should encode prefab too?
}

Expand Down
Loading

0 comments on commit fff4f47

Please sign in to comment.