getId(); $variant = $item->getDamage(); if(!$override and self::isRegistered($id, $variant)){ throw new \RuntimeException("Trying to overwrite an already registered item"); } $offset = self::getListOffset($id); $sublist = self::$list[$offset] ?? new \SplFixedArray(); if($sublist->getSize() < $variant + 1){ $sublist->setSize($variant + 1); } $sublist[$variant] = clone $item; self::$list[$offset] = $sublist; } /** * Returns an instance of the Item with the specified id, meta, count and NBT. * * @param int $id * @param int $meta * @param int $count * @param CompoundTag|string $tags * * @return Item * @throws \TypeError */ public static function get(int $id, int $meta = 0, int $count = 1, $tags = "") : Item{ if(!is_string($tags) and !($tags instanceof CompoundTag)){ throw new \TypeError("`tags` argument must be a string or CompoundTag instance, " . (is_object($tags) ? "instance of " . get_class($tags) : gettype($tags)) . " given"); } try{ $sublist = self::$list[self::getListOffset($id)]; /** @var Item|null $listed */ if($sublist !== null and isset($sublist[$meta])){ $item = clone $sublist[$meta]; }elseif($id < 256){ //intentionally includes negatives, for extended block IDs /* Blocks must have a damage value 0-15, but items can have damage value -1 to indicate that they are * crafting ingredients with any-damage. */ $item = new ItemBlock($id, $meta); }else{ //negative damage values will fallthru to here, to avoid crazy shit with crafting wildcard hacks $item = new Item($id, $meta); } }catch(\RuntimeException $e){ throw new \InvalidArgumentException("Item ID $id is invalid or out of bounds"); } $item->setCount($count); $item->setCompoundTag($tags); if($item instanceof Durable){ //nasty, but necessary for BC reasons $item->setDamage($meta); } return $item; } /** * Tries to parse the specified string into Item ID/meta identifiers, and returns Item instances it created. * * Example accepted formats: * - `diamond_pickaxe:5` * - `minecraft:string` * - `351:4 (lapis lazuli ID:meta)` * * If multiple item instances are to be created, their identifiers must be comma-separated, for example: * `diamond_pickaxe,wooden_shovel:18,iron_ingot` * * @param string $str * @param bool $multiple * * @return Item[]|Item * * @throws \InvalidArgumentException if the given string cannot be parsed as an item identifier */ public static function fromString(string $str, bool $multiple = false){ if($multiple){ $blocks = []; foreach(explode(",", $str) as $b){ $blocks[] = self::fromString($b, false); } return $blocks; }else{ $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); if(!isset($b[1])){ $meta = 0; }elseif(is_numeric($b[1])){ $meta = (int) $b[1]; }else{ throw new \InvalidArgumentException("Unable to parse \"" . $b[1] . "\" from \"" . $str . "\" as a valid meta value"); } if(is_numeric($b[0])){ $item = self::get((int) $b[0], $meta); }elseif(defined(ItemIds::class . "::" . strtoupper($b[0]))){ $item = self::get(constant(ItemIds::class . "::" . strtoupper($b[0])), $meta); }else{ throw new \InvalidArgumentException("Unable to resolve \"" . $str . "\" to a valid item"); } return $item; } } /** * Returns whether the specified item ID is already registered in the item factory. * * @param int $id * @param int $variant * * @return bool */ public static function isRegistered(int $id, int $variant = 0) : bool{ if($id < 256){ return BlockFactory::isRegistered($id); } $sublist = self::$list[self::getListOffset($id)]; return $sublist !== null and isset($sublist[$variant]); } private static function getListOffset(int $id) : int{ if($id < -0x8000 or $id > 0x7fff){ throw new \InvalidArgumentException("ID must be in range " . -0x8000 . " - " . 0x7fff); } return $id & 0xffff; } }