Paper Plugin Guide
File mode0

Java basics

How code reads

Statements, blocks, names and the compile-then-run cycle, explained from zero.

Beginner31 min read

Before you can write plugins, you need to read code the way Java reads it. On this page you will run your first Java program, learn what every symbol on a line means, read and fix real error messages, and see how the same ideas show up inside a Paper plugin.

What a program is

A program is a list of instructions for the computer. The computer does exactly what the instructions say, one after another, from the top of the list to the bottom. It never guesses what you meant. If an instruction is missing, it does not happen. If two instructions are in the wrong order, they run in the wrong order.

You write those instructions as text in files that end with .java. That text is called source code. People can read source code, but the computer cannot run it directly. Two steps turn your text into something that runs:

  1. Compile. A tool called the compiler (its name is javac) reads your .java files, checks that they follow Java's grammar rules, and translates them into bytecode: compact instructions stored in .class files. If something is wrong, the compiler stops and tells you where.
  2. Run. The Java Virtual Machine (the JVM, started with the java command) reads the bytecode and carries out the instructions.
From source file to a running plugin You write MyPlugin.java, javac turns it into MyPlugin.class files, Gradle packs them into a .jar, you put the jar in the server's plugins folder, and Paper loads it. You write MyPlugin.java source code javac the Java compiler checks and translates Compiled files MyPlugin.class code the JVM can run Gradle packs a jar the build tool ./gradlew build Plugin jar my-plugin-1.0.0.jar in build/libs Server folder plugins/ you copy the jar here Paper loads it on start Gradle runs javac for you, so you rarely call it by hand.
Your .java text is compiled into .class bytecode, packed into a jar for plugins, and run by the JVM.

Why two steps? Because compiling catches many mistakes before anything runs. A typo in a plugin is much better found on your PC than on a live server with 50 players. It also means the same compiled plugin jar runs on Windows, Linux and macOS servers, because each of them has a JVM that understands bytecode.

For plugins, you will rarely type javac yourself. Gradle (see Your toolbox) runs the compiler for you, packs the .class files into a jar, and the Paper server, which is itself a Java program running on a JVM, loads that jar. Same two steps, just automated.

Your first program

Here is a complete Java program. Press Run (or the green arrow next to void main) and look at the output below the code.

Hello.javaRuns on Java 25
1void main() {2    IO.println("Hello, Minecraft!");3    IO.println("This is my first Java program.");4}
  1. The starting point. When you run the program, Java looks for main and runs the lines inside its braces. void means "this gives nothing back when it finishes"; you will meet it again in every plugin.
  2. One instruction: print a line of text. IO is a helper built into Java 25 for input and output, and println means "print line": show the text, then move to a new line.
  3. A second instruction. It runs after the first one finishes, so its text appears underneath.
  4. The closing brace ends main. When Java reaches it, the program is done.

The output is exactly the two pieces of text inside the quotes, in the same order as the lines. The quotes themselves are not printed: they only mark where the text starts and stops.

Running it on your own PC

You can run the same file outside this guide in two ways:

  • In IntelliJ: create a plain Java project (FileNewProject..., choose Java and JDK 25), add a file called Hello.java, paste the code, and click the green arrow that appears next to void main. The output shows in the Run window at the bottom.
  • In a terminal: go to the folder with the file and run java Hello.java. For a single file, the java command compiles it in memory and runs it right away.
PowerShell
 java Hello.javaHello, Minecraft!This is my first Java program.

Reading a line of code

Look at the line IO.println("Hello, Minecraft!"); again. It has five parts, and almost every line you will write in a plugin has the same parts:

A program line and a plugin line have the same shape the thing . the action the input, inside ( ) end IO . println ( "Hello, Minecraft!" ) ; player . sendRichMessage ( "<green>Welcome!" ) ; Top row: a plain Java program. IO prints the text in the terminal. Bottom row: plugin code. The player gets the text in their chat. Read it as: "thing, do this action, with this input." The semicolon ends the instruction.
The thing, a dot, the action, the input in parentheses, and a semicolon. The plugin line in the bottom row follows the exact same pattern.
PartIn the exampleWhat it means
The thingIOWho should do the work. In plugins this is often a player, the event or the server.
The dot."Use something that belongs to this thing." Read it as "'s": player.getName() is "the player's name".
The actionprintlnA method: a named action the thing knows how to do.
The input("Hello, Minecraft!")The arguments the action needs, inside parentheses. Text inside double quotes is a String.
The end;The semicolon ends the instruction, like a period ends a sentence.

Now read this line from a real plugin. You already know how to read it, even though you have never seen it before:

From a join listener
1player.sendRichMessage("<green>Welcome!");
  1. The thing is player, the action is sendRichMessage, and the input is the text "<green>Welcome!". Result: that player sees a green "Welcome!" in chat.
Welcome!

Some actions need no input, but the parentheses are still there: player.getName() asks the player for their name and needs nothing extra. Some actions need several inputs, separated by commas. You will see both all the time.

The classic way to write main

Java 25 lets you start a program with a short void main(). Older Java needed more ceremony, and you will see this longer form in almost every tutorial and Stack Overflow answer online. It does exactly the same thing:

ClassicHello.javaRuns on Java 25
1public class ClassicHello {2    public static void main(String[] args) {3        System.out.println("Hello, Minecraft!");4        System.out.println("This is my first Java program.");5    }6}
  1. A class is a named container for code. In Java, all code lives inside a class. The short void main() form simply hides this line from you. A file's public class must have the same name as the file: ClassicHello lives in ClassicHello.java.
  2. The classic starting point. public means anyone may call it, static means it can run without creating anything first, and String[] args receives extra words typed after the program name. You will learn each word on later pages.
  3. The classic way to print a line. System.out is the terminal's output, and println prints a line on it.
  4. Closes main.
Short form (Java 25)Classic form (all Java versions)
void main() { ... }public class Name { public static void main(String[] args) { ... } }
IO.println("text");System.out.println("text");
IO.print("text"); (no new line)System.out.print("text");

The Java pages in this guide use the short form, because it lets you focus on the idea being taught. Plugins always use full classes, and you will read plenty of them below.

Statements, semicolons and blocks

Statements run from top to bottom

Each complete instruction is called a statement. Statements run in order, one at a time. This little program pretends to start a server:

ServerStartup.javaRuns on Java 25
1void main() {2    IO.println("Loading world...");3    IO.println("Spawning villagers...");4    IO.println("Opening the doors...");5    IO.println("Server is ready!");6}
  1. Runs first.
  2. Runs second, only after the first line is completely finished.
  3. Runs last. If you moved this line to the top, "Server is ready!" would print before the world loaded, which would be a lie.

Order matters in plugins too. If your plugin sends a player a message and then teleports them, they read the message at the old spot. Swap the two lines and they read it after arriving. Same instructions, different result.

Semicolons end statements; indentation is for humans

Java does not care about line breaks or spaces between statements. It looks for the semicolon to know where one statement ends. This program is badly laid out, but it runs perfectly:

MessyButWorks.javaRuns on Java 25
1void main() {2IO.println("Line one");3        IO.println("Line two");   IO.println("Line three");4    IO.println(5        "Line four"6    );7}
  1. No indentation at all. Java does not mind.
  2. Two statements on one line. The semicolon after the first one tells Java where it ends.
  3. One statement spread over three lines. Java keeps reading until it finds the semicolon.

So why do the other examples look so tidy? Because people read code far more often than they write it. Consistent indentation (four spaces per level) shows at a glance which lines belong together. In IntelliJ, press Ctrl+Alt+L to reformat a file neatly. Do it often.

Braces make blocks

A pair of curly braces { } groups statements into a block. Blocks can sit inside other blocks, like boxes inside boxes. A plugin's main class is a big block, and each method inside it is a smaller block with its own statements:

public final class MyPlugin extends JavaPlugin { public void onEnable() { getLogger().info("Starting"); getLogger().info("Ready"); } public void onDisable() { getLogger().info("Bye"); } } Class block holds the whole plugin Method block one action the server can call, like onEnable Statement one instruction, ends with ;
A class block holds method blocks, and each method block holds statements that run top to bottom.

Every { needs a matching }. When you forget one, the compiler gets confused and often reports the problem far below where it really is, with a message such as "reached end of file while parsing". IntelliJ highlights the matching brace when you click next to one, which makes it easy to check.

Text and numbers

The quotes matter a lot. Text inside double quotes is printed exactly as written. Without quotes, Java treats what you wrote as code and works it out first. Run this and compare each line of code with its output:

TextAndNumbers.javaRuns on Java 25
1void main() {2    IO.println("2 + 3");3    IO.println(2 + 3);4    IO.println(64 * 3);5    IO.println("One stack holds " + 64 + " items.");6    IO.println("Three stacks hold " + (64 * 3) + " items.");7}
  1. Inside quotes, 2 + 3 is just five characters of text, so it prints as-is.
  2. No quotes: Java calculates 2 + 3 and prints the answer, 5.
  3. The * symbol means multiply. 64 is a full stack of most items.
  4. A + between text and a number glues them together into one piece of text.
  5. The parentheses make Java calculate 64 * 3 first, then glue the answer into the sentence.

You will use this all the time in plugins, for example "You have " + coins + " coins". The next pages explain numbers and text properly: Variables and types, Operators and math and Working with text.

Names and capital letters

Java is case sensitive: capital and small letters are different letters. IO, Io and io are three different names, and only IO exists. This is the single most common reason a beginner's code does not compile.

The names you choose yourself, for classes, methods and stored values, are called identifiers. They follow a few rules:

  • Use letters, digits, _ and $ only. No spaces, no dashes, no dots.
  • Do not start with a digit: player2 is fine, 2player is not.
  • Do not use a Java keyword such as class or void as a name.

On top of the rules, every Java developer follows the same naming habits, called naming conventions. Java does not force them, but every Paper class follows them, so following them too makes your code look like everyone else's and helps you guess names:

StyleUsed forExamples you will meet in plugins
camelCase (starts small, each new word capitalized)Methods and variablesonEnable, getPlayer, sendRichMessage, coins, maxHomes
PascalCase (starts capital)Classes and other typesJavaPlugin, PlayerJoinEvent, ItemStack, MyPlugin
UPPER_SNAKE_CASE (capitals with underscores)Constants and fixed lists of valuesMaterial.DIAMOND_SWORD, Attribute.MAX_HEALTH, MAX_HOMES
all lowercase with dotsPackages (folders of classes)com.example.myplugin, org.bukkit.entity

The conventions are a superpower when reading code. In Material.DIAMOND_SWORD you can tell from the capitals alone that Material is a type and DIAMOND_SWORD is a fixed value. In player.getName(), the small letters and parentheses tell you player is a stored value and getName is an action.

Keywords

Keywords are words that Java reserves for its own grammar. IntelliJ shows them in a different color. There are about 50 of them, and you do not need to learn them now: each one gets explained on the page where you first need it. These are the ones you will see in almost every plugin:

KeywordsWhat they are forLearn them in
int, double, boolean, var, finalStoring valuesVariables and types
if, else, switch, returnMaking decisionsMaking decisions
for, while, break, continueRepeating thingsLoops
void, staticDefining methodsMethods
class, new, this, public, privateClasses and objectsClasses and objects
extends, implementsBuilding on other classes, like JavaPlugin and ListenerInheritance and interfaces
package, importOrganizing classes into foldersPackages and imports
true, false, nullSpecial values: yes, no, and "nothing here"Variables and types

One more thing you will meet in code online: notes for humans called comments. A line starting with //, or text between /* and */, is ignored by the compiler. This guide keeps its code free of comments and explains everything in the numbered notes instead, so the code you copy stays clean.

When code goes wrong

Mistakes are normal, even for experts. What matters is reading the error message, because it nearly always tells you exactly where to look. There are two kinds of errors, and they happen at different times.

Compile errors: Java cannot understand you

A compile error means the code breaks Java's grammar rules, so the compiler refuses to translate it. Nothing runs at all. This program forgot one semicolon:

MissingSemicolon.javaRuns on Java 25
1void main() {2    IO.println("Welcome to the server!")3    IO.println("Have fun!");4}
  1. The semicolon at the end of this line is missing.

Read the error from left to right:

  • MissingSemicolon.java: the file with the problem.
  • :2: the line number. Go to line 2.
  • error: ';' expected: what Java wanted but did not find.
  • The line underneath shows your code, and the ^ points at the exact spot.

Here is the case-sensitivity mistake from earlier, written io instead of IO:

WrongCase.javaRuns on Java 25
1void main() {2    io.println("Welcome to the server!");3}
  1. Small io does not exist. Only IO does.

"cannot find symbol" means "you used a name I do not know". The symbol: variable io line tells you which name. When you see this, check the spelling and the capital letters first, and then check whether you forgot an import (see Packages and imports).

In IntelliJ you rarely need to compile to see these errors: the editor underlines the problem in red as you type, and hovering the red line shows the same message.

IntelliJ underlines the mistake in red and shows the compiler's message when you hover it.

Runtime errors: Java understood, but something failed

A runtime error happens while the program runs. The grammar was fine, so it compiled, but one instruction asked for something impossible. Java stops and throws an exception: an error report with a name. This program tries to share 12 diamonds among 0 players:

SplitDiamonds.javaRuns on Java 25
1void main() {2    int diamonds = 12;3    int players = 0;4    IO.println("Splitting " + diamonds + " diamonds...");5    IO.println("Each player gets " + diamonds / players);6    IO.println("Done!");7}
  1. Stores the number 12 under the name diamonds. You will learn about stored values on the next page.
  2. Stores 0 under the name players.
  3. This line runs fine, so its text appears in the output.
  4. Dividing by zero is impossible in whole-number math. Java throws an exception here and stops.
  5. Never runs, because the program crashed on the line above.

The first line of output proves that line 4 ran. Then comes the error report, called a stack trace:

  • java.lang.ArithmeticException: the name of the problem (a math problem).
  • / by zero: the details.
  • at SplitDiamonds.main(SplitDiamonds.java:5): where it happened: in main, in the file SplitDiamonds.java, on line 5.

Plugins crash the same way, and the stack trace appears in the server console. If a join listener divided by zero, a player joining would print something like this. Notice that the "at" line names your class, your method and the exact line:

Server console
[14:06:03 INFO]: Steve joined the game[14:06:03 ERROR]: Could not pass event PlayerJoinEvent to HowCodeReads v1.0.0java.lang.ArithmeticException: / by zero    at com.example.javahowcodereadsplugin.GreetingListener.onJoin(GreetingListener.java:14) ~[HowCodeReads-1.0.0.jar:?]

The good news: one crashing event handler does not crash the server. Paper catches the exception, prints it, and carries on. The bad news: your feature silently stops working for that event, so always keep an eye on the console while testing. Null, exceptions and stack traces teaches stack traces in depth, and the Error Doctor explains any error you paste into it.

How plugin code is different

Everything so far started at main and ran to the end. A plugin has no main at all. Instead, the Paper server is the program that is running, and it calls your code at the right moments:

  • When the server starts, Paper calls your plugin's onEnable method.
  • When something happens in the game, such as a player joining, Paper calls the matching event handler in your plugin.
  • When the server stops, Paper calls your onDisable method.

Inside each of those methods, your statements still run top to bottom, exactly like in the programs above. You just do not decide when the method starts. That is the server's job.

What happens when the server starts and stops Steps in order: the server starts, reads the plugins folder, reads plugin.yml, creates the main class, calls onLoad, loads worlds, calls onEnable, then runs until the server stops and calls onDisable. 1. Server starts Paper boots 2. Reads the plugins folder 3. Reads plugin.yml 4. Creates your main class 5. onLoad() very early 6. Worlds load Paper loads them 7. onEnable() your setup code 8. Running 20 ticks a second 9. Server stops /stop or crash 10. onDisable() your cleanup Boxes with code names are the methods you write in your main class. Paper calls them for you. Read left to right, then continue on the next row
The server finds your jar, reads plugin.yml, creates your main class and calls onEnable. Later it calls your event handlers whenever something happens.

This small plugin makes the order visible. Read it with what you now know: things, dots, actions, inputs, semicolons and blocks.

HowCodeReadsPlugin.javaCompiles on Paper 26.3Compile Lab
Where this file livesjava-how-code-reads-pluginsrcmainjavacomexamplejavahowcodereadspluginHowCodeReadsPlugin.java

The package com.example.javahowcodereadsplugin is the folder path com/example/javahowcodereadsplugin inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.

  • java-how-code-reads-plugin/
    • src/main/
      • java/com/example/javahowcodereadsplugin/Package com.example.javahowcodereadsplugin
        • GreetingListener.javaListener: reacts to events
        • HowCodeReadsPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
      • resources/Files copied into the jar as they are
        • plugin.ymlTells Paper the plugin's name, version and main class
    • build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
    • gradle.propertiesVersion numbers used by the build
    • gradlew.batRuns Gradle on Windows without installing it
    • settings.gradle.ktsThe project's name

Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.

1package com.example.javahowcodereadsplugin;2 3import org.bukkit.plugin.java.JavaPlugin;4 5public final class HowCodeReadsPlugin extends JavaPlugin {6 7    @Override8    public void onEnable() {9        getLogger().info("Step 1: the server called onEnable.");10        getLogger().info("Step 2: registering the join listener.");11        getServer().getPluginManager().registerEvents(new GreetingListener(), this);12        getLogger().info("Step 3: setup is done. Waiting for players...");13    }14 15    @Override16    public void onDisable() {17        getLogger().info("The server is stopping. Goodbye!");18    }19}
  1. The package: which folder this file lives in. Every plugin file starts with this line.
  2. Says which JavaPlugin we mean: the one in Paper's org.bukkit.plugin.java package. IntelliJ writes import lines for you.
  3. The class block for the whole plugin. extends JavaPlugin makes it a Paper plugin, which is how the server knows it may call onEnable and onDisable.
  4. A label that says "this method is one that JavaPlugin expects". If you misspell the method name, this label makes the compiler stop with an error instead of silently ignoring your method.
  5. There is no main. This method is your starting point, and the server calls it once when your plugin starts.
  6. Thing: getLogger(), the plugin's logger. Action: info, which writes a line to the server console. Input: the text.
  7. Tells the server about the listener class below, so the server knows to call it when players join. This runs second, after Step 1's log line.
  8. Runs last. After this line, onEnable is finished and the server carries on starting.
  9. Called once when the server stops. A good place to save data.
GreetingListener.javaCompiles on Paper 26.3Compile Lab
Where this file livesjava-how-code-reads-pluginsrcmainjavacomexamplejavahowcodereadspluginGreetingListener.java

The package com.example.javahowcodereadsplugin is the folder path com/example/javahowcodereadsplugin inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.

  • java-how-code-reads-plugin/
    • src/main/
      • java/com/example/javahowcodereadsplugin/Package com.example.javahowcodereadsplugin
        • GreetingListener.javayou are hereListener: reacts to events
        • HowCodeReadsPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
      • resources/Files copied into the jar as they are
        • plugin.ymlTells Paper the plugin's name, version and main class
    • build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
    • gradle.propertiesVersion numbers used by the build
    • gradlew.batRuns Gradle on Windows without installing it
    • settings.gradle.ktsThe project's name

Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.

1package com.example.javahowcodereadsplugin;2 3import org.bukkit.entity.Player;4import org.bukkit.event.EventHandler;5import org.bukkit.event.Listener;6import org.bukkit.event.player.PlayerJoinEvent;7 8public final class GreetingListener implements Listener {9 10    @EventHandler11    public void onJoin(PlayerJoinEvent event) {12        Player player = event.getPlayer();13        player.sendRichMessage("<gold>Hello, " + player.getName() + "!");14        player.sendRichMessage("<gray>This message came from your plugin.");15        player.sendRichMessage("<gray>Your plugin ran these lines in order, top to bottom.");16    }17}
  1. Marks this class as a listener: a class whose methods react to things that happen in the game.
  2. Tells Paper "call the next method when its event happens".
  3. The server calls this method every time a player joins. You never call it yourself.
  4. Asks the event which player joined and stores the answer under the name player, so the next lines can use it.
  5. Thing: player. Action: sendRichMessage. Input: text glued together with the player's name using +, just like TextAndNumbers did.
  6. The second chat line. It always arrives after the first.
  7. The third line, last of all.

And plugin.yml tells the server which class is the main class:

plugin.ymlCompiles on Paper 26.3Compile Lab
Where this file livesjava-how-code-reads-pluginsrcmainresourcesplugin.yml

Files in src/main/resources are copied into the jar exactly as they are. Paper looks for plugin.yml at the top of the jar.

  • java-how-code-reads-plugin/
    • src/main/
      • java/com/example/javahowcodereadsplugin/Package com.example.javahowcodereadsplugin
        • GreetingListener.javaListener: reacts to events
        • HowCodeReadsPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
      • resources/Files copied into the jar as they are
        • plugin.ymlyou are hereTells Paper the plugin's name, version and main class
    • build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
    • gradle.propertiesVersion numbers used by the build
    • gradlew.batRuns Gradle on Windows without installing it
    • settings.gradle.ktsThe project's name

Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.

1name: HowCodeReads2version: '1.0.0'3main: com.example.javahowcodereadsplugin.HowCodeReadsPlugin4api-version: '26.3'5description: Logs its startup steps and greets players, to show the order code runs in.
  1. The full name of the class the server creates and calls onEnable on: package plus class name.
  2. The Paper version this plugin was written for.

When you start the server, join, and stop the server again, the console shows each step in order:

Server console
[14:05:10 INFO]: [HowCodeReads] Enabling HowCodeReads v1.0.0[14:05:10 INFO]: [HowCodeReads] Step 1: the server called onEnable.[14:05:10 INFO]: [HowCodeReads] Step 2: registering the join listener.[14:05:10 INFO]: [HowCodeReads] Step 3: setup is done. Waiting for players...[14:05:42 INFO]: Steve joined the game[14:09:00 INFO]: [HowCodeReads] Disabling HowCodeReads v1.0.0[14:09:00 INFO]: [HowCodeReads] The server is stopping. Goodbye!

And Steve sees this in chat when he joins:

Steve joined the game
Hello, Steve!
This message came from your plugin.
Your plugin ran these lines in order, top to bottom.
Plain Java programPaper plugin
Where it startsmainonEnable, called by the server
Who starts itYou, with Run or javaThe Paper server, when it starts
When it endsAt the end of mainWhen the server stops (onDisable)
Printing textIO.println to the terminalgetLogger().info to the console, sendRichMessage to players
How it is builtjava Hello.javaGradle compiles it into a jar for the plugins folder

You can download this plugin as a Gradle project, open it in IntelliJ and run it with Run Paper Server, as shown in Your first plugin.

Practice

Try it

Your server's welcome board

Start from this program and make it print a welcome board for a server: a title line, two rules, and a line about the starter kit. The number of ender pearls in the kit must be calculated by Java as 5 stacks of 16, not typed in as 80.

WelcomeBoardStart.javaRuns on Java 25
1void main() {2    IO.println("Welcome to my server!");3}

Your output should look like this:

Output
=== Welcome to Skyblock Island ===Rule 1: Be kind in chat.Rule 2: No griefing.Starter kit: 80 ender pearls
Hint 1

Each line of output needs its own IO.println(...); statement.

Hint 2

To put a calculated number in the middle of text, glue three pieces together: "Starter kit: " + (5 * 16) + " ender pearls".

Show the solution

Four statements, run top to bottom. The parentheses around 5 * 16 make Java calculate before gluing.

WelcomeBoard.javaRuns on Java 25
1void main() {2    IO.println("=== Welcome to Skyblock Island ===");3    IO.println("Rule 1: Be kind in chat.");4    IO.println("Rule 2: No griefing.");5    IO.println("Starter kit: " + (5 * 16) + " ender pearls");6}
Try it

Fix the shop program

This program has two mistakes. Find and fix both, using the error messages to guide you.

FixMe.javaRuns on Java 25
1void main() {2    IO.println("The shop is open!")3    Io.println("Diamonds cost 5 coins.");4    IO.println("Have fun shopping!");5}
Hint 1

The compiler only reports one error. Fix line 2 first, then run again.

Hint 2

After the first fix, the compiler finds the second mistake:

FixMeHalfway.javaRuns on Java 25
1void main() {2    IO.println("The shop is open!");3    Io.println("Diamonds cost 5 coins.");4    IO.println("Have fun shopping!");5}
Show the solution

Line 2 was missing its semicolon, and line 3 said Io instead of IO. The compiler only showed the second mistake after the first was fixed, which is why you always fix the top error and try again.

FixMeFixed.javaRuns on Java 25
1void main() {2    IO.println("The shop is open!");3    IO.println("Diamonds cost 5 coins.");4    IO.println("Have fun shopping!");5}
Try it

Add a line to the plugin

In HowCodeReadsPlugin, add a fourth log line at the end of onEnable that prints how many items a double chest holds. A double chest has 54 slots, and each slot holds 64 items. Let Java do the multiplication.

Hint

Copy one of the existing getLogger().info(...) lines and change the text. Glue the number in with +, just like the welcome board.

Show the solution

Add this as the last statement inside onEnable, after the "Step 3" line:

HowCodeReadsPlugin.java (new line)
1getLogger().info("A double chest holds " + (54 * 64) + " items.");

Restart the server and the console shows [HowCodeReads] A double chest holds 3456 items. right after Step 3, because it is the last statement in the block.

Want more? The Practice Arena has short challenges for this chapter that check your answer with real tests.

Recap

  • A program is a list of instructions that run from top to bottom, in order.
  • Java is compiled first (javac turns .java into .class bytecode) and then run by the JVM. Gradle does both steps for plugins.
  • Most lines read as "thing, dot, action, input in parentheses, semicolon": player.sendRichMessage("Hi");.
  • Statements end with ;. Braces { } group statements into blocks. Indentation is only for humans.
  • Java is case sensitive. Classes use PascalCase, methods and variables use camelCase, constants use UPPER_SNAKE_CASE.
  • Compile errors stop the code from building; runtime errors (exceptions) stop it while it runs. Both tell you the file and line.
  • Plugins have no main. The server calls onEnable, your event handlers and onDisable, and each of them runs top to bottom.

Quick quiz

  1. What does the compiler (javac) do?

  2. In player.sendRichMessage("<green>Hi");, which part is the action (the method)?

  3. Your code says Player player = event.getplayer(); and IntelliJ shows "cannot find symbol". What is wrong?

  4. A program prints "Loading...", then crashes with ArithmeticException: / by zero. What kind of error is this?

  5. A plugin has no main method. How does its code ever start running?

Next steps