← Java tutorials

LESSON 37 / 40 · FREE TUTORIAL

Create Custom Items

Write a reliable identity tag on the miner's compass

100–120 minutesEnvironment: JDK 21, ordinary Paper 1.21.11, client of the same version. Server plugins are loaded by Paper; only explicitly labeled rule tests use main.Paper plug-in practice

WHAT YOU WILL MAKE

Miner's compass identified with PDC

The name is shown to the player, and the identity data is given to the program. Make an original compass with instructions, identify it with four checks of material, namespace, type and value, and then let the main hand right-click to only display the task once. Prerequisites: command, event, null value check and List. The new project BlockAcademyL37 follows the construction script of Lesson 31 and continues to lock Java21 and ordinary Paper1.21.11.

Before you start: It is recommended to complete Lessons 31–36 first

01 / 13

Before starting: first recognize the running position

The first 30 lessons lay the foundation for ordinary Java programs. At this stage, the code is connected to the Paper server: the Minecraft client is responsible for entering the world, the server loads the plug-in, and the events and commands hand over player actions to your methods.

This book uses JDK 21 and Paper 1.21.11, and the client version is also consistent. The latest version on the download page may have changed, select it by version number. This book uses normal plugin.yml and the Bukkit API; no claims are made to support Folia at this stage.

The short examples in the picture frame are partial fragments. Read the location description of this section first, and do not save each as a plug-in. The file name will be clearly given for the complete file; when the same file is divided into multiple segments, they will be connected by number, and the package name, import and outer class will only appear once.

  • Prediction: Write success conditions and expected messages first.
  • Run: After successful construction, install and restart on the dedicated local test server.
  • Verification: Ordinary players, management identities and consoles take corresponding paths respectively.

02 / 13

The article body and instructions are modified separately

The ordinary compass is renamed "Super Miner Compass" in the anvil, so come and apply for special abilities. The guard looked at the name and almost let me go. The display name can be changed, and reliable identification requires viewing the identity tag written by the plug-in.

ItemStack is the item and ItemMeta is the description and additional data. Write the PDC tag corresponding to NamespacedKey when making; check the material, data type and value when identifying. The name is responsible for allowing players to understand it, and the label is responsible for allowing the program to recognize it correctly.

The article body and instructions are modified separately
  1. ItemStackItem body
  2. ItemMetaName description and additional data
  3. setItemMetaWrite modifications back to ontology

First make a readable compass. See the instructions in this section for the position.

ItemStack item = new ItemStack(Material.COMPASS);
var meta = item.getItemMeta();
meta.displayName(Component.text("Miner's Compass"));
meta.lore(List.of(Component.text("Right-click the main hand to view classroom tasks")));
item.setItemMeta(meta);

Deliberately missing the last step · Intentional mistake, predict first and then fix

// If you forget setItemMeta, Check if the name and description are actually saved.

03 / 13

The anvil can be renamed but cannot prove identity.

Rename the ordinary compass to "Miner's Compass", it looks real, but the name is just a label. The program checks for PDC tags hidden in the item's data. The display name is responsible for the player to recognize it, and the data key is responsible for the program to recognize it.

The anvil can be renamed but cannot prove identity.
  1. Display nameConvenient for players to understand
  2. NamespacedKeyStable data location
  3. BYTE value 1Identity mark of this chapter

Create a key for this plug-in · See the instructions in this section for the location

compassKey = new NamespacedKey(this, "miner_compass");

Write a mark with a clear type. See the description in this section for the location.

meta.getPersistentDataContainer().set(
    compassKey, PersistentDataType.BYTE, (byte) 1);
Checklist
InformationPurpose
Display name and loreLet players understand
NamespacedKeyNamespace plus key name to reduce conflicts
BYTE type and value 1The compass identity agreed in this lesson

04 / 13

Put production and recognition into the same class

Create a new CompassItems.java and put it into the de.teacherma.lesson37 package. Use the same key and the same type for production and identification to avoid writing miner_compass on one side and looking for mining_compass on the other, which is like sending the key to the wrong door.

Put production and recognition into the same class
  1. Create compassSelect original material
  2. Write metadataName, lore and PDC
  3. Write back itemssetItemMeta and then return

Complete CompassItems.java · Complete file

package de.teacherma.lesson37;

import java.util.List;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;

public final class CompassItems {
    private CompassItems() { }
    public static ItemStack create(NamespacedKey key) {
        ItemStack item = new ItemStack(Material.COMPASS);
        var meta = item.getItemMeta();
        meta.displayName(Component.text("Miner's Compass"));
        meta.lore(List.of(Component.text("Right-click the main hand to view classroom tasks")));
        meta.getPersistentDataContainer().set(key, PersistentDataType.BYTE, (byte) 1);
        item.setItemMeta(meta);
        return item;
    }
    public static boolean isOurs(ItemStack item, NamespacedKey key) {
        if (item == null || item.getType() != Material.COMPASS || !item.hasItemMeta())
            return false;
        Byte marker = item.getItemMeta().getPersistentDataContainer()
                .get(key, PersistentDataType.BYTE);
        return Byte.valueOf((byte) 1).equals(marker);
    }
}

