Skip to content

Commit

Permalink
Tutorial on how to register an item (real)
Browse files Browse the repository at this point in the history
  • Loading branch information
lightyisreal committed Oct 13, 2024
1 parent b844fa8 commit 42120e6
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 59 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"vscord.app.privacyMode.enable": true
}
71 changes: 42 additions & 29 deletions src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@
use pocketmine\item\SuspiciousStew;
use pocketmine\item\VanillaItems as Items;

final class ItemSerializerDeserializerRegistrar{
final class ItemSerializerDeserializerRegistrar
{

public function __construct(
private ?ItemDeserializer $deserializer,
private ?ItemSerializer $serializer
){
) {
$this->register1to1BlockMappings();
$this->register1to1ItemMappings();
$this->register1to1BlockWithMetaMappings();
Expand All @@ -60,7 +61,8 @@ public function __construct(
$this->registerMiscItemMappings();
}

public function map1to1Item(string $id, Item $item) : void{
public function map1to1Item(string $id, Item $item): void
{
$this->deserializer?->map($id, fn() => clone $item);
$this->serializer?->map($item, fn() => new Data($id));
}
Expand All @@ -71,20 +73,22 @@ public function map1to1Item(string $id, Item $item) : void{
* @phpstan-param \Closure(TItem, int) : void $deserializeMeta
* @phpstan-param \Closure(TItem) : int $serializeMeta
*/
public function map1to1ItemWithMeta(string $id, Item $item, \Closure $deserializeMeta, \Closure $serializeMeta) : void{
$this->deserializer?->map($id, function(Data $data) use ($item, $deserializeMeta) : Item{
public function map1to1ItemWithMeta(string $id, Item $item, \Closure $deserializeMeta, \Closure $serializeMeta): void
{
$this->deserializer?->map($id, function (Data $data) use ($item, $deserializeMeta): Item {
$result = clone $item;
$deserializeMeta($result, $data->getMeta());
return $result;
});
$this->serializer?->map($item, function(Item $item) use ($id, $serializeMeta) : Data{
$this->serializer?->map($item, function (Item $item) use ($id, $serializeMeta): Data {
/** @phpstan-var TItem $item */
$meta = $serializeMeta($item);
return new Data($id, $meta);
});
}

public function map1to1Block(string $id, Block $block) : void{
public function map1to1Block(string $id, Block $block): void
{
$this->deserializer?->mapBlock($id, fn() => $block);
$this->serializer?->mapBlock($block, fn() => new Data($id));
}
Expand All @@ -95,13 +99,14 @@ public function map1to1Block(string $id, Block $block) : void{
* @phpstan-param \Closure(TBlock, int) : void $deserializeMeta
* @phpstan-param \Closure(TBlock) : int $serializeMeta
*/
public function map1to1BlockWithMeta(string $id, Block $block, \Closure $deserializeMeta, \Closure $serializeMeta) : void{
$this->deserializer?->mapBlock($id, function(Data $data) use ($block, $deserializeMeta) : Block{
public function map1to1BlockWithMeta(string $id, Block $block, \Closure $deserializeMeta, \Closure $serializeMeta): void
{
$this->deserializer?->mapBlock($id, function (Data $data) use ($block, $deserializeMeta): Block {
$result = clone $block;
$deserializeMeta($result, $data->getMeta());
return $result;
});
$this->serializer?->mapBlock($block, function(Block $block) use ($id, $serializeMeta) : Data{
$this->serializer?->mapBlock($block, function (Block $block) use ($id, $serializeMeta): Data {
$meta = $serializeMeta($block);
return new Data($id, $meta);
});
Expand All @@ -111,15 +116,16 @@ public function map1to1BlockWithMeta(string $id, Block $block, \Closure $deseria
* @param Item[] $items
* @phpstan-param array<int, Item> $items
*/
public function map1ToNItem(string $id, array $items) : void{
$this->deserializer?->map($id, function(Data $data) use ($items) : Item{
public function map1ToNItem(string $id, array $items): void
{
$this->deserializer?->map($id, function (Data $data) use ($items): Item {
$result = $items[$data->getMeta()] ?? null;
if($result === null){
if ($result === null) {
throw new ItemTypeDeserializeException("Unhandled meta value " . $data->getMeta() . " for item ID " . $data->getName());
}
return clone $result;
});
foreach($items as $meta => $item){
foreach ($items as $meta => $item) {
$this->serializer?->map($item, fn() => new Data($id, $meta));
}
}
Expand All @@ -129,7 +135,8 @@ public function map1ToNItem(string $id, array $items) : void{
* Mappings here are only necessary when the item has a dedicated item ID; in these cases, the blockstate is not
* included in the itemstack, and the item ID may be different from the block ID.
*/
private function register1to1BlockMappings() : void{
private function register1to1BlockMappings(): void
{
$this->map1to1Block(Ids::ACACIA_DOOR, Blocks::ACACIA_DOOR());
$this->map1to1Block(Ids::BIRCH_DOOR, Blocks::BIRCH_DOOR());
$this->map1to1Block(Ids::BREWING_STAND, Blocks::BREWING_STAND());
Expand Down Expand Up @@ -158,7 +165,8 @@ private function register1to1BlockMappings() : void{
/**
* Registers mappings for item IDs which directly correspond to PocketMine-MP items.
*/
private function register1to1ItemMappings() : void{
private function register1to1ItemMappings(): void
{
$this->map1to1Item(Ids::ACACIA_BOAT, Items::ACACIA_BOAT());
$this->map1to1Item(Ids::ACACIA_SIGN, Items::ACACIA_SIGN());
$this->map1to1Item(Ids::AMETHYST_SHARD, Items::AMETHYST_SHARD());
Expand All @@ -179,6 +187,7 @@ private function register1to1ItemMappings() : void{
$this->map1to1Item(Ids::BOW, Items::BOW());
$this->map1to1Item(Ids::BOWL, Items::BOWL());
$this->map1to1Item(Ids::BREAD, Items::BREAD());
$this->map1to1Item(Ids::BREEZE_ROD, Items::BREEZE_ROD());
$this->map1to1Item(Ids::BRICK, Items::BRICK());
$this->map1to1Item(Ids::BUCKET, Items::BUCKET());
$this->map1to1Item(Ids::CARROT, Items::CARROT());
Expand Down Expand Up @@ -405,7 +414,8 @@ private function register1to1ItemMappings() : void{
* This can only be used if the target item type doesn't require any additional properties, since the items are
* indexed by their base type ID.
*/
private function register1ToNItemMappings() : void{
private function register1ToNItemMappings(): void
{
$this->map1ToNItem(Ids::ARROW, [
0 => Items::ARROW(),
//TODO: tipped arrows
Expand Down Expand Up @@ -457,19 +467,20 @@ private function register1ToNItemMappings() : void{
* TODO: try and make this less ugly; for the most part the logic is symmetrical, it's just difficult to write it
* in a unified manner.
*/
private function register1to1BlockWithMetaMappings() : void{
private function register1to1BlockWithMetaMappings(): void
{
$this->map1to1BlockWithMeta(
Ids::BED,
Blocks::BED(),
function(Bed $block, int $meta) : void{
function (Bed $block, int $meta): void {
$block->setColor(DyeColorIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown bed color ID $meta"));
},
fn(Bed $block) => DyeColorIdMap::getInstance()->toId($block->getColor())
);
$this->map1to1BlockWithMeta(
Ids::SKULL,
Blocks::MOB_HEAD(),
function(MobHead $block, int $meta) : void{
function (MobHead $block, int $meta): void {
$block->setMobHeadType(MobHeadTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown mob head type ID $meta"));
},
fn(MobHead $block) => MobHeadTypeIdMap::getInstance()->toId($block->getMobHeadType())
Expand All @@ -481,51 +492,52 @@ function(MobHead $block, int $meta) : void{
* TODO: try and make this less ugly; for the most part the logic is symmetrical, it's just difficult to write it
* in a unified manner.
*/
private function register1to1ItemWithMetaMappings() : void{
private function register1to1ItemWithMetaMappings(): void
{
$this->map1to1ItemWithMeta(
Ids::BANNER,
Items::BANNER(),
function(Banner $item, int $meta) : void{
function (Banner $item, int $meta): void {
$item->setColor(DyeColorIdMap::getInstance()->fromInvertedId($meta) ?? throw new ItemTypeDeserializeException("Unknown banner meta $meta"));
},
fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor())
);
$this->map1to1ItemWithMeta(
Ids::FIREWORK_STAR,
Items::FIREWORK_STAR(),
function(FireworkStar $item, int $meta) : void{
function (FireworkStar $item, int $meta): void {
// Colors will be defined by CompoundTag deserialization.
},
fn(FireworkStar $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getExplosion()->getFlashColor())
);
$this->map1to1ItemWithMeta(
Ids::MEDICINE,
Items::MEDICINE(),
function(Medicine $item, int $meta) : void{
function (Medicine $item, int $meta): void {
$item->setType(MedicineTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown medicine type ID $meta"));
},
fn(Medicine $item) => MedicineTypeIdMap::getInstance()->toId($item->getType())
);
$this->map1to1ItemWithMeta(
Ids::POTION,
Items::POTION(),
function(Potion $item, int $meta) : void{
function (Potion $item, int $meta): void {
$item->setType(PotionTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown potion type ID $meta"));
},
fn(Potion $item) => PotionTypeIdMap::getInstance()->toId($item->getType())
);
$this->map1to1ItemWithMeta(
Ids::SPLASH_POTION,
Items::SPLASH_POTION(),
function(SplashPotion $item, int $meta) : void{
function (SplashPotion $item, int $meta): void {
$item->setType(PotionTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown potion type ID $meta"));
},
fn(SplashPotion $item) => PotionTypeIdMap::getInstance()->toId($item->getType())
);
$this->map1to1ItemWithMeta(
Ids::SUSPICIOUS_STEW,
Items::SUSPICIOUS_STEW(),
function(SuspiciousStew $item, int $meta) : void{
function (SuspiciousStew $item, int $meta): void {
$item->setType(SuspiciousStewTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown suspicious stew type ID $meta"));
},
fn(SuspiciousStew $item) => SuspiciousStewTypeIdMap::getInstance()->toId($item->getType())
Expand All @@ -539,8 +551,9 @@ function(SuspiciousStew $item, int $meta) : void{
* Most of these are single PocketMine-MP items which map to multiple IDs depending on their properties, which is
* complex to implement in a generic way.
*/
private function registerMiscItemMappings() : void{
foreach(DyeColor::cases() as $color){
private function registerMiscItemMappings(): void
{
foreach (DyeColor::cases() as $color) {
$id = DyeColorIdMap::getInstance()->toItemId($color);
$this->deserializer?->map($id, fn() => Items::DYE()->setColor($color));
}
Expand Down
22 changes: 14 additions & 8 deletions src/item/ItemTypeIds.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
* WARNING: These are NOT a replacement for Minecraft legacy IDs. Do **NOT** hardcode their values, or store them in
* configs or databases. They will change without warning.
*/
final class ItemTypeIds{
final class ItemTypeIds
{

private function __construct(){
private function __construct()
{
//NOOP
}

Expand Down Expand Up @@ -330,28 +332,32 @@ private function __construct(){
public const ELYTRA = 20291;
public const TRIDENT = 20292;
public const SHIELD = 20293;
public const BREEZE_ROD = 20294;

public const FIRST_UNUSED_ITEM_ID = 20294;
public const FIRST_UNUSED_ITEM_ID = 20295;

private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

/**
* Returns a new runtime item type ID, e.g. for use by a custom item.
*/
public static function newId() : int{
public static function newId(): int
{
return self::$nextDynamicId++;
}

public static function fromBlockTypeId(int $blockTypeId) : int{
if($blockTypeId < 0){
public static function fromBlockTypeId(int $blockTypeId): int
{
if ($blockTypeId < 0) {
throw new \InvalidArgumentException("Block type IDs cannot be negative");
}
//negative item type IDs are treated as block IDs
return -$blockTypeId;
}

public static function toBlockTypeId(int $itemTypeId) : ?int{
if($itemTypeId > 0){ //not a blockitem
public static function toBlockTypeId(int $itemTypeId): ?int
{
if ($itemTypeId > 0) { //not a blockitem
return null;
}
return -$itemTypeId;
Expand Down
Loading

0 comments on commit 42120e6

Please sign in to comment.