Paper Plugin Guide
File mode0

Start here

Your first plugin, step by step

Create a project, write a welcome message, run a real Paper server and see it in game.

Beginner31 min read

On this page you will make a real Paper plugin from nothing: get a project, write a few lines of Java, start a Paper 26.3 test server with one click, join it from Minecraft, and see your plugin greet you in chat. Then you will build the plugin into a jar file you could put on any server.

You do not need to understand every word yet. This page is about getting something working and seeing the whole journey once. Every piece you touch here has its own chapter later, and the last section maps each piece to the page that explains it.

What you will build

Your first plugin does two small things. When the server starts, it writes a line in the server's console, the text window where the server reports what it is doing. When a player joins, it sends that player a welcome message in chat:

Welcome to the server, Steve!
This message comes from your very first plugin.
Steve joined the game

The gold and gray lines come from your plugin. The yellow line is Minecraft's normal join message, which the server sends last, after every plugin has had its turn. Small, but it uses the same pieces as every big plugin: a main class the server starts, a listener that reacts to something players do, and a jar file that carries it all.

Step 1: get a project

A plugin starts life as a project: a folder with your code, a few settings files, and a recipe that tells Gradle how to turn your code into a plugin. Gradle is the build tool: it downloads the Paper code your plugin needs and packs everything into a jar. Nobody types those settings files from scratch; you start from a ready-made project. Pick one of these ways:

This works on any computer, and it is the way this page assumes.

  1. Download the starter project for this page (a small zip file).
  2. Open your Downloads folder, right-click the zip and choose Extract All....
  3. Windows makes a folder called first-plugin-start with a second first-plugin-start folder inside it. The inner one, the folder that contains build.gradle.kts, is your project. Move that inner folder somewhere you keep code, for example C:\Users\YourName\IdeaProjects, which IntelliJ uses by default.

The starter already has everything set up: the Gradle files, a main class called FirstPlugin, a plugin.yml, and two ready-made buttons for IntelliJ called Run Paper Server and Build Plugin Jar.

If you have the paper-templates folder on your PC, its new-plugin.cmd script makes a fresh project with your names already filled in. Open PowerShell and type:

PowerShell
 cd C:\Users\jacec\IdeaProjects\paper-templates .\new-plugin.cmd -Name FirstPlugin -Open

The script copies the clean-slate template to C:\Users\jacec\IdeaProjects\first-plugin, renames the package to com.example.firstplugin and the main class to FirstPlugin, and -Open opens it in IntelliJ for you, so you can skip the "File > Open" part of Step 2.

Two small differences from the starter download: the template's plugin.yml says '${version}' instead of '1.0.0' (Gradle fills that in when it builds, so leave it), and the main class may contain a few test lines. You will replace the whole main class in Step 3 anyway.

With the Minecraft Development plugin installed, IntelliJ can create a plugin project for you: FileNewProject..., choose Minecraft on the left, then pick Paper as the platform and fill in the names.

The Minecraft project wizard in IntelliJ

It works, but the wizard makes a Maven project (with a pom.xml file) instead of a Gradle one, and it has no Run Paper Server button, so the steps on this page will not match exactly. Use one of the other two ways for now. Anatomy of a plugin project shows what the Maven version looks like.

Step 2: open the project in IntelliJ

  1. Open the folder

    Start IntelliJ IDEA and choose FileOpen... (or Open on the welcome screen). Select the project folder itself, the one that contains build.gradle.kts, and click OK. Do not pick a folder above it or a single file inside it.

    Select the project folder, not a file inside it
  2. Trust the project

    IntelliJ asks whether you trust this project, because opening a Gradle project runs its build script. You made it (or downloaded it from this guide), so click Trust Project.

    Choose Trust Project
  3. Wait for Gradle

    The first time, Gradle downloads itself and the Paper API, the code library that lets your plugin talk to the server. A progress bar runs at the bottom of the window. This can take a few minutes on the first project and a few seconds after that. Until it finishes, IntelliJ may underline code in red because it does not know the Paper classes yet. That is normal; wait for the bar to disappear.

  4. Find your files

    The project tree on the left shows your files. Click the small arrows to open src, main, java, and the folders inside, until you see FirstPlugin.java. Also open resources to find plugin.yml.

    Your code lives under src/main/java, and plugin.yml under src/main/resources

These are the only two files you will change on this page:

  • first-plugin-start/
    • build.gradle.ktsThe build recipe. You will not change it today
    • src/
      • main/
        • java/
          • com/example/firstplugin/
            • FirstPlugin.javaYour main class: the server starts it
        • resources/
          • plugin.ymlTells Paper the plugin's name, version and main class

The folder com/example/firstplugin may show up in IntelliJ as one line, com.example.firstplugin. That is the same thing, written with dots: a package, which is Java's word for a folder of code. Anatomy of a plugin project goes through every other file in the project.

Step 3: write the main class

Every plugin has exactly one main class. A class is a block of Java code with a name; the main class is the one Paper creates when your plugin starts. Double-click FirstPlugin.java to open it, and make it look exactly like this. If you used the starter download, it already does.

FirstPlugin.javaCompiles on Paper 26.3Compile Lab
Where this file livesfirst-plugin-startsrcmainjavacomexamplefirstpluginFirstPlugin.java

The package com.example.firstplugin is the folder path com/example/firstplugin 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.

  • first-plugin-start/
    • src/main/
      • java/com/example/firstplugin/Package com.example.firstplugin
        • FirstPlugin.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.firstplugin;2 3import org.bukkit.plugin.java.JavaPlugin;4 5public final class FirstPlugin extends JavaPlugin {6 7    @Override8    public void onEnable() {9        getLogger().info("Hello! FirstPlugin is up and running.");10    }11 12    @Override13    public void onDisable() {14        getLogger().info("Goodbye! FirstPlugin is shutting down.");15    }16}
  1. Says which package (folder) this file lives in. It must match the real folders: com/example/firstplugin.
  2. Borrows JavaPlugin from the Paper API. Without this line, Java would not know what JavaPlugin means.
  3. Creates a class called FirstPlugin. The file name must be the class name plus .java. public lets Paper see it; final means no other class builds on top of it.
  4. The key part: your class is a kind of JavaPlugin. It gets everything a plugin needs (a logger, access to the server, and much more) for free.
  5. An annotation, a label for Java. It says "this replaces a method that JavaPlugin already has". If you misspell the method name, Java now tells you instead of silently ignoring it.
  6. A method is a named list of instructions. Paper calls onEnable once, when your plugin starts. This is where every plugin sets itself up.
  7. Writes a line in the server console. getLogger() gets your plugin's logger, and .info(...) writes an ordinary information line. The text between the double quotes is printed as-is.
  8. Paper calls onDisable once, when the server stops. Big plugins save their data here.
  9. Writes a farewell line as the server stops. The braces { } mark where each method and the class begin and end. Every { needs a matching }, and every instruction ends with a semicolon ;.

Notice what is missing: you never call onEnable yourself, and there is no "start here" line. Paper is the program that runs. Your plugin is a set of methods that Paper calls at the right moments. That is the biggest difference between plugin code and the small Java programs in How code reads.

Now open plugin.yml. This small file is how Paper finds your main class inside the jar:

plugin.ymlCompiles on Paper 26.3Compile Lab
Where this file livesfirst-plugin-startsrcmainresourcesplugin.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.

  • first-plugin-start/
    • src/main/
      • java/com/example/firstplugin/Package com.example.firstplugin
        • FirstPlugin.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: FirstPlugin2version: '1.0.0'3main: com.example.firstplugin.FirstPlugin4api-version: '26.3'5description: My first Paper plugin.
  1. Your plugin's name, shown in the console and in /plugins. Letters, digits and underscores only, no spaces.
  2. Your plugin's version. The quotes keep it as text.
  3. The full name of your main class: package, a dot, then the class name. If this does not match your code exactly, including capital letters, the plugin will not load.
  4. The Paper version you wrote the plugin for. Paper uses it to decide how to treat your plugin.
  5. Optional. A short sentence about the plugin, shown by /version FirstPlugin.

plugin.yml explained covers every other field you can put here.

Step 4: start a test server

You do not need to download or set up a Minecraft server. The project includes a run configuration called Run Paper Server: a saved button that builds your plugin, downloads Paper 26.3, and starts a server on your own PC with your plugin already loaded.

  1. Pick Run Paper Server

    At the top of the IntelliJ window there is a drop-down list next to a green triangle. Choose Run Paper Server in it.

    Pick Run Paper Server, then press the green arrow
  2. Press the green Run button

    A Run window opens at the bottom. The first time, the server is downloaded first, so give it a minute. Later starts take a few seconds.

  3. Watch for your line

    The console fills with text. Look for the lines that start with [FirstPlugin]. When you see Done, the server is ready.

This is what the first run looks like (many lines are left out where you see ...):

Run: Run Paper Server
> Task :compileJava> Task :processResources> Task :classes> Task :jar> Task :runServerFetching latest Paper build for version 26.3...Latest build for 26.3 is 143.Downloading Paper 26.3 build 143...Starting Paper...[14:02:03 INFO]: [bootstrap] Loading Paper 26.3-143-main@ff3655a (2026-10-02T14:13:32Z) for Minecraft 26.3[14:02:03 INFO]: [PluginInitializerManager] Bukkit plugins (1): - FirstPlugin (1.0.0)...[14:02:07 WARN]: You have used the Paper command line EULA agreement flag....[14:02:08 INFO]: Starting Minecraft server on *:25565[14:02:08 INFO]: [FirstPlugin] Loading server plugin FirstPlugin v1.0.0[14:02:09 INFO]: Preparing level "world"...[14:02:11 INFO]: [FirstPlugin] Enabling FirstPlugin v1.0.0[14:02:11 INFO]: [FirstPlugin] Hello! FirstPlugin is up and running....[14:02:12 INFO]: Done (9.299s)! For help, type "help"

Read it from the top. The > Task lines are Gradle: it compiles your code (turns your Java text into a form the computer can run) and packs it into a jar, the single file a plugin is shipped in. Then it downloads Paper and starts it. Paper lists the plugins it found, says it is loading yours, builds the world, and then enables it. "Enabling" is the moment Paper calls your onEnable method, which is why your "Hello!" line comes right after it.

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
What happens to your plugin when the server starts

A few lines look scary but are normal on a test server:

  • The EULA lines. Every Minecraft server must accept Mojang's EULA (the rules for running a server). The test server accepts it with a setting in the build file, and Paper reminds you of that with these yellow warnings.
  • WARNING: A terminally deprecated method in sun.misc.Unsafe has been called. This comes from a library inside Paper, not from your plugin. Ignore it.

The Run window is also a server console you can type into. Click in it, type plugins and press Enter. The server lists your plugin, in green because it is enabled:

Run: Run Paper Server
 plugins[14:02:20 INFO]: Server Plugins (1):[14:02:20 INFO]: Bukkit Plugins:[14:02:20 INFO]:  - FirstPlugin

Commands typed in the console have no slash in front. In the game you would type /plugins.

Step 5: join your server

Leave the server running. Start Minecraft Java Edition 26.3, click Multiplayer, then Direct Connection, type localhost as the server address, and click Join Server. localhost means "this computer".

Direct Connection, server address localhost

You arrive in a fresh world. Nothing special happens yet, because so far your plugin only writes to the console. Back in IntelliJ the console shows you joining:

Run: Run Paper Server
[14:03:41 INFO]: Steve[/127.0.0.1:52134] logged in with entity id 87 at ([world]8.5, 64.0, 8.5)[14:03:41 INFO]: Steve joined the game

Step 6: greet players with a listener

Now make the plugin do something players can see. When something happens in the game, such as a player joining, Paper creates an event and offers it to every plugin that wants it. A listener is a class that says "tell me about these events". You need two changes: a new listener class, and one line in the main class that hands the listener to Paper.

Create the listener class

  1. Make a new class

    In the project tree, right-click the com.example.firstplugin package and choose NewJava Class. Type JoinListener and press Enter. IntelliJ creates JoinListener.java next to FirstPlugin.java, with the package line already filled in.

  2. Type the code

    Make the file look exactly like the code below. When IntelliJ shows a name in red, click it and press Alt+Enter to add the missing import line. If it offers several classes with the same name, pick the one from org.bukkit.

JoinListener.javaCompiles on Paper 26.3Compile Lab
Where this file livesfirst-pluginsrcmainjavacomexamplefirstpluginJoinListener.java

The package com.example.firstplugin is the folder path com/example/firstplugin 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.

  • first-plugin/
    • src/main/
      • java/com/example/firstplugin/Package com.example.firstplugin
        • FirstPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
        • JoinListener.javayou are hereListener: reacts to events
      • 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.firstplugin;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 JoinListener implements Listener {9 10    @EventHandler11    public void onJoin(PlayerJoinEvent event) {12        Player player = event.getPlayer();13        player.sendRichMessage("<gold>Welcome to the server, " + player.getName() + "!");14        player.sendRichMessage("<gray>This message comes from your very first plugin.");15    }16}
  1. Same package as the main class, because the file sits in the same folder.
  2. Four imports, one for each Paper type this file uses: Player, EventHandler, Listener and PlayerJoinEvent.
  3. Marks this class as a listener. Without it, registerEvents in the main class would not accept it and Java would report an error.
  4. Tells Paper "call the method below when its event happens". A method without this label is never called.
  5. The part in brackets, PlayerJoinEvent event, is what matters: it means "run me whenever a player joins". The name onJoin is just a description; you could call it anything.
  6. Asks the event which player joined and keeps that player in a variable called player, a named box for a value, so the next lines can use it.
  7. Sends a chat message to this one player. <gold> is a MiniMessage color tag. The + signs glue three pieces of text together: the start of the sentence, the player's name, and "!".
  8. A second message, in gray. Each sendRichMessage call makes one chat line.

Gluing the name into the message is safe here because Minecraft names only contain letters, digits and underscores. For text that players type themselves, such as chat, you will use placeholders instead; MiniMessage shows how.

Register the listener

A listener does nothing until Paper knows about it. Open FirstPlugin.java and add one line at the start of onEnable:

FirstPlugin.javaCompiles on Paper 26.3Compile Lab
Where this file livesfirst-pluginsrcmainjavacomexamplefirstpluginFirstPlugin.java

The package com.example.firstplugin is the folder path com/example/firstplugin 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.

  • first-plugin/
    • src/main/
      • java/com/example/firstplugin/Package com.example.firstplugin
        • FirstPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
        • JoinListener.javaListener: reacts to events
      • 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.firstplugin;2 3import org.bukkit.plugin.java.JavaPlugin;4 5public final class FirstPlugin extends JavaPlugin {6 7    @Override8    public void onEnable() {9        getServer().getPluginManager().registerEvents(new JoinListener(), this);10        getLogger().info("Hello! FirstPlugin is up and running.");11    }12 13    @Override14    public void onDisable() {15        getLogger().info("Goodbye! FirstPlugin is shutting down.");16    }17}
  1. The new line. Read it from left to right: get the server, get its plugin manager (the part of Paper that looks after plugins), and register events. new JoinListener() creates your listener; this means "this plugin", so Paper knows who owns it.
  2. The log line from before. It now comes after the registration, so seeing it in the console also tells you the listener was registered without problems.

Try it

Your server is still running the old version of the plugin. Java code cannot be swapped into a running server safely, so you restart it:

  1. Click in the console, type stop and press Enter. The server saves the world and shuts down. Your onDisable line appears on the way out: [FirstPlugin] Goodbye! FirstPlugin is shutting down.
  2. Press the green Run button again. Gradle compiles your new code and builds a new jar before the server starts, so you never need to build by hand while testing.
  3. In Minecraft, join localhost again.
Welcome to the server, Steve!
This message comes from your very first plugin.
Steve joined the game

That is your plugin talking. Only you see the gold and gray lines, because the code sends them to player, the player who joined.

The edit-run loop

What you just did is how every plugin developer works, all day: change the code, run the server, test in the game, stop the server, and go again.

1. Edit your code in IntelliJ, then save 2. Run Paper Server builds the jar, starts Paper 3. Join and test localhost in Minecraft 4. Stop the server red square, or type stop Repeat for every change
The loop you will repeat for every change

Try one round now. Change <gold> to <aqua> in JoinListener, stop the server, run it, and join again. The welcome line is now aqua. A few habits make the loop smooth:

  • Stop with stop. Typing stop saves the world cleanly. The red square Stop button in IntelliJ also works, but it can end the server abruptly, like pulling a plug, so use stop when you can.
  • Stay in the game. When the server stops, Minecraft shows "Connection Lost". Click Back to Server List; once the server is up again, join from the list.
  • Read the console after every start. If your code has a problem, the console says so in red. Fixing it right away is much easier than guessing later.
  • Never use reload commands to load new code. Stop and start the server instead. Build, install and test explains why.

Step 7: build the plugin jar

Run Paper Server builds a jar for its own use. When you want to give the plugin to a real server, build it on purpose. Pick Build Plugin Jar in the run drop-down and press the green Run button. Or open the terminal in IntelliJ (ViewTool WindowsTerminal) and type:

Terminal
 .\gradlew.bat build> Task :compileJava> Task :processResources> Task :classes> Task :jar> Task :assemble> Task :compileTestJava NO-SOURCE> Task :processTestResources NO-SOURCE> Task :testClasses UP-TO-DATE> Task :test NO-SOURCE> Task :check UP-TO-DATE> Task :buildBUILD SUCCESSFUL in 9s3 actionable tasks: 3 executed

BUILD SUCCESSFUL means the jar is ready. You find it in the project's build\libs folder. In IntelliJ, open build and then libs in the project tree; in File Explorer, right-click the jar in IntelliJ and choose Open InExplorer.

The finished plugin jar in build\libs

The jar's name is the project name plus the version: first-plugin-start-1.0.0.jar for the starter download, or first-plugin-1.0.0.jar if you used new-plugin.cmd. That single file is your whole plugin. Copy it into the plugins folder of any Paper 26.3 server, restart that server, and your welcome message works there too. Build, install and test covers installing in detail.

When something goes wrong

Almost every first-plugin problem is one of these. The console nearly always tells you which, so read it from the first red line.

You seeWhat it meansFix
Minecraft 26.1 and newer requires running the server with Java 25 or above.The server was started with an older Java.Install Java 25 (see Your toolbox). Run Paper Server picks Java 25 automatically through the build file, so this usually appears when you start a server some other way.
**** FAILED TO BIND TO PORT! and Address already in useAnother server is already running on port 25565, the network "door" Minecraft servers use.You probably pressed Run twice. Stop the other server (check every tab in the Run window), then start again.
Cannot find main class `com.example.firstplugin.Firstplugin'The main: line in plugin.yml does not match your class.Copy the package and class name exactly, capitals included.
Your plugin is red in plugins, or missing from itIt failed to load or enable.Scroll up in the console to the first ERROR line that mentions your plugin.
No welcome message, and no errorThe listener is not registered, or @EventHandler is missing.Check the registerEvents line and the annotation.
Red code everywhere in IntelliJGradle has not finished importing, or a build file changed.Wait for the progress bar, or open the Gradle window and press the reload button.

For any other message, paste it into the Error Doctor, or look it up in the Error encyclopedia.

What you just did

You touched almost every part of plugin development in one page. Here is each piece in plain words, and where to learn it properly:

What you didWhat it really wasLearn more
Downloaded a starter folderA Gradle project: your code plus the build recipeAnatomy of a plugin project
Wrote plugin.ymlThe label that tells Paper your plugin's name and main classplugin.yml explained
Wrote class FirstPlugin extends JavaPluginA class that builds on Paper's plugin classClasses and objects, Inheritance
Filled in onEnable and onDisableMethods Paper calls when your plugin starts and stopsThe main class and plugin lifecycle
Wrote JoinListenerA listener with an event handler for PlayerJoinEventEvents and listeners
Wrote "<gold>Welcome..."Formatted text in MiniMessageMiniMessage
Used Player player = ... and +A variable and text joiningVariables and types, Working with text
Pressed Run Paper Server, typed stop, built the jarThe development loopBuild, install and test

You can download the finished plugin from this page to compare it with yours, or open it in the Compile Lab to edit and compile it in your browser.

Try it

Make it yours

Change the welcome so it uses your server's name and your favorite colors. Then add a third line that tells players how to get help, for example "Type /help if you are lost". Stop, run, join and check the result.

Hint 1

Each sendRichMessage call is one chat line. Copy the line with <gray>, paste it under itself, and change the text.

Hint 2

Colors you can try: <red>, <green>, <aqua>, <light_purple>, <yellow>. Add <bold> after a color to make the text bold.

Show the solution

One possible answer. The text inside the quotes is up to you; what matters is that each line keeps its quotes, its closing bracket and its semicolon.

JoinListener.java (inside onJoin)
1Player player = event.getPlayer();2player.sendRichMessage("<aqua><bold>Welcome to Skyreach, " + player.getName() + "!");3player.sendRichMessage("<gray>This message comes from your very first plugin.");4player.sendRichMessage("<yellow>Type /help if you are lost.");
Welcome to Skyreach, Steve!
This message comes from your very first plugin.
Type /help if you are lost.
Try it

How many players are online?

Change the welcome so it also tells the player how many players are online right now, counting themselves, and ends with a friendly line of your choice.

Hint 1

A player can reach the server with player.getServer(). Type a dot after it and look for a method about online players.

Hint 2

getOnlinePlayers() gives you all online players as a collection, and .size() on a collection tells you how many things are in it. Store the result in an int (a whole number) variable.

Show the solution

The new line counts the players first and keeps the number in online. Then the number is glued into the message with +, just like the name.

JoinListener.javaCompiles on Paper 26.3Compile Lab
Where this file livesfirst-plugin-exercisesrcmainjavacomexamplefirstpluginJoinListener.java

The package com.example.firstplugin is the folder path com/example/firstplugin 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.

  • first-plugin-exercise/
    • src/main/
      • java/com/example/firstplugin/Package com.example.firstplugin
        • FirstPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
        • JoinListener.javayou are hereListener: reacts to events
      • 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.

10@EventHandler11public void onJoin(PlayerJoinEvent event) {12    Player player = event.getPlayer();13    int online = player.getServer().getOnlinePlayers().size();14    player.sendRichMessage("<gold>Welcome to the server, " + player.getName() + "!");15    player.sendRichMessage("<gray>Players online right now: <green>" + online);16    player.sendRichMessage("<aqua>Have fun and be nice!");17}
  1. The player who just joined already counts as online, so on your test server this is 1.
  2. Two colors in one line: the label is gray, the number is green.
Welcome to the server, Steve!
Players online right now: 1
Have fun and be nice!

Recap

  • A plugin project is a folder with your Java code, plugin.yml and a Gradle build recipe. Start from the starter download or new-plugin.cmd.
  • The main class extends JavaPlugin. Paper calls its onEnable when the plugin starts and onDisable when it stops.
  • plugin.yml tells Paper the plugin's name, version, main class and api-version. The main line must match your class exactly.
  • A listener class implements Listener, has @EventHandler methods, and must be registered in onEnable.
  • Run Paper Server builds your plugin and starts a test server; join it with localhost. Stop it with stop and run again after every change.
  • Build Plugin Jar (or .\gradlew.bat build) puts the finished jar in build\libs.

Quick quiz

  1. When does Paper call your plugin's onEnable method?

  2. You wrote JoinListener perfectly, but nothing appears when you join, and the console shows no error. What is the most likely reason?

  3. Your plugin.yml says main: com.example.firstplugin.Firstplugin but the class is called FirstPlugin. What happens?

  4. You changed the welcome message while the test server was running. How do you see the change in the game?

  5. Where does Build Plugin Jar put the finished plugin?

Next steps