05 / 13

Check material type and value

item may be null when empty-handed. Ordinary compasses may not have meta; if they do have meta, they may not have our marks. The identification function eliminates mismatches in order, and finally compares whether the BYTE value is 1. Only checking "key exists" will include other tag values.

Check material type and value
  1. Material COMPASSExclude irrelevant items first
  2. Key and typeRead corresponding BYTE mark
  3. Value equals 1It is recognized as the compass of this chapter

Safety comparison may be empty values · See the description in this section for the location

Byte marker = meta.getPersistentDataContainer()
        .get(compassKey, PersistentDataType.BYTE);
boolean ours = Byte.valueOf((byte) 1).equals(marker);

Delete the fragment of the custom identity · See the description in this section for the location

meta.getPersistentDataContainer().remove(compassKey);
item.setItemMeta(meta);
Checklist
Item dataRecognition results
Ordinary compass or unmarkedfalse
Same key STRING text "1"false
Same key BYTE value 2false
COMPASS plus the same key BYTE value 1true

06 / 13

Don’t rush to report with both hands

Interaction events may be triggered by two hands respectively. If the off-hand is also holding a compass, two mission prompts may appear for the same action. We only accept EquipmentSlot.HAND, which is the main hand; we do not rely on "delete a message after sending it" to remedy it.

Stop and think about it: Should the True Compass still be recognized after it has been renamed by the player? Should the ordinary compass gain abilities after being renamed with the same name?

Don’t rush to report with both hands
  1. Main handEquipmentSlot.HAND
  2. Right click actionBoth air and blocks are accepted
  3. Item matchingPrompt once after passing three levels

Before entering the next step along the arrow, the conditions of the previous step must be met; the rejected branch returns early.

First screen the main hand. See the description in this section for the position.

if (event.getHand() != EquipmentSlot.HAND) return;

Both right-clicks are allowed. See the description in this section for the position.

Action action = event.getAction();
if (action != Action.RIGHT_CLICK_AIR
        && action != Action.RIGHT_CLICK_BLOCK) return;
  • The master holds the compass in this lesson and right-clicks the air: a task is displayed.
  • Right-click the block with the main hand: the task is displayed, and the original interaction with the block can still be continued.
  • Holding the compass in the second hand or waving the left button: No tasks will be displayed in this lesson.

07 / 13

Reading tasks does not mean approval of interaction

When right-clicking on the air, the original version may predict "no action" and mark the event as cancelled. Therefore this read-only prompt listener does not have ignoreCancelled=true set. It only looks at message concurrency prompts, does not restore cancellation, and does not authorize block operations.

Reading the task does not represent approval of the interaction
  1. Read only observationOnly send task text
  2. Original interactionNot canceled or reinstated by this chapter
  3. There are side effects in the futureLicensing strategy must be redesigned

The entrance and location of the read-only observation method is explained in this section.

@EventHandler(priority = EventPriority.MONITOR)
public void onUse(PlayerInteractEvent event) {
    // After the main hand, actions, and items are all matched, only the task text is sent..
}

Do not unblock protection for prompts · Intentional mistakes, predict first and then repair

event.setCancelled(false); // Don’t do this in this lesson

08 / 13

Register command for compass project

The project name is set to BlockAcademyL37. The resources directory only requires plugin.yml, and there is no config.yml in this lesson. The main class and item tool class are in the lesson37 package, and the build script follows Lesson 31.

Register command for compass project
  1. CompassItemsMaking and identifying shared rules
  2. CompassPluginRegistration command and listener
  3. plugin.ymlStatement to receive orders and permissions

Complete plugin.yml · Complete file

name: BlockAcademyL37
version: 1.0.0
main: de.teacherma.lesson37.CompassPlugin
api-version: '1.21.11'
author: TeacherMa
website: https://www.teacher-ma.de
commands:
  minercompass:
    description: Get the Classroom Miner's Compass
    usage: /minercompass
permissions:
  blockacademy.compass.give:
    default: op

Java files in the complete project · Complete files

src/main/java/de/teacherma/lesson37/
  CompassItems.java
  CompassPlugin.java

09 / 13

The first half of the compass main class

Create new CompassPlugin.java. This page first declares dependencies, creates a key, and registers commands and listeners; the next page continues with commands and interaction logic. Don't add a new class between two pages.

The first half of the compass main class
  1. When enabledCreate stable keys
  2. Command registrationThe collection entrance can be found
  3. Monitoring registrationYou can receive notifications by right clicking on the main hand

Main class part 1·sequentially connected to the same source file

package de.teacherma.lesson37;

import java.util.Objects;
import net.kyori.adventure.text.Component;
import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.plugin.java.JavaPlugin;

