
org.bukkit.inventory.ItemStack.kt Maven / Gradle / Ivy
package org.bukkit.inventory
import com.google.common.collect.ImmutableMap
import io.github.uinnn.serializer.serial.ItemSerializer
import kotlinx.serialization.Serializable
import net.minecraft.server.NBTTagCompound
import org.apache.commons.lang.Validate
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.Utility
import org.bukkit.configuration.serialization.ConfigurationSerializable
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.material.MaterialData
/**
* A base for item stacks.
*/
abstract class ItemStackBase : Cloneable, ConfigurationSerializable, Comparable {
/**
* The backend property to refers to the type id of this item.
*/
internal var typeById: Int = 0
set(value) {
field = value
if (this.meta != null) {
this.meta = Bukkit.getItemFactory().asMetaFor(meta, getType0())
}
createData(0)
}
/**
* The backend property to refers to this item meta.
*/
internal var meta: ItemMeta? = null
/**
* The backend property to refers to the material data of this item.
*/
internal var materialData: MaterialData? = null
/**
* Returns the type id of this item stack.
*/
open var typeId: Int
get() = typeById
set(value) {
typeById = value
}
/**
* Gets the type of this item
*/
open var type: Material
get() = getType0(typeById)
set(value) {
typeById = value.id
}
/**
* Gets the amount stack of this item.
*/
open var amount: Int = 1
/**
* Gets the durability of this item
*/
open var durability: Short = 0
/**
* Gets the MaterialData for this stack of items
*/
open var data: MaterialData
get() {
val mat: Material = type
if (materialData == null && mat.data != null)
materialData = mat.getNewData(durability.toByte())
return materialData!!
}
set(value) {
val mat: Material = type
materialData = if (materialData == null || mat.data == null) value
else {
if (data.javaClass == mat.data || data.javaClass == MaterialData::class.java) value
else throw IllegalArgumentException("Provided data is not of type " + mat.data.name + ", found " + data.javaClass.name)
}
}
/**
* Gets a copy of this ItemStacks's [ItemMeta].
*/
open var itemMeta: ItemMeta
get() = meta ?: Bukkit.getItemFactory().getItemMeta(getType0())
set(value) {
setItemMeta0(value, getType0())
}
/**
* Set the ItemMeta of this ItemStack.
*/
open fun setSafeItemMeta(itemMeta: ItemMeta?): Boolean = setItemMeta0(itemMeta, getType0())
/**
* Checks to see if any meta data has been defined.
*/
open val hasItemMeta: Boolean
get() = !Bukkit.getItemFactory().equals(meta, null)
/**
* Checks to see if any meta data has been defined.
*/
open fun hasItemMeta(): Boolean = hasItemMeta
/**
* Get the maximum stacksize for the material hold in this ItemStack.
* (Returns -1 if it has no idea)
*/
open val maxStackSize: Int
get() = type.maxStackSize
/**
* Gets a map containing all enchantments and their levels on this item.
*/
open var enchantments: Map
get() = meta?.enchants ?: ImmutableMap.of()
set(value) = addUnsafeEnchantments(value)
/**
* Checks if this ItemStack contains the given [Enchantment]
*/
open fun containsEnchantment(ench: Enchantment): Boolean = meta != null && meta!!.hasEnchant(ench)
/**
* Gets the level of the specified enchantment on this item stack
*/
open fun getEnchantmentLevel(ench: Enchantment): Int = meta?.getEnchantLevel(ench) ?: 0
/**
* Adds the specified enchantments to this item stack.
*
* This method is the same as calling [addEnchantment] for each element of the map.
*/
@Utility
open fun addEnchantments(enchantments: Map) {
Validate.notNull(enchantments, "Enchantments cannot be null")
for ((key, value) in enchantments) {
addEnchantment(key, value)
}
}
/**
* Adds the specified [Enchantment] to this item stack.
*
* If this item stack already contained the given enchantment (at any level), it will be replaced.
*/
@Utility
open fun addEnchantment(ench: Enchantment, level: Int) {
Validate.notNull(ench, "Enchantment cannot be null")
require(!(level < ench.startLevel || level > ench.maxLevel)) { "Enchantment level is either too low or too high (given " + level + ", bounds are " + ench.startLevel + " to " + ench.maxLevel + ")" }
//require(ench.canEnchantItem(this)) { "Specified enchantment cannot be applied to this itemstack" }
addUnsafeEnchantment(ench, level)
}
/**
* Adds the specified enchantments to this item stack in an unsafe manner.
*
* This method is the same as calling [addUnsafeEnchantment] for
* each element of the map.
*/
@Utility
open fun addUnsafeEnchantments(enchantments: Map) {
for ((key, value) in enchantments) {
addUnsafeEnchantment(key, value)
}
}
/**
* Adds the specified [Enchantment] to this item stack.
*
* If this item stack already contained the given enchantment (at any level), it will be replaced.
*
* This method is unsafe and will ignore level restrictions or item type.
* Use at your own discretion.
*/
open fun addUnsafeEnchantment(ench: Enchantment, level: Int) {
if (meta == null)
meta = Bukkit.getItemFactory().getItemMeta(getType0())
meta!!.addEnchant(ench, level, true)
}
/**
* Removes the specified [Enchantment] if it exists on this ItemStack
*/
open fun removeEnchantment(ench: Enchantment): Int {
val level = getEnchantmentLevel(ench)
if (level == 0 || meta == null) {
return level
}
meta!!.removeEnchant(ench)
return level
}
override fun serialize(): MutableMap {
val result: MutableMap = LinkedHashMap()
result["type"] = type.name
if (durability.toInt() != 0)
result["damage"] = durability
if (amount != 1)
result["amount"] = amount
val meta: ItemMeta = itemMeta
if (!Bukkit.getItemFactory().equals(meta, null))
result["meta"] = meta
return result
}
/**
* This method is the same as equals, but does not consider stack size
* (amount).
*/
@Utility
open fun isSimilar(stack: ItemStack?): Boolean {
if (stack == null)
return false
return if (stack === this) true
else typeById == stack.typeId && durability == stack.durability
&& hasItemMeta() == stack.hasItemMeta()
&& (!hasItemMeta() || Bukkit.getItemFactory().equals(itemMeta, stack.itemMeta))
}
override fun compareTo(other: ItemStackBase): Int = amount.compareTo(other.amount)
internal fun getType0(id: Int): Material = Material.getMaterial(id) ?: Material.AIR
internal fun getType0(): Material = getType0(typeById)
internal fun createData(data: Byte) {
val mat: Material? = Material.getMaterial(typeById)
this.data = if (mat == null) MaterialData(type, data) else mat.getNewData(data)
}
internal fun setItemMeta0(itemMeta: ItemMeta?, material: Material): Boolean {
if (itemMeta == null) {
meta = null
return true
}
if (!Bukkit.getItemFactory().isApplicable(itemMeta, material)) {
return false
}
meta = Bukkit.getItemFactory().asMetaFor(itemMeta, material)
if (meta === itemMeta) {
meta = itemMeta.clone()
}
return true
}
}
/**
* Represents a stack of items
*/
@Serializable(ItemSerializer::class)
open class ItemStack internal constructor() : ItemStackBase() {
/**
* Returns the NMS handler of this item stack.
*/
val handler: net.minecraft.server.ItemStack
get() = CraftItemStack.asNMSCopy(this)
/**
* Gets the persistent NBT tag of this item stack.
*/
var tag: NBTTagCompound
get() = handler.tag ?: NBTTagCompound()
set(value) {
handler.tag = value
}
constructor(type: Material) : this(type.id)
constructor(type: Material, amount: Int) : this(type.id, amount)
constructor(type: Material, amount: Int, durability: Short) : this(type.id, amount, durability)
constructor(type: Int) : this(type, 1)
constructor(type: Int, amount: Int) : this(type, amount, 0)
constructor(type: Int, amount: Int, durability: Short) : this() {
this.typeById = type
this.amount = amount
this.durability = durability
}
constructor(type: Int, amount: Int, durability: Short, data: Byte?) : this() {
this.typeById = type
this.amount = amount
this.durability = durability
if (data != null) {
createData(data)
this.durability = data.toShort()
}
}
constructor(type: Material, amount: Int, durability: Short, data: Byte?) :
this(type.id, amount, durability, data)
constructor(stack: ItemStack) : this() {
typeById = stack.typeById
amount = stack.amount
durability = stack.durability
data = stack.data
if (stack.hasItemMeta)
setItemMeta0(stack.itemMeta, getType0())
}
@Utility
override fun addEnchantment(ench: Enchantment, level: Int) {
require(ench.canEnchantItem(this)) { "Specified enchantment cannot be applied to this itemstack" }
super.addEnchantment(ench, level)
}
@Utility
override fun toString(): String {
val toString = StringBuilder("ItemStack{").append(type.name).append(" x ").append(amount)
if (hasItemMeta())
toString.append(", ").append(itemMeta)
return toString.append('}').toString()
}
@Utility
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ItemStack) return false
return amount == other.amount && isSimilar(other)
}
public override fun clone(): ItemStack {
return try {
val itemStack = super.clone() as ItemStack
if (meta != null)
itemStack.meta = meta!!.clone()
if (materialData != null)
itemStack.data = data.clone()
itemStack
} catch (e: CloneNotSupportedException) {
throw Error(e)
}
}
@Utility
override fun hashCode(): Int {
var hash = 1
hash = hash * 31 + typeId
hash = hash * 31 + amount
hash = hash * 31 + (durability.toInt() and 0xffff)
hash = hash * 31 + if (hasItemMeta()) if (meta == null) itemMeta.hashCode() else meta.hashCode() else 0
return hash
}
companion object {
/**
* Required method for configuration serialization
*/
@JvmStatic
fun deserialize(args: Map): ItemStack {
val type = Material.getMaterial(args["type"] as String?)
var damage: Short = 0
var amount = 1
if (args.containsKey("damage"))
damage = (args["damage"] as Number?)!!.toShort()
if (args.containsKey("amount"))
amount = (args["amount"] as Number?)!!.toInt()
val result = ItemStack(type, amount, damage)
if (args.containsKey("enchantments")) { // Backward compatiblity, @deprecated
val raw = args["enchantments"]
if (raw is Map<*, *>) {
for ((key, value) in raw) {
val enchantment = Enchantment.getByName(key.toString())
if (enchantment != null && value is Int)
result.addUnsafeEnchantment(enchantment, (value as Int?)!!)
}
}
} else if (args.containsKey("meta")) { // We cannot and will not have meta when enchantments (pre-ItemMeta) exist
val raw = args["meta"]
if (raw is ItemMeta)
result.setSafeItemMeta(raw as ItemMeta?)
}
return result
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy