Skip to content

Instantly share code, notes, and snippets.

@alvin0319
Created February 7, 2022 14:05
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alvin0319/3f235b8eb05f6adfd18062b9ce70bb44 to your computer and use it in GitHub Desktop.
Save alvin0319/3f235b8eb05f6adfd18062b9ce70bb44 to your computer and use it in GitHub Desktop.
Specifications and explanations for implementing custom items on the server

Before starting

I wrote this specifications/explanation based on PocketMine-MP, Other softwares like Nukkit can be different with PM.

Table of contents

Table of contents generated with markdown-toc

What is ItemComponentPacket?

ItemComponentPacket is the packet for setting item's components such as equippable in OffHand, setting block break speed, max stack size, etc.

But, This packet CANNOT override the existing item.

I don't describe what components exist and how to use on here, But you can refer to wiki.vg and bedrock.dev to find out them.

The required NBT components to display your item correctly

There is the root CompoundTag.

The following NBT components should be added:

Component Name Tag Type Description
item_properties Tag_Compound A Group of components that will be used to activate item ability
minecraft:identifier Tag_Short A Runtime ID of item that will client use
minecraft:display_name Tag_Compound A CompoundTag that contains only one String Tag that is used to display item's name

The item_properties component

item_properties component requires those NBT components to show your item:

minecraft:icon: Tag_Compound

The minecraft:icon component should contain the tag named texture, which is the path of your custom item's png file.

Note that the path is NOT the absolute path.

Let's suppose that you have your excalibur item, located at textures/items/excalibur.png.

Then the texture's value should be excalibur. without including the file's extension.

The minecraft:identifier component

The minecraft:identifier is used by client to identify items by runtime id.

Runtime IDs are flexible. It may change at any time depending on the version of Minecraft. However, custom items are an exception.

The Runtime ID should not conflict with the existing item. So I suggest you to use 1000+ above IDs to prevent conflict with an existing items.

The minecraft:display_name component

The minecraft:display_name component is used to display item.

This component's tag type is CompoundTag, but it only contains one StringTag.

A StringTag named value is used to display item name, its value will be shown the client.

The Basic structure of Serialized NBT

If I describe NBT structure as json, it will be look like:

{
    "item_properties": {
        "allow_off_hand": 1,
        "max_stack_size": 1,
        "minecraft:icon": {
            "texture": "path/to/texture"
        }
    },
    "minecraft:identifier": 1000,
    "minecraft:display_name": {
        "value": "excalibur"
    }
}

Step to send custom item by plugin

In PocketMine-MP, you need to do two things to send a custom item.

First, register a custom item in the ItemTypeDictionary.

However, currently in PocketMine-MP, you cannot add items to ItemTypeDictionary in the normal way.

The ItemTypeDictionary should be set to an array containing ItemTypeEntry.

ItemTypeEntry must pass the namespace of the item as the first argument, the runtime ID of the item as the second argument, and a logical value of componentBased as the third argument.

Second, we need to register the item's runtime ID and generic ID with the ItemTranslator.

There are two array fields in ItemTranslator that we need to send items: simpleCoreToNetMapping, simpleNetToCoreMapping

coreToNetMapping must contain an array of ItemID => RuntimeID format, and netToCoreMapping must contain an array of RuntimeID => ItemID format.

If all the above descriptions are written as PocketMine plugin code, it will be as follows:

$itemId = 1000;
$runtimeId = $itemId + 5000;
(function() use ($itemId, $runtimeId) : void{
    $this->simpleCoreToNetMapping[$id] = $runtimeId;
    $this->simpleNetToCoreMapping[$runtimeId] = $id;
)->call(ItemTranslator::getInstance());
(function() use ($itemId, $runtimeId) : void{
    $this->itemTypes[] = new ItemTypeEntry("alvin0319:awsome_custom_item", $runtimeId, true); // pass componentBased to true to enable item components
})->call(GlobalItemTypeDictionary::getInstance()->getDictionary());

Finally, when the player enters the server, we need to send an ItemComponentPacket to the player.

Expressing this in the PocketMine-MP plug-in code is as follows.

public function onPlayerJoin(PlayerJoinEvent $event) : void{
    $player = $event->getPlayer();
    $entries = [];
    $itemId = 1000;
    $runtimeId = $itemId + 5000;
    $nbt = CompoundTag::create()
        ->setTag("item_properties", CompoundTag::create()
            ->setTag("minecraft:icon", CompoundTag::create()
                ->setString("texture", "excallibur")
            )->setByte("allow_off_hand", 1)
            ->setInt("max_stack_size", 1)
        )->setShort("minecraft:identifier", $runtimeId)
        ->setTag("minecraft:display_name", CompoundTag::create()
            ->setString("value", "Excallibur")
        );
    $entries[] = new ItemComponentPacketEntry("alvin0319:awesome_custom_item", new CachableNbt($nbt));
    $player->getNetworkSession()->sendDataPacket(ItemComponentPacket::create($entries));
}

Contribution is always welcome, by commenting in this gist!

If you have any question, leave it here. I'll add them on this gist!

@TheBlackPlague
Copy link

TheBlackPlague commented Feb 7, 2022

It's also important to note if your item is mapped like so: YourNamespace:Your_Item in the item_textures.json file in your resource pack, while the texture is stored in textures/items/Your_Item, then you will need to set the texture accordingly. Texture would in the above case be YourNamespace:Your_Item and not just Your_Item. The texture value you give is mapped with item_textures.json which then the client maps to the relevant file. It's important to recognize this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment