← Java tutorials

LESSON 33 / 40 · FREE TUTORIAL

The Block Event Detective

Read the events and establish clear rules of exploitation at the border.

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

Spawn point protection and miner license

An event is a clue package for an action. Recognize the world, location and mode first, and then decide whether to cancel; use the same number rules to draw the protection range of the birth point, and distinguish between permission to destroy and actual pickup. Front-end backpack: listener, conditional judgment, method and authority. The new project BlockAcademyL33 continues to use Java21, Paper1.21.11 and the Lesson 31 build script.

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

01 / 15

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 / 15

The crime scene is hidden in event

The detective got the news that "the cube was dug" and almost put the entire server on cordon. First ask which world, which mode, and which block, and then decide whether to block it. This is the listener that can handle the case.

event is the information packet of this action. First filter the actions that have nothing to do with the rules, and then determine the permissions and locations of the rest. The cancellation event changes whether this action occurs; just sending a warning text will not automatically cancel it.

The crime scene is hidden in event
  1. Read the scenePlayers, Materials and Coordinates
  2. Limited scopeWorld, location and mode
  3. Decide permissionAllow or cancel this action

Read the scene · See the description in this section for the location

var player = event.getPlayer();
var block = event.getBlock();
Material type = block.getType();
String worldName = block.getWorld().getName();

Temporary diagnostic log · See the description in this section for the location

getLogger().info(player.getName() + " -> " + type
        + " @ " + block.getX() + "," + block.getY() + "," + block.getZ());

03 / 15

First screen the world and game mode

The training rules only apply to the world named world. Spawn point protection takes effect in all modes of the world; diamond licenses only apply to survival mode. By writing the scope clearly, administrators will know why creative mode behaves differently.

First screen the world and game mode
  1. world worldReturn directly to other worlds
  2. Birth point circleAll modes are checked
  3. Survival licenseCheck diamond permissions outside the circle

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

Only handles the practice world · See the description in this section for the location

if (!event.getBlock().getWorld().getName().equals("world")) return;

The license only checks survival mode. See the description in this section for the location.

// This sentence is placed after the birth point protection
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
Checklist
SceneSpawn point protectionDiamond License
world survivalCheckCheck
world creationCheckSkip
Other worldsSkipSkip

04 / 15

Diamond also has a deep slate jacket

Only judging DIAMOND_ORE will miss DEEPSLATE_DIAMOND_ORE. Write the rule "Is it a Diamond Ore" as a method, and all listening methods call the same judgment.

Diamonds also have deep slate coatings
  1. DIAMOND_OREOrdinary Diamond Ore
  2. DEEPSLATE_DIAMOND_OREDeep Slate Diamond Ore
  3. Other materialsLicense rules do not interfere

Unified judgment of ore·See the description in this section for its location

private boolean isDiamond(Material type) {
    return type == Material.DIAMOND_ORE
            || type == Material.DEEPSLATE_DIAMOND_ORE;
}

Quickly exit irrelevant blocks · See the description in this section for the location

if (!isDiamond(event.getBlock().getType())) return;
// Only two kinds of diamond ores are processed below
  • Enter STONE into this rule and the answer should be false.
  • Enter the two Diamond Ores separately, and the answer should be true.
  • When adding gold mines, first list the corresponding material names, and then modify the method and test table.

05 / 15

Tell the player the reason when canceling the action

The authority node is like a license number. If you are not qualified to mine diamonds, cancel this destruction and explain the reason; it will not automatically issue a paper license, nor will it allow the player to obtain other abilities of the administrator.

Tell the player the reason when canceling the action
  1. No licenseCancel and give reasons
  2. LicensedStill must pass spawn point rules
  3. CanceledDo not restore other people's decisions

Reject mining without a license · See the description in this section for the location

if (!player.hasPermission("blockacademy.mine.diamond")) {
    event.setCancelled(true);
    player.sendMessage(Component.text("Diamond Ores require a miner's license"));
    return;
}

Permission statement for this lesson · See the description in this section for the location

permissions:
  blockacademy.mine.diamond:
    default: op
  blockacademy.bypass:
    default: op

06 / 15

The birth point protection is a cylinder

Use the square coordinates of the birth point as the center of the circle, and only compare X and Z. The radius is 10 tiles, and the borders are included, so the top and below are also protected. Only after confirming the same world can it make sense to compare coordinates.

Only look at the guard circle of X/Z: radius 10, including the boundary

dx² + dz² ≤ 10²

XZO (0,0)A (6,8)B (10,1)r=10
X / ZDistance squaredProtected?
O (0, 0)0Yes
A (6, 8)100Yes, on the boundary
B (10, 1)101No

Measure distance in the X/Z plane; ignore Y height.

Can be compared without taking the square root · See the description in this section for the position

double dx = (double) block.getX() - spawn.getBlockX();
double dz = (double) block.getZ() - spawn.getBlockZ();
boolean protectedArea = dx * dx + dz * dz <= 100.0;
Checklist
Relative coordinates dx dzSum of squaresprotected
0 00Yes
6 8100Yes, boundary
10 1101No
11 0121No

07 / 15

Rule processing and observation are separated

Priority determines the order and does not mean who is more powerful. Ordinary listeners can modify events; MONITOR is used to observe the processing results and will not change events according to the agreement. ignoreCancelled=true will skip canceled events.

Rule processing and observation are separated
  1. Rule monitoringCan be canceled according to contract
  2. MONITOROnly observe, do not modify events
  3. ignoreCancelledSkip canceled actions

Rule method · See the description in this section for the location

@EventHandler(ignoreCancelled = true)
public void protect(BlockBreakEvent event) {
    // Check conditions, if necessary setCancelled(true)
}

Observation method·See the description in this section for the location

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void observe(BlockBreakEvent event) {
    // Only observe, do not cancel, do not restore, do not modify the block
}

08 / 15

Detectives cannot block the server corridor

Block events run frequently. Download the web page or save the entire file with each piece dug, and the player will perform the slide show in place. Do cheap filtering first, only update the memory when necessary, and save the strategy for the data management chapter.

Stop and think about it: the code only sends out the prompt "cannot dig here", but the block is still dug away. Is it a chat glitch? What's missing?

Detectives cannot block server corridors
  1. Do cheap filtering firstExit the irrelevant world as soon as possible
  2. Only update necessary informationEach event is kept short
  3. Slow work will be arranged separatelyDo not download or rewrite large files chunk by chunk

Release irrelevant events as soon as possible · See the description in this section for the location

if (!inLessonWorld(event)) return;
if (event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
if (!isDiamond(event.getBlock().getType())) return;

Wrong event approach · Intentional mistakes, predict first and then fix

// Do not call time-consuming operations in every mining event
// Download web pages, scan the entire world, save huge files
  • The coordinates can be temporarily printed during the test; after confirming the rules, remove the log printed for each block.
  • Frequently rejecting prompts will cause the screen to swipe. You can limit the frequency of prompts after learning to time the player in Lesson 34.
  • Do not regard "asynchronous" as a universal acceleration button; the thread limit of the world API is discussed in Lesson 35.

09 / 15

Take out the geometric rules and test them

Create a new MiningRules.java in the same package as the main class. This class only handles numbers and does not require starting Minecraft. Verify the boundary first, and then connect the method to the event. It will be much easier to locate the problem.

Take out the geometric rules and test them
  1. Inside the circleDistance squared is less than 100
  2. Border6×6+8×8=100
  3. Outside the circle10×10+1×1=101

Complete MiningRules class · Complete file

package de.teacherma.lesson33;

public final class MiningRules {
    private MiningRules() { }
    public static boolean isProtected(int x, int z, int sx, int sz) {
        double dx = (double) x - sx;
        double dz = (double) z - sz;
        return dx * dx + dz * dz <= 100.0;
    }
}

Check calculation in ordinary main · See the description in this section for the location

System.out.println(MiningRules.isProtected(6, 8, 0, 0));
System.out.println(MiningRules.isProtected(10, 1, 0, 0));
System.out.println(MiningRules.isProtected(106, 208, 100, 200));

Output

true
false
true

10 / 15

The first half of the miner license process

Create MinerLicensePlugin.java. This page is merged with the next page in order, and the MiningRules.java of the previous page is retained. The main class registers the listener and unifies the judgment of the world and ores.

Miner License Process Part 1
  1. Unified entranceRegister when enabled
  2. Unified judgmentThe world and two Diamond Ores
  3. Protection takes priorityFirst check the bypass permissions within the circle

Main class part 1 · This paragraph 1/2, sequential connection

package de.teacherma.lesson33;

import net.kyori.adventure.text.Component;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.plugin.java.JavaPlugin;

public final class MinerLicensePlugin extends JavaPlugin implements Listener {
    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);
    }
    private boolean inLessonWorld(BlockBreakEvent event) {
        return event.getBlock().getWorld().getName().equals("world");
    }
    private boolean isDiamond(Material type) {
        return type == Material.DIAMOND_ORE
                || type == Material.DEEPSLATE_DIAMOND_ORE;
    }
    @EventHandler(ignoreCancelled = true)
    public void protect(BlockBreakEvent event) {

Main Class Part 1·This paragraph 2/2, sequential connection

        if (!inLessonWorld(event)) return;
        var player = event.getPlayer();
        var block = event.getBlock();
        var spawn = block.getWorld().getSpawnLocation();
        boolean nearSpawn = MiningRules.isProtected(block.getX(), block.getZ(),
                spawn.getBlockX(), spawn.getBlockZ());
        if (nearSpawn && !player.hasPermission("blockacademy.bypass")) {

11 / 15

Miner License Procedure Second Half

Continue to the previous page to complete the cancellation rules and observation messages. For the basic six lines of src/main/resources/plugin.yml, refer to Lesson 31. Change the name to BlockAcademyL33 and main to de.teacherma.lesson33.MinerLicensePlugin; then add the permission statement of this lesson.

Miner Licensing Process Second Half
  1. Cancel rulesExplain the reason when rejecting
  2. Mode diversionLicense is just for survival
  3. Observation resultsReport is allowed to mine when not canceled

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

            event.setCancelled(true);
            player.sendMessage(Component.text("Please keep the original appearance within ten blocks of the birth point"));
            return;
        }
        if (player.getGameMode() != GameMode.SURVIVAL) return;
        if (isDiamond(block.getType())
                && !player.hasPermission("blockacademy.mine.diamond")) {
            event.setCancelled(true);
            player.sendMessage(Component.text("Diamond Ores require a miner's license"));
        }
    }

    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
    public void observe(BlockBreakEvent event) {
        if (!inLessonWorld(event)) return;
        if (event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
        if (!isDiamond(event.getBlock().getType())) return;
        getServer().broadcast(Component.text(
                event.getPlayer().getName() + " Permission to mine Diamond Ores!"));
    }
}

plugin.yml: Put the entry and permissions in the same complete file

src/main/resources/plugin.yml · Complete file

name: BlockAcademyL33
version: 1.0.0
main: de.teacherma.lesson33.MinerLicensePlugin
api-version: '1.21.11'
author: TeacherMa
website: https://www.teacher-ma.de
permissions:
  blockacademy.mine.diamond:
    default: op
  blockacademy.bypass:
    default: op

12 / 15

The acceptance rules should distinguish who stops first

A spawn point and a license may prevent a mining at the same time. When testing the license, get out of the protective circle first, otherwise you will always see the spawn point prompt. Only one condition is changed for each row of the test table.

The acceptance rules must distinguish who stops first
  1. Protection testInside the circle, boundary and outside the circle
  2. License TestStep out of the circle and then change the permissions
  3. Observation testNo success message shall be sent when canceled
Checklist
ConditionsExpected
In the world circle, there is no bypass permissionDestruction of all modes is canceled
world boundary 6 8, no bypassCancel; Boundaries are also protected
Survival outside the circle, no license, Diamond OreCancel and prompt license
Survival outside the circle, no license, ordinary stonesAllow
Survival outside the circle, with license, two kinds of Diamond OresAllow and broadcast permission to mine
Other worldsThis plug-in does not intervene
The event was first canceled by other plug-insDo not broadcast success message

13 / 15

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 PickaxeExplain cylindrical shape
  2. Iron PickaxeAdd ore and check for acceptance
  3. Diamond PickaxeLimit rejection prompt frequency
  • Wooden pickaxe: prompts when digging two kinds of gold mines, while ordinary stones remain quiet.
  • Iron Pickaxe: Change the protection radius to 8 blocks, and add tests within the radius, border and outside the radius.
  • Diamond Pickaxe: Combined with the 34th Lesson Map, record the number of times allowed to be mined for each player; indicate whether the cancellation event counts.
  • Acceptance requirements: Test ordinary Diamond Ores and deep slate Diamond Ores respectively; record whether it is allowed and the reasons within the protection radius, on the boundary, and outside the boundary.
Checklist
I can complete it independentlyMasteredPractice again
Ability to filter by world and material□□
Can explain cylinder protection boundary□□
Can distinguish between cancellation and observation□□

14 / 15

Rule verification that can run independently

Create new checks/L33RuleCheck.java in the project and save the following code completely. Create a new checks/sources.txt, write the following two paths, and then execute the command from the project root directory. This is a normal Java test, not the main plugin class.

L33RuleCheck.java · Complete file

import de.teacherma.lesson33.MiningRules;

public class L33RuleCheck {
    public static void main(String[] args) {
        System.out.println(MiningRules.isProtected(0, 0, 0, 0));
        System.out.println(MiningRules.isProtected(6, 8, 0, 0));
        System.out.println(MiningRules.isProtected(10, 1, 0, 0));
        System.out.println(MiningRules.isProtected(106, 108, 100, 100));
    }
}

Output

true
true
false
true

checks/sources.txt · Full content

src/main/java/de/teacherma/lesson33/MiningRules.java
checks/L33RuleCheck.java

Project root directory terminal command · Execute two lines respectively

javac -encoding UTF-8 -d checks/out @checks/sources.txt
java -cp checks/out L33RuleCheck

15 / 15

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.