← Java tutorials

LESSON 32 / 40 · FREE TUTORIAL

Add Server Commands

Distributing toolkits with parameters, permissions and completions

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

/kit toolkit with parameter checking

A /kit command first checks the caller, permissions and parameters before putting items into the backpack. You'll take care of player, console, and full inventory situations at the same time, so failures are clearly responded to. Front backpack: Gradle project and Paper test server of Lesson 31. Copy the project to BlockAcademyL32, keep the build script, and change the project name in settings to BlockAcademyL32.

Before you start: It is recommended to complete Lesson 31 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

First recognize who is giving the order

The villager entered the command to receive the tool kit at the console, but the program asked him for the backpack. The console has no hands and no hotbar. In the first level of the command, first identify the caller, then count the parameters, and finally open the recipe box.

Treat the command as a gated route: the backpack is modified only after the caller type, authority, number of parameters, and parameter content have all passed. Failure is also a process that requires a clear reply. Don’t let players only see silence.

First recognize who is giving the order
  1. PlayerCan receive a backpack
  2. ConsoleObtained an explicit rejection
  3. Common rulesOnly change items after checking

Obtain players safely. See the instructions in this section for their positions.

if (!(sender instanceof Player player)) {
    sender.sendMessage("Please collect the tool kit in the game");
    return true;
}
player.sendMessage(Component.text("Backpack owner confirmed"));

Wrong cast · Intentional error, predict first and then fix

Player player = (Player) sender; // Console execution will cause errors

03 / 13

Count parameters first and then open the grid

When entering /kit miner, kit is the command name, and there is only miner in args; args[0] is not kit. When the player only types /kit, the array length is 0, and reading the 0th cell is out of bounds.

Count the parameters first and then open the grid
  1. Count parameters firstLength must be 1
  2. Read the 0th box againIt is a miner, not a kit
  3. Finally recognize the formulaUnknown options explicitly rejected

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

Only accepts one parameter. See the description in this section for the position.

if (args.length != 1) {
    player.sendMessage(Component.text("Usage: /kit miner or /kit builder"));
    return true;
}

Comparison that is not affected by system language · See the description in this section for the location

String name = args[0].toLowerCase(Locale.ROOT);
Checklist
EnterLengthResult
/kit0Show usage
/kit miner1Select miner package
/kit miner extra2Show usage
/kit wizard1Prompt unknown toolkit

04 / 13

The command list and permissions must be matched

Create lesson32 package, the main class is called KitPlugin, replace plugin.yml in the resource directory. The exercise allows players to receive it by default, and the code still checks permissions to facilitate the withdrawal of a player's qualifications by the permission management system in the future.

The command list and permissions must be matched
  1. command kitList corresponds to getCommand
  2. Authority nodeCode checks before execution
  3. Default rulesThis chapter defaults to true

Complete plugin.yml of this lesson · Complete file

name: BlockAcademyL32
version: 1.0.0
main: de.teacherma.lesson32.KitPlugin
api-version: '1.21.11'
author: TeacherMa
website: https://www.teacher-ma.de
commands:
  kit:
    description: Get classroom tool kit
    usage: /kit <miner|builder>
permissions:
  blockacademy.kit:
    default: true

Pre-execution check authority · See the description in this section for the location

if (!player.hasPermission("blockacademy.kit")) {
    player.sendMessage(Component.text("You have not received the permission yet"));
    return true;
}

05 / 13

Where to go when the backpack is full of items?

addItem attempts to put the item into the backpack and returns the part that was not put in. The returned Map records the remaining items, and the key is the serial number of the input item, not the backpack slot. You can’t just say “received successfully” and forget about the remaining items.

Where to go when the backpack is full of items?
  1. Try to enter the packageaddItem modifies backpack
  2. Get the remainderReturn the items in the Map
  3. Handle truthfullyDropped at feet and prompted

Send items and process the remaining items. See the description in this section for the location.

var leftovers = player.getInventory().addItem(
        new ItemStack(Material.TORCH, 16));
for (ItemStack item : leftovers.values()) {
    player.getWorld().dropItemNaturally(player.getLocation(), item);
}

Feedback must conform to the results. See the description in this section for the location.

String message = leftovers.isEmpty()
        ? "Put all into backpack" : "The remaining items fell at your feet";
player.sendMessage(Component.text(message));

06 / 13

Press Tab to give the player a hint

Command completion only recommends possible inputs, and parameters still need to be checked during actual execution. Otherwise, players can bypass rules that only exist in menus by manually entering a strange name.

Press Tab to give the player a hint
  1. Completion promptOnly legal names are recommended
  2. Perform verificationManual input still needs to be checked
  3. Unknown prefixReturn empty list

Filter completion items from the list · See the description in this section for the location

String prefix = args[0].toLowerCase(Locale.ROOT);
return List.of("miner", "builder").stream()
        .filter(name -> name.startsWith(prefix)).toList();

Do not access parameters that do not exist. See the description in this section for the location.

if (args.length != 1) return List.of();
if (!sender.hasPermission("blockacademy.kit")) return List.of();
Checklist
Current parametersRecommend
Empty stringminer and builder
mminer
bbuilder
zEmpty list

07 / 13

The tool kit formula is first written on paper

Today's challenge is two kits. First write down the recipe clearly, and then string together input, judgment, distribution and feedback. The practice version can be obtained repeatedly; the cooling function will be added after learning the 34th lesson.

Toolkit formula is first written on paper
  1. miner1 stone pickaxe and 16 torches
  2. builder32 wooden boards, 16 glass
  3. Unknown nameStop issuing after returning null

Use switch to select formula · See the description in this section for the location

ItemStack[] items = switch (name) {
    case "miner" -> new ItemStack[] {
        new ItemStack(Material.STONE_PICKAXE),
        new ItemStack(Material.TORCH, 16)};
    default -> null;
};
Checklist
ToolkitItem oneItem two
miner1 stone pick16 torches
builder32 oak planksGlass 16 pieces

08 / 13

Toolkit program first half

Create new src/main/java/de/teacherma/lesson32/KitPlugin.java. Copy this page first, then the next page immediately after; do not include package names, imports, or extra braces.

Toolkit program first half
  1. A source documentThe front and rear parts are connected sequentially
  2. Check firstTypes, Permissions and Parameters
  3. After passingJust enter formula and distribution

KitPlugin Part 1 · This section 1/2, sequential connection

package de.teacherma.lesson32;

import java.util.List;
import java.util.Locale;
import java.util.Objects;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;

public final class KitPlugin extends JavaPlugin {
    @Override
    public void onEnable() {
        var kit = Objects.requireNonNull(getCommand("kit"), "Please check plugin.yml");
        kit.setExecutor(this);
        kit.setTabCompleter(this);
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command,
                             String label, String[] args) {
        if (!(sender instanceof Player player)) {

KitPlugin Part 1 · This paragraph 2/2, sequential connection

            sender.sendMessage("Please collect the tool kit in the game");
            return true;
        }
        if (!player.hasPermission("blockacademy.kit")) {
            player.sendMessage(Component.text("You have not received the permission yet"));
            return true;
        }
        if (args.length != 1) {
            player.sendMessage(Component.text("Usage: /kit miner or /kit builder"));
            return true;
        }

09 / 13

The lower part of the toolkit program

The following page follows immediately. Distribution, remaining items, and replenishment are all placed in the same class. Keep the boundaries between the two @Override methods, and don’t let the creepers count the number of braces for you.

Toolkit program lower half
  1. DistributionaddItem and process the rest
  2. FeedbackExplain actual results
  3. CompletionFilter by current prefix

KitPlugin Part 2 · This paragraph 1/2, sequential connection

        ItemStack[] items = switch (args[0].toLowerCase(Locale.ROOT)) {
            case "miner" -> new ItemStack[] {
                new ItemStack(Material.STONE_PICKAXE),
                new ItemStack(Material.TORCH, 16)};
            case "builder" -> new ItemStack[] {
                new ItemStack(Material.OAK_PLANKS, 32),
                new ItemStack(Material.GLASS, 16)};
            default -> null;
        };
        if (items == null) {
            player.sendMessage(Component.text("Only recognize miner and builder"));
            return true;
        }
        var leftovers = player.getInventory().addItem(items);
        for (ItemStack item : leftovers.values()) {
            player.getWorld().dropItemNaturally(player.getLocation(), item);
        }
        player.sendMessage(Component.text(leftovers.isEmpty()
                ? "The tool kit has been placed in the backpack" : "The backpack is full and the remaining items fall at my feet"));
        return true;
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command,
                                       String alias, String[] args) {

KitPlugin Part 2 · This paragraph 2/2, sequential connection

        if (!sender.hasPermission("blockacademy.kit") || args.length != 1)
            return List.of();
        String prefix = args[0].toLowerCase(Locale.ROOT);
        return List.of("miner", "builder").stream()
                .filter(name -> name.startsWith(prefix)).toList();
    }
}

10 / 13

Walk through the failure path as well

First free up the backpack to do a successful test, and then actively create empty parameters, unfamiliar parameters and a full backpack. The goal of testing is to know what will happen with each input. You can't just declare that you have passed the level just because "I got a pickaxe once."

Also walk through the failed path
  1. NormalBoth packages comply with the formula
  2. BorderZero parameters, full backpack
  3. RejectUnknown options, console, no permissions
Checklist
OperationExpected
player/kit miner1 stone pickaxe and 16 torches
player/kit builder32 wood panels and 16 glass
Console kit minerPrompt to receive it in the game, no exception
/kit or /kit wizardGive clear feedback and do not send items
Collect after the backpack is filledThere are remaining items at your feet, the information is accurate
Enter /kit m and press TabComplete miner

11 / 13

Make commands easier to maintain

When there are more and more commands, each branch needs to write a huge distribution code, and repairing it once is like digging an entire mountain. Divide the recipe into a method, and the command method is only responsible for checking and calling.

Stop and think about it: What judgment should be completed before reading args[0]? Why can't zero parameters be remedied by the subsequent recipe branch?

Make commands easier to maintain
  1. Clear responsibilitiesCommand responsible for inspection
  2. Formula methodOnly return item list
  3. Test synchronizationChanging the rules also changes the expectations

Extract a recipe method. See the description in this section for the location.

private ItemStack[] minerKit() {
    return new ItemStack[] {
        new ItemStack(Material.STONE_PICKAXE),
        new ItemStack(Material.TORCH, 16)};
}

The help messages must also be consistent. See the description in this section for the location.

player.sendMessage(Component.text("Usage: /kit miner or /kit builder"));
// plugin.yml Medium usage Write the same selection range
  • Change the case to MINER and expect the miner package to still be recognized.
  • Changed unknown option to wizard, no expected item changes.
  • Change the tool kit quantity to the new value in the recipe table, and then update the acceptance record.

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 PickaxeImplement name greetings
  2. Iron PickaxeComplete two kinds of packages and failure records
  3. Diamond PickaxeIncreased independent cooldown per player
  • Wooden Pickaxe: Implement /hello, sending greetings with names to players; the console can also get reasonable prompts.
  • Iron Pickaxe: Complete two tool kits, submit empty parameters and full backpack test records.
  • Diamond Pickaxe: After learning the 34th expansion, receive the command to add a 30-second cooldown for each player, and verify that the two players do not affect each other.
  • Acceptance requirements: Use players and consoles to test separately, and complete scenarios of no permissions, zero parameters, unknown formulas, and full backpacks; items will not be issued if the confirmation fails.
Checklist
I can complete it independentlyMasteredPractice again
First check the sender and parameters□□
Can handle the remaining items in the backpack□□
Able to explain authority and completion□□

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.