public final class CompassPlugin extends JavaPlugin implements Listener {
    private NamespacedKey compassKey;
    @Override
    public void onEnable() {
        compassKey = new NamespacedKey(this, "miner_compass");
        Objects.requireNonNull(getCommand("minercompass")).setExecutor(this);
        getServer().getPluginManager().registerEvents(this, this);
    }

10 / 13

lower half of compass main class

Immediately following the previous page. Check the sender, permissions and parameters before receiving it; put the compass that cannot fit in your backpack at your feet. The interactive method only does three filters and one prompt, and is short enough for you to explain line by line.

The lower half of the compass main class
  1. ReceivingFirst check identity, permissions and parameters
  2. RemainingFull backpack is handled truthfully
  3. ObservationDo not change the block, only send prompts

Main class part 2·sequentially connected to the same source file

    @Override
    public boolean onCommand(CommandSender sender, Command command,
                             String label, String[] args) {
        if (!(sender instanceof Player p)) {
            sender.sendMessage("Please get the compass in the game"); return true;
        }
        if (!p.hasPermission("blockacademy.compass.give")) {
            p.sendMessage("Please collect it after authorization from the teacher"); return true;
        }
        if (args.length != 0) { p.sendMessage("Usage: /minercompass"); return true; }
        var left = p.getInventory().addItem(CompassItems.create(compassKey));
        left.values().forEach(item -> p.getWorld().dropItemNaturally(p.getLocation(), item));
        p.sendMessage(left.isEmpty() ? "The compass has been included in the package, try right-clicking on the main hand" : "Compass at feet");
        return true;
    }
    @EventHandler(priority = EventPriority.MONITOR)
    public void onUse(PlayerInteractEvent event) {
        if (event.getHand() != EquipmentSlot.HAND) return;
        Action action = event.getAction();
        if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return;
        if (!CompassItems.isOurs(event.getItem(), compassKey)) return;
        event.getPlayer().sendMessage(Component.text("Classroom tasks: Prepare tools and participate in the ore sprint competition!"));
    }
}

11 / 13

real and fake compasses take the exam together

Only testing the true compass will miss most of the problems. Prepare an ordinary compass, a renamed compass and a compass for this lesson, and let the "fake students" also take the exam. Document expectations and actualities, and don’t declare graduation just by seeing a sentence in the chat box.

Take the exam with both true and false compasses
  1. Genuine itemsStill identifiable after name change
  2. Fake items with the same nameNo recognition without PDC
  3. Action BoundaryNo prompts for empty hand, off-hand and left button
Checklist
TestExpected
True compass main hand right click airDisplay a task
True Compass main hand right-click boxPrompt appears, box interaction is not changed by this lesson
Ordinary compass with the same nameDo not display tasks
Empty hand or left buttonDo not display tasks
The true compass is only placed in the deputy handDo not display tasks
Receive full backpackPlace the remaining compass at your feet and prompt
Restart normally after saving itemsPlug-ins with the same name and key still identify tags

12 / 13

Expansion challenges and own acceptance records

Leave the source code, actual running results and a problem record. Explain it in your own words first, and then run the verification; where the results are different from the predictions, they are often the most worthy of discussion.

Expansion tasks and clearance review
  1. Wooden PickaxeAdd two lines of lore
  2. Iron PickaxeTest keys, types and values
  3. Diamond PickaxeDesign three tools with different labels
  • Wooden pickaxe: Add two lines of lore to the compass and explain why setItemMeta is needed.
  • Iron pickaxe: four identification tests of production material, key, type and value. Ordinary items cannot be accidentally triggered.
  • Diamond Pickaxe: Design a three-piece miner set, with different labels to distinguish functions; indicate the behavior after receiving permissions and restarting.
  • Acceptance requirements: Test real items, fake items with the same name, empty hands, off-hands, and renamed real items; one operation cannot be repeated with two hands.
Checklist
I can complete it independentlyMasteredPractice again
Ability to create and save ItemMeta□□
Can read and write PDC identity tags□□
Can handle main hand and right click actions□□

13 / 13

Leave the evidence for the next adventure

Divide "can compile", "number rules pass" and "actual performance of the game is correct" into three columns. Scenarios that are not running are written to be tested; when bugs are discovered, trigger conditions, expected, actual, minimum repairs, and retest results are recorded.

Your verification record
CategoryThe operation you selectedEvidence left behind
NormalA successful path for this lessonVersion and actual output
BorderThe moment before the end, the maximum value or the full backpackCompare item by item with contract
RejectWrong parameters, no permissions or irrelevant eventsThe feedback is clear and the status has not been changed by mistake.
  • Explain why each arrow in this lesson points from here to there; the parallel cards have no sequential meaning.
  • First change a rule, then modify the description and expectations at the same time, and retest with the same input.

TAKE IT WITH YOU

Take the lesson offline

The complete lesson, code, diagrams and exercises, in your preferred language.