All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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