Files
OpenWebUI-RAG/games/minecraft/develoment/server/papermc/papermc-dev-docs.md
2025-11-16 21:20:46 +07:00

10547 lines
344 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

```markdown
# PaperMC Knowledge Base
## General
### Contributing
* [Contributing](Contributing) - Information on how to contribute to the PaperMC project.
### Miscellaneous
* [Using databases](Using databases) - Recommended way to store a large amount of data.
* [Debugging your plugin](Debugging your plugin) - Common ways to debug your plugin.
* [Minecraft internals](Minecraft internals) - Overview of how to use internals in your plugin.
* [Reading stacktraces](Reading stacktraces) - Basics of how to read stacktraces produced by the JVM when an exception occurs.
## API
### Command API
* [Command API](Command API) - Guide to Paper's Brigadier command API.
* **Basics**
* [Introduction](Command%20API) - A guide to Paper's Brigadier command API.
* [Command trees](Command%20trees) - An extensive guide to building up a command tree.
* [Arguments and literals](Arguments%20and%20literals) - An extensive guide to command arguments and literals.
* [Executors](Executors) - A guide to execution logic for Brigadier commands.
* [Registration](Registration) - A guide to registering Brigadier commands.
* [Requirements](Requirements) - A guide to setting requirements for commands.
* [Suggestions](Suggestions) - Documentation about defining custom argument suggestions.
* [Custom arguments](Custom%20arguments) - Guide on custom arguments.
* **Arguments**
* [Minecraft-specific](Minecraft-specific) - Everything regarding the essential Brigadier arguments.
* [Location](Location) - BlockPosition, FinePosition and World argument documentation.
* [Entities and players](Entities%20and%20players) - Player and Entity arguments documentation.
* [Registry](Registry) - Documentation for arguments retrieving registry values.
* [Paper-specific](Paper-specific) - Documentation for arguments handling miscellaneous Paper API values.
* [Enums](Enums) - Documentation for EntityAnchor, GameMode and similar enum value arguments.
* [Predicates](Predicates) - Documentation for arguments that allow value validation.
* [Adventure](Adventure) - Documentation for all arguments returning Adventure API objects.
* **Miscellaneous**
* [Basic commands](Basic%20commands) - An overview of a Bukkit-style command declaration using Brigadier.
* [Comparison](Comparison) - A comparison between Brigadier and Bukkit commands.
### Component API
* [Component API](Component%20API) - Guide to Adventure components.
* [Introduction](Component%20API) - An introduction to how components work.
* [Internationalization](Internationalization) - How to use Adventure's internationalization.
* [Audiences](Audiences) - How to use Adventure's Audiences.
* [Signed messages](Signed%20messages) - A guide to working with SignedMessage objects.
### Event API
* [Event API](Event%20API) - Guide to Paper's event system.
* [Listeners](Listeners) - Developer guide for how to listen to the broadcasted events.
* [Custom events](Custom%20events) - A guide to show you how to add custom events to your plugin.
* [Handler lists](Handler%20lists) - An explanation to what an event's HandlerList is.
* [Chat events](Chat%20events) - An outline on AsyncChatEvent and how to handle it.
### Entity API
* [Entity API](Entity%20API) - Documentation for working with entities.
* [Teleportation](Teleportation) - Guide on entity teleportation.
* [Display entities](Display%20entities) - Guide on display entities.
### Inventories
* [Inventories](Inventories) - Documentation for working with inventories.
* [Menu Type API](Menu%20Type%20API) - A guide to the Menu Type API.
* [Custom InventoryHolders](Custom%20InventoryHolders) - How to use a custom InventoryHolder to identify custom inventories.
### Lifecycle API
* [Lifecycle API](Lifecycle%20API) - Documentation for Paper's Lifecycle API.
* [Introduction](Lifecycle%20API) - An introduction to Paper's Lifecycle API.
* [Datapack discovery](Datapack%20discovery) - A guide to including datapacks in your plugin and registering them with the lifecycle API.
### Experimental
* [Data components](Data%20components) - A guide to the ItemStack data component API.
* [Persistent data container (PDC)](Persistent%20data%20container%20(PDC)) - A guide to the PDC API for storing data.
* [Scheduling](Scheduling) - A guide on how to use BukkitScheduler to run code at specific times.
* [Plugin messaging](Plugin%20messaging) - How to communicate with clients or proxies.
* [Plugin configuration](Plugin%20configuration) - How to create configuration files for your plugins to customize behavior.
* [Registries](Registries) - A guide to registries and their modification on Paper.
* [Dialog API](Dialog%20API) - A guide to the dialog API introduced in 1.21.7.
* [Recipes](Recipes) - How to create and manage recipes.
* [Particles](Particles) - A comprehensive guide to particle spawning.
## Development
* [Development](Development) - Welcome to the Paper development guide!
* [Getting started](Development#getting-started)
* [Paper plugins](Paper%20plugins) - A development guide for how to write Paper-specific plugins.
* [Project setup](Project%20setup) - Step-by-step instructions on how to set up a plugin development environment.
* [How plugins work](How%20plugins%20work) - How plugins work in Paper.
* [plugin.yml](plugin.yml) - A guide to Bukkit's plugin.yml file.
* [paperweight-userdev](paperweight-userdev) - A guide on how to use the paperweight-userdev Gradle plugin to access internal code.
## Command API - Arguments
### Minecraft-specific Arguments
#### Location Argument
* **Block position argument**
* Used for retrieving the position of a block. Works the same way as the first argument of the `/setblock <position> <block>` Vanilla command.
* Returns a `BlockPosition` variable from the `BlockPositionResolver`.
* Example Usage:
```java
public static LiteralCommandNode<CommandSourceStack> blockPositionArgument() {
return Commands.literal("blockpositionargument")
.then(Commands.argument("arg", ArgumentTypes.blockPosition())
.executes(ctx -> {
final BlockPositionResolver blockPositionResolver = ctx.getArgument("arg", BlockPositionResolver.class);
final BlockPosition blockPosition = blockPositionResolver.resolve(ctx.getSource());
ctx.getSource().getSender().sendPlainMessage(
"Put in " + blockPosition.x() + " " + blockPosition.y() + " " + blockPosition.z());
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Fine position argument**
* Works similarly to the block position argument, with the difference being that it can accept decimal (precise) location input.
* The optional overload (`ArgumentTypes.finePosition(boolean centerIntegers)`), which defaults to false if not set, will center whole input, meaning 5 becomes 5.5 (5.0 would stay as 5.0 though), as that is the “middle” of a block. This only applies to X/Z. The y coordinate is untouched by this operation.
* Returns a `FinePositionResolver`. You can resolve that by running `FinePositionResolver#resolve(CommandSourceStack)` to get the resulting `FinePosition`.
* Example Usage:
```java
public static LiteralCommandNode<CommandSourceStack> finePositionArgument() {
return Commands.literal("fineposition")
.then(Commands.argument("arg", ArgumentTypes.finePosition(true))
.executes(ctx -> {
final FinePositionResolver resolver = ctx.getArgument("arg", FinePositionResolver.class);
final FinePosition finePosition = resolver.resolve(ctx.getSource());
ctx.getSource().getSender().sendRichMessage(
"Position: <red><x></red> <green><y></green> <blue><z></blue>",
Placeholder.unparsed("x", Double.toString(finePosition.x())),
Placeholder.unparsed("y", Double.toString(finePosition.y())),
Placeholder.unparsed("z", Double.toString(finePosition.z()))
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **World argument**
* Allows the user to select one of the currently loaded worlds.
* Returns a generic Bukkit `World` object.
* Example Usage:
```java
public static LiteralCommandNode<CommandSourceStack> worldArgument() {
return Commands.literal("teleport-to-world")
.then(Commands.argument("world", ArgumentTypes.world())
.executes(ctx -> {
final World world = ctx.getArgument("world", World.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.COMMAND);
ctx.getSource().getSender().sendRichMessage(
"Successfully teleported <player> to <aqua><world></aqua>",
Placeholder.component("player", player.name()),
Placeholder.unparsed("world", world.getName())
);
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendRichMessage("<red>This command requires a player!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### Entities and players Argument
* The arguments described in this section relate to arguments which you can use to retrieve entities. Their main usage is the selection of command targets.
* All of these have entity selectors (`@a`, `@e`, `@n`, etc.) as valid inputs, though they require the `minecraft.command.selector` permission in order to be able to be used. The specific arguments may allow or disallow certain selectors.
* Due to the permission requirement for selectors it is advised to add a `requires` statement to your command:
`.requires(ctx -> ctx.getSender().hasPermission("minecraft.command.selector"))`
* **Entity argument**
* Returns a list of exactly one entity.
* Returns an `EntitySelectorArgumentResolver`.
* Safe to call `List#getFirst()` to retrieve that entity.
* You can resolve it using `ArgumentResolver#resolve(CommandSourceStack)`
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> entityArgument() {
return Commands.literal("entityarg")
.then(Commands.argument("arg", ArgumentTypes.entity())
.executes(ctx -> {
final EntitySelectorArgumentResolver entitySelectorArgumentResolver = ctx.getArgument("arg", EntitySelectorArgumentResolver.class);
final List<Entity> entities = entitySelectorArgumentResolver.resolve(ctx.getSource());
ctx.getSource().getSender().sendRichMessage(
"Found <green><entityname>",
Placeholder.component("entityname", entities.getFirst().name())
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Entities argument**
* Accepts any amount of entities, with the minimum amount of entities being 1.
* Returns an `EntitySelectorArgumentResolver`.
* Can be resolved using `ArgumentResolver#resolve(CommandSourceStack)`, which returns a `List<Entity>`.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> entitiesArgument() {
return Commands.literal("entitiesarg")
.then(Commands.argument("arg", ArgumentTypes.entities())
.executes(ctx -> {
final EntitySelectorArgumentResolver entitySelectorArgumentResolver = ctx.getArgument("arg", EntitySelectorArgumentResolver.class);
final List<Entity> entities = entitySelectorArgumentResolver.resolve(ctx.getSource());
final Component foundEntities = Component.join(JoinConfiguration.commas(true),
entities.stream().map(Entity::name).toList());
ctx.getSource().getSender().sendRichMessage(
"Found <green><entitynames>",
Placeholder.component("entitynames", foundEntities)
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Player argument**
* Allows to retrieve a `PlayerSelectorArgumentResolver` for player arguments.
* For this “single player” argument, you can safely get the target player by running `PlayerSelectorArgumentResolver.resolve(ctx.getSource()).getFirst()`, which returns a `Player` object.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> playerArgument() {
return Commands.literal("player")
.then(Commands.argument("target", ArgumentTypes.player())
.executes(ctx -> {
final PlayerSelectorArgumentResolver targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver.class);
final Player target = targetResolver.resolve(ctx.getSource()).getFirst();
target.setVelocity(new Vector(0, 100, 0));
target.sendRichMessage("<rainbow>Yeeeeeeeeeet");
ctx.getSource().getSender().sendRichMessage(
"Yeeted <target>!",
Placeholder.component("target", target.name())
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Players argument**
* The “multiple players” argument works similarly to the “single player” argument, also returning a `PlayerSelectorArgumentResolver`.
* Instead of just resolving to exactly one `Player`, this one can resolve to more than just one player - which you should account for in case of using this argument.
* `PlayerSelectorArgumentResolver.resolve(ctx.getSource())` returns a `List<Player>`, which you can just iterate through.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> playersArgument() {
return Commands.literal("players")
.then(Commands.argument("targets", ArgumentTypes.players())
.executes(ctx -> {
final PlayerSelectorArgumentResolver targetResolver = ctx.getArgument("targets", PlayerSelectorArgumentResolver.class);
final List<Player> targets = targetResolver.resolve(ctx.getSource());
final CommandSender sender = ctx.getSource().getSender();
for (final Player target : targets) {
target.setVelocity(new Vector(0, 100, 0));
target.sendRichMessage("<rainbow>Yeeeeeeeeeet");
sender.sendRichMessage(
"Yeeted <target>!",
Placeholder.component("target", target.name())
);
}
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Player profiles argument**
* Can retrieve both offline and online players.
* Returns the result of the argument as a `PlayerProfileListResolver`, which resolves to a `Collection<PlayerProfile>`.
* This collection can be iterated to get the resulting profile(s). Usually, it only returns a single `PlayerProfile` if retrieving a player by name, but it can return multiple if using the entity selectors (like `@a` on online players). Thus it always makes sense to run whatever operation you want to run on all entries in the collection instead of just the first one.
* This argument will run API calls to Mojang servers in order to retrieve player information for players which have never joined the server before.
* It is suggested to resolve this argument in an asynchronous context in order to not cause any server lag.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> playerProfilesArgument() {
return Commands.literal("lookup")
.then(Commands.argument("profile", ArgumentTypes.playerProfiles())
.executes(ctx -> {
final PlayerProfileListResolver profilesResolver = ctx.getArgument("profile", PlayerProfileListResolver.class);
final Collection<PlayerProfile> foundProfiles = profilesResolver.resolve(ctx.getSource());
for (final PlayerProfile profile : foundProfiles) {
ctx.getSource().getSender().sendPlainMessage("Found " + profile.getName());
}
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### Enum Argument
* **Entity anchor argument**
* Has two valid inputs: `feet` and `eyes`.
* The resulting `LookAnchor` is mainly used for methods like `Entity#lookAt(Position, LookAnchor)` or `Player#lookAt(Entity, LookAnchor, LookAnchor)`.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> entityAnchorArgument() {
return Commands.literal("entityanchor")
.then(Commands.argument("arg", ArgumentTypes.entityAnchor())
.executes(ctx -> {
final LookAnchor lookAnchor = ctx.getArgument("arg", LookAnchor.class);
ctx.getSource().getSender().sendRichMessage(
"You chose <aqua><anchor></aqua>!",
Placeholder.unparsed("anchor", lookAnchor.name())
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **GameMode argument**
* Works the same way as the first argument of the Vanilla `/gamemode <gamemode>` command.
* Accepts any of the 4 valid game modes, returning a `GameMode` enum to use in code.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> gameModeArgument() {
return Commands.literal("gamemodearg")
.then(Commands.argument("arg", ArgumentTypes.gameMode())
.executes(ctx -> {
final GameMode gamemode = ctx.getArgument("arg", GameMode.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.setGameMode(gamemode);
player.sendRichMessage(
"Your gamemode has been set to <red><gamemode></red>!",
Placeholder.component("gamemode", Component.translatable(gamemode))
);
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendPlainMessage("This command requires a player!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **HeightMap argument**
* Consists of the following, valid inputs: `motion_blocking`, `motion_blocking_no_leaves`, `ocean_floor`, and `world_surface`.
* It is often used for declaring relative positioning for data packs or the `/execute positioned over <height_map>` command.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> heightMapArgument() {
return Commands.literal("heightmap")
.then(Commands.argument("arg", ArgumentTypes.heightMap())
.executes(ctx -> {
final HeightMap heightMap = ctx.getArgument("arg", HeightMap.class);
ctx.getSource().getSender().sendRichMessage(
"You selected <gold><selection></gold>",
Placeholder.unparsed("selection", heightMap.name())
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Scoreboard display slot argument**
* Allows you to retrieve a `DisplaySlot` enum value from the user.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> scoreboardDisplaySlotArgument() {
return Commands.literal("scoreboarddisplayslot")
.then(Commands.argument("slot", ArgumentTypes.scoreboardDisplaySlot())
.executes(ctx -> {
final DisplaySlot slot = ctx.getArgument("slot", DisplaySlot.class);
ctx.getSource().getSender().sendPlainMessage("You selected: " + slot.getId());
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Template mirror argument**
* The user has 3 valid input possibilities: `front_back`, `left_right`, and `none`.
* You can retrieve the result of the argument as a `Mirror` enum value.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> templateMirrorArgument() {
return Commands.literal("templatemirror")
.then(Commands.argument("mirror", ArgumentTypes.templateMirror())
.executes(ctx -> {
final Mirror mirror = ctx.getArgument("mirror", Mirror.class);
ctx.getSource().getSender().sendPlainMessage("You selected: " + mirror.name());
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Template rotation argument**
* The user has 4 valid input possibilities: `180`, `clockwise_90`, `counterclockwise_90`, and `none`.
* You can retrieve the result of the argument as a `StructureRotation` enum value.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> templateRotationArgument() {
return Commands.literal("templaterotation")
.then(Commands.argument("rotation", ArgumentTypes.templateRotation())
.executes(ctx -> {
final StructureRotation rotation = ctx.getArgument("rotation", StructureRotation.class);
ctx.getSource().getSender().sendPlainMessage("You selected: " + rotation.name());
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### Adventure Argument
* **Component argument**
* Expects the JSON representation of a text component, making it inappropriate for general user input.
* The result is returned as an Adventure component to work with.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> componentArgument() {
return Commands.literal("componentargument")
.then(Commands.argument("arg", ArgumentTypes.component())
.executes(ctx -> {
final Component component = ctx.getArgument("arg", Component.class);
ctx.getSource().getSender().sendRichMessage(
"Your message: <input>",
Placeholder.component("input", component)
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Key argument**
* Allows a user to put in any artificial (namespaced) key, ensuring its validity.
* Returns a `Key`, which can be used at various other places in the Paper API.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> keyArgument() {
return Commands.literal("key")
.then(Commands.argument("key_input", ArgumentTypes.key())
.executes(ctx -> {
final Key key = ctx.getArgument("key_input", Key.class);
ctx.getSource().getSender().sendRichMessage(
"You put in <aqua><key></aqua>!",
Placeholder.unparsed("key", key.asString())
);
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* **Named color argument**
* Provides the user with the ability to select between the 16 built-in “named” text colors.
* Returns a `NamedTextColor`, which you can use for applying a color to components.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> namedColorArgument() {
return Commands.literal("namedcolor")
.then(Commands.argument("color", ArgumentTypes.namedColor())
.then(Commands.argument("message", StringArgumentType.greedyString())
.executes(ctx -> {
final NamedTextColor color = ctx.getArgument("color", NamedTextColor.class);
final String msg = StringArgumentType.getString(ctx, "message");
ctx.getSource().getSender().sendMessage(Component.text(msg).color(color));
return Command.SINGLE_SUCCESS;
})))
.build();
}
```
* **Adventure style argument**
* Similar to the component argument, this argument is not really appropriate for general user input, as it also follows the JSON format for displaying components.
* Returns its value in the form of a `Style` object.
* This can be applied to any component using `Component#style(Style)`.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> styleArgument() {
return Commands.literal("style")
.then(Commands.argument("style", ArgumentTypes.style())
.then(Commands.argument("message", StringArgumentType.greedyString())
.executes(ctx -> {
final Style style = ctx.getArgument("style", Style.class);
final String msg = StringArgumentType.getString(ctx, "message");
ctx.getSource().getSender().sendRichMessage(
"Your input: <input>",
Placeholder.component("input", Component.text(message).style(style))
);
return Command.SINGLE_SUCCESS;
})))
.build();
}
```
* **Signed message argument**
* Allows a player to send an argument in the form of a signed message to the server.
* Returns a `SignedMessageResolver`.
* In order to call its `#resolve` method, you have to pass in two parameters:
* The argument name
* The `CommandContext<CommandSourceStack>` object
* The resolved value is a `CompletableFuture<SignedMessage>`, whose `SignedMessage` value you can handle using `thenAccept(Consumer<T>)`.
* Example usage:
```java
public static LiteralCommandNode<CommandSourceStack> signedMessageArgument() {
return Commands.literal("signedmessage")
.then(Commands.argument("target", ArgumentTypes.player())
.then(Commands.argument("message", ArgumentTypes.signedMessage())
.executes(MinecraftArguments::executeSignedMessageCommand)))
.build();
}
private static int executeSignedMessageCommand(final CommandContext<CommandSourceStack> ctx)
throws CommandSyntaxException {
final Player target = ctx.getArgument("target", PlayerSelectorArgumentResolver.class)
.resolve(ctx.getSource())
.getFirst();
final SignedMessageResolver messageResolver = ctx.getArgument("message", SignedMessageResolver.class);
messageResolver.resolveSignedMessage("message", ctx)
.thenAccept(msg -> {
target.sendMessage(msg, ChatType.CHAT.bind(ctx.getSource().getSender().name()));
});
return Command.SINGLE_SUCCESS;
}
```
## Event API - Chat Events
### Chat Events
* The chat event has evolved a few times over the years.
* This guide will explain how to properly use the new `AsyncChatEvent` and its `ChatRenderer`.
* The `AsyncChatEvent` is an improved version of the old `AsyncPlayerChatEvent` that allows you to render chat messages individually for each player.
#### AsyncChatEvent vs ChatEvent
* The key difference between `AsyncChatEvent` and `ChatEvent` is that `AsyncChatEvent` is fired asynchronously.
* This means that it does not block the main thread and sends the chat message when the listener has completed.
* Be aware that using the Bukkit API in an asynchronous context (i.e. the event handler) is unsafe and exceptions may be thrown.
* If you need to use the Bukkit API, you can use `ChatEvent`.
* However, we recommend using `BukkitScheduler`.
#### Understanding the renderer
* The renderer is Papers way of allowing plugins to modify the chat message before it is sent to the player.
* This is done by using the `ChatRenderer` interface with its `ChatRenderer#render(Player, Component, Component, Audience)` method.
* Previously, this was done by using the `AsyncPlayerChatEvent` with its `AsyncPlayerChatEvent#setFormat(String)` method.
```java
ChatRenderer#render
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
// ...
}
```
* The `render` method is called when a chat message is sent to the player.
* `source`: The player that sent the message.
* `sourceDisplayName`: The display name of the player that sent the message.
* `message`: The message that was sent.
* `viewer`: The player that is receiving the message.
* **ChatRenderer.ViewerUnaware**
* If your renderer does not need to know about the viewer, you can use the `ChatRenderer.ViewerUnaware` interface instead of the `ChatRenderer` interface.
* This will benefit performance as the message will only be rendered once instead of each individual player.
#### Using the renderer
* There are two ways to use the renderer:
1. Implementing the `ChatRenderer` interface in a class.
2. Using a lambda expression.
* Depending on the complexity of your renderer, you may want to use one or the other.
#### Implementing the ChatRenderer interface
* Tell the event to use the renderer by using the `AbstractChatEvent#renderer()` method.
```java
ChatListener.java
public class ChatListener implements Listener, ChatRenderer {
// Implement the ChatRenderer and Listener interface
// Listen for the AsyncChatEvent
@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer(this);
// Tell the event to use our renderer
}
// Override the render method
@Override
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
// ...
}
}
```
* **Note:** If you decide to create a separate class for your renderer, it is important to know that you dont need to instantiate the class every time the event is called. In this case, you can use the singleton pattern to create a single instance of the class.
#### Using a lambda expression
```java
ChatListener.java
public class ChatListener implements Listener {
@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer((source, sourceDisplayName, message, viewer) -> {
// ...
});
}
}
```
#### Rendering the message
* Return a new `Component` that contains the message you want to send.
```java
ChatListener.java
public class ChatListener implements Listener, ChatRenderer {
// Listener logic
@Override
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
return sourceDisplayName.append(Component.text(": ")).append(message);
}
}
```
# Command API Arguments
This knowledge base covers the various arguments available in the PaperMC Command API.
## Minecraft-specific Arguments
The `ArgumentTypes` class provides access to Minecraft-specific arguments.
### Quick Overview
| Method Name | Return Value | Quick Link |
| -------------------------- | ------------------------------ | --------------------------------------------- |
| `blockPosition()` | `BlockPositionResolver` | [Block Position Argument](#block-position-argument) |
| `blockState()` | `BlockState` | [Block State Argument](#block-state-argument) |
| `component()` | `Component (Kyori)` | [Component Argument](#component-argument) |
| `doubleRange()` | `DoubleRangeProvider` | [Double Range argument](#double-range-argument) |
| `entity()` | `EntitySelectorArgumentResolver`| [Entity Argument](#entity-argument) |
| `entities()` | `EntitySelectorArgumentResolver`| [Entities Argument](#entities-argument) |
| `entityAnchor()` | `LookAnchor` | Entity Anchor Argument |
| `finePosition(boolean centerIntegers)` | `FinePositionResolver` | [Fine Position Argument](#fine-position-argument) |
| `gameMode()` | `GameMode` | GameMode Argument |
| `heightMap()` | `HeightMap` | HeightMap Argument |
| `integerRange()` | `IntegerRangeProvider` | [Integer Range Argument](#integer-range-argument) |
| `itemPredicate()` | `ItemStackPredicate` | [Item Predicate Argument](#item-predicate-argument) |
| `itemStack()` | `ItemStack` | [ItemStack Argument](#itemstack-argument) |
| `key()` | `Key (Kyori)` | [Key Argument](#key-argument) |
| `namedColor()` | `NamedTextColor (Kyori)` | [Named Color Argument](#named-color-argument) |
| `namespacedKey()` | `NamespacedKey` | [Bukkit NamespacedKey Argument](#namespacedkey-argument) |
| `objectiveCriteria()` | `Criteria` | [Objective Criteria Argument](#objective-criteria-argument) |
| `player()` | `PlayerSelectorArgumentResolver`| [Player Argument](#player-argument) |
| `players()` | `PlayerSelectorArgumentResolver`| [Players Argument](#players-argument) |
| `playerProfiles()` | `PlayerProfileListResolver` | [Player Profiles Argument](#player-profiles-argument) |
| `resource(RegistryKey)` | `(Depends on RegistryKey)` | [Resource Argument](#resource-argument) |
| `resourceKey(RegistryKey)` | `(Depends on RegistryKey)` | [Resource Key Argument](#resource-key-argument) |
| `style()` | `Style (Kyori)` | [Style Argument](#adventure-style-argument) |
| `signedMessage()` | `SignedMessageResolver` | [Signed Message Argument](#signed-message-argument) |
| `scoreboardDisplaySlot()` | `DisplaySlot` | Scoreboard Display Slot Argument |
| `time(int mintime)` | `Integer` | [Time Argument](#time-argument) |
| `templateMirror()` | `Mirror` | Template Mirror Argument |
| `templateRotation()` | `StructureRotation` | Template Rotation Argument |
| `uuid()` | `UUID` | [UUID Argument](#uuid-argument) |
| `world()` | `World` | [World Argument](#world-argument) |
## Location Arguments
These arguments are used for retrieving location-based data.
### Block position argument
The block position argument is used for retrieving the position of a block.
It works the same way as the first argument of the `/setblock <position> <block>` Vanilla command.
* To retrieve the `BlockPosition` variable from the `BlockPositionResolver`, it must be resolved using the command source.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> blockPositionArgument() {
return Commands
.literal("blockpositionargument")
.then(Commands.argument("arg", ArgumentTypes.blockPosition())
.executes(ctx -> {
final BlockPositionResolver blockPositionResolver = ctx.getArgument("arg", BlockPositionResolver.class);
final BlockPosition blockPosition = blockPositionResolver.resolve(ctx.getSource());
ctx.getSource().getSender().sendPlainMessage(
"Put in " + blockPosition.x() + " " + blockPosition.y() + " " + blockPosition.z());
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Fine position argument
The fine position argument works similarly to the block position argument, with the only difference being that it can accept decimal (precise) location input.
The optional overload `ArgumentTypes.finePosition(boolean centerIntegers)`, which defaults to false if not set, will center whole input, meaning 5 becomes 5.5 (5.0 would stay as 5.0 though), as that is the “middle” of a block. This only applies to X/Z. The y coordinate is untouched by this operation.
* This argument returns a `FinePositionResolver`.
* Resolve by running `FinePositionResolver#resolve(CommandSourceStack)` to get the resulting `FinePosition`.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> finePositionArgument() {
return Commands
.literal("fineposition")
.then(Commands.argument("arg", ArgumentTypes.finePosition(true))
.executes(ctx -> {
final FinePositionResolver resolver = ctx.getArgument("arg", FinePositionResolver.class);
final FinePosition finePosition = resolver.resolve(ctx.getSource());
ctx.getSource().getSender().sendRichMessage("Position: <red><x></red> <green><y></green> <blue><z></blue>",
Placeholder.unparsed("x", Double.toString(finePosition.x())),
Placeholder.unparsed("y", Double.toString(finePosition.y())),
Placeholder.unparsed("z", Double.toString(finePosition.z())));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### World argument
This argument allows the user to select one of the currently loaded world.
* You can retrieve the result as a generic Bukkit `World` object.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> worldArgument() {
return Commands
.literal("teleport-to-world")
.then(Commands.argument("world", ArgumentTypes.world())
.executes(ctx -> {
final World world = ctx.getArgument("world", World.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.COMMAND);
ctx.getSource().getSender().sendRichMessage("Successfully teleported <player> to <aqua><world></aqua>",
Placeholder.component("player", player.name()),
Placeholder.unparsed("world", world.getName()));
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendRichMessage("<red>This command requires a player!</red>");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
## Entities and Players Arguments
These arguments are used for retrieving entities and players.
### Entity argument
* This argument returns an `EntitySelectorArgumentResolver`.
* Use `ArgumentResolver#resolve(CommandSourceStack)` to resolve the entity.
* The `getFirst()` method returns a single `Entity` object.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> entityArgument() {
return Commands
.literal("entity")
.then(Commands.argument("target", ArgumentTypes.entity())
.executes(ctx -> {
final EntitySelectorArgumentResolver targetResolver = ctx.getArgument("target", EntitySelectorArgumentResolver.class);
final Entity entity = targetResolver.resolve(ctx.getSource()).getFirst();
ctx.getSource().getSender().sendRichMessage("Found <green><entityname></green>",
Placeholder.component("entityname", entity.name()));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
If the executing player doesnt have the `minecraft.command.selector` permission:
Your device does not support video playback.
If the executing player has the `minecraft.command.selector` permission:
Your device does not support video playback.
### Entities argument
In contrast to the single entity argument, this multiple-entities argument accepts any amount of entities, with the minimum amount of entities being 1.
* They can be resolved using `ArgumentResolver#resolve(CommandSourceStack)`, which returns a `List<Entity>`.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> entitiesArgument() {
return Commands
.literal("entitiesarg")
.then(Commands.argument("arg", ArgumentTypes.entities())
.executes(ctx -> {
final EntitySelectorArgumentResolver entitySelectorArgumentResolver = ctx.getArgument("arg", EntitySelectorArgumentResolver.class);
final List<Entity> entities = entitySelectorArgumentResolver.resolve(ctx.getSource());
final Component foundEntities = Component.join(JoinConfiguration.commas(true), entities.stream().map(Entity::name).toList());
ctx.getSource().getSender().sendRichMessage("Found <green><entitynames></green>",
Placeholder.component("entitynames", foundEntities));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Player argument
The player argument allows to retrieve a `PlayerSelectorArgumentResolver` for player arguments.
* For this “single player” argument, you can safely get the target player by running `PlayerSelectorArgumentResolver.resolve(ctx.getSource()).getFirst()`, which returns a `Player` object.
#### Example usage
This command yeets the targeted player into the air!
```java
public static LiteralCommandNode<CommandSourceStack> playerArgument() {
return Commands
.literal("player")
.then(Commands.argument("target", ArgumentTypes.player())
.executes(ctx -> {
final PlayerSelectorArgumentResolver targetResolver = ctx.getArgument("target", PlayerSelectorArgumentResolver.class);
final Player target = targetResolver.resolve(ctx.getSource()).getFirst();
target.setVelocity(new Vector(0, 100, 0));
target.sendRichMessage("<rainbow>Yeeeeeeeeeet</rainbow>");
ctx.getSource().getSender().sendRichMessage("Yeeted <target>!",
Placeholder.component("target", target.name()));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Players argument
The “multiple players” argument works similarly to the “single player” argument, also returning a `PlayerSelectorArgumentResolver`.
* Instead of just resolving to exactly one `Player`, this one can resolve to more than just one player - which you should account for in case of using this argument.
* `PlayerSelectorArgumentResolver.resolve(ctx.getSource())` returns a `List<Player>`, which you can just iterate through.
#### Example usage
Extending the “single player” yeet command to support multiple targets can look like this:
```java
public static LiteralCommandNode<CommandSourceStack> playersArgument() {
return Commands
.literal("players")
.then(Commands.argument("targets", ArgumentTypes.players())
.executes(ctx -> {
final PlayerSelectorArgumentResolver targetResolver = ctx.getArgument("targets", PlayerSelectorArgumentResolver.class);
final List<Player> targets = targetResolver.resolve(ctx.getSource());
final CommandSender sender = ctx.getSource().getSender();
for (final Player target : targets) {
target.setVelocity(new Vector(0, 100, 0));
target.sendRichMessage("<rainbow>Yeeeeeeeeeet</rainbow>");
sender.sendRichMessage("Yeeted <target>!",
Placeholder.component("target", target.name()));
}
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Player profiles argument
The player profiles argument is a very powerful argument which can retrieve both offline and online players.
* It returns the result of the argument as a `PlayerProfileListResolver`, which resolves to a `Collection<PlayerProfile>`.
* This collection can be iterated to get the resulting profile(s).
* Usually, it only returns a single `PlayerProfile` if retrieving a player by name, but it can return multiple if using the entity selectors (like `@a` on online players).
* It always makes sense to run whatever operation you want to run on all entries in the collection instead of just the first one.
* This argument will run API calls to Mojang servers in order to retrieve player information for players which have never joined the server before.
* It is suggested to resolve this argument in an asynchronous context in order to not cause any server lag.
* Sometimes, these API calls may fail.
#### Example usage - player lookup command
```java
public static LiteralCommandNode<CommandSourceStack> playerProfilesArgument() {
return Commands
.literal("lookup")
.then(Commands.argument("profile", ArgumentTypes.playerProfiles())
.executes(ctx -> {
final PlayerProfileListResolver profilesResolver = ctx.getArgument("profile", PlayerProfileListResolver.class);
final Collection<PlayerProfile> foundProfiles = profilesResolver.resolve(ctx.getSource());
for (final PlayerProfile profile : foundProfiles) {
ctx.getSource().getSender().sendPlainMessage("Found " + profile.getName());
}
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
## Registry Arguments
Registries in Minecraft hold information about item or block types, enchantments, potion effects, and more.
There are two types of registry arguments: `resource` and `resourceKey`.
* `resource` argument returns the parsed value.
* `resourceKey` only returns a `TypedKey`, which you can use to retrieve the value yourself.
### Resource argument
* You can get a `ArgumentType<T>` reference to it using `ArgumentTypes.resource(RegistryKey<T>)`.
* A selection of possible registry keys can be found below.
* They are accessed in a static context using the `RegistryKey` interface.
* Each entry in `RegistryKey` returns a `RegistryKey<T>`.
* The `<T>` generic parameter describes the return type.
#### Example
```java
public static LiteralCommandNode<CommandSourceStack> enchantmentRegistry() {
return Commands
.literal("enchants-registry")
.then(Commands.argument("enchantment", ArgumentTypes.resource(RegistryKey.ENCHANTMENT))
.executes(ctx -> {
final Enchantment enchantment = ctx.getArgument("enchantment", Enchantment.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
final ItemStack stack = player.getInventory().getItemInMainHand();
stack.addUnsafeEnchantment(enchantment, 10);
ctx.getSource().getSender().sendRichMessage(
"Enchanted <player>'s <item> with <enchantment>!",
Placeholder.component("player", player.name()),
Placeholder.component("item", Component.translatable(stack)),
Placeholder.component("enchantment", enchantment.displayName(10))
);
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendRichMessage("<red>This command requires a player!</red>");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
#### Caution
There are certain edge-cases, where this argument, due to missing registries on the client, will cause a `Network Protocol Error`.
Basically, the only argument where this is the case right now is with the `STRUCTURE` registry key.
```java
// Registering this command will cause clients to not be able to connect to the server.
final LiteralCommandNode<CommandSourceStack> invalidRegistryArgument = Commands
.literal("registry-structure")
.then(Commands.argument("value", ArgumentTypes.resource(RegistryKey.STRUCTURE)))
.build();
```
Due to this fact, it is advised to only use the `STRUCTURE` registry key argument with a `resourceKey(...)` argument type and parse the values yourself.
### Resource key argument
For the client, there is barely any difference between the using `ArgumentTypes.resource` or `ArgumentTypes.resourceKey`. The only difference is that using `ArgumentTypes.resourceKey` does not provide error checking.
The resource argument provides a much cleaner user experience, whilst the `resourceKey` argument has one very important use case: You get the raw `TypedKey<T>` returned as an argument result.
This object is particularly useful, as it provides all information required to be able to retrieve a value from a registry yourself.
Unless you have a specific reason for using the `resourceKey` argument over the `resource` one, the `resource` argument is preferred due to the client-side error checking and simple usability.
#### Direct code comparison
Here is a simple code snipped on how one could use the `RegistryKey.ITEM` registry with a `resource` argument type:
```java
Commands
.argument("item", ArgumentTypes.resource(RegistryKey.ITEM))
.executes(ctx -> {
final ItemType item = ctx.getArgument("item", ItemType.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(item.createItemStack());
}
return Command.SINGLE_SUCCESS;
});
```
Here is the same code, using a `resourceKey` argument type. Instead of directly retrieving the argument using `ctx.getArgument("item", TypedKey.class)`, we instead use the `RegistryArgumentExtractor` to retrieve our `TypedKey<ItemType>`.
```java
Commands
.argument("item", ArgumentTypes.resourceKey(RegistryKey.ITEM))
.executes(ctx -> {
final TypedKey<ItemType> itemKey = RegistryArgumentExtractor.getTypedKey(ctx, RegistryKey.ITEM, "item");
ItemType item = RegistryAccess.registryAccess().getRegistry(itemKey.registryKey()).get(itemKey.key());
if (item == null) {
ctx.getSource().getSender().sendRichMessage("<red>Please provide a valid item!</red>");
return Command.SINGLE_SUCCESS;
}
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(item.createItemStack());
}
return Command.SINGLE_SUCCESS;
});
```
#### Using a TypedKey
1. In order to get the correct registry, you can run `RegistryAccess#getRegistry(RegistryKey)`.
2. In order to get a `RegistryAccess`, you can just use the static `RegistryAccess.registryAccess()` method.
3. The `RegistryKey` is retrieved using `TypedKey#registryKey()`.
4. Now, in order to get the final value `T`, you can run `Registry#get(Key)`, where the key can be retrieved using `TypedKey#key()`.
5. This will return the backing instance from that resource key or null, if no value has been found.
#### Use case over resource argument
The main use case for this argument type is the ability to store the key (the value returned to you by `TypedKey#key`).
If you want to be able to store the exact user input and be able to retrieve the backed instance without much trouble, that is the way to do it.
### Registry key previews
The following `RegistryKeys` exist:
| RegistryKeys Field | Return Value | Preview Video |
| ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ATTRIBUTE` | `Attribute` | [Attribute](#attribute) |
| `BANNER_PATTERN` | `PatternType` | [Banner Pattern](#banner-pattern) |
| `BIOME` | `Biome` | [Biome](#biome) |
| `BLOCK` | `BlockType` | [Block](#block) |
| `CAT_VARIANT` | `Cat.Type` | [Cat Variant](#cat-variant) |
| `CHICKEN_VARIANT` | `Chicken.Variant` | [Chicken Variant](#chicken-variant) |
| `COW_VARIANT` | `Cow.Variant` | [Cow Variant](#cow-variant) |
| `DAMAGE_TYPE` | `DamageType` | [Damage Type](#damage-type) |
| `DATA_COMPONENT_TYPE` | `DataComponentType` | [Data Component Type](#data-component-type) |
| `DIALOG` | `Dialog` | [Dialog](#dialog) |
| `ENCHANTMENT` | `Enchantment` | [Enchantment](#enchantment) |
| `ENTITY_TYPE` | `EntityType` | [Entity Type](#entity-type) |
| `FLUID` | `Fluid` | [Fluid](#fluid) |
| `FROG_VARIANT` | `Frog.Variant` | [Frog Variant](#frog-variant) |
| `GAME_EVENT` | `GameEvent` | [Game Event](#game-event) |
| `INSTRUMENT` | `MusicInstrument` | [Instrument](#instrument) |
| `ITEM` | `ItemType` | [Item](#item) |
| `JUKEBOX_SONG` | `JukeboxSong` | [Jukebox Song](#jukebox-song) |
| `MAP_DECORATION_TYPE` | `MapCursor.Type` | [Map Decoration Type](#map-decoration-type) |
| `MEMORY_MODULE_TYPE` | `MemoryKey<?>` | [Memory Module Type](#memory-module-type) |
| `MENU` | `MenuType` | [Menu](#menu) |
| `MOB_EFFECT` | `PotionEffectType` | [Mob effect](#mob-effect) |
| `PAINTING_VARIANT` | `Art` | [Painting variant](#painting-variant) |
| `PARTICLE_TYPE` | `Particle` | [Particle](#particle) |
| `PIG_VARIANT` | `Pig.Variant` | [Pig Variant](#pig-variant) |
| `POTION` | `PotionType` | [Potion](#potion) |
| `SOUND_EVENT` | `Sound` | [Sound](#sound) |
| `STRUCTURE` | `Structure` | [Structure](#structure) |
| `STRUCTURE_TYPE` | `StructureType` | [Structure type](#structure-type) |
| `TRIM_MATERIAL` | `TrimMaterial` | [Trim material](#trim-material) |
| `TRIM_PATTERN` | `TrimPattern` | [Trim pattern](#trim-pattern) |
| `VILLAGER_PROFESSION` | `Villager.Profession` | [Villager Profession](#villager-profession) |
| `VILLAGER_TYPE` | `Villager.Type` | [Villager Type](#villager-type) |
| `WOLF_SOUND_VARIANT` | `Wolf.SoundVariant` | [Wolf Sound Variant](#wolf-sound-variant) |
| `WOLF_VARIANT` | `Wolf.Variant` | [Wolf Variant](#wolf-variant) |
#### Attribute
Your device does not support video playback.
#### Banner pattern
Your device does not support video playback.
#### Biome
Your device does not support video playback.
#### Block
Your device does not support video playback.
#### Cat variant
Your device does not support video playback.
#### Chicken variant
Your device does not support video playback.
#### Cow variant
Your device does not support video playback.
#### Damage type
Your device does not support video playback.
#### Data component type
Your device does not support video playback.
#### Dialog
Your device does not support video playback.
#### Enchantment
Your device does not support video playback.
#### Entity type
Your device does not support video playback.
#### Fluid
Your device does not support video playback.
#### Frog variant
Your device does not support video playback.
#### Game event
Your device does not support video playback.
#### Instrument
Your device does not support video playback.
#### Item
Your device does not support video playback.
#### Jukebox Song
Your device does not support video playback.
#### Map decoration type
Your device does not support video playback.
#### Memory module type
Your device does not support video playback.
#### Menu
Your device does not support video playback.
#### Mob effect
Your device does not support video playback.
#### Painting variant
Your device does not support video playback.
#### Particle
Your device does not support video playback.
#### Pig variant
Your device does not support video playback.
#### Potion
Your device does not support video playback.
#### Sound
Your device does not support video playback.
#### Structure
This argument kicks the client, so no preview for this one ¯\_(ツ)_/¯
#### Structure type
Your device does not support video playback.
#### Trim material
Your device does not support video playback.
#### Trim pattern
Your device does not support video playback.
#### Villager profession
Your device does not support video playback.
#### Villager type
Your device does not support video playback.
#### Wolf sound variant
Your device does not support video playback.
#### Wolf variant
Your device does not support video playback.
## Paper-specific Arguments
These arguments return objects frequently used in Paper API.
### Block state argument
The block state argument can be used for getting a block type and explicit, associated data.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> blockStateArgument() {
return Commands
.literal("blockstateargument")
.then(Commands.argument("arg", ArgumentTypes.blockState())
.executes(ctx -> {
final BlockState blockState = ctx.getArgument("arg", BlockState.class);
ctx.getSource().getSender().sendPlainMessage("You specified a " + blockState.getType() + "!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### ItemStack argument
The item stack argument is the way to retrieve an `ItemStack` following the same argument format as the Vanilla `/give <player> <item> [<amount>]` command as its second argument.
* The user may also define components to further customize the `ItemStack`.
* If you only require a `Material`, you should instead check out the [registry arguments](#registry-arguments).
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> itemStackArgument() {
return Commands
.literal("itemstack")
.then(Commands.argument("stack", ArgumentTypes.itemStack())
.executes(ctx -> {
final ItemStack itemStack = ctx.getArgument("stack", ItemStack.class);
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(itemStack);
ctx.getSource().getSender().sendRichMessage("<green>Successfully gave <player> a <item></green>",
Placeholder.component("player", player.name()),
Placeholder.component("item", Component.translatable(itemStack)));
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendRichMessage("<red>This argument requires a player!</red>");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### NamespacedKey argument
This argument allows the user to provide any artificial (namespaced) key.
* The return value of this argument is a `NamespacedKey`, which makes it useful when dealing with Bukkit API.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> namespacedKeyArgument() {
return Commands
.literal("namespacedkey")
.then(Commands.argument("key", ArgumentTypes.namespacedKey())
.executes(ctx -> {
final NamespacedKey key = ctx.getArgument("key", NamespacedKey.class);
ctx.getSource().getSender().sendRichMessage("You put in <aqua><key></aqua>!",
Placeholder.unparsed("key", key.toString()));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Time argument
The time argument allows the user to define a time frame, similar to the Vanilla `/time <set|time> <time>` time argument. The user has 4 possible ways of inputting time:
* Just as a number: This resolves to as usual ticks (`/timearg 1`—> 1 tick)
* With a `t` suffix: This also resolves to ticks (`/timearg 1t`—> 1 tick)
* With a `s` suffix: This resolves to seconds, meaning multiplying the first number by 20. (`/timearg 1s`—> 20 ticks)
* With a `d` suffix. This resolves as in-game days, meaning multiplying the first number by 24000. (`/timearg 1d`—> 24000 ticks)
If you choose to use this argument, it is advised to explain to the users what these suffixes mean, as real time (`s` suffix) is mixed with in-game time (`t` and `d` suffix).
The `ArgumentTypes.time()` method has one additional overload: `ArgumentTypes.time(int mintime)`. This allows to set the minimum required amount of ticks this argument has to resolve to. By default this value is set to 0.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> timeArgument() {
return Commands
.literal("timearg")
.then(Commands.argument("time", ArgumentTypes.time())
.executes(ctx -> {
final int timeInTicks = IntegerArgumentType.getInteger(ctx, "time");
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getWorld().setFullTime(player.getWorld().getFullTime() + timeInTicks);
player.sendRichMessage("Moved time forward by " + timeInTicks + " ticks!");
return Command.SINGLE_SUCCESS;
}
ctx.getSource().getSender().sendPlainMessage("This argument requires a player!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### UUID argument
The UUID argument allows the user to input a valid UUID.
* You can retrieve that value as a `UUID` object, which is used in various places, like `Bukkit.getOfflinePlayer(UUID)`.
* This argument is not very user-friendly, which is why it is suggested to only use this as a moderation or debug argument.
* For user input regarding offline player retrieval, the [player profiles argument](#player-profiles-argument) is preferred, as it allows by-name lookup.
#### Example usage - Lookup command
```java
public static LiteralCommandNode<CommandSourceStack> uuidArgument() {
return Commands
.literal("uuid-lookup")
.then(Commands.argument("uuid", ArgumentTypes.uuid())
.executes(ctx -> {
final UUID uuid = ctx.getArgument("uuid", UUID.class);
final OfflinePlayer result = Bukkit.getOfflinePlayer(uuid);
ctx.getSource().getSender().sendRichMessage("Has <aqua><uuid></aqua> played before: <result>",
Placeholder.unparsed("uuid", uuid.toString()),
Placeholder.parsed("result", result.hasPlayedBefore() ? "<green>true</green>" : "<red>false</red>"));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
### Objective criteria argument
You can retrieve the argument value as a `Criteria` enum value, which can be used with `Scoreboard` objects.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> objectiveCriteriaArgument() {
return Commands
.literal("objectivecriteria")
.then(Commands.argument("criteria", ArgumentTypes.objectiveCriteria())
.executes(ctx -> {
final Criteria criteria = ctx.getArgument("criteria", Criteria.class);
ctx.getSource().getSender().sendRichMessage("Default render type for <criteria>: <rendertype>",
Placeholder.unparsed("criteria", criteria.getName()),
Placeholder.unparsed("rendertype", criteria.getDefaultRenderType().name()));
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
#### In-game preview
Your device does not support video playback.
## Predicates Arguments
A predicate allows for checking for valid values. These arguments are dedicated to checking whether some sort of value is valid according to user input.
### Double range argument
This argument can be used as a predicate for numbers, which require precise input.
#### Example usage
```java
public static LiteralCommandNode<CommandSourceStack> doubleRangeArgument() {
return Commands
.literal("doublerange")
.then(Commands.argument("arg", ArgumentTypes.doubleRange())
.executes(ctx -> {
final DoubleRangeProvider doubleRangeProvider = ctx.getArgument("arg", DoubleRangeProvider.class);
final CommandSender sender = ctx.getSource().getSender();
for (int i = 0; i < 5; i++) {
sender.sendRich
```markdown
# Command API Knowledge Base
## Table of Contents
* [Minecraft Arguments](#minecraft-arguments)
* [Suggestions](#suggestions)
* [Command Trees](#command-trees)
* [Arguments and Literals](#arguments-and-literals)
* [Registry](#registry)
* [Executors](#executors)
## Minecraft Arguments
### Integer Range Argument
This argument only accepts integers.
#### Example Usage
```java
public
static
LiteralCommandNode<
CommandSourceStack
>
integerRangeArgument
()
{
return
Commands
.
literal
(
"
integerrange
"
)
.
then
(
Commands
.
argument
(
"
range
"
,
ArgumentTypes
.
integerRange
())
.
then
(
Commands
.
argument
(
"
tested_integer
"
,
IntegerArgumentType
.
integer
())
.
executes
(
MinecraftArguments
::
runIntegerRangeCommand
)))
.
build
()
;
}
private
static
int
runIntegerRangeCommand
(
final
CommandContext<
CommandSourceStack
>
ctx
)
{
final
IntegerRangeProvider
integerRangeProvider
=
ctx
.
getArgument
(
"
range
"
,
IntegerRangeProvider
.
class
)
;
final
int
integerToTest
=
IntegerArgumentType
.
getInteger
(
ctx,
"
tested_integer
"
)
;
if
(
integerRangeProvider
.
range
()
.
contains
(
integerToTest
)
) {
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
<aqua><input></aqua> <green>is</green> inside the specified range!
"
,
Placeholder
.
unparsed
(
"
input
"
,
Integer
.
toString
(
integerToTest
))
)
;
return
Command
.
SINGLE_SUCCESS
;
}
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
<aqua><input></aqua> <red>is not</red> inside the specified range!
"
,
Placeholder
.
unparsed
(
"
input
"
,
Integer
.
toString
(
integerToTest
))
)
;
return
Command
.
SINGLE_SUCCESS
;
}
```
#### In-Game Preview
(Video playback not supported)
### Item Predicate Argument
This argument allows for checking whether an item fits some predicate. It is useful for filtering out certain items based on some criteria.
#### Example Usage
```java
public
static
LiteralCommandNode<
CommandSourceStack
>
itemPredicateArgument
()
{
return
Commands
.
literal
(
"
itempredicate
"
)
.
then
(
Commands
.
argument
(
"
predicate
"
,
ArgumentTypes
.
itemPredicate
())
.
executes
(
ctx
->
{
final
ItemStackPredicate
predicate
=
ctx
.
getArgument
(
"
predicate
"
,
ItemStackPredicate
.
class
)
;
final
ItemStack
defaultWoodenSword
=
ItemType
.
WOODEN_SWORD
.
createItemStack
()
;
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
Does predicate include a default wooden sword? <result>
"
,
Placeholder
.
parsed
(
"
result
"
,
predicate
.
test
(
defaultWoodenSword
)
?
"
<green>true</green>
"
:
"
<red>false</red>
"
)
)
;
return
Command
.
SINGLE_SUCCESS
;
}
))
.
build
()
;
}
```
#### In-Game Preview
(Video playback not supported)
## Suggestions
Sometimes, you want to send your own suggestions to users. For this, you can use the `suggests(SuggestionProvider<CommandSourceStack>)` method when declaring arguments.
### Examining `SuggestionProvider<S>`
The `SuggestionProvider<S>` interface is defined as follows:
```java
@FunctionalInterface
public
interface
SuggestionProvider
<
S
> {
CompletableFuture
<
Suggestions
>
getSuggestions
(
final
CommandContext
<
S
>
context
,
final
SuggestionsBuilder
builder
)
throws
CommandSyntaxException
;
}
```
* For Paper, `S` is usually `CommandSourceStack`.
* It's a functional interface, so a lambda or method reference can be used.
* The lambda takes a `CommandContext<S>` and `SuggestionsBuilder` and returns a `CompletableFuture<Suggestions>`.
A very simple lambda for our suggests method might look like this:
```java
Commands
.
argument
(
"
name
"
,
StringArgumentType
.
word
())
.
suggests
(
(ctx, builder)
->
builder
.
buildFuture
())
;
```
This example obviously does not suggest anything, as we havent added any suggestions yet.
### The `SuggestionsBuilder`
The `SuggestionsBuilder` has a few methods we can use to construct our suggestions:
### Input Retrieval
The input retrieval methods are: `getInput()`, `getStart()`, `getRemaining()`, and `getRemainingLowerCase()`.
| Method | Return Value | Description |
| ---------------------- | ------------------------------- | ----------------------------------------------------------- |
| `getInput()` | `/customsuggestions Asumm13Text` | The full chat input |
| `getStart()` | `19` | The index of the first character of the arguments input |
| `getRemaining()` | `Asumm13Text` | The input for the current argument |
| `getRemainingLowerCase()` | `asumm13text` | The input for the current argument, lowercased |
Example Input: `/customsuggestions Asumm13Text`
### Suggestions
The following overloads of the `SuggestionsBuilder#suggest` method add values that will be send to the client as argument suggestions:
| Overload | Description |
| ----------------------- | -------------------------------------------------- |
| `suggest(String)` | Adds a String to the suggestions |
| `suggest(String, Message)` | Adds a String with a tooltip to the suggestions |
| `suggest(int)` | Adds an int to the suggestions |
| `suggest(int, Message)` | Adds an int with a tooltip to the suggestions |
There are two ways of retrieving a `Message` instance:
* Using `LiteralMessage`, which can be used for basic, non-formatted text.
* Using the `MessageComponentSerializer`, which can be used to serialize `Component` objects into `Message` objects.
For example, if you add a suggestion like this:
```java
builder
.
suggest
(
"
suggestion
"
,
MessageComponentSerializer
.
message
()
.
serialize
(
MiniMessage
.
miniMessage
()
.
deserialize
(
"
<green>Suggestion tooltip
"
)
))
;
```
It will look like this on the client:
### Building
There are two methods we can use to build our `Suggestions` object. The only difference between those is that one directly returns the finished `Suggestions` object, whilst the other one returns a `CompletableFuture<Suggestions>`.
The reason for these two methods is that `SuggestionProvider` expects the return value to be `CompletableFuture<Suggestions>`. This for once allows for constructing your suggestions asynchronously inside a `CompletableFuture.supplyAsync(Supplier<Suggestions>)` statement, or synchronously directly inside our lambda and returning the final `Suggestions` object asynchronously.
Here are the same suggestions declared in the two different ways mentioned above:
```java
// Here, you are safe to use all Paper API
Commands
.
argument
(
"
name
"
,
StringArgumentType
.
word
())
.
suggests
(
(ctx, builder)
->
{
builder
.
suggest
(
"
first
"
)
;
builder
.
suggest
(
"
second
"
)
;
return
builder
.
buildFuture
()
;
}
)
;
// Here, most Paper API is not usable
Commands
.
argument
(
"
name
"
,
StringArgumentType
.
word
())
.
suggests
(
(ctx, builder)
->
CompletableFuture
.
supplyAsync
(
()
->
{
builder
.
suggest
(
"
first
"
)
;
builder
.
suggest
(
"
second
"
)
;
return
builder
.
build
()
;
}
))
;
```
### Example: Suggesting Amounts in a Give Item Command
In commands, where you give players items, you oftentimes include an amount argument. We could suggest `1`, `16`, `32`, and `64` as common amounts for items given. The command implementation could look like this:
```java
@NullMarked
public
class
SuggestionsTest
{
public
static
LiteralCommandNode
<
CommandSourceStack
>
constructGiveItemCommand
()
{
// Create new command: /giveitem
return
Commands
.
literal
(
"
giveitem
"
)
// Require a player to execute the command
.
requires
(
ctx
->
ctx
.
getExecutor
()
instanceof
Player
)
// Declare a new ItemStack argument
.
then
(
Commands
.
argument
(
"
item
"
,
ArgumentTypes
.
itemStack
())
// Declare a new integer argument with the bounds of 1 to 99
.
then
(
Commands
.
argument
(
"
amount
"
,
IntegerArgumentType
.
integer
(
1
,
99
))
// Here, we use method references, since otherwise, our command definition would grow too big
.
suggests
(
SuggestionsTest
::
getAmountSuggestions
)
.
executes
(
SuggestionsTest
::
executeCommandLogic
)
)
)
.
build
()
;
}
private
static
CompletableFuture
<
Suggestions
>
getAmountSuggestions
(
final
CommandContext
<
CommandSourceStack
>
ctx
,
final
SuggestionsBuilder
builder
)
{
// Suggest 1, 16, 32, and 64 to the user when they reach the 'amount' argument
builder
.
suggest
(
1
)
;
builder
.
suggest
(
16
)
;
builder
.
suggest
(
32
)
;
builder
.
suggest
(
64
)
;
return
builder
.
buildFuture
()
;
}
private
static
int
executeCommandLogic
(
final
CommandContext
<
CommandSourceStack
>
ctx
)
{
// We know that the executor will be a player, so we can just silently return
if
(
!
(
ctx
.
getSource
()
.
getExecutor
()
instanceof
Player
player)) {
return
Command
.
SINGLE_SUCCESS
;
}
// If the player has no empty slot, we tell the player that they have no free inventory space
final
int
firstEmptySlot
=
player
.
getInventory
()
.
firstEmpty
()
;
if
(firstEmptySlot
==
-
1
) {
player
.
sendRichMessage
(
"
<light_purple>You do not have enough space in your inventory!
"
)
;
return
Command
.
SINGLE_SUCCESS
;
}
// Retrieve our argument values
final
ItemStack
item
=
ctx
.
getArgument
(
"
item
"
,
ItemStack
.
class
)
;
final
int
amount
=
IntegerArgumentType
.
getInteger
(
ctx,
"
amount
"
)
;
// Set the item's amount and give it to the player
item
.
setAmount
(
amount
)
;
player
.
getInventory
()
.
setItem
(
firstEmptySlot, item
)
;
// Send a confirmation message
player
.
sendRichMessage
(
"
<light_purple>You have been given <white><amount>x</white> <aqua><item></aqua>!
"
,
Placeholder
.
component
(
"
amount
"
,
Component
.
text
(
amount
))
,
Placeholder
.
component
(
"
item
"
,
Component
.
translatable
(
item
)
.
hoverEvent
(
item
))
)
;
return
Command
.
SINGLE_SUCCESS
;
}
}
```
And here is how the command looks in-game:
(Video playback not supported)
### Example: Filtering by User Input
If you have multiple values, it is suggested that you filter your suggestions by what the user has already put in. For this, we can declare the following, simple command as a test:
```java
public
static
LiteralCommandNode<
CommandSourceStack
>
constructStringSuggestionsCommand
()
{
final
List
<
String
>
names
=
List
.
of
(
"
Alex
"
,
"
Andreas
"
,
"
Stephanie
"
,
"
Sophie
"
,
"
Emily
"
)
;
return
Commands
.
literal
(
"
selectname
"
)
.
then
(
Commands
.
argument
(
"
name
"
,
StringArgumentType
.
word
())
.
suggests
(
(ctx, builder)
->
{
names
.
stream
()
.
filter
(
entry
->
entry
.
toLowerCase
()
.
startsWith
(
builder
.
getRemainingLowerCase
()))
.
forEach
(
builder
::
suggest
)
;
return
builder
.
buildFuture
()
;
}
)
)
.
build
()
;
}
```
This simple setup filters suggestions by user input, providing a smooth user experience when running the command:
(Video playback not supported)
## Command Trees
Command trees are the structure of Brigadier commands. This section explains how to understand and build them.
### What is a Tree?
A command tree is a hierarchical structure where the base command is the "root" and subsequent arguments or subcommands are "branches."
* **Root:** The base command (e.g., `/customplugin`).
* **Branch:** Arguments or subcommands following the root (e.g., `reload`, `tphere`).
Example:
```
/customplugin reload
/customplugin tphere
/customplugin killall
```
The root is `/customplugin`, and `reload`, `tphere`, and `killall` are branches.
### How Can We Visualize A Tree In Code?
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
root
=
Commands
.
literal
(
"
customplugin
"
)
;
```
This defines the root of the command tree. Branches can be added using the `.then(...)` method.
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
root
=
Commands
.
literal
(
"
customplugin
"
)
;
root
.
then
(
Commands
.
literal
(
"
reload
"
))
;
root
.
then
(
Commands
.
literal
(
"
tphere
"
))
;
root
.
then
(
Commands
.
literal
(
"
killall
"
))
;
```
Here, `reload`, `tphere`, and `killall` are added as subcommands (child literals) of the `customplugin` root.
### Creating a More Advanced Command
Consider the following command structure:
```
/advanced
┣━┳ killall
┃ ┣━━ entities
┃ ┣━━ players
┃ ┗━━ zombies
┗━┳ eat
┣━━ ice-cream
┗━━ main-dish
```
This allows for:
* `/advanced killall entities`
* `/advanced killall players`
* `/advanced killall zombies`
* `/advanced eat ice-cream`
* `/advanced eat main-dish`
To create this, define the literals furthest from the root first:
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
entities
=
Commands
.
literal
(
"
entities
"
)
;
LiteralArgumentBuilder
<
CommandSourceStack
>
players
=
Commands
.
literal
(
"
players
"
)
;
LiteralArgumentBuilder
<
CommandSourceStack
>
zombies
=
Commands
.
literal
(
"
zombies
"
)
;
LiteralArgumentBuilder
<
CommandSourceStack
>
iceCream
=
Commands
.
literal
(
"
ice-cream
"
)
;
LiteralArgumentBuilder
<
CommandSourceStack
>
mainDish
=
Commands
.
literal
(
"
main-dish
"
)
;
```
Then, define the next layer (`killall` and `eat`):
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
killall
=
Commands
.
literal
(
"
killall
"
)
;
LiteralArgumentBuilder
<
CommandSourceStack
>
eat
=
Commands
.
literal
(
"
eat
"
)
;
```
Add the child elements to their parents:
```java
killall
.
then
(
entities
)
;
killall
.
then
(
players
)
;
killall
.
then
(
zombies
)
;
eat
.
then
(
iceCream
)
;
eat
.
then
(
mainDish
)
;
```
Finally, create the root node and add the `killall` and `eat` subcommands:
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
advancedCommandRoot
=
Commands
.
literal
(
"
advanced
"
)
;
advancedCommandRoot
.
then
(
killall
)
;
advancedCommandRoot
.
then
(
eat
)
;
```
### Chaining `then` Method Calls Together
The `.then()` method returns the same element it was called on, allowing for chaining:
```java
killall
.
then
(
entities
)
.
then
(
players
)
.
then
(
zombies
)
;
```
This avoids storing every child node in its own variable. It can also be written as:
```java
killall
.
then
(
Commands
.
literal
(
"
entities
"
))
.
then
(
Commands
.
literal
(
"
players
"
))
.
then
(
Commands
.
literal
(
"
zombies
"
))
;
```
The entire command tree can be built using chained calls:
```java
LiteralArgumentBuilder
<
CommandSourceStack
>
advancedCommandRoot
=
Commands
.
literal
(
"
advanced
"
)
.
then
(
Commands
.
literal
(
"
eat
"
)
.
then
(
Commands
.
literal
(
"
ice-cream
"
))
.
then
(
Commands
.
literal
(
"
main-dish
"
))
)
.
then
(
Commands
.
literal
(
"
killall
"
)
.
then
(
Commands
.
literal
(
"
entities
"
))
.
then
(
Commands
.
literal
(
"
players
"
))
.
then
(
Commands
.
literal
(
"
zombies
"
))
)
;
```
## Arguments and Literals
This section explains the difference between arguments and literals in Brigadier commands and how to use them.
### Introduction
The `.then(...)` method of an `ArgumentBuilder<CommandSourceStack, ?>` takes another `ArgumentBuilder<CommandSourceStack, ?>` object.
Two implementations of `ArgumentBuilder` are:
* `RequiredArgumentBuilder`: Created with `Commands.argument(String, ArgumentType<T>)`.
* `LiteralArgumentBuilder`: Created with `Commands.literal(String)`.
**Argument:** A variable input by the user. It's semi-unpredictable but should always return a valid entry of the object it backs.
**Literal:** A non-variable input by the user. Used to define predictable input, as each literal is a new branch on the command tree.
### Literals
Literals cannot be accessed in code but define a specific branch in the command tree.
```java
Commands
.
literal
(
"
plant
"
)
.
then
(
Commands
.
literal
(
"
tree
"
)
.
executes
(
ctx
->
{
/* Here we are on /plant tree */
}
)
)
.
then
(
Commands
.
literal
(
"
grass
"
)
.
executes
(
ctx
->
{
/* Here we are on /plant grass */
}
))
;
```
The `executes` method declares logic for the branch. Without `executes`, the branch is not executable.
### Arguments
Arguments are created using `Commands.argument(String, ArgumentType<T>)`. This returns a `RequiredArgumentBuilder`. The `T` type parameter declares the return type of the argument, which can be used inside the `executes` method.
Built-in argument types:
| Name | Return value | Possible Input | Description |
| -------------------------- | ------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `BoolArgumentType.bool()` | `Boolean` | `true`/`false` | Only allows a boolean value |
| `IntegerArgumentType.integer()` | `Integer` | `253`, `-123`, `0` | Any valid integer |
| `LongArgumentType.longArg()` | `Long` | `25418263123783` | Any valid long |
| `FloatArgumentType.floatArg()` | `Float` | `253.2`, `-25.0` | Any valid float |
| `DoubleArgumentType.doubleArg()` | `Double` | `4123.242`, `-1.1` | Any valid double |
| `StringArgumentType.word()` | `String` | `letters-and+1234567` | A single word. May only contain letters and numbers and these characters: `+`, `-`, `_`, and `.`. |
| `StringArgumentType.string()` | `String` | `"with spaces"` | A single word, or, if quoted, any valid string with spaces |
| `StringArgumentType.greedyString()` | `String` | `unquoted spaces` | The literal written input. May contain any characters. Has to be the last argument |
### Boolean Argument Type and Argument Parsing
```java
Commands
.
literal
(
"
serverflight
"
)
.
then
(
Commands
.
argument
(
"
allow
"
,
BoolArgumentType
.
bool
())
.
executes
(
ctx
->
{
boolean
allowed
=
ctx
.
getArgument
(
"
allow
"
,
boolean
.
class
)
;
/* Toggle server flying */
}
)
)
;
```
The `Commands.argument(String, ArgumentType)` method takes the node name. The `executes` lambda has a method called `T getArgument(String, Class<T>)`. The first parameter is the name of the method we want to retrieve. The second parameter is the return value of the argument.
### Number Arguments
Number arguments (`IntegerArgumentType.integer()`, etc.) have three overloads:
* `IntegerArgumentType.integer()`: Any value between `Integer.MIN_VALUE` and `Integer.MAX_VALUE`.
* `IntegerArgumentType.integer(int min)`: Any value between `min` and `Integer.MAX_VALUE`.
* `IntegerArgumentType.integer(int min, int max)`: Any value between `min` and `max`.
Example:
```java
Commands
.
literal
(
"
flyspeed
"
)
.
then
(
Commands
.
argument
(
"
speed
"
,
FloatArgumentType
.
floatArg
(
0
,
1.0f
))
.
executes
(
ctx
->
{
float
speed
=
ctx
.
getArgument
(
"
speed
"
,
float
.
class
)
;
/* Set player's flight speed */
return
Command
.
SINGLE_SUCCESS
;
}
)
)
;
```
Some arguments have special ways of being retrieved:
```java
float
speed
=
FloatArgumentType
.
getFloat
(
ctx,
"
speed
"
)
;
```
Parsers for Brigadier-native arguments exist:
* `BoolArgumentType.getBool`
* `IntegerArgumentType.getInteger`
* `LongArgumentType.getLong`
* `FloatArgumentType.getFloat`
* `DoubleArgumentType.getDouble`
* `StringArgumentType.getString`
Native arguments provide client-side error checking.
### String Arguments
Three string arguments: `word`, `string`, and `greedyString`.
* `word`: Only accepts a single word consisting of alphanumerical characters and these special characters: `+`, `-`, `_`, and `.`.
* `string`: If unquoted, it follows the same rules as `word`. If quoted, any combination of unicode characters is allowed. Quotes can be escaped using a backslash `\`.
* `greedyString`: Does not perform any parsing and allows any input. Must be the last argument. Quotes are counted as literal characters.
### Further Reference
#### Minecraft Arguments
Custom arguments are defined by Paper and accessed via the `ArgumentTypes` class.
#### Custom Arguments
Implement the `CustomArgumentType<T, N>` interface to define your own arguments.
## Registry
Registries in Minecraft hold information like item or block types, enchantments, potion effects, and more.
Two types of registry arguments:
* `resource`: Returns the parsed value.
* `resourceKey`: Returns a `TypedKey`, which can be used to retrieve the value.
### Resource Argument
Get a `ArgumentType<T>` reference using `ArgumentTypes.resource(RegistryKey<T>)`. Possible registry keys are accessed statically through the `RegistryKey` interface.
Example:
```java
public
static
LiteralCommandNode<
CommandSourceStack
>
enchantmentRegistry
()
{
return
Commands
.
literal
(
"
enchants-registry
"
)
.
then
(
Commands
.
argument
(
"
enchantment
"
,
ArgumentTypes
.
resource
(
RegistryKey
.
ENCHANTMENT
))
.
executes
(
ctx
->
{
final
Enchantment
enchantment
=
ctx
.
getArgument
(
"
enchantment
"
,
Enchantment
.
class
)
;
if
(
ctx
.
getSource
()
.
getExecutor
()
instanceof
Player
player) {
final
ItemStack
stack
=
player
.
getInventory
()
.
getItemInMainHand
()
;
stack
.
addUnsafeEnchantment
(
enchantment,
10
)
;
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
Enchanted <player>'s <item> with <enchantment>!
"
,
Placeholder
.
component
(
"
player
"
,
player
.
name
())
,
Placeholder
.
component
(
"
item
"
,
Component
.
translatable
(
stack
))
,
Placeholder
.
component
(
"
enchantment
"
,
enchantment
.
displayName
(
10
))
)
;
return
Command
.
SINGLE_SUCCESS
;
}
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
<red>This command requires a player!
"
)
;
return
Command
.
SINGLE_SUCCESS
;
}
))
.
build
()
;
}
```
This defines an `enchantment` argument using an enchantment registry key resource and retrieves the value using `ctx.getArgument("enchantment", Enchantment.class)`.
#### Caution
Using the `STRUCTURE` registry key with the `resource` argument can cause a Network Protocol Error due to missing client registries. Use `resourceKey(...)` instead and parse the values yourself.
### Resource Key Argument
The client sees little difference between `ArgumentTypes.resource` and `ArgumentTypes.resourceKey`. The main difference is that `ArgumentTypes.resourceKey` does not provide error checking.
The `resourceKey` argument provides the raw `TypedKey<T>` as an argument result, which contains all the information needed to retrieve a value from a registry.
#### Tip
Unless there's a specific reason to use `resourceKey`, `resource` is preferred due to client-side error checking and ease of use.
### Direct Code Comparison
Using `RegistryKey.ITEM` with a `resource` argument type:
```java
Commands
.
argument
(
"
item
"
,
ArgumentTypes
.
resource
(
RegistryKey
.
ITEM
))
.
executes
(
ctx
->
{
final
ItemType
item
=
ctx
.
getArgument
(
"
item
"
,
ItemType
.
class
)
;
if
(
ctx
.
getSource
()
.
getExecutor
()
instanceof
Player
player) {
player
.
getInventory
()
.
addItem
(
item
.
createItemStack
())
;
}
return
Command
.
SINGLE_SUCCESS
;
}
)
;
```
Using `RegistryKey.ITEM` with a `resourceKey` argument type:
```java
Commands
.
argument
(
"
item
"
,
ArgumentTypes
.
resourceKey
(
RegistryKey
.
ITEM
))
.
executes
(
ctx
->
{
final
TypedKey
<
ItemType
>
itemKey
=
RegistryArgumentExtractor
.
getTypedKey
(
ctx,
RegistryKey
.
ITEM
,
"
item
"
)
;
ItemType
item
=
RegistryAccess
.
registryAccess
()
.
getRegistry
(
itemKey
.
registryKey
())
.
get
(
itemKey
.
key
())
;
if
(item
==
null
) {
ctx
.
getSource
()
.
getSender
()
.
sendRichMessage
(
"
<red>Please provide a valid item!
"
)
;
return
Command
.
SINGLE_SUCCESS
;
}
if
(
ctx
.
getSource
()
.
getExecutor
()
instanceof
Player
player) {
player
.
getInventory
()
.
addItem
(
item
.
createItemStack
())
;
}
return
Command
.
SINGLE_SUCCESS
;
}
)
```
### Using a TypedKey
1. Get the correct registry using `RegistryAccess#getRegistry(RegistryKey)`. Get a `RegistryAccess` using `RegistryAccess.registryAccess()`. The `RegistryKey` is retrieved using `TypedKey#registryKey()`.
2. Get the final value `T` using `Registry#get(Key)`, where the key can be retrieved using `TypedKey#key()`. This returns the backing instance from that resource key or null if no value is found.
### Use Case Over Resource Argument
The main use case is the ability to store the key (the value returned by `TypedKey#key`). This allows storing the exact user input and retrieving the backed instance easily.
### Registry Key Previews
| RegistryKeys Field | Return Value |
| -------------------- | ------------------- |
| `ATTRIBUTE` | `Attribute` |
| `BANNER_PATTERN` | `PatternType` |
| `BIOME` | `Biome` |
| `BLOCK` | `BlockType` |
| `CAT_VARIANT` | `Cat.Type` |
| `CHICKEN_VARIANT` | `Chicken.Variant` |
| `COW_VARIANT` | `Cow.Variant` |
| `DAMAGE_TYPE` | `DamageType` |
| `DATA_COMPONENT_TYPE` | `DataComponentType` |
| `DIALOG` | `Dialog` |
| `ENCHANTMENT` | `Enchantment` |
| `ENTITY_TYPE` | `EntityType` |
| `FLUID` | `Fluid` |
| `FROG_VARIANT` | `Frog.Variant` |
| `GAME_EVENT` | `GameEvent` |
| `INSTRUMENT` | `MusicInstrument` |
| `ITEM` | `ItemType` |
| `JUKEBOX_SONG` | `JukeboxSong` |
| `MAP_DECORATION_TYPE` | `MapCursor.Type` |
| `MEMORY_MODULE_TYPE` | `MemoryKey<?>` |
| `MENU` | `MenuType` |
| `MOB_EFFECT` | `PotionEffectType` |
| `PAINTING_VARIANT` | `Art` |
| `PARTICLE_TYPE` | `Particle` |
| `PIG_VARIANT` | `Pig.Variant` |
| `POTION` | `PotionType` |
| `SOUND_EVENT` | `Sound` |
| `STRUCTURE` | `Structure` |
| `STRUCTURE_TYPE` | `StructureType` |
| `TRIM_MATERIAL` | `TrimMaterial` |
| `TRIM_PATTERN` | `TrimPattern` |
| `VILLAGER_PROFESSION` | `Villager.Profession`|
| `VILLAGER_TYPE` | `Villager.Type` |
| `WOLF_SOUND_VARIANT` | `Wolf.SoundVariant`|
| `WOLF_VARIANT`
```markdown
# Command API Knowledge Base
## Introduction
Paper's command system is built on top of Minecraft's Brigadier command system, providing a powerful and flexible way to define commands and arguments.
**Advantages over the Bukkit command system:**
* Less parsing or error checking required by the developer for arguments.
* Better user experience with client error checking.
* Integration with reload events, allowing commands usable in datapacks.
* Easier creation of subcommands.
**Resources:**
* [Command Tree](#command-trees)
* [Arguments and Literals](#arguments-and-literals)
* [Command Executors](#executors)
* [Command Registration](#registration)
* [Command Requirements](#requirements)
* [Argument Suggestions](#suggestions)
* [Custom Arguments](#custom-arguments)
* [Minecraft Arguments](#minecraft-specific)
**Future Pages:**
* Tutorial: Creating Utility Commands
* The Command Dispatcher
* Forks and Redirects
* Tutorial: Extending the vanilla execute command
**Additional Support:**
* Discord server: #paper-dev channel
## Command Trees
(This section's content is not available in the provided documents.)
## Arguments and Literals
(This section's content is not available in the provided documents.)
## Executors
This section covers the `executes(...)` method from the `ArgumentBuilder` class.
**Examining the `executes` method:**
```java
ArgumentBuilder.java
public T executes(Command<S> command);
```
The `Command<S>` interface is a `FunctionalInterface`, allowing the use of lambda statements.
```java
Command.java
@FunctionalInterface
public interface Command<S> {
int SINGLE_SUCCESS = 1;
int run(CommandContext<S> ctx) throws CommandSyntaxException;
}
```
The `run` method in the `Command<S>` interface has one parameter, `CommandContext<S>`, and returns an integer.
* `CommandContext<S>`: Provides information about the sender and command arguments.
* `S getSource()`: Returns the source of the command, which is always a `CommandSourceStack` for the `executes` method.
* `V getArgument(String, Class<V>)`: Retrieves arguments from the command.
`CommandSourceStack` methods:
* `Location getLocation()`
* `CommandSender getSender()`: Returns the command sender.
* `@Nullable Entity getExecutor()`: Returns the command executor, relevant when using `/execute as <entity> run <our_command>`.
**Example: Flyspeed Command**
```java
Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0f))
.executes(ctx -> {
float speed = FloatArgumentType.getFloat(ctx, "speed");
CommandSender sender = ctx.getSource().getSender();
Entity executor = ctx.getSource().getExecutor();
if (!(executor instanceof Player player)) {
sender.sendPlainMessage("Only players can fly!");
return Command.SINGLE_SUCCESS;
}
player.setFlySpeed(speed);
if (sender == executor) {
player.sendPlainMessage("Successfully set your flight speed to " + speed);
return Command.SINGLE_SUCCESS;
}
sender.sendRichMessage("Successfully set <playername>'s flight speed to " + speed,
Placeholder.component("playername", player.name()));
player.sendPlainMessage("Your flight speed has been set to " + speed);
return Command.SINGLE_SUCCESS;
})
);
```
**Explanation:**
* Defines a `/flyspeed` command with a float argument "speed" (0-1).
* Retrieves the speed argument using `FloatArgumentType.getFloat`.
* Retrieves the `CommandSourceStack` and then the sender and executor.
* `CommandSender`: Interface implemented by entities (including players) and the `ConsoleCommandSender`.
* Checks if the executor is a `Player`.
* Sets the player's flight speed and sends a confirmation message.
* Handles cases where the command is executed by the player themselves or by another sender (using `/execute`).
* Returns `Command.SINGLE_SUCCESS` (value 1) upon successful execution.
**Logic Separation:**
If the command logic is too large, use method references to improve readability.
```java
public class FlightSpeedCommand {
public static LiteralArgumentBuilder<CommandSourceStack> createCommand() {
return Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0f))
.executes(FlightSpeedCommand::runFlySpeedLogic));
}
private static int runFlySpeedLogic(CommandContext<CommandSourceStack> ctx) {
float speed = FloatArgumentType.getFloat(ctx, "speed");
CommandSender sender = ctx.getSource().getSender();
Entity executor = ctx.getSource().getExecutor();
if (!(executor instanceof Player player)) {
sender.sendPlainMessage("Only players can fly!");
return Command.SINGLE_SUCCESS;
}
player.setFlySpeed(speed);
if (sender == executor) {
player.sendPlainMessage("Successfully set your flight speed to " + speed);
return Command.SINGLE_SUCCESS;
}
sender.sendRichMessage("Successfully set <playername>'s flight speed to " + speed,
Placeholder.component("playername", player.name()));
player.sendPlainMessage("Your flight speed has been set to " + speed);
return Command.SINGLE_SUCCESS;
}
}
```
## Registration
Brigadier commands are registered using the `LifecycleEventManager` in Paper. This ensures commands are re-registered after server reload events.
**Accessing the `LifecycleEventManager`:**
1. **Plugin Bootstrapper (Preferred):** Requires `paper-plugin.yml`.
```java
public class CustomPluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(BootstrapContext context) {
context.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
// register your commands here ...
});
}
}
```
* `context.getLifecycleManager()`: Returns a `LifecycleEventManager<BootstrapContext>` object.
* `LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)`: Registers the lifecycle event.
* `LifecycleEvents.COMMANDS`: Specifies the event type for command registration.
* `LifecycleEventHandler`: A functional interface with the `run` method. The lambda parameter is a `ReloadableRegistrarEvent<Commands>`.
* `ReloadableRegistrarEvent<Commands>`:
* `ReloadableRegistrarEvent.Cause cause()`
* `Commands registrar()`: Provides access to the `Commands` class for registering commands.
2. **Plugin Main Class:**
```java
public final class PluginMainClass extends JavaPlugin {
@Override
public void onEnable() {
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
// register your commands here ...
});
}
}
```
* `JavaPlugin#getLifecycleManager()`: Returns a `LifecycleEventManager<Plugin>`.
* The rest of the methods work the same way as with the PluginBootstrap.
**Registering Commands using the `Commands` Class:**
The `Commands` class provides overloads for the `Commands#register` method.
**Registering a `LiteralCommandNode`:**
```java
LiteralCommandNode<CommandSourceStack> buildCommand = Commands.literal("testcmd")
.then(Commands.literal("argument_one"))
.then(Commands.literal("argument_two"))
.build();
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
commands.registrar().register(buildCommand);
});
```
**Registering a `BasicCommand`:**
```java
final BasicCommand basicCommand = ...;
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
commands.registrar().register("commandname", basicCommand);
});
```
**Further Reference:**
* [LifecycleEventManager](link not provided in the document)
## Requirements
(This section's content is not available in the provided documents.)
## Suggestions
This section describes how to send custom suggestions to users using the `suggests(SuggestionProvider<CommandSourceStack>)` method.
**Examining `SuggestionProvider<S>`:**
```java
SuggestionProvider.java
@FunctionalInterface
public interface SuggestionProvider<S> {
CompletableFuture<Suggestions> getSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) throws CommandSyntaxException;
}
```
* Similar to `Command<S>`, this is a functional interface.
* Lambda parameters: `CommandContext<S>` and `SuggestionsBuilder`.
* Return value: `CompletableFuture<Suggestions>`.
**Example of a simple lambda:**
```java
Commands.argument("name", StringArgumentType.word())
.suggests((ctx, builder) -> builder.buildFuture());
```
**The `SuggestionsBuilder`:**
Provides methods to construct suggestions.
**Input Retrieval Methods:**
| Method | Return Value | Description | Example Input: `/customsuggestions Asumm13Text` |
| ------------------------- | --------------------------- | --------------------------------------------- | ------------------------------------------------ |
| `getInput()` | `/customsuggestions Asumm13Text` | The full chat input | |
| `getStart()` | `19` | The index of the first character of the argument's input | |
| `getRemaining()` | `Asumm13Text` | The input for the current argument | |
| `getRemainingLowerCase()` | `asumm13text` | The input for the current argument, lowercased | |
**Suggestions Methods:**
| Overload | Description |
| ------------------------ | --------------------------------------------------------------------------- |
| `suggest(String)` | Adds a String to the suggestions |
| `suggest(String, Message)` | Adds a String with a tooltip to the suggestions |
| `suggest(int)` | Adds an int to the suggestions |
| `suggest(int, Message)` | Adds an int with a tooltip to the suggestions |
* `Message` can be retrieved using:
* `LiteralMessage`: For basic, non-formatted text.
* `MessageComponentSerializer`: For serializing `Component` objects into `Message` objects.
**Example of adding a suggestion with a tooltip:**
```java
builder.suggest("suggestion", MessageComponentSerializer.message().serialize(MiniMessage.miniMessage().deserialize("<green>Suggestion tooltip")));
```
**Building:**
* `SuggestionsBuilder#build()`: Returns the finished `Suggestions` object directly.
* `SuggestionsBuilder#buildFuture()`: Returns a `CompletableFuture<Suggestions>`.
The `SuggestionProvider` expects a `CompletableFuture<Suggestions>` return value. This allows for asynchronous construction of suggestions.
**Example of declaring suggestions:**
```java
// Synchronous
Commands.argument("name", StringArgumentType.word())
.suggests((ctx, builder) -> {
builder.suggest("first");
builder.suggest("second");
return builder.buildFuture();
});
// Asynchronous
Commands.argument("name", StringArgumentType.word())
.suggests((ctx, builder) -> CompletableFuture.supplyAsync(() -> {
builder.suggest("first");
builder.suggest("second");
return builder.build();
}));
```
**Example: Suggesting Amounts in a Give Item Command:**
```java
@NullMarked
public class SuggestionsTest {
public static LiteralCommandNode<CommandSourceStack> constructGiveItemCommand() {
return Commands.literal("giveitem")
.requires(ctx -> ctx.getExecutor() instanceof Player)
.then(Commands.argument("item", ArgumentTypes.itemStack())
.then(Commands.argument("amount", IntegerArgumentType.integer(1, 99))
.suggests(SuggestionsTest::getAmountSuggestions)
.executes(SuggestionsTest::executeCommandLogic)))
.build();
}
private static CompletableFuture<Suggestions> getAmountSuggestions(final CommandContext<CommandSourceStack> ctx, final SuggestionsBuilder builder) {
builder.suggest(1);
builder.suggest(16);
builder.suggest(32);
builder.suggest(64);
return builder.buildFuture();
}
private static int executeCommandLogic(final CommandContext<CommandSourceStack> ctx) {
if (!(ctx.getSource().getExecutor() instanceof Player player)) {
return Command.SINGLE_SUCCESS;
}
final int firstEmptySlot = player.getInventory().firstEmpty();
if (firstEmptySlot == -1) {
player.sendRichMessage("<light_purple>You do not have enough space in your inventory!");
return Command.SINGLE_SUCCESS;
}
final ItemStack item = ctx.getArgument("item", ItemStack.class);
final int amount = IntegerArgumentType.getInteger(ctx, "amount");
item.setAmount(amount);
player.getInventory().setItem(firstEmptySlot, item);
player.sendRichMessage("<light_purple>You have been given <white><amount>x</white> <aqua><item></aqua>!",
Placeholder.component("amount", Component.text(amount)),
Placeholder.component("item", Component.translatable(item).hoverEvent(item)));
return Command.SINGLE_SUCCESS;
}
}
```
**Example: Filtering by User Input:**
```java
public static LiteralCommandNode<CommandSourceStack> constructStringSuggestionsCommand() {
final List<String> names = List.of("Alex", "Andreas", "Stephanie", "Sophie", "Emily");
return Commands.literal("selectname")
.then(Commands.argument("name", StringArgumentType.word())
.suggests((ctx, builder) -> {
names.stream()
.filter(entry -> entry.toLowerCase().startsWith(builder.getRemainingLowerCase()))
.forEach(builder::suggest);
return builder.buildFuture();
}))
.build();
}
```
## Custom Arguments
Custom arguments are wrappers around existing argument types, allowing developers to provide suggestions and reusable parsing logic.
**Why Use Custom Arguments?**
Example: An argument for an online operator player. Without custom arguments, it requires significant logic in the `executes(...)` method and duplication if used in multiple command nodes.
**Example without Custom Arguments:**
```java
Commands.argument("player", ArgumentTypes.player())
.suggests((ctx, builder) -> {
Bukkit.getOnlinePlayers().stream()
.filter(ServerOperator::isOp)
.map(Player::getName)
.filter(name -> name.toLowerCase(Locale.ROOT).startsWith(builder.getRemainingLowerCase()))
.forEach(builder::suggest);
return builder.buildFuture();
})
.executes(ctx -> {
final Player player = ctx.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(ctx.getSource()).getFirst();
if (!player.isOp()) {
final Message message = MessageComponentSerializer.message().serialize(text(player.getName() + " is not a server operator!"));
throw new SimpleCommandExceptionType(message).create();
}
ctx.getSource().getSender().sendRichMessage("Player <player> is an operator!", Placeholder.component("player", player.displayName()));
return Command.SINGLE_SUCCESS;
});
```
**Example with Custom Arguments:**
**OppedPlayerArgument.java**
```java
@NullMarked
public final class OppedPlayerArgument implements CustomArgumentType<Player, PlayerSelectorArgumentResolver> {
private static final SimpleCommandExceptionType ERROR_BAD_SOURCE = new SimpleCommandExceptionType(MessageComponentSerializer.message().serialize(Component.text("The source needs to be a CommandSourceStack!")));
private static final DynamicCommandExceptionType ERROR_NOT_OPERATOR = new DynamicCommandExceptionType(name -> {
return MessageComponentSerializer.message().serialize(Component.text(name + " is not a server operator!"));
});
@Override
public Player parse(StringReader reader) {
throw new UnsupportedOperationException("This method will never be called.");
}
@Override
public <S> Player parse(StringReader reader, S source) throws CommandSyntaxException {
if (!(source instanceof CommandSourceStack stack)) {
throw ERROR_BAD_SOURCE.create();
}
final Player player = getNativeType().parse(reader).resolve(stack).getFirst();
if (!player.isOp()) {
throw ERROR_NOT_OPERATOR.create(player.getName());
}
return player;
}
@Override
public ArgumentType<PlayerSelectorArgumentResolver> getNativeType() {
return ArgumentTypes.player();
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> ctx, SuggestionsBuilder builder) {
Bukkit.getOnlinePlayers().stream()
.filter(ServerOperator::isOp)
.map(Player::getName)
.filter(name -> name.toLowerCase(Locale.ROOT).startsWith(builder.getRemainingLowerCase()))
.forEach(builder::suggest);
return builder.buildFuture();
}
}
```
**Command Declaration:**
```java
Commands.argument("player", new OppedPlayerArgument())
.executes(ctx -> {
final Player player = ctx.getArgument("player", Player.class);
ctx.getSource().getSender().sendRichMessage("Player <player> is an operator!", Placeholder.component("player", player.displayName()));
return Command.SINGLE_SUCCESS;
});
```
This is more readable, easier to understand, and reusable.
**Examining the `CustomArgumentType` Interface:**
```java
package io.papermc.paper.command.brigadier.argument;
@NullMarked
public interface CustomArgumentType<T, N> extends ArgumentType<T> {
@Override
T parse(final StringReader reader) throws CommandSyntaxException;
@Override
default <S> T parse(final StringReader reader, final S source) throws CommandSyntaxException {
return ArgumentType.super.parse(reader, source);
}
ArgumentType<N> getNativeType();
@Override
@ApiStatus.NonExtendable
default Collection<String> getExamples() {
return this.getNativeType().getExamples();
}
@Override
default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return ArgumentType.super.listSuggestions(context, builder);
}
}
```
**Generic Types:**
* `T`: The type returned when `CommandContext#getArgument` is called.
* `N`: The native type of the underlying argument.
* `S`: The command source type (usually `CommandSourceStack`).
**Methods:**
| Method Declaration | Description |
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ArgumentType<N> getNativeType()` | Declares the underlying argument type used as a base for client-side validation. |
| `T parse(final StringReader reader) throws CommandSyntaxException` | Used if `T parse(StringReader, S)` is not overridden. Runs conversion and validation logic. |
| `default <S> T parse(final StringReader reader, final S source) throws CommandSyntaxException` | If overridden, this method is preferred over `T parse(StringReader)`. Includes the source in parsing logic. |
| `default Collection<String> getExamples()` | Should **not** be overridden. Used internally for argument type differentiation. |
| `default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder)` | Equivalent to `RequiredArgumentBuilder#suggests(SuggestionProvider<S>)`. Override to send custom suggestions. |
**A Very Basic Implementation:**
```java
package io.papermc.commands;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class BasicImplementation implements CustomArgumentType<String, String> {
@Override
public String parse(StringReader reader) {
return reader.readUnquotedString();
}
@Override
public ArgumentType<String> getNativeType() {
return StringArgumentType.word();
}
}
```
**`CustomArgumentType.Converted<T, N>`:**
Used when parsing the native type to a new type. Extends `CustomArgumentType` and adds two methods:
* `T convert(N nativeType) throws CommandSyntaxException;`
* `default <S> T convert(final N nativeType, final S source) throws CommandSyntaxException { return this.convert(nativeType); }`
These methods provide the parsed native type instead of a `StringReader`, reducing the need for manual string reader operations.
**Error Handling During the Suggestions Phase:**
Showing invalid input in red is **not possible** with custom arguments because the client can only validate known arguments, and a `CommandSyntaxException` cannot be thrown during the suggestions phase. Literals can achieve this, but they cannot be dynamically modified during server runtime.
**Example: Ice-cream Argument:**
```java
// IceCreamFlavor.java
package io.papermc.commands.icecream;
import org.jspecify.annotations.NullMarked;
@NullMarked
public enum IceCreamFlavor {
VANILLA,
CHOCOLATE,
STRAWBERRY;
@Override
public String toString() {
return name().toLowerCase();
}
}
// IceCreamArgument.java
package io.papermc.commands.icecream;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.jspecify.annotations.NullMarked;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
@NullMarked
public class IceCreamArgument implements CustomArgumentType.Converted<IceCreamFlavor, String> {
private static final DynamicCommandExceptionType ERROR_INVALID_FLAVOR = new DynamicCommandExceptionType(flavor -> {
return LegacyComponentSerializer.legacySection().serialize(Component.text(flavor + " is not a valid flavor!"));
});
@Override
public IceCreamFlavor convert(String nativeType) throws CommandSyntaxException {
try {
return IceCreamFlavor.valueOf(nativeType.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException ignored) {
throw ERROR_INVALID_FLAVOR.create(nativeType);
}
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
for (IceCreamFlavor flavor : IceCreamFlavor.values()) {
String name = flavor.toString();
if (name.startsWith(builder.getRemainingLowerCase())) {
builder.suggest(flavor.toString());
}
}
return builder.buildFuture();
}
@Override
public ArgumentType<String> getNativeType() {
return StringArgumentType.word();
}
@Override
public IceCreamFlavor parse(StringReader reader) throws CommandSyntaxException {
return null;
}
}
```
**Command Declaration:**
```java
Commands.literal("icecream")
.then(Commands.argument("flavor", new IceCreamArgument())
.executes(ctx -> {
final IceCreamFlavor flavor = ctx.getArgument("flavor", IceCreamFlavor.class);
ctx.getSource().getSender().sendRichMessage("<b><red>Y<green>U<aqua>M<light_purple>!</b> You just had a scoop of <flavor>!",
Placeholder.unparsed("flavor", flavor.toString()));
return Command.SINGLE_SUCCESS;
}))
.build();
```
## Minecraft-specific
(This section's content is not available in the provided documents.)
```markdown
# API
## Introduction
This knowledge base provides information on various APIs available in Paper and Folia.
## Component API
### Introduction
Components offer a structured way to represent text in Minecraft, providing advantages over plain text strings. Paper and Velocity implement the Adventure API for enhanced component support.
### Why you should use Components
* Components are tree-like structures, inheriting styles and colors.
* Components can translate text based on client language or show client-specific keybinds.
* Components support RGB colors and interaction events.
* Mojang plans to remove client support for the legacy `§` format.
### Usage
* Components are used for item names, lore, bossbars, team prefixes/suffixes, and custom names.
* Deprecated methods dealing with the legacy format indicate better component alternatives.
### Creating components
* Components are interacted with as objects.
* Use builders to construct complex components efficiently.
* Example:
```java
// This is a sub-optimal construction of the
// component as each change creates a new component
final Component component = Component.text("Hello").color(TextColor.color(0x13f832)).append(Component.text("world!", NamedTextColor.GREEN));
/* This is an optimal use of the builder to create
the same component. Also note that Adventure
Components are designed for use with static method imports
to make code less verbose */
final Component component = text().content("Hello").color(color(0x13f832)).append(text("world!", GREEN)).build();
```
* Refer to the Adventure documentation for in-depth information.
### MiniMessage
* MiniMessage is a string representation of components.
* It is superior to the legacy string format, supporting style inheritance and complex component types.
* Example:
```java
final Component component = MiniMessage.miniMessage().deserialize("<#438df2><b>This is the parent component; its style is applied to all children.\n<u><!b>This is the first child, which is rendered after the parent</!b></u><key:key.inventory></b></#438df2>");
// if the syntax above is too verbose for you, create a helper method!
public final class Components {
public static Component mm(String miniMessageString) {
// mm, short for MiniMessage
return MiniMessage.miniMessage().deserialize(miniMessageString);
}
}
```
### JSON format
(No information provided in the document)
### Serializers
(No information provided in the document)
#### GsonComponentSerializer
(No information provided in the document)
#### MiniMessage
(No information provided in the document)
#### PlainTextComponentSerializer
(No information provided in the document)
#### LegacyComponentSerializer
(No information provided in the document)
### Internationalization
It is generally a good idea to support translations in your plugin to appeal to a larger user base. Adventure simplifies this by adding a server-side translation layer.
* **GlobalTranslator**: All translation is done through `GlobalTranslator`. You can render translations yourself and add new sources by creating instances of `TranslationStore` or implementing the `Translator` interface.
* **Where Translations Work**: Server-side translations work anywhere the component API exists, except for `ItemStack` display text. This includes chat, entity display names, scoreboards, and tab lists.
* **ResourceBundle Example**:
```java
src/main/resources/your/plugin/Bundle_en_US.properties
some.translation.key=Translated Message: {0}
TranslationStore.StringBased<MessageFormat> store = TranslationStore.messageFormat(Key.key("namespace:value"));
ResourceBundle bundle = ResourceBundle.getBundle("your.plugin.Bundle", Locale.US, UTF8ResourceBundleControl.get());
store.registerAll(Locale.US, bundle, true);
GlobalTranslator.translator().addSource(store);
Component.translatable("some.translation.key", Component.text("The Argument"))
```
This will show to clients using the US English language:
```text
Translated Message: The Argument
```
### Audiences
Audiences wrap a collection of recipients that can receive messages. They are used to send messages to individual players, groups of players, or the entire server.
* **Who is an Audience?**
* All `CommandSender`s are single audiences (players, console, command blocks).
* `Server`, `Team`, and `World` are forwarding audiences (made up of multiple audiences).
* All `Audience` methods are available on `CommandSender`, `Server`, `Team`, and `World`.
* **ForwardingAudience**: Wraps a collection of `Audience` instances and forwards messages.
```java
// Server is a ForwardingAudience which includes all online players and the console
ForwardingAudience audience = Bukkit.getServer();
// To construct an audience from a collection of players, use:
Audience audience = Audience.audience(Audience...);
// If you pass in a single Audience, it will be returned as-is. If you pass in a collection of Audiences, they will be
// wrapped in a ForwardingAudience.
```
* **What do Audiences do?**
* Interact with players by sending messages, playing sounds, and showing bossbars.
* Send other parts of the API to players (e.g., `Audience#sendMessage(Component)`).
* **Pointers**: Provide arbitrary information like display name or UUID.
```java
// Get the uuid from an audience member, returning an Optional<UUID>
Optional<UUID> uuid = audience.get(Identity.UUID);
// Get the display name, returning a default
Component name = audience.getOrDefault(Identity.DISPLAY_NAME, Component.text("no display name!"));
```
### Signed messages
(No information provided in the document)
## Event API
### Listeners
(No information provided in the document)
### Custom events
(No information provided in the document)
### Handler lists
(No information provided in the document)
### Chat events
(No information provided in the document)
## Entity API
### Teleportation
(No information provided in the document)
### Display entities
(No information provided in the document)
### Inventories
(No information provided in the document)
### Menu Type API
(No information provided in the document)
#### Experimental
(No information provided in the document)
### Custom InventoryHolders
(No information provided in the document)
## Lifecycle API
### Introduction
(No information provided in the document)
### Datapack discovery
(No information provided in the document)
#### Experimental
(No information provided in the document)
## Data components
#### Experimental
(No information provided in the document)
## Persistent data container (PDC)
(No information provided in the document)
## Scheduling
(No information provided in the document)
## Plugin messaging
(No information provided in the document)
## Plugin configuration
(No information provided in the document)
## Registries
#### Experimental
(No information provided in the document)
## Dialog API
#### Experimental
(No information provided in the document)
## Recipes
(No information provided in the document)
## Particles
(No information provided in the document)
## Supporting Paper and Folia
(No information provided in the document)
## Roadmap
(No information provided in the document)
## Miscellaneous
(No information provided in the document)
## Using databases
(No information provided in the document)
## Debugging your plugin
(No information provided in the document)
## Minecraft internals
(No information provided in the document)
### Reading stacktraces
(No information provided in the document)
## Contributing
(No information provided in the document)
## Events
(No information provided in the document)
## Command API
### Basics
#### Introduction
(No information provided in the document)
#### Command trees
(No information provided in the document)
#### Arguments and literals
(No information provided in the document)
#### Executors
(No information provided in the document)
#### Registration
##### Registering commands
Comparison of registering commands using the old Bukkit way and the new Paper way.
###### The old Bukkit way
* Define a class extending `BukkitCommand` and implement `execute(...)` and `tabComplete(...)` methods.
* Example:
```java
BukkitPartyCommand.java
package your.package.name;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NullMarked;
import java.util.List;
@NullMarked
public class BukkitPartyCommand extends BukkitCommand {
public BukkitPartyCommand(String name, String description, String usageMessage, List<String> aliases) {
super(name, description, usageMessage, aliases);
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (args.length == 0) {
sender.sendPlainMessage("Please provide a player!");
return false;
}
final Player targetPlayer = Bukkit.getPlayer(args[0]);
if (targetPlayer == null) {
sender.sendPlainMessage("Please provide a valid player!");
return false;
}
targetPlayer.sendPlainMessage(sender.getName() + " started partying with you!");
sender.sendPlainMessage("You are now partying with " + targetPlayer.getName() + "!");
return true;
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
if (args.length == 1) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
return List.of();
}
}
```
* Register the command:
```java
PluginClass.java
this.getServer().getCommandMap().register(this.getName().toLowerCase(), new BukkitPartyCommand("bukkitparty", "Have a party", "/bukkitparty <player>", List.of()));
```
###### The new Paper way
* Retrieve a `LiteralCommandNode<CommandSourceStack>` using `Commands.literal(final String literal)`.
* Define arguments and executors using `LiteralArgumentBuilder<CommandSourceStack>`.
* Build the `LiteralCommandNode` using `LiteralArgumentBuilder#build()`.
* Example:
```java
PaperPartyCommand.java
public static LiteralCommandNode<CommandSourceStack> createCommand(final String commandName) {
return Commands.literal(commandName)
.then(Commands.argument("target", ArgumentTypes.player()).executes(ctx -> {
final PlayerSelectorArgumentResolver playerSelector = ctx.getArgument("target", PlayerSelectorArgumentResolver.class);
final Player targetPlayer = playerSelector.resolve(ctx.getSource()).getFirst();
final CommandSender sender = ctx.getSource().getSender();
targetPlayer.sendPlainMessage(sender.getName() + " started partying with you!");
sender.sendPlainMessage("You are now partying with " + targetPlayer.getName() + "!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
* Register the command using `LifecycleEventManager`:
```java
PluginClass.java
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> {
commands.registrar().register(PaperPartyCommand.createCommand("paperparty"), "Have a nice party");
});
```
#### Requirements
This section explains how to restrict commands using the `requires(Predicate<S>)` method of the `ArgumentBuilder<S>` class.
* **Defining permissions**:
* Check permissions on the `command sender`.
* Example:
```java
Commands.literal("testcmd")
.requires(sender -> sender.getSender().hasPermission("permission.test"))
.executes(ctx -> {
ctx.getSource().getSender().sendRichMessage("<gold>You have permission to run this command!</gold>");
return Command.SINGLE_SUCCESS;
});
```
* Require a sender to be a server operator:
```java
Commands.literal("testcmd")
.requires(sender -> sender.getSender().isOp())
.executes(ctx -> {
ctx.getSource().getSender().sendRichMessage("<gold>You are a server operator!</gold>");
return Command.SINGLE_SUCCESS;
});
```
* **Defining more advanced predicates**:
* Any boolean can be returned in the predicate.
* Example (checking for a diamond sword):
```java
Commands.literal("givesword")
.requires(sender -> sender.getExecutor() instanceof Player player && !player.getInventory().contains(Material.DIAMOND_SWORD))
.executes(ctx -> {
if (ctx.getSource().getExecutor() instanceof Player player) {
player.getInventory().addItem(ItemType.DIAMOND_SWORD.createItemStack());
}
return Command.SINGLE_SUCCESS;
});
```
* Client-side command visibility issue: The client might show the command as executable even if the requirement is not met.
* Solution: Use `Player#updateCommands()` to resend commands to the client.
```java
Commands.literal("reloadcommands")
.executes(ctx -> {
if (ctx.getSource().getExecutor() instanceof Player player) {
player.updateCommands();
player.sendRichMessage("<gold>Successfully updated your commands!</gold>");
}
return Command.SINGLE_SUCCESS;
});
```
* **Automating command reloads**:
* Automate command reloads instead of forcing players to reload manually.
* `updateCommands()` is thread-safe but should be used sparingly to avoid excessive bandwidth usage.
* **Restricted commands**:
* From 1.21.6 onwards, commands can be restricted, requiring confirmation from the player.
* Vanilla commands requiring operator status are restricted by default.
* **Restricting your commands**:
* Wrap the predicate with `Commands.restricted(...)`.
* Example:
```java
Commands.literal("test-req")
.requires(Commands.restricted(source -> true))
.executes(ctx -> {
ctx.getSource().getSender().sendRichMessage("You passed!");
return Command.SINGLE_SUCCESS;
});
```
* Complex example:
```java
Commands.literal("mycommand")
.requires(Commands.restricted(source -> source.getSender().hasPermission("my.custom.permission") && source.getExecutor() instanceof Player player && player.getGameMode() == GameMode.ADVENTURE))
.executes(ctx -> {
// Command logic
});
```
#### Suggestions
(No information provided in the document)
#### Custom arguments
(No information provided in the document)
##### Arguments
(No information provided in the document)
##### Minecraft-specific
(No information provided in the document)
###### Location
(No information provided in the document)
###### Entities and players
(No information provided in the document)
###### Registry
(No information provided in the document)
##### Paper-specific
(No information provided in the document)
###### Enums
(No information provided in the document)
###### Predicates
(No information provided in the document)
### Adventure
(No information provided in the document)
### Miscellaneous
#### Basic commands
Paper provides the `BasicCommand` interface for simple commands.
* Implement the `BasicCommand` interface.
* Override the `execute(CommandSourceStack source, String[] args)` method.
* Optional methods:
* `Collection<String> suggest(CommandSourceStack source, String[] args)`: Tab completion suggestions.
* `boolean canUse(CommandSender sender)`: Basic Brigadier `requires` structure.
* `@Nullable String permission()`: Sets the required permission.
* **Simple Usage Example**:
```java
YourCommand.java
package your.package.name;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class YourCommand implements BasicCommand {
@Override
public void execute(CommandSourceStack source, String[] args) {
}
}
```
* `CommandSourceStack` provides information about the sender, location, and executor.
* **Optional Methods**:
* `suggest(CommandSourceStack, String[])`: Provides tab completion suggestions, similar to `TabCompleter`.
* `canUse(CommandSender)`: Defines a basic Brigadier `requires` structure. Overriding this negates the effect of overriding `permission()`.
* `permission()`: Sets the required permission.
```java
BasicCommand.java
default boolean canUse(final CommandSender sender) {
final String permission = this.permission();
return permission == null || sender.hasPermission(permission);
}
```
* **Registering Basic Commands**:
```java
YourPlugin.java
public class YourPlugin extends JavaPlugin {
@Override
public void onEnable() {
BasicCommand yourCommand = ...;
registerCommand("mycommand", yourCommand);
}
}
```
* **Functional Interfaces**: You can use lambda expressions for simple commands, but it is not recommended for readability.
```java
@Override
public void onEnable() {
registerCommand("quickcmd", (source, args) -> source.getSender().sendRichMessage("<yellow>Hello!</yellow>"));
}
```
* **Example: Broadcast Command**:
```java
BroadcastCommand.java
package your.package.name;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class BroadcastCommand implements BasicCommand {
@Override
public void execute(CommandSourceStack source, String[] args) {
final Component name = source.getExecutor() != null ? source.getExecutor().name() : source.getSender().name();
if (args.length == 0) {
source.getSender().sendRichMessage("<red>You cannot send an empty broadcast!</red>");
return;
}
final String message = String.join(" ", args);
final Component broadcastMessage = MiniMessage.miniMessage().deserialize("<red><bold>BROADCAST</red> <name> <dark_gray>»</dark_gray> <message>",
Placeholder.component("name", name),
Placeholder.unparsed("message", message));
Bukkit.broadcast(broadcastMessage);
}
@Override
@Nullable
public String permission() {
return "example.broadcast.use";
}
}
```
Register the command:
```java
PluginMainClass.java
@Override
public void onEnable() {
registerCommand("broadcast", new BroadcastCommand());
}
```
* **Adding Suggestions**: Suggest player names.
```java
@Override
public Collection<String> suggest(CommandSourceStack source, String[] args) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
```
Filter suggestions based on input:
```java
@Override
public Collection<String> suggest(CommandSourceStack source, String[] args) {
if (args.length == 0) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
return Bukkit.getOnlinePlayers().stream().map(Player::getName).filter(name -> name.toLowerCase().startsWith(args[args.length - 1].toLowerCase())).toList();
}
```
* **Final Code Example**:
```java
BroadcastCommand.java
package your.package.name;
import io.papermc.paper.command.brigadier.BasicCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
@NullMarked
public class BroadcastCommand implements BasicCommand {
@Override
public void execute(CommandSourceStack source, String[] args) {
final Component name = source.getExecutor() != null ? source.getExecutor().name() : source.getSender().name();
if (args.length == 0) {
source.getSender().sendRichMessage("<red>You cannot send an empty broadcast!</red>");
return;
}
final String message = String.join(" ", args);
final Component broadcastMessage = MiniMessage.miniMessage().deserialize("<red><bold>BROADCAST</red> <name> <dark_gray>»</dark_gray> <message>",
Placeholder.component("name", name),
Placeholder.unparsed("message", message));
Bukkit.broadcast(broadcastMessage);
}
@Override
@Nullable
public String permission() {
return "example.broadcast.use";
}
@Override
public Collection<String> suggest(CommandSourceStack source, String[] args) {
if (args.length == 0) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
return Bukkit.getOnlinePlayers().stream().map(Player::getName).filter(name -> name.toLowerCase().startsWith(args[args.length - 1].toLowerCase())).toList();
}
}
```
#### Comparison
(No information provided in the document)
```
```markdown
# Component API
This documentation page applies to both the Paper and Velocity projects. Since Minecraft 1.7, the game has utilized components to represent text to be displayed by clients. Paper and Velocity natively implements the Adventure API to add component support wherever possible.
## Why You Should Use Components
* Previously, text was a linear structure with confusing symbols like `§c` and `§k` to control basic colors and styles.
* Components are a tree-like structure that inherits style and colors from their parents.
* Components have several types which do different things than just display raw text, like translating text to the clients language based on a key, or showing a client-specific keybind to a player.
* All these component types support more style options like any RGB color, interaction events (click and hover). The other component types and these style options have poor or missing representations in the legacy string format.
## Usage
Representing text as components is now the supported way of representing text for Paper and Velocity. They are used for almost all aspects of text being displayed to clients. Text like item names, lore, bossbars, team prefixes and suffixes, custom names, and much more all support components in respective APIs. Mojang has stated that client support for the legacy format with `§` will be removed in the future.
**Tip:** In the Paper API, there are lots of deprecated methods and types that deal with this legacy format. This is to signal that a better alternative in components is available and should be migrated to going forward.
## Creating Components
Components can be interacted with as objects. There are different interfaces for each type along with builders for all the types. These objects are immutable so when constructing more complex components, its recommended to use builders to avoid creating new Component instances with every change.
```java
// This is a sub-optimal construction of the
// component as each change creates a new component
final Component component = Component.text("Hello")
.color(TextColor.color(0x13f832))
.append(Component.text("world!", NamedTextColor.GREEN));
/* This is an optimal use of the builder to create
the same component. Also note that Adventure
Components are designed for use with static method imports
to make code less verbose */
final Component component = text()
.content("Hello")
.color(color(0x13f832))
.append(text("world!", GREEN))
.build();
```
**In-Depth Documentation:** For complete documentation on the Adventure Component API Paper and Velocity use, please look at the [Adventure documentation](https://adventure.kyori.net/).
## MiniMessage
Paper and Velocity include the MiniMessage library, which is a string representation of components. If you prefer working with strings rather than objects, MiniMessage is vastly superior to the legacy string format. It can utilize the tree structure for style inheritance and can represent the more complex component types while legacy cannot.
```java
final Component component = MiniMessage.miniMessage().deserialize(
"<#438df2><b>This is the parent component; its style is " +
"applied to all children.\n<u><!b>This is the first child, " +
"which is rendered after the parent</!b></u><key:key.inventory></b></#438df2>"
);
// if the syntax above is too verbose for you, create a helper method!
public final class Components {
public static Component mm(String miniMessageString) {
// mm, short for MiniMessage
return MiniMessage.miniMessage().deserialize(miniMessageString);
}
}
```
```java
import static io.papermc.docs.util.Components.mm;
// replace with your own package
final Component component = mm("<blue>Hello <red>World!");
```
We recommend using this format for user-facing input such as commands or configuration values.
**In-Depth Documentation:** MiniMessage is a part of Adventure, and you can find its documentation on [Adventures documentation](https://adventure.kyori.net/).
**Tip:** MiniMessage has a [web viewer](https://webui.advntr.dev/), which is useful for constructing more complicated components and seeing the results in real time.
## JSON Format
Components can be serialized and deserialized from a standard JSON format. This format is used in Vanilla in various commands which accept component arguments like `/tellraw`. Below is a simple example of this format.
```json
{
"text": "This is the parent component; its style is applied to all children.\n",
"color": "#438df2",
"bold": true,
"extra": [
{
"text": "This is this first child, which is rendered after the parent",
"underlined": true,
// This overrides the parent's "bold" value just for this component
"bold": false
},
{
// This is a keybind component which will display the client's keybind for that action
"keybind": "key.inventory"
}
]
}
```
**In-Depth Documentation:** The JSON format is fully documented on the [Minecraft Wiki](https://minecraft.wiki/w/Raw_JSON_text_format).
**Tip:** There are online tools to make generating this format much easier like [JSON Text Generator](https://minecraftjson.com/).
## Serializers
Paper and Velocity come bundled with different serializers for converting between `Component`s and other forms of serialized text.
### GsonComponentSerializer
Converts between `Component` and JSON-formatted strings with convenience methods to directly deal with Gsons `JsonElement`. This conversion is lossless and is the preferred form of serialization for components that do not have to be edited by users regularly.
### MiniMessage
Converts between `Component` and a MiniMessage-formatted string. This conversion is lossless and is the preferred form of serialization for components that have to be edited by users. There is also extensive customization you can add to the serializer, which is [documented here](https://github.com/KyoriPowered/adventure/tree/4.x/mini-message#serializers).
### PlainTextComponentSerializer
Serializes a `Component` into a plain text string. This is very lossy as all style information as well as most other types of components will lose information. There may be special handling for `TranslatableComponent`s to be serialized into a default language, but generally this shouldnt be used except in certain circumstances, like logging to a text file.
### LegacyComponentSerializer
**Caution:** This is not recommended for use as the legacy format may be removed in the future.
Converts between `Component` and the legacy string format. This conversion is very lossy as component types and events do not have a legacy string representation. A more useful use case is converting legacy text to MiniMessage format in a migration process.
```java
final String legacyString = ChatColor.RED + "This is a legacy" + ChatColor.GOLD + "string";
// runs the legacy string through two serializers to convert legacy -> MiniMessage
final String miniMessageString = MiniMessage.miniMessage().serialize(LegacyComponentSerializer.legacySection().deserialize(legacyString));
```
**Note:** There are 2 built-in legacy serializers, one dealing with `§` symbols and the other for `&` symbols. They have their own instances available through `LegacyComponentSerializer#legacySection()` and `LegacyComponentSerializer#legacyAmpersand()`.
---
# Signed Messages
Since Minecraft version 1.19, the client now signs any messages it sends so that they are uniquely identifiable and verifiable to be sent by a specific player. With this update, they also introduced the ability delete specific messages previously sent by a player.
**Note:** This guide does not go in-depth into the specifics of chat signing and its implementation on either client or server. For a full overview, you can refer to [this linked gist](https://gist.github.com/kennytv/c11519942f68458e8314803a6579ed6e).
## How are signed messages represented in code?
Paper uses Adventures `SignedMessage` object to represent a signed message. We differentiate two kinds of signed messages: system messages and non-system messages.
* System messages (checked with `SignedMessage#isSystem()`) are messages send by the server.
* Non-system messages are not.
You can also differentiate the signed plain text `String` content of the message (`SignedMessage#message()`) from the unsigned, nullable `Component` content (`SignedMessage#unsignedContent()`).
## Obtaining a signed message
Signed messages can be obtained in two ways:
* From an `AsyncChatEvent` using `AbstractChatEvent#signedMessage()`.
* From an `ArgumentTypes.signedMessage()` Brigadier argument type.
## Using signed messages
You can send signed message objects to an `Audience` using the `Audience#sendMessage(SignedMessage, ChatType.Bound)` method. You can obtain a `ChatType.Bound` object from the `ChatType` interface.
Deleting messages is much simpler. Adventure provides the `Audience#deleteMessage(SignedMessage)` or `Audience#deleteMessage(SignedMessage.Signature)` methods for that.
## Example: Making user sent messages deletable
For our example, we will create a chat format plugin which allows a user to delete their own messages in case they made a mistake. For this we will use the `AsyncChatEvent`.
### AsyncChatEvent
The `AsyncChatEvent` is covered in the [chat events](chat-events.md) documentation page. If you want to read up on more detail on the chat renderer, you can do so there.
### In-game preview
*(No preview provided in the original document)*
### Code
**SignedChatListener.java**
```java
package io.papermc.docs.signedmessages;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class SignedChatListener implements Listener {
@EventHandler
void onPlayerChat(AsyncChatEvent event) {
// We modify the chat format, so we use a chat renderer.
event.renderer((player, playerName, message, viewer) -> {
// This is the base format of our message. It will format chat as "<player> » <message>".
final Component base = Component.textOfChildren(
playerName.colorIfAbsent(NamedTextColor.GOLD),
Component.text(" » ", NamedTextColor.DARK_GRAY),
message
);
// Send the base format to any player who is not the sender.
if (viewer != player) {
return base;
}
// Create a base delete suffix. The creation is separated into two
// parts purely for readability reasons.
final Component deleteCrossBase = Component.textOfChildren(
Component.text("[", NamedTextColor.DARK_GRAY),
Component.text("X", NamedTextColor.DARK_RED, TextDecoration.BOLD),
Component.text("]", NamedTextColor.DARK_GRAY)
);
// Add a hover and click event to the delete suffix.
final Component deleteCross = deleteCrossBase
.hoverEvent(Component.text("Click to delete your message!", NamedTextColor.RED))
// We retrieve the signed message with event.signedMessage() and request a server-wide deletion if the
// deletion cross were to be clicked.
.clickEvent(ClickEvent.callback(audience -> Bukkit.getServer().deleteMessage(event.signedMessage())));
// Send the base format but with the delete suffix.
return base.appendSpace().append(deleteCross);
});
}
}
```
---
# Custom Events
Creating custom events is a great way to add functionality to your plugin. This will allow other plugins to listen to your custom events and add functionality to your plugin.
## Creating a custom event
To create a custom event, you need to create a class that extends `Event`. Each event requires a `HandlerList` that will contain all the listeners that are listening to that event. The only exception to this requirement is when you have an event class that cannot be fired, but serves as a parent for other events instead. An example of this is `BlockPistonEvent`, which cannot be listened to directly. This list is used to call the listeners when the event is called.
**Note:** Although `getHandlerList` is not inherited from `Event`, you need to add a static `getHandlerList()` method and return the `HandlerList` for your event. Both methods are required for your event to work.
**PaperIsCoolEvent.java**
```java
public class PaperIsCoolEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
}
```
Now that we have created our event, we can add some functionality to it. Perhaps this will contain a message that will be broadcast to the server when the event is called.
**PaperIsCoolEvent.java**
```java
public class PaperIsCoolEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList();
private Component message;
public PaperIsCoolEvent(Component message) {
this.message = message;
}
public Component getMessage() {
return this.message;
}
public void setMessage(Component message) {
this.message = message;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
}
```
## Calling the event
Now that we have created our event, we can call it.
**ExamplePlugin.java**
```java
public class ExamplePlugin extends JavaPlugin {
// ...
public void callCoolPaperEvent() {
PaperIsCoolEvent coolEvent = new PaperIsCoolEvent(Component.text("Paper is cool!"));
coolEvent.callEvent();
// Plugins could have changed the message from inside their listeners here. So we need to get the message again.
// This event structure allows for other plugins to change the message to their taste.
// Like, for example, a plugin that adds a prefix to all messages.
Bukkit.broadcast(coolEvent.getMessage());
}
}
```
## Implementing cancellation
If you want to allow your event to be cancelled, you can implement the `Cancellable` interface.
**PaperIsCoolEvent.java**
```java
public class PaperIsCoolEvent extends Event implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private Component message;
private boolean cancelled;
// ...
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}
```
Now, when the event is called, you can check if it is cancelled and act accordingly.
**ExamplePlugin.java**
```java
public class ExamplePlugin extends JavaPlugin {
// ...
public void callCoolPaperEvent() {
PaperIsCoolEvent coolEvent = new PaperIsCoolEvent(Component.text("Paper is cool!"));
coolEvent.callEvent();
if (!coolEvent.isCancelled()) {
Bukkit.broadcast(coolEvent.getMessage());
}
}
}
```
When an event is cancellable, `Event#callEvent()` will return false if the event was cancelled. This allows you to directly use `callEvent` in your `if` statement, instead of having to check `Cancellable#isCancelled()` manually.
**ExamplePlugin.java**
```java
public class ExamplePlugin extends JavaPlugin {
// ...
public void callCoolPaperEvent() {
PaperIsCoolEvent coolEvent = new PaperIsCoolEvent(Component.text("Paper is cool!"));
if (coolEvent.callEvent()) {
// Directly get the output from callEvent
Bukkit.broadcast(coolEvent.getMessage());
}
}
}
```
---
# Custom InventoryHolders
`InventoryHolder`s are a way to identify your plugins inventories in events.
## Why use an `InventoryHolder`?
`InventoryHolder`s simplify the steps you need to do to make sure an inventory was created by your plugin. Using inventory names for identification is unreliable, as other plugins, or even players, can create inventories with names the exact same as yours. With components, you also need to make sure the name is exactly the same or serialize it to other formats. Custom `InventoryHolder`s have no such downsides and by using them youre guaranteed to have methods available to handle your inventory.
## Creating a custom holder
The first step is to implement the `InventoryHolder` interface. We can do this the following way: create a new class that will create our `Inventory` in the constructor.
**Note:** The constructor takes your main plugin class as an argument in order to create the `Inventory`. If you wish, you can use the static method `Bukkit#createInventory(InventoryHolder, int)` instead and remove the argument.
**MyInventory.java**
```java
public class MyInventory implements InventoryHolder {
private final Inventory inventory;
public MyInventory(MyPlugin plugin) {
// Create an Inventory with 9 slots, `this` here is our InventoryHolder.
this.inventory = plugin.getServer().createInventory(this, 9);
}
@Override
public Inventory getInventory() {
return this.inventory;
}
}
```
## Opening the inventory
To open the inventory, first we have to instantiate our `MyInventory` class and then open the inventory for the player. You can do that wherever you need.
**Note:** We pass an instance of our plugins main class as its required by the constructor. If youve used the static method and removed the constructor argument you dont have to pass it here.
```java
Player player;
// Assume we have a Player instance.
// This can be a command, another event or anywhere else you have a Player.
MyInventory myInventory = new MyInventory(myPlugin);
player.openInventory(myInventory.getInventory());
```
## Listening to an event
Once we have the inventory open, we can listen to any inventory events we like and check if `Inventory#getHolder()` returns an instance of our `MyInventory`.
```java
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Inventory inventory = event.getInventory();
// Check if the holder is our MyInventory,
// if yes, use instanceof pattern matching to store it in a variable immediately.
if (!(inventory.getHolder(false) instanceof MyInventory myInventory)) {
// It's not our inventory, ignore it.
return;
}
// Do what we need in the event.
}
```
## Storing data on the holder
You can store extra data for your inventories on the `InventoryHolder` by adding fields and methods to your class. Lets make an inventory that counts the amount of times we clicked a stone inside it.
First, lets modify our `MyInventory` class a little:
**MyInventory.java**
```java
public class MyInventory implements InventoryHolder {
private final Inventory inventory;
private int clicks = 0; // Store the amount of clicks.
public MyInventory(MyPlugin plugin) {
this.inventory = plugin.getServer().createInventory(this, 9);
// Set the stone that we're going to be clicking.
this.inventory.setItem(0, ItemStack.of(Material.STONE));
}
// A method we will call in the listener whenever the player clicks the stone.
public void addClick() {
this.clicks++;
this.updateCounter();
}
// A method that will update the counter item.
private void updateCounter() {
this.inventory.setItem(8, ItemStack.of(Material.BEDROCK, this.clicks));
}
@Override
public Inventory getInventory() {
return this.inventory;
}
}
```
Now, we can modify our listener to check if the player clicked the stone, and if so, add a click.
```java
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// We're getting the clicked inventory to avoid situations where the player
// already has a stone in their inventory and clicks that one.
Inventory inventory = event.getClickedInventory();
// Add a null check in case the player clicked outside the window.
if (inventory == null || !(inventory.getHolder(false) instanceof MyInventory myInventory)) {
return;
}
event.setCancelled(true);
ItemStack clicked = event.getCurrentItem();
// Check if the player clicked the stone.
if (clicked != null && clicked.getType() == Material.STONE) {
// Use the method we have on MyInventory to increment the field
// and update the counter.
myInventory.addClick();
}
}
```
**Note:** You can store the created `MyInventory` instance, e.g. on a `Map<UUID, MyInventory>` for per-player use, or as a field to share the inventory between all players, and use it to persist the counter even when opening the inventory for the next time.
---
# Data Components
**(Experimental)** The data component API is currently experimental, and is additionally subject to change across versions.
The data component API provides a version-specific interface for accessing and manipulating item data that is otherwise not representable by the `ItemMeta` API. Through this API, you can read and modify properties of an item, so called data components, in a stable and object-oriented manner.
## Introduction
### What is a data component?
A data component represents a piece of data associated with an item. Vanilla items can have properties such as custom model data, container loot contents, banner patterns, or potion effects.
### Structure
For implementation details, [click here](https://github.com/PaperMC/Paper/pull/6913).
### The prototype (default values)
Items come with an initial set of components that we call the prototype. These components are defined on the `ItemType` of the `ItemStack`. They control the base behavior of the item, representing a brand new item without any modifications.
The prototype gives items their initial properties such as if they are food, a tool, a weapon, etc.
### The patch
The patch represents the modifications made to the item. This may include giving it a custom name, modifying the enchantments, damaging it, or adding to the lore. The patch is applied on top of the prototype, allowing us to make modifications to an item.
The patch also allows for removing components that were previously in the prototype. This is shown by the `minecraft:tool` example in red. We are removing this component, so this sword item will no longer break cobweb or other sword blocks faster.
We can also add new components, as seen from the new `minecraft:enchantment_glint_override` component, which allows us to make it appear as if it were enchanted.
## Differences compared to `ItemMeta`
The `ItemMeta` API provides methods to modify `ItemStack`s in a hierarchical manner, such as `CompassMeta`, which allows you to modify the components of a `minecraft:compass`.
While `ItemMeta` is still very useful, it does not properly represent the prototype/patch relationship that Minecraft items use.
### Key differences
#### Expanded data model
The data component API exposes a much broader and more detailed set of item properties than `ItemMeta`. Data components allow the entire item to be modified in a fashion that better represents how Minecraft does item modifications.
#### Version-specific
The data component API is designed to adapt to version changes. The data component API may experience breaking changes on version updates as Minecraft makes changes to components. Backwards compatibility is not promised.
Because `ItemMeta` is represented in a different format, breaking changes made to components by Mojang may not result in breaking changes to `ItemMeta`.
#### Builders and immutability
Many complex data components require a builder approach for construction and editing. All data types that are returned by the api are also immutable, so they will not directly modify the component.
#### Patch-only
`ItemMeta` only represents the patch of an `ItemStack`. This means that you cannot get the original properties (prototype) of the `ItemStack`, such as its default durability or default attributes.
#### No snapshots
Currently, `ItemMeta` represents a *snapshot* of an `ItemStack`s patched map. This is expensive as it requires the entire patch to be read, even values that you may not be using.
The data component API integrates directly with `ItemStack`. Although conceptually similar, the data component API focuses on explicit, strongly typed data retrieval and updates without this additional overhead.
## When should I use `DataComponents` or `ItemMeta`?
You would want to use `ItemMeta` if you:
* Are doing only simple changes to `ItemStack`s
* Want to keep cross-version compatibility with your plugin
You would want to use data components if you:
* Want more complicated `ItemStack` modifications
* Do not care about cross-version compatibility
* Want to access default (prototype) values
* Want to remove components from an `ItemStack`s prototype
## Basic usage
The data component API will fetch values according to the behavior seen in game. So, if the patch removes the `minecraft:tool` component, trying to get that component will return null.
### Retrieving a prototype value
```java
// Get the default durability of diamond sword
int defaultDurability = Material.DIAMOND_SWORD.getDefaultData(DataComponentTypes.MAX_DAMAGE);
```
### Checking for a data component
```java
// Check if this item has a custom name data component
boolean hasCustomName = stack.hasData(DataComponentTypes.CUSTOM_NAME);
logger.info("Has custom name? " + hasCustomName);
```
### Reading a valued data component
```java
// The damage of an item can be null, so we require a null check
Integer damageValue = stack.getData(DataComponentTypes.DAMAGE);
if (damageValue != null) {
logger.info("Current damage: " + damageValue);
} else {
logger.info("This item doesn't have a damage component set.");
}
// Certain components, like the max stack size, will always be present on an item
Integer maxStackSize = stack.getData(DataComponentTypes.MAX_STACK_SIZE);
```
### Setting a valued data component
```java
// Set a custom model data value on this item
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA,
CustomModelData.customModelData()
.addFloat(0.5f)
.addFlag(true)
.build());
```
### Removing or resetting a data component
```java
// Remove an existing component (e.g. tool)
stack.unsetData(DataComponentTypes.TOOL);
// Reset a component to the default (prototype) value for its item type (e.g. max stack size)
stack.resetData(DataComponentTypes.MAX_STACK_SIZE);
```
### Non-valued data components
Some components are only flags and dont carry any sort of value:
```java
// Make the item a glider to be used like elytra (combined with the equippable component)
stack.setData(DataComponentTypes.GLIDER);
// Remove the glider flag
stack.unsetData(DataComponentTypes.GLIDER);
```
## Advanced usage with builders
Many data components have complex structures that require builders.
### Modifying prototype component values
```java
ItemStack helmet = ItemStack.of(Material.DIAMOND_HELMET);
// Get the equippable component for this item, and make it a builder.
// Note: Not all types have .toBuilder() methods
// This is the prototype value of the diamond helmet.
Equippable.Builder builder = helmet.getData(DataComponentTypes.EQUIPPABLE).toBuilder();
// Make the helmet look like netherite
// We get the prototype equippable value from NETHERITE_HELMET
builder.assetId(Material.NETHERITE_HELMET.getDefaultData(DataComponentTypes.EQUIPPABLE).assetId());
// And give it a spooky sound when putting it on
builder.equipSound(SoundEventKeys.ENTITY_GHAST_HURT);
// Set our new item
helmet.setData(DataComponentTypes.EQUIPPABLE, builder);
```
This will create a diamond helmet that looks like a netherite helmet and plays a spooky ghast sound when equipped.
### Example: Written book
```java
ItemStack book = ItemStack.of(Material.WRITTEN_BOOK);
WrittenBookContent.Builder builder = WrittenBookContent.writtenBookContent("My Book", "AuthorName");
// Add a page
builder.addPage(Component.text("This is a new page!"));
// Add a page that shows differently for people who have swear filtering on
// Players who have disabled filtering, will see "I hate Paper!", while those with filtering on will see the "I love Paper!".
builder.addFilteredPage(Filtered.of(Component.text("I hate Paper!"), Component.text("I love Paper!")));
// Change generation
builder.generation(1);
// Apply changes
book.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, builder.build());
```
### Example: Cool sword
```java
ItemStack sword = ItemStack.of(Material.DIAMOND_SWORD);
sword.setData(DataComponentTypes.LORE,
ItemLore.lore()
.addLine(Component.text("Cool sword!"))
.build());
sword.setData(DataComponentTypes.ENCHANTMENTS,
ItemEnchantments.itemEnchantments()
.add(Enchantment.SHARPNESS, 10)
.build());
sword.setData(DataComponentTypes.RARITY, ItemRarity.RARE);
sword.unsetData(DataComponentTypes.TOOL); // Remove the tool component
sword.setData(DataComponentTypes.MAX_DAMAGE, 10);
sword.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); // Make it glow!
```
### Matching items without certain data components
When comparing items, you sometimes want to ignore certain values. For this, we can use the `ItemStack#matchesWithoutData` method.
For example, here we compare two diamond swords whilst ignoring their durability:
```java
ItemStack originalSword = ItemStack.of(Material.DIAMOND_SWORD);
ItemStack damagedSword = ItemStack.of(Material.DIAMOND_SWORD);
damagedSword.setData(DataComponentTypes.DAMAGE, 100);
boolean match = damagedSword.matchesWithoutData(originalSword, Set.of(DataComponentTypes.DAMAGE), false);
logger.info("Do the sword match? " + match);
// -> true
```
---
# Debugging your plugin
Debugging your plugin is vital to being able to fix bugs and issues in your plugin. This page will cover some of the most common debugging techniques.
## Printing to the console
One of the most common debugging techniques is to print to the console. This is likely something youve done before, as its very simple. This has a few downsides, though. It can be hard to find the print statements in the console, and it can be hard to remove them all when youre done debugging. Most notably, you have to recompile your plugin and restart the server to add or remove debugging.
When debugging, you can use `System.out.println("");` to print to the console. It is recommended to use your plugins logger instead though, as it will be easier to know which plugin the log has come from. This can be done simply with:
```java
plugin.getComponentLogger().debug(Component.text("SuperDuperBad Thing has happened"));
```
### Logger Levels
In some consoles, using the `warning` level will print the message in different colors. This can be useful for finding your print statements in the console.
## Using a remote debugger
A debugger is a tool that allows you to pause your code at a certain point and inspect the values of variables. This can be very useful for finding out why your code isnt working as expected and also for finding out where your code is going wrong.
### Setting up the debugger
To use a debugger, you need to set up your IDE to use it. This is different for each IDE, but for the sake of this guide, we will be using IntelliJ IDEA.
To set up a debugger in IntelliJ, you need to create a new run configuration. You can do this by clicking the dropdown next to the run button and clicking `Edit Configurations...`:
*(Image of IntelliJ Edit Configurations)*
Then, click the `+` button in the top left and select `Remote JVM Debug`. You can then name the configuration whatever you want, and click `Apply`:
*(Image of IntelliJ Remote JVM Debug Configuration)*
Finally, copy the command line arguments from the window, and paste these into your servers startup script. These will go after the `java` command and before `-jar`. Once you have done this, you can click `OK`. For example:
```terminal
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar paper-1.21.10.jar nogui
```
Once your server is running, you can use the bug icon in the top right to connect your debugger to the server:
*(Image of IntelliJ Debug Button)*
### Using the debugger
Lets say we have this code:
```java
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
Location location = player.getLocation();
if (location.getWorld() == null) return;
if (location.getWorld().getEnvironment() == World.Environment.NETHER) {
player.sendMessage("You are in the nether!");
}
}
```
You can add a breakpoint to the line by clicking on the line number:
*(Image of IntelliJ Breakpoint)*
This will pause the code when it reaches that line. You can then use the debugger to inspect the values of variables:
*(Image of IntelliJ Debugger)*
You can inspect the values of each of the variables in the current scope. You can also use the buttons in the top to step from one breakpoint to the next. If needed, you can also use the text box at the top to evaluate expressions for debugging purposes.
## Using direct debugging
Direct debugging will allow you to run the server directly from your IDE, and will allow you to use breakpoints and step through your code.
We can achieve this by using [JPenillas Gradle plugin](https://github.com/jpenilla/run-paper) to run the server directly from the IDE.
See [here](https://github.com/jpenilla/run-paper) for instructions on how to set up the plugin.
---
# Display entities
Added in 1.19.4, display entities are a powerful way to display various things in the world, like blocks, items and text. By default, these entities have no hitbox, dont move, make sounds or take damage, making them the perfect for all kinds of applications, like holograms.
## Types
### Text
Text can be displayed via a TextDisplay entity.
```java
TextDisplay display = world.spawn(location, TextDisplay.class, entity -> {
// customize the entity!
entity.text(Component.text("Some awesome content", NamedTextColor.BLACK));
entity.setBillboard(Display.Billboard.VERTICAL); // pivot only around the vertical axis
entity.setBackgroundColor(Color.RED); // make the background red
// see the Display and TextDisplay Javadoc, there are many more options
});
```
### Blocks
Blocks can be displayed
```markdown
# PaperMC Knowledge Base
## Configuration
### World Configuration
- Includes settings for worlds within the server.
### Global Configuration
- Includes settings that apply to the entire server.
### Configuration Files
* **bukkit.yml**: Configuration file for Bukkit settings.
* **spigot.yml**: Configuration file for Spigot settings.
* **commands.yml**: Configuration file for command aliases and settings.
* **permissions.yml**: Configuration file for managing permissions.
* **help.yml**: Configuration file for custom help messages.
* **server.properties**: Configuration file for basic server settings.
* **Vanilla data files**: Vanilla Minecraft data files.
* **Paper plugins**: Configuration for Paper-specific plugin settings.
* **Commands**: Defines server commands and their properties.
* **System properties**: System-level settings for the server.
* **Permissions**: Defines permissions and their assignments.
* **Miscellaneous**: Other configuration options.
* **Bug fixes**: Configuration related to bug fixes.
* **Frequently asked questions**: Configuration related to frequently asked questions.
## Development
### Getting Started
* Project Setup: Setting up the development environment.
* How Plugins Work: Explanation of plugin functionality.
* Paper Plugins: Information about Paper-specific plugins.
### Experimental Features
* plugin.yml: Plugin configuration file.
* paperweight-userdev: Information about paperweight user development.
### API
#### Command API
* **Basics**
* Introduction: Overview of the command API.
* Command Trees: Structuring commands in a tree-like format.
* Arguments and Literals: Defining command arguments and literals.
* Executors: Handling command execution.
* Registration: Registering commands with the server.
* Requirements: Setting requirements for command usage.
* Suggestions: Providing command suggestions.
* Custom Arguments: Creating custom argument types.
* **Arguments**
* Minecraft-Specific: Arguments related to Minecraft entities and objects.
* Location: Handling location-based arguments.
* Entities and Players: Specifying entities and players as arguments.
* Registry: Using Minecraft registries in arguments.
* Paper-Specific: Arguments specific to Paper.
* Enums: Using enums as arguments.
* Predicates: Using predicates for argument validation.
* Adventure: Using the Adventure library for command components.
* Miscellaneous: Other command API features.
* Basic commands: Basic command examples.
* Comparison: Command comparisons.
#### Component API
* Introduction: Overview of the Component API.
* Internationalization: Supporting multiple languages.
* Audiences: Sending messages to specific audiences.
* Signed Messages: Handling signed messages.
#### Event API
* Listeners: Creating event listeners.
* Custom Events: Defining custom events.
* Handler Lists: Managing event handlers.
* Chat Events: Handling chat-related events.
* Entity API: Interacting with entities.
* Teleportation: Managing entity teleportation.
* Display entities: Using display entities to show blocks, items, and text.
* Inventories: Managing inventories.
* Menu Type API: Creating custom menu types.
* Experimental: This API is experimental and may change.
* Custom InventoryHolders: Managing custom inventory holders.
#### Lifecycle API
* Introduction: Overview of the Lifecycle API.
* Datapack Discovery: Discovering and managing datapacks.
* Experimental: This API is experimental and may change.
* Data Components: Manipulating item data.
* Experimental: This API is experimental and may change.
* Persistent Data Container (PDC): Storing persistent data.
* Scheduling: Scheduling tasks within the plugin.
* Plugin Messaging: Sending messages between plugins.
* Plugin Configuration: Managing plugin configuration.
* Registries
* Experimental: This API is experimental and may change.
* Dialog API
* Experimental: This API is experimental and may change.
#### Dialog API
* **Overview:** Introduction to the Dialog API.
* **What is a dialog?** Dialogs are custom in-game menus for displaying information or gathering user input.
* **Showing dialogs:** Use `/dialog show <players> <dialog>` command or `Audience#showDialog(DialogLike)`. Get built-in dialogs from the `Dialog` interface or create new ones with `Dialog#create`.
* **Built-in dialogs:**
* Server Links
* Quick Actions
* Custom Options
* **Adding server links:** Retrieve the `ServerLinks` instance from `Bukkit.getServer().getServerLinks()` and use its methods.
* **Creating dialogs dynamically:** Use the `Dialog#create` method. Dialogs require a base and a type.
* **Dialog base:** Created using `DialogBase.builder(Component title)`. Can declare:
* `afterAction(DialogAfterAction)`: Action after dialog is closed.
* `canCloseWithEscape(boolean)`: Whether the dialog can be closed with the `esc` key.
* `externalTitle(Component)`: Title for buttons that open this dialog.
* `body(List<? extends DialogBody>)`: The body of the dialog.
* `inputs(List<? extends DialogInput>)`: The inputs of the dialog.
* **Dialog body:** Can contain `DialogBody.plainMessage(Component)` for text or `DialogBody.item(ItemStack)` for items.
* **Dialog input:**
* `DialogInput.bool`: Tick box (true/false).
* `DialogInput.singleOption`: Multiple-choice button.
* `DialogInput.text`: String input field.
* `DialogInput.numberRange`: Slider for number input.
* **Dialog type:**
* `notice()`: Simple dialog with one button.
* `confirmation(ActionButton yesButton, ActionButton noButton)`: Dialog with yes/no buttons.
* `dialogList(RegistrySet dialogs)`: Dialog for opening specified dialogs.
* `multiAction(List<ActionButton> actions)`: Dialog for displaying multiple buttons.
* `serverLinks(ActionButton exitAction, int columns, int buttonWidth)`: Server links dialog.
* **Registering dialogs in the registry:** Register during a registry modification lifecycle event in your plugin's bootstrapper. Allows referencing the dialog in commands.
* **Closing dialogs:**
* `Adventure#closeDialog()`: Closes the dialog and returns to the previous screen.
* `Player#closeInventory()`: Closes the dialog and any other open screens.
* **Example: A blocking confirmation dialog:** Shows a dialog before allowing a player to join.
* **The dialog:** A confirmation dialog registered in the bootstrapper.
* **Requiring the player to agree before allowing them to join:** Uses a `CompletableFuture` to await a response.
* **Example: Retrieving and parsing user input:** Shows a dialog with number range inputs.
* **Reading the input:** Listen to `PlayerCustomClickEvent` and retrieve values from `DialogResponseView`.
* **Using callbacks:** Use `DialogAction.customClick(DialogActionCallback, ClickCallback.Options)` to register a callback locally.
#### Data Components
* Experimental API for accessing and manipulating item data not representable by ItemMeta.
* Allows reading and modifying properties of an item in a stable, object-oriented manner.
* **Introduction**
* A data component represents a piece of data associated with an item (e.g., custom model data, loot contents).
* **Structure**
* **Prototype (default values):** Initial set of components defined on the ItemType of the ItemStack.
* **Patch:** Modifications made to the item (e.g., custom name, enchantments). Applied on top of the prototype. Allows for removing components from the prototype.
* **Differences compared to ItemMeta**
* ItemMeta doesn't represent the prototype/patch relationship.
* **Key differences**
* **Expanded data model:** Exposes a broader and more detailed set of item properties.
* **Version-specific:** Designed to adapt to version changes, may experience breaking changes on updates.
* **Builders and immutability:** Complex components require a builder approach. All data types returned by the API are immutable.
* **Patch-only:** ItemMeta only represents the patch, not the original properties (prototype).
* **No snapshots:** Data component API integrates directly with ItemStack for explicit, strongly typed data retrieval.
* **When should I use DataComponents or ItemMeta?**
* Use ItemMeta for simple changes and cross-version compatibility.
* Use DataComponents for complicated modifications, accessing prototype values, and removing components.
* **Basic usage**
* Fetches values according to in-game behavior.
* **Retrieving a prototype value:** Get default values directly from the Material.
* **Checking for a data component:** Use `stack.hasData(DataComponentTypes.COMPONENT_NAME)`.
* **Reading a valued data component:** Use `stack.getData(DataComponentTypes.COMPONENT_NAME)`. Requires null check.
* **Setting a valued data component:** Use `stack.setData(DataComponentTypes.COMPONENT_NAME, value)`.
* **Removing or resetting a data component:** Use `stack.unsetData(DataComponentTypes.COMPONENT_NAME)` to remove, `stack.resetData(DataComponentTypes.COMPONENT_NAME)` to reset to default.
* **Non-valued data components:** Some components are flags (e.g., `DataComponentTypes.GLIDER`).
* **Advanced usage with builders**
* Many components require builders for complex structures.
* **Modifying prototype component values:** Use `.toBuilder()` to modify the prototype.
* **Example: Written book:** Demonstrates creating a written book with custom content and filtering.
* **Example: Cool sword:** Demonstrates adding lore, enchantments, and removing the tool component.
* **Matching items without certain data components:** Use `ItemStack#matchesWithoutData` to compare items ignoring specific components.
* Recipes
* Particles
* Supporting Paper and Folia
* Roadmap
* Miscellaneous
* Using databases
* Debugging your plugin
* Minecraft internals
* Reading stacktraces
* Contributing
* Events
* GitHub
* Javadoc
* Discord
#### Custom InventoryHolders
* **Overview:** `InventoryHolder`s are a way to identify your plugin's inventories in events.
* **Why use an InventoryHolder?** Simplifies identifying inventories created by your plugin, more reliable than using inventory names.
* **Creating a custom holder:** Implement the `InventoryHolder` interface. Create a class that creates the `Inventory` in the constructor.
* **Opening the inventory:** Instantiate the `InventoryHolder` class and open the inventory for the player using `player.openInventory(inventoryHolder.getInventory())`.
* **Listening to an event:** Listen to inventory events and check if `Inventory#getHolder()` returns an instance of your custom `InventoryHolder`.
* **Storing data on the holder:** Add fields and methods to your `InventoryHolder` class to store extra data for your inventories.
#### Debugging Your Plugin
* **Overview:** Debugging is crucial for fixing bugs in your plugin.
* **Printing to the console:**
* Use `plugin.getComponentLogger().debug(Component.text("message"))` instead of `System.out.println("")`.
* Using `warning` level in the logger can make messages stand out.
* **Using a remote debugger:**
* A debugger allows you to pause code and inspect variables.
* **Setting up the debugger:**
* Create a new "Remote JVM Debug" run configuration in your IDE.
* Copy the command line arguments from the IDE and paste them into your server's startup script (after `java` and before `-jar`).
* **Using the debugger:**
* Add breakpoints by clicking on the line number in your IDE.
* Inspect variables and step through code using the debugger controls.
* **Using direct debugging:**
* Run the server directly from your IDE using a Gradle plugin.
#### Display Entities
* Added in 1.19.4, display entities are a powerful way to display blocks, items and text.
* By default, these entities have no hitbox, dont move, make sounds or take damage.
* **Types:**
* **Text:** Display text via a `TextDisplay` entity.
* **Blocks:** Display blocks via a `BlockDisplay` entity.
* **Items:** Display items via an `ItemDisplay` entity. Can also display blocks, with the difference being the position in the model.
* **Transformation:**
* Displays can have an arbitrary affine transformation applied to them.
* Transformations are applied in this order: Scale, Rotation, Translation.
* Visualizing transformations: Use the Transformation Visualizer website for quick prototyping!
* **Scale:** Scale the display entity.
* **Rotation:** Rotate the display entity.
* **Translation:** Apply a position offset to the display entity.
* **Interpolation:**
* Transformations and teleports can be linearly interpolated by the client to create a smooth animation.
* **Transformation:** Smoothly rotate a block/item/text in-place.
* **Teleportation:** Interpolate the movement of the entire display entity between two points.
* **Use cases:**
* Displays have many different use cases, ranging from stationary decoration to complex animation.
* Make a decoration thats visible to only specific players using `Entity#setVisibleByDefault()` and `Player#showEntity()` / `Player#hideEntity()`.
* They can also be added as passengers to entities with the `Entity#addPassenger()` / `Entity#removePassenger()` methods, useful for making styled name tags!
* If the display is only used temporarily, its persistence can be disabled with `Entity#setPersistent()`.
* Make sure to remove the display afterward with `Entity#remove()`.
```markdown
# Teleportation
Entities can be instantaneously teleported to specific positions, synchronously and asynchronously with the `teleport` and `teleportAsync` API.
## Performance
If you expect to teleport into unloaded chunks, it is recommended to use the `teleportAsync` API, as it avoids doing synchronous chunk loads, which put a lot of stress on the servers main thread - hurting overall performance.
```java
entity.teleport(location);
// loads chunks synchronously and teleports the entity
entity.teleportAsync(location)
.thenAccept(success -> {
// loads chunks asynchronously and teleports the entity
// this code is ran when the teleport completes
// the Future is completed on the main thread, so it is safe to use the API here
if (success) {
// the entity was teleported successfully!
}
});
```
**Danger:** You should **NEVER** call `.get()` / `.join()` on the `teleportAsync` Future on the main thread, as it **WILL** deadlock your server if the chunk you're teleporting into is not loaded.
## Look at
The `lookAt` API allows you to make a player look at a certain position or entity.
```java
player.lookAt(position, LookAnchor.EYES // the player's eyes will be facing the position);
player.lookAt(entity, LookAnchor.EYES // the player's eyes will be facing the entity
, LookAnchor.FEET // the player will be facing the entity's feet);
```
## Teleport flags
Teleport flags offer a way to teleport entities whilst being able to customize behavior. This allows you to do things like teleport players using relative flags and being able to retain passengers. All available teleport flags can be found in the `TeleportFlag` class.
## Relative teleportation
Teleport a player relatively, preventing velocity from being reset in the X, Y and Z axes.
```java
player.teleport(location, TeleportFlag.Relative.VELOCITY_X, TeleportFlag.Relative.VELOCITY_Y, TeleportFlag.Relative.VELOCITY_Z);
```
## Retaining passengers
Teleport an entity with the `RETAIN_PASSENGERS` flag, allowing its passengers to be transferred with the entity.
```java
entity.teleport(location, TeleportFlag.EntityState.RETAIN_PASSENGERS);
```
---
# Supporting Paper and Folia
Folia is a fork of Paper, which is currently maintained by the PaperMC team. It adds the ability to split the world into regions.
## Checking for Folia
Depending on what platform your plugin is running on, you may need to implement features differently. For this, you can use this utility method to check if the current server is running Folia:
```java
private static boolean isFolia() {
try {
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
```
## Schedulers
In order to support Paper and Folia, you must use the correct scheduler. Folia has different types of schedulers that can be used for different things:
* Global
* Region
* Async
* Entity
If you use these schedulers when running Paper, they will be internally handled to provide the same functionality as if you were running Folia.
### Global scheduler
The tasks that you run on the global scheduler will be executed on the global region. You should use this scheduler for any tasks that do not belong to any particular region. These can be fetched with:
```java
GlobalRegionScheduler globalScheduler = server.getGlobalRegionScheduler();
```
### Region scheduler
The region scheduler will be in charge of running tasks for the region that owns a certain location. Do not use this scheduler for operations on entities, as this scheduler is tied to the region. Each entity has its own scheduler which will follow it across regions.
Example: Setting a block to a beehive.
```java
Location locationToChange = ...;
RegionScheduler scheduler = server.getRegionScheduler();
scheduler.execute(plugin, locationToChange, () -> {
locationToChange.getBlock().setType(Material.BEEHIVE);
});
```
We pass the location as a parameter to the `RegionScheduler` as it needs to work out which region to execute on.
### Async scheduler
The async scheduler can be used for running tasks independent of the server tick process. This can be fetched with:
```java
AsyncScheduler asyncScheduler = server.getAsyncScheduler();
```
### Entity scheduler
Entity schedulers are used for executing tasks on an entity. These will follow the entity wherever it goes, so you must use these instead of the region schedulers.
```java
EntityScheduler scheduler = entity.getScheduler();
```
---
# Listeners
Events are an efficient way to listen for specific actions that happen in the game. They can be called by the server or by plugins. Plugins are able to call custom events, such as a player completing a quest, for other plugins to listen for.
## Your listener class
To listen for events, you need to create a class that implements `Listener`. It is recommended to name it something related to the events you are listening for.
```java
public class ExampleListener implements Listener {
// ...
}
```
## @EventHandler
To listen for an event, you need to create a method that is annotated with `@EventHandler`. This method can be named anything you want, but it is recommended to name it something meaningful related to the event it is listening for.
## The listener method
The method body does not need to return any data, for this reason, use `void` as the return type. Listeners take in a single parameter, which is the event that is being listened to.
```java
public class ExampleListener implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
// ...
}
}
```
## Events
There is no list of events that can be listened to, however take a [look](https://www.google.com) to see all classes that extend `Event`. An event can only be listened to if it has a static `getHandlerList` method.
## Registering the listener
To register the listener, you need to call `Bukkit.getPluginManager().registerEvents()` and pass in your listener class instance and an instance of your plugin. This is commonly done in the `onEnable()` method of your plugin.
```java
public class ExamplePlugin extends JavaPlugin {
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(new ExampleListener(), this);
}
}
```
## Event priority
You can also specify the priority of the event.
```java
public class ExampleListener implements Listener {
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerMove(PlayerMoveEvent event) {
// ...
}
}
```
There are six different priorities that you can use:
* `EventPriority.LOWEST`
* `EventPriority.LOW`
* `EventPriority.NORMAL`
* `EventPriority.HIGH`
* `EventPriority.HIGHEST`
* `EventPriority.MONITOR`
The higher the priority, the later the event is called. If it is important that your plugin has the last say in a certain event, you should use `EventPriority.HIGHEST`.
**Note:** The `MONITOR` priority is used to monitor the event, but not change it. It is called after all other priorities have been called.
## Event cancellation
Some events can be cancelled, preventing the given action from being completed. These events implement `Cancellable`.
```java
public class ExampleListener implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
event.setCancelled(true);
}
}
```
**Caution:** It is important to consider that another plugin could have cancelled or changed the event before your plugin is called. Always check the event before doing anything with it.
Once an event is cancelled, it will continue to call any other listeners for that event unless they add `ignoreCancelled = true` to the `@EventHandler` annotation to ignore cancelled events.
```java
public class ExampleListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
// ...
}
}
```
---
# Handler lists
Every `Event` that can be listened to has a `HandlerList` containing all the listeners that are listening to that event. This list is used to call the listeners when the event is called.
## Getting the handler list for an event
To get the handler list for an event, you can call `getHandlerList()` on the specific event class.
```java
public class ExampleListener implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
HandlerList handlerList = event.getHandlerList();
// ...
}
// Or:
public ExampleListener() {
// Access the handler list through the static getter
HandlerList handlerList = PlayerJoinEvent.getHandlerList();
// ...
}
}
```
## Unregistering a listener
To unregister a listener, you can call `unregister()` on the `HandlerList` that the listener is registered to.
```java
public class ExampleListener implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
HandlerList handlerList = event.getHandlerList();
handlerList.unregister(this);
// ...
}
// Or:
public ExampleListener() {
// Access the handler list through the static getter
HandlerList handlerList = PlayerJoinEvent.getHandlerList();
handlerList.unregister(this);
// Granted this is a pretty stupid example...
}
}
```
You can unregister based on `Listener` or `Plugin` for more convenience.
Likewise, you can also unregister all listeners for a specific event by calling `unregisterAll()` on the `HandlerList`.
---
# Paper plugins
Paper plugins allow developers to take advantage of more modern concepts introduced by Mojang, such as datapacks, to expand the field of what the Paper API is able to introduce.
**Experimental:** This is experimental and may be subject to change.
## How do I use them?
Similarly to Bukkit plugins, you have to introduce a `paper-plugin.yml` file into your JAR resources folder. This will not act as a drop-in replacement for `plugin.yml`, as some things need to be declared differently. You still have the ability to include both `paper-plugin.yml` and `plugin.yml` in the same JAR.
Example configuration:
```yaml
name: Paper-Test-Plugin
version: '1.0'
main: io.papermc.testplugin.TestPlugin
description: Paper Test Plugin
api-version: '1.21.10'
bootstrapper: io.papermc.testplugin.TestPluginBootstrap
loader: io.papermc.testplugin.TestPluginLoader
```
## Dependency declaration
Paper plugins change how to declare dependencies in your `paper-plugin.yml`:
```yaml
dependencies:
bootstrap:
# Let's say that RegistryPlugin registers some data that your plugin needs to use
# We don't need this during runtime, so it's not required in the server section.
# However, can be added to both if needed
RegistryPlugin:
load: BEFORE
required: true
join-classpath: true # Defaults to true
server:
# Add a required "RequiredPlugin" dependency, which will load AFTER your plugin.
RequiredPlugin:
load: AFTER
required: true
# This means that your plugin will not have access to their classpath
join-classpath: false
```
With Paper plugins, dependencies are split into two sections:
* **bootstrap**: Dependencies that you will be using in the `bootstrap`.
* **server**: Dependencies that are used for the core functionality of your plugin, whilst the server is running.
Lets take a look at a dependency:
```yaml
RegistryPlugin:
load: BEFORE # Defaults to OMIT
required: true # Defaults to true
join-classpath: true # Defaults to true
```
* `load` (`BEFORE` | `AFTER` | `OMIT`): Whether this plugin should load before or after your plugin. Note: `OMIT` has undefined ordering behavior.
* `required`: Whether this plugin is required for your plugin to load.
* `join-classpath`: Whether your plugin should have access to their classpath. This is used for plugins that need to access other plugins internals directly.
**Cyclic Loading**: In certain cases, plugins may be able to introduce cyclic loading loops, which will prevent the server from starting. Please read the [cyclic loading guide](https://www.google.com) for more information.
Examples:
```yaml
# Suppose we require ProtocolLib to be loaded for our plugin
ProtocolLib:
load: BEFORE
required: true
# Now, we are going to register some details for a shop plugin
# So the shop plugin should load after our plugin
SuperShopsXUnlimited:
load: AFTER
required: false
# Now, we are going to need to access a plugins classpath
# So that we can properly interact with it.
SuperDuperTacoParty:
required: true
join-classpath: true
```
## What is it used for?
Paper plugins lay down the framework for some future API. Our goals are to open more modern API that better aligns with Vanilla. Paper plugins allow us to do just that by making a new way to load plugin resources before the server has started by using `bootstrappers`.
## Bootstrapper
Paper plugins are able to identify their own bootstrapper by implementing `PluginBootstrap` and adding the class of your implementation to the `bootstrapper` field in the `paper-plugin.yml`.
```java
public class TestPluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(BootstrapContext context) {
}
@Override
public JavaPlugin createPlugin(PluginProviderContext context) {
return new TestPlugin("My custom parameter");
}
}
```
A bootstrapper also allows you to change the way your plugin is initialized, allowing you to pass values into your plugin constructor.
Currently, bootstrappers do not offer much new API and are highly experimental. This may be subject to change once more API is introduced.
## Loaders
Paper plugins are able to identify their own plugin loader by implementing `PluginLoader` and adding the class of your implementation to the `loader` field in the `paper-plugin.yml`.
The goal of the plugin loader is the creation of an expected/dynamic environment for the plugin to load into. This, as of right now, only applies to creating the expected classpath for the plugin, e.g. supplying external libraries to the plugin.
```java
public class TestPluginLoader implements PluginLoader {
@Override
public void classloader(PluginClasspathBuilder classpathBuilder) {
classpathBuilder.addLibrary(new JarLibrary(Path.of("dependency.jar")));
MavenLibraryResolver resolver = new MavenLibraryResolver();
resolver.addDependency(new Dependency(new DefaultArtifact("com.example:example:version"), null));
resolver.addRepository(new RemoteRepository.Builder("paper", "default", "https://repo.papermc.io/repository/maven-public/").build());
classpathBuilder.addLibrary(resolver);
}
}
```
Currently, you are able to add two different library types: `JarLibrary` and `MavenLibraryResolver`.
**Danger:** If you wish to resolve libraries from Maven Central, use a mirror, as using Maven Central directly as a CDN is against the Maven Central Terms of Service, and users of your plugin may hit rate limits.
You should use Papers default mirror, configured by the `PAPER_DEFAULT_CENTRAL_REPOSITORY` environment variable and `org.bukkit.plugin.java.LibraryLoader.centralURL` system property:
```java
resolver.addRepository(new RemoteRepository.Builder("central", "default", MavenLibraryResolver.MAVEN_CENTRAL_DEFAULT_MIRROR).build());
```
Using the Maven Central repository (i.e. `*.maven.org` or `*.maven.apache.org`) will cause a warning to be shown in the console.
## Differences
### Bukkit serialization system
Paper plugins still support the serialization system (`org.bukkit.configuration.serialization`) that Bukkit uses. However, custom classes will not be automatically registered for serialization. In order to use `ConfigurationSection#getObject`, you **must** call `ConfigurationSerialization#registerClass(Class)` before you attempt to fetch objects from configurations.
### Classloading isolation
Paper plugins are not able to access each other unless given explicit access by depending on another plugin, etc. This helps prevent Paper plugins from accidentally accessing each others dependencies, and in general helps ensure that plugins are only able to access what they explicitly depend on.
Paper plugins have the ability to bypass this, being able to access OTHER plugins classloaders by adding a `join-classpath` option to their `paper-plugin.yml`.
```yaml
Plugin:
join-classpath: true # Means you have access to their classpath
```
Note, other Paper plugins will still be unable to access your classloader.
### Load order logic split
In order to better take advantage of classloading isolation, Paper plugins do **not** use the `dependencies` field to determine load order. This was done for a variety of reasons, mostly to allow better control and allow plugins to properly share classloaders. See [declaring dependencies](https://www.google.com) for more information on how to declare the load order of your plugin.
### Commands
Paper plugins do not use the `commands` field to register commands. This means that you do not need to include all of your commands in the `paper-plugin.yml` file. Instead, you can register commands using the [Brigadier Command API](https://www.google.com).
### Cyclic plugin loading
Cyclic loading describes the phenomenon when a plugin loading causes a loop that eventually cycles back to the original plugin.
Unlike Bukkit plugins, Paper plugins will not attempt to resolve cyclic loading issues.
However, if Paper detects a loop that cannot be resolved, you will get an error that looks like this:
```
[ERROR]: [LoadOrderTree] =================================
[ERROR]: [LoadOrderTree] Circular plugin loading detected:
[ERROR]: [LoadOrderTree] 1) Paper-Test-Plugin1 -> Paper-Test-Plugin -> Paper-Test-Plugin1
[ERROR]: [LoadOrderTree] Paper-Test-Plugin1 loadbefore: [Paper-Test-Plugin]
[ERROR]: [LoadOrderTree] Paper-Test-Plugin loadbefore: [Paper-Test-Plugin1]
[ERROR]: [LoadOrderTree] Please report this to the plugin authors of the first plugin of each loop or join the PaperMC Discord server for further help.
[ERROR]: [LoadOrderTree] =================================
```
It is up to you to resolve these cyclical loading issues.
---
# How plugins work
Plugins are a way to extend the functionality of a Minecraft server. They are written in JVM-based languages such as Java, Kotlin, Groovy or Scala. Plugins are loaded from the `plugins` folder in the server directory. Plugins will be loaded from a `.jar` file. Each plugin has a main class that is specified in the plugins `plugin.yml` file. This class must extend JavaPlugin, and is the entry point for the plugin and is where the plugins lifecycle methods are defined.
**Caution:** We do not recommend writing code inside your main classs constructor as there are no guarantees about what API is available at that point. Instead, you should use the `onLoad` method to initialize your plugin. Also, do not call your plugins constructor directly. This will cause issues with your plugin.
## Plugin lifecycle
Plugins are loaded and unloaded at runtime. When a plugin is loaded, it is initialized and enabled. When a plugin is unloaded, it is disabled and finalized.
### Initialization
When a plugin is loaded, it is initialized. This means that the plugin is loaded into memory and its `onLoad` method is called. This method is used to initialize the plugin and set up any resources that it needs. Most of the Bukkit API is not available at this point, so it is not safe to interact with it.
### Enabling
When a plugin is enabled, its `onEnable` method is called. This method is used to set up any resources that the plugin needs to run. This method is called when the plugin is initialized but before the server has started ticking, so it is safe to register event listeners and other resources that the plugin needs to run, however often not safe to interact with a lot of APIs.
This is when you can also open database connections, start threads, and other things that are not safe to do in the `onLoad` method.
### Disabling
When a plugin is disabled, its `onDisable` method is called. This method is used to clean up any resources that the plugin has allocated. This method is called before all plugins are unloaded, and is meant for any cleanup that needs to be done before the plugin is unloaded. This may include saving data to disk or closing connections to databases.
## Event listeners
Events are a way for plugins to listen to things that happen in the server and run code when they are fired. For example, `PlayerJoinEvent` is fired when a player joins the server. This is a more performant way to run code when something happens, as opposed to constantly checking. See our [event listener page](https://www.google.com) for more.
Some events are cancellable. This means that when the event is fired, it can be cancelled which negates or stops the effect of the event. For example, `PlayerMoveEvent` is cancellable. This means that when it is cancelled, the player will not move. This is useful for things like anti-cheat, where you want to cancel the event if the player is moving too fast.
It is important to think about how “hot” an event is when writing event listeners. A “hot” event is an event that is fired very often. For example, `PlayerMoveEvent` is fired every time a player moves. This means that if you have a lot of expensive code in your event listener, it will be run every time a player moves. This can cause a lot of lag. It is important to keep event listeners as lightweight as possible. One possible way is to quickly check if the event should be handled, and if not, return. For example, if you only want to handle the event if the player is moving from one block to another, you can check if the players location has changed blocks. If it hasnt, you can return from the listener.
## Commands
Commands are a way for players, the console, RCON and command blocks to run code on the server. Commands are registered by plugins and can be run by command senders. For example, the `/help` command is registered by the server and can be run by players. Commands can be run by players by typing them in the chat or by running them from a command block.
Commands can have arguments. For example, the `/give` command takes an argument for the player to give the item to and an argument for the item to give. Arguments are separated by spaces. For example, the command `/give Notch diamond` will give the player named Notch a diamond. Note here that the arguments are `["Notch", "diamond"]`.
## Permissions
Permissions are a way to control who can run commands and who can listen to events. Permissions are registered by plugins and can be checked by other plugins. Permissions can be granted to players and groups. Permissions can have a hierarchical nature, if defined so by the plugin in their `plugin.yml`. For example, a plugin can define `example.command.help` as a sub-permission of `example.command`. This means that if a player has the `example.command` permission, they will also have the `example.command.help` permission.
**Note:** Permission plugins can allow the usage of wildcard permissions using the `*` character to grant any permission or sub-permission available, allowing hierarchical permissions even if not set by the plugin itself. For example, granting `example.command.*` through a permission plugin with wildcard support will grant access to all permissions starting with `example.command` itself.
It is **not** recommended to use wildcard permissions, especially `*` (All permissions), as it can be a huge security risk, as well as potentially causing unwanted side effects to a player. Use with caution.
## Configuration
Plugins can have configuration files. These files are used to store data that the plugin needs to run. For example, a plugin that adds a new block to the game might have a configuration file that stores the blocks ID. Configuration files should be stored in the plugins data folder, within the `plugins` folder. The server offers a YAML configuration API that can be used to read and write configuration files. See [here](https://www.google.com) for more information.
## Scheduling tasks
Plugins can schedule tasks to run at a later time. This is useful for things like running code after a certain amount of time has passed. For example, a plugin might want to run code after 5 seconds. This can be done by scheduling a task to run after 100 ticks - one second is 20 ticks during normal operation. It is important to note that tasks might be delayed if the server is lagging. For example, if the server is only running at 10 ticks per second, a task that is scheduled to run after 100 ticks will take 10 seconds.
In Java, typically you could use `Thread#sleep()` to delay the execution of code. However, if the code is running on the main thread, this will cause the server to pause for the delay. Instead, you should use the `Scheduler` API to schedule tasks to run later.
Learn more about the [Scheduler API](https://www.google.com).
## Components
Since Minecraft 1.7 and the introduction of “components”, plugins can now send messages to players that contain rich text. This means that plugins can send messages that contain things like colors, bold text, and clickable links. Colors were always possible, but only through the use of legacy color codes.
Paper implements a library called [Adventure](https://www.google.com) that makes it easy to create and send messages to players. Learn more about the [Adventure API](https://www.google.com) from their docs or our docs [here](https://www.google.com).
---
# Minecraft internals
The code that runs Minecraft is not open source. Bukkit is an API that allows plugins to interact with the server. This is implemented by CraftBukkit and interacts with Minecrafts code. You will often hear the terms NMS and CraftBukkit when talking about Minecraft internals.
**Using Minecraft internals is not recommended.** This is because using internal code directly is not guaranteed to be stable and it changes often. This means that your plugin may break when a new version of Minecraft is released. Whenever possible, you should use API instead of internals.
**PaperMC will offer no direct support for programming against Minecraft internals.**
## What is NMS?
NMS stands for `net.minecraft.server` and refers to a Java package that contains a lot of Mojangs code. This code is proprietary and is not open source. This code is not guaranteed to be stable when invoked externally and may change at any time.
## Accessing Minecraft internals
In order to use Mojang and CraftBukkit code, you may either use the `paperweight-userdev` Gradle plugin or use reflection. `paperweight-userdev` is the recommended way to access internal code as it is easier to use due to being able to have the remapped code in your IDE. You can find out more about this in the [`paperweight-userdev`](https://www.google.com) section.
However, if you are unable to use `paperweight-userdev`, you can use reflection.
## Reflection
Reflection is a way to access code at runtime. This allows you to access code that may not be available at compile time. Reflection is often used to access internal code across multiple versions. However, reflection does come with performance impacts if used improperly. For example, if you are accessing a method or field more than once, you should cache the `Field` / `Method` to prevent the performance impact of looking up the field/method each time.
### 1.20.4 and older
The internal CraftBukkit code was relocated to `org.bukkit.craftbukkit.<version>` unless you ran a Mojang-mapped version of Paper. This was unlikely to be the case in most production environments until 1.20.5. This means that any attempts to reflect had to include the version. For example, `org.bukkit.craftbukkit.v1_20_R2.CraftServer` was the full class and package name for the CraftServer class in version 1.20.2. You could access these classes easily with some reflection utilities.
```java
private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackageName();
public static String cbClass(String clazz) {
return CRAFTBUKKIT_PACKAGE + "." + clazz;
}
// You can then use this method to get the CraftBukkit class:
Class.forName(cbClass("entity.CraftBee"));
```
Minecrafts code is obfuscated. This means that the names of classes and methods are changed to make them harder to understand. Paper deobfuscates these identifiers for development and since 1.20.5, also for runtime.
### 1.20.4 and older
Previously, to provide compatibility with legacy plugins, Paper was reobfuscated at runtime. You could use a library like [`reflection-remapper`](https://www.google.com) to automatically remap the reflection references. This allowed you to use the deobfuscated, Mojang-mapped names in your code. This was recommended as it made the code easier to understand.
## Mojang-mapped servers
Running a Mojang-mapped (moj-map) server is an excellent way to streamline your processes because you can develop using the same mappings that will be present at runtime. This eliminates the need for remapping in your compilation, which in turn simplifies debugging and allows you to hotswap plugins.
As of 1.20.5, Paper ships with a Mojang-mapped runtime by default instead of reobfuscating the server to Spigot mappings. By adopting Mojang mappings, you ensure that your plugin wont require internal remapping at runtime.
For more information, see the [plugin remapping](https://www.google.com) section and [userdev](https://www.google.com) documentation covering these changes.
## Getting the current Minecraft version
You can get the current Minecraft version to allow you to use the correct code for a specific version. This can be done with one of the following methods:
```java
// Example value: 1.21.10
String minecraftVersion = Bukkit.getServer().getMinecraftVersion();
// Example value: 1.21.10-R0.1-SNAPSHOT
String bukkitVersion = Bukkit.getServer().getBukkitVersion();
// Example value for 1.20.1: 3465
int dataVersion = Bukkit.getUnsafe().getDataVersion();
```
### Parsing the version
Parsing the version from the package name of classes is no longer possible as of 1.20.5 as Paper stopped relocating the CraftBukkit package. See the [reflection](https://www.google.com) section for more information.
```
```markdown
# How Plugins Work
## Overview
Plugins are a way to extend the functionality of a Minecraft server. They are written in JVM-based languages such as Java, Kotlin, Groovy, or Scala. Plugins are loaded from the `plugins` folder in the server directory, specifically from `.jar` files. Each plugin has a main class, specified in the `plugin.yml` file, which extends `JavaPlugin`. This class serves as the entry point for the plugin and defines its lifecycle methods.
**Caution:** Avoid writing code inside the main class's constructor, as API availability is not guaranteed at that point. Instead, use the `onLoad` method for initialization. Do not call the plugin's constructor directly.
## Plugin Lifecycle
Plugins are loaded and unloaded at runtime.
* **Loading:** The plugin is initialized and enabled.
* **Unloading:** The plugin is disabled and finalized.
### Initialization
When a plugin is loaded, it is initialized, meaning:
* The plugin is loaded into memory.
* Its `onLoad` method is called.
The `onLoad` method is used to:
* Initialize the plugin.
* Set up any resources it needs.
Most of the Bukkit API is not available at this point, so interacting with it is not safe.
### Enabling
When a plugin is enabled:
* Its `onEnable` method is called.
The `onEnable` method is used to:
* Set up resources needed for the plugin to run.
* Register event listeners and other resources.
* Open database connections and start threads.
This method is called when the plugin is initialized but before the server has started ticking.
### Disabling
When a plugin is disabled:
* Its `onDisable` method is called.
The `onDisable` method is used to:
* Clean up any allocated resources.
* Save data to disk.
* Close database connections.
This method is called before all plugins are unloaded.
## Event Listeners
Events allow plugins to listen to actions happening on the server and execute code when these events are triggered.
* Example: `PlayerJoinEvent` is fired when a player joins the server.
Events are more performant than constantly checking for actions.
Some events are cancellable, stopping the effect of the event.
* Example: `PlayerMoveEvent` can be cancelled to prevent player movement.
It's important to consider how "hot" an event is (how often it's fired) and keep event listeners lightweight to avoid lag.
* "Hot" events like `PlayerMoveEvent` fire very often.
* Optimize listeners by quickly checking if the event should be handled and returning if not.
## Commands
Commands are a way for players, the console, RCON, and command blocks to run code on the server.
* Registered by plugins.
* Run by command senders.
* Example: `/help` is registered by the server.
Commands can have arguments, separated by spaces.
* Example: `/give Notch diamond` gives the player named Notch a diamond.
## Permissions
Permissions control who can run commands and listen to events.
* Registered by plugins.
* Checked by other plugins.
* Granted to players and groups.
Permissions can be hierarchical.
* Example: `example.command.help` is a sub-permission of `example.command`.
**Note:** Wildcard permissions (e.g., `example.command.*`) should be used with caution due to security risks and potential side effects.
## Configuration
Plugins can have configuration files to store data.
* Stored in the plugin's data folder within the `plugins` folder.
* The server offers a YAML configuration API for reading and writing configuration files.
## Scheduling Tasks
Plugins can schedule tasks to run at a later time.
* Useful for running code after a certain amount of time has passed.
* Tasks might be delayed if the server is lagging.
Use the `Scheduler` API instead of `Thread#sleep()` to avoid pausing the server.
* One second is 20 ticks during normal operation.
## Components
Plugins can send messages to players with rich text (colors, bold text, clickable links).
* Paper implements the `Adventure` library for creating and sending messages.
---
# Minecraft Internals
## Overview
The code that runs Minecraft is not open source. Bukkit provides an API for plugins to interact with the server, implemented by CraftBukkit. The terms NMS and CraftBukkit are commonly used when discussing Minecraft internals.
**Caution:** Using Minecraft internals is not recommended due to instability and potential breakage with new Minecraft versions. Use the API instead whenever possible. PaperMC offers no direct support for programming against Minecraft internals.
## What is NMS?
NMS stands for `net.minecraft.server` and refers to a Java package containing much of Mojang's code. This code is proprietary, not open source, and not guaranteed to be stable when invoked externally. It may change at any time.
## Accessing Minecraft Internals
Mojang and CraftBukkit code can be accessed using either the `paperweight-userdev` Gradle plugin or reflection.
* `paperweight-userdev` is recommended for easier use with remapped code in your IDE.
* Reflection can be used if `paperweight-userdev` is not an option.
### Reflection
Reflection allows accessing code at runtime, including code that may not be available at compile time. It is often used to access internal code across multiple versions.
* **Performance Impact:** Reflection can have performance impacts if used improperly.
* **Caching:** Cache `Field` / `Method` objects when accessing them more than once to prevent performance issues.
#### 1.20.4 and Older
CraftBukkit code was located at `org.bukkit.craftbukkit.<version>` unless using a Mojang-mapped version of Paper. Reflection required including the version. For example:
`org.bukkit.craftbukkit.v1_20_R2.CraftServer`
Minecraft's code is obfuscated. Paper deobfuscates these identifiers for development and, since 1.20.5, also for runtime.
Previously, Paper was reobfuscated at runtime to provide compatibility with legacy plugins. Libraries like `reflection-remapper` could be used to automatically remap reflection references.
### Mojang-Mapped Servers
Running a Mojang-mapped (moj-map) server streamlines development by using the same mappings at runtime, simplifying debugging and allowing hotswapping of plugins. As of 1.20.5, Paper ships with a Mojang-mapped runtime by default.
## Getting the Current Minecraft Version
You can get the current Minecraft version to use the correct code for a specific version.
```java
// Example value: 1.21.10
String minecraftVersion = Bukkit.getServer().getMinecraftVersion();
// Example value: 1.21.10-R0.1-SNAPSHOT
String bukkitVersion = Bukkit.getServer().getBukkitVersion();
// Example value for 1.20.1: 3465
int dataVersion = Bukkit.getUnsafe().getDataVersion();
```
Parsing the version from the package name of classes is no longer possible as of 1.20.5.
---
# Lifecycle API
## Overview
The Lifecycle API is used for lifecycle-related registration. It is currently used by the Brigadier command API and is planned for use with the Registry Modification API. This API is beneficial for systems initialized very early in the startup process.
## LifecycleEventManager
The `LifecycleEventManager` is tied to either a `Plugin` instance or a `BootstrapContext`, depending on where you access it.
**Example (Plugin):**
```java
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> lifecycleManager = this.getLifecycleManager();
}
```
**Example (Bootstrap):**
```java
@Override
public void bootstrap(BootstrapContext context) {
final LifecycleEventManager<BootstrapContext> lifecycleManager = context.getLifecycleManager();
}
```
## LifecycleEvents
After obtaining the `LifecycleEventManager`, create an event handler by selecting an event type from `LifecycleEvents`.
**Example:**
```java
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> lifecycleManager = this.getLifecycleManager();
PrioritizedLifecycleEventHandlerConfiguration<LifecycleEventOwner> config = LifecycleEvents.SOME_EVENT.newHandler((event) -> {
// Handler for the event
});
}
```
## Configuration
Each handler can be configured in several ways, depending on the event type.
### Priority
Sets the execution order relative to other handlers of the same event type. Lower numbers are run earlier. The default priority is 0.
### Monitor
Marks the handler to be called after all other non-monitor handlers have been called. Use this only to inspect state, not to modify it.
Priority and monitor state are exclusive. Setting one resets the other.
**Example:**
```java
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> lifecycleManager = this.getLifecycleManager();
PrioritizedLifecycleEventHandlerConfiguration<LifecycleEventOwner> config = LifecycleEvents.SOME_EVENT.newHandler((event) -> {
// Handler for the event
});
config.priority(10); // sets a priority of 10
// or
config.monitor(); // marks the handler as a monitor
}
```
## Registering
After configuring the handler, register it with the lifecycle manager.
**Example:**
```java
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> lifecycleManager = this.getLifecycleManager();
PrioritizedLifecycleEventHandlerConfiguration<LifecycleEventOwner> config = LifecycleEvents.SOME_EVENT.newHandler((event) -> {
// Handler for the event
}).priority(10);
lifecycleManager.registerEventHandler(config);
}
```
A shorthand method exists for registering the handler without configuration:
```java
@Override
public void onEnable() {
final LifecycleEventManager<Plugin> lifecycleManager = this.getLifecycleManager();
lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS, (event) -> {
// Handler for the event
});
}
```
**Note:** Some event types have special behaviors that restrict certain mechanics. Plugin reloading (via `/bukkit:reload` or `Server#reload()`) is disabled if plugins register handlers in certain situations.
## Why Does This Exist?
This API is necessary because some events fire before `JavaPlugin` instances or the `MinecraftServer` instance are created, right at the start of server startup. The existing Bukkit event system is not designed to exist at this time.
**Technical Explanation:**
* Bukkit events cannot have generics due to reflective registration, leading to many useless classes for similar patterns like registry modification events.
* The existing system always has priorities. Lifecycle events may not always want priorities.
* The existing system exists too late, relying on the `Plugin` instance, which does not exist during bootstrapping.
* A new system allows using interfaces and server implementations for events, simplifying them.
* A new system enforces, at compile time, which events you can register where based on the context of the registration.
---
# Menu Type API
**Experimental**
The Menu Type API is experimental and may change in the future.
## Overview
The Menu Type API allows developers to replicate various Minecraft menu types, such as chests, crafting tables, and villager trade menus, which were not perfectly replicable with the old Bukkit inventory API.
## What is a menu?
Menus, also referred to as views, are user interfaces that can be created and viewed by players. The `MenuType` interface declares all possible menu types.
Menus are the visual representations of inventories, which are the containers.
Menus created using this API follow the same logic as Vanilla. Some menus can directly represent a block, like a furnace, while `MERCHANT` can represent a merchant entity.
## What are inventory views?
An `InventoryView` is a specific view created from a menu type.
An inventory view links together two separate inventories and always has a player viewing them. The bottom linked inventory is the players inventory.
Some views have specialized subinterfaces for quickly checking their type, like `FurnaceView`. For other views, you can use the `InventoryView#getMenuType` method.
## Building inventory views from menu types
The most common way to create inventory views is by using their respective builders. Every menu type has a builder which can be used to customize the resulting view.
**Example:**
```java
// MenuType.CRAFTING is used to open a crafting table.
MenuType.CRAFTING.builder()
// Set the title of the view, which will be displayed at the top.
.title(Component.text("The inventory view's title"))
// Determines whether the server should check if the player can reach the location.
.checkReachable(true)
// Set the location. Because of checkReachable being set to `true`, this has to be a valid
// crafting table. The server will check and make sure that the player does not get pushed
// away too far to use the crafting table and will close the player's inventory if the
// crafting table were to be pushed away.
.location(location)
// Build this view for the provided player, linking the inventory of the crafting table
// together with the player's own inventory into an inventory view.
.build(player)
// Open the view.
.open();
```
Builders can be reused to reduce code repetition.
## Opening blocks that have menus
Almost all inventory views have a block attached to them, except for `MenuType.MERCHANT`, which has a `Merchant` attached to it.
There are two types of blocks that have menus:
* **Block entity blocks (tile entity blocks):** Have a state associated with them (e.g., beacon, chests, furnaces).
* **Stateless blocks:** Do not have any state associated with them (e.g., crafting tables, grindstones, anvils).
When you open a specific location with the `#location` builder method, and the block matches the expected block from the menu type, the state of that block can change, and all players can see the change live.
## Persistent inventory views
Inventory views can be reused for persistent operations.
**Example:** A `/persistent` command that opens a players own, persistent, stash.
```java
// A map to store all inventory views in.
private static final Map<Player, InventoryView> VIEWS = new HashMap<>();
public static LiteralCommandNode<CommandSourceStack> createCommand() {
return Commands.literal("persistent")
.executes(ctx -> {
if (!(ctx.getSource().getExecutor() instanceof Player player)) {
return 0;
}
// First, attempt to get a stored view.
InventoryView view = VIEWS.get(player);
// If there is no view currently stored, create it.
if (view == null) {
view = MenuType.GENERIC_9X6.builder()
.title(Component.text(player.getName() + "'s stash", NamedTextColor.DARK_RED))
.build(player);
// And finally store it in the map.
VIEWS.put(player, view);
}
// As the inventory view is directly bound to the player, we do not have
// to reassign the player and can just open it.
view.open();
return Command.SINGLE_SUCCESS;
})
.build();
}
@EventHandler
void onPlayerQuit(PlayerQuitEvent event) {
InventoryView view = VIEWS.remove(event.getPlayer());
if (view != null) {
Inventory topInventory = view.getTopInventory();
// Save the contents of the inventory to a file or database.
}
}
```
On the quit event:
1. Remove the player entry from the map.
2. Store the top inventory somewhere so it persists across server restarts.
---
# Datapack Discovery
**Experimental**
The Datapack Discovery API is experimental and may change in the future.
## Overview
The Datapack Discovery API, part of the Lifecycle API, allows developers to modify core server aspects, specifically datapacks. This allows including datapacks directly within plugin JAR files.
**Note:** This feature requires understanding of the Lifecycle API, datapack format, and the use of a `paper-plugin.yml` file.
## The Datapack Discovery Lifecycle Event
The `LifecycleEvents.DATAPACK_DISCOVERY` lifecycle event allows adding, checking for, and removing datapacks about to be loaded by the server.
**Tip:** Code examples are assumed to be inside a `PluginBootstrap`'s `bootstrap(BootstrapContext context)` method and within an event handler.
```java
@NullMarked
public class CustomPluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(BootstrapContext context) {
context.getLifecycleManager().registerEventHandler(LifecycleEvents.DATAPACK_DISCOVERY.newHandler(
event -> {
// All code is contained here.
}
));
}
}
```
## Retrieving All Currently Discovered Datapacks
Use `DatapackRegistrar#getDiscoveredPacks()` to retrieve discovered datapacks.
```java
context.getLogger().info("The following datapacks were found: {}",
String.join(", ", event.registrar().getDiscoveredPacks().keySet()));
```
Output example:
`[00:26:12 INFO]: [PaperDocsTestProject] The following datapacks were found: file/bukkit, minecart_improvements, paper, redstone_experiments, trade_rebalance, vanilla`
## Removing Discovered Datapacks
Use `DatapackRegistrar#removeDiscoveredPack(String name)` to prevent datapacks from being discovered.
```java
final Set<String> datapacksToRemove = Set.of("minecart_improvements", "redstone_experiments", "trade_rebalance");
datapacksToRemove.forEach(datapack -> event.registrar().removeDiscoveredPack(datapack));
context.getLogger().info("The following datapacks were found: {}",
String.join(", ", event.registrar().getDiscoveredPacks().keySet()));
```
Output example:
`[00:35:39 INFO]: [PaperDocsTestProject] The following datapacks were found: file/bukkit, paper, vanilla`
## Registering Custom Datapacks
The primary use case is adding plugin-included datapacks. You must include the datapack's *source files* (not the zip) in the plugin's JAR file, typically under `src/main/resources`.
**Tip:** If you don't have a datapack, check out [Vanilla Tweaks](https://vanillatweaks.net/) for examples.
### Including the Datapack in Your Plugin
Add the datapack to your plugin's `src/main/resources` folder, with at least one extra folder level. For example, `resources/custom_datapack`.
Folder structure:
```
src/main/resources
custom_datapack
pack.mcmeta
data/
...
```
Build your plugin and verify that there is a `custom_datapack` folder in the root of your plugin's JAR file.
### Discovering the Datapack
Call `DatapackRegistrar#discoverPack(URI uri, String id)`. The URI should point to your datapacks folder in your JAR. Use `getClass().getResource("/custom_datapack").toURI()` to get the URI.
**Important:** The preceding slash is crucial.
The ID can be set to whatever you want to identify your datapack with. The final name of the loaded pack will be `<YourPluginName>/<id>`.
```java
try {
URI uri = Objects.requireNonNull(getClass().getResource("/custom_datapack")).toURI();
event.registrar().discoverPack(uri, "provided");
} catch (URISyntaxException | IOException e) {
throw new RuntimeException(e);
}
```
### Verifying that the Datapack Loaded Correctly
Verify by executing the command `/datapack list enabled`.
Alternatively, check the loaded status during plugin execution in the `onLoad` method:
```java
public final class CustomJavaPlugin extends JavaPlugin {
@Override
public void onLoad() {
Datapack pack = this.getServer().getDatapackManager().getPack(getPluginMeta().getName() + "/provided");
if (pack != null) {
if (pack.isEnabled()) {
this.getLogger().info("The datapack loaded successfully!");
} else {
this.getLogger().warn("The datapack failed to load.");
}
}
}
}
```
Example console output:
```
[01:10:12 INFO]: [PaperDocsTestProject] Loading server plugin PaperDocsTestProject v1.0-DEV
[01:10:12 INFO]: [PaperDocsTestProject] The datapack loaded successfully!
```
---
# Persistent Data Container (PDC)
## Overview
The Persistent Data Container (PDC) is a way to store custom data on a variety of objects, including items, entities, and block entities.
**Supported Classes:**
* `ItemStack`
* `Chunk`
* `World`
* `Entity`
* `TileState`
* `Structure`
* `GeneratedStructure`
* `Raid`
* `OfflinePlayer`
* `ItemMeta`
## What is it used for?
The PDC provides a reliable and performant way to store arbitrary data on objects, replacing older methods like:
* **NBT tags:** Requires reflection and is unreliable long-term.
* **Lore and display names:** Prone to collisions and slow to access.
The PDC doesn't rely on server internals and handles the data lifecycle automatically.
## Adding data
To store data in the PDC:
1. Create a `NamespacedKey` to identify the data.
2. Get the `PersistentDataContainer` from the object.
3. Use the `set` method to store the data.
```java
NamespacedKey key = new NamespacedKey(pluginInstance, "example-key");
World world = Bukkit.getServer().getWorlds().getFirst();
PersistentDataContainer pdc = world.getPersistentDataContainer();
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
```
`ItemStack` requires using its builder-style consumer:
```java
NamespacedKey key = ...;
ItemStack item = ItemStack.of(Material.DIAMOND);
item.editPersistentDataContainer(pdc -> {
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
});
```
**Note:** `ItemStack#editPersistentDataContainer()` is available in 1.21.4+. For older versions, modify the `ItemMeta` instead. For 1.16.5+, theres the `ItemStack#editMeta()` method.
**Note:** Reuse `NamespacedKey` objects whenever possible. Construct them with either a `Plugin` instance and a `String` identifier, or a `String` namespace and a `String` identifier.
## Getting data
To retrieve data from the PDC:
1. Know the `NamespacedKey` and `PersistentDataType` of the data.
2. Use the `get` or `getOrDefault` method to retrieve the data.
```java
NamespacedKey key = ...;
World world = ...;
PersistentDataContainer pdc = world.getPersistentDataContainer();
String value = pdc.getOrDefault(key, PersistentDataType.STRING, "<null>");
player.sendPlainMessage(value);
```
## Data types
The PDC supports a wide range of data types:
* Byte
* Byte Array
* Double
* Float
* Integer
* Integer Array
* Long
* Long Array
* Short
* String
* Boolean
* **Tag Containers:** Nested PDCs.
```java
PersistentDataContainer container = ...;
PersistentDataContainer newContainer = container.getAdapterContext().newPersistentDataContainer();
```
* **Lists:** Lists of data that can be stored via another persistent data type.
```java
container.set(key, PersistentDataType.LIST.listTypeFrom(PersistentDataType.STRING), List.of("a", "list", "of", "strings"));
container.set(key, PersistentDataType.LIST.strings(), List.of("a", "list", "of", "strings"));
List<String> strings = container.get(key, PersistentDataType.LIST.strings());
```
```markdown
# Persistent Data Container (PDC)
The Persistent Data Container (PDC) is a way to store custom data on various objects like items, entities, and block entities.
**Supported Classes:**
* `ItemStack`
* `Chunk`
* `World`
* `Entity`
* `TileState`
* `Structure`
* `GeneratedStructure`
* `Raid`
* `OfflinePlayer`
* `ItemMeta`
## What is it used for?
* **Reliable Storage:** Provides a reliable and performant way to store arbitrary data.
* **No Server Internals:** Doesn't rely on accessing server internals, reducing breakage on future versions.
* **Lifecycle Management:** Removes the need to manually track data lifecycle; PDC data is saved when the entity unloads.
* **Replaces Older Methods:** A better alternative to NBT tags (requires reflection, unreliable) and lore/display names (prone to collisions, slow access).
## Adding data
To store data in the PDC, you need:
* `NamespacedKey`: To identify the data.
* `PersistentDataContainer`: The object to store data on.
* The data itself.
```java
NamespacedKey key = new NamespacedKey(pluginInstance, "example-key"); // Create a NamespacedKey
World world = Bukkit.getServer().getWorlds().getFirst();
PersistentDataContainer pdc = world.getPersistentDataContainer();
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
```
For `ItemStack` (1.21.4+):
```java
NamespacedKey key = ...;
ItemStack item = ItemStack.of(Material.DIAMOND);
item.editPersistentDataContainer(pdc -> {
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
});
```
**Important Notes:**
* `ItemStack#editPersistentDataContainer()` is available in 1.21.4+. For older versions, use `ItemMeta`.
* For 1.16.5+, `ItemStack#editMeta()` is available.
* Reuse `NamespacedKey` objects. They can be constructed with a `Plugin` instance and a `String` identifier, or a `String` namespace and a `String` identifier. The first option is preferred for automatic namespacing.
## Getting data
To retrieve data:
* Know the `NamespacedKey`.
* Know the `PersistentDataType`.
```java
NamespacedKey key = ...; // Use the same key as the adding-data example
World world = ...; // Use the same world as the adding-data example
PersistentDataContainer pdc = world.getPersistentDataContainer();
String value = pdc.getOrDefault(key, PersistentDataType.STRING, "<null>"); // Utilize the data from the PDC
player.sendPlainMessage(value); // Do something with the value
```
Use `getOrDefault` when needing non-null values, especially for Adventure's `Component.text(String)`.
## Data types
The PDC supports:
* `Byte`, `Byte Array`
* `Double`
* `Float`
* `Integer`, `Integer Array`
* `Long`, `Long Array`
* `Short`
* `String`
* `Boolean`
* **Tag Containers:** Nested PDCs.
```java
PersistentDataContainer container = ...; // Get an existing container
PersistentDataContainer newContainer = container.getAdapterContext().newPersistentDataContainer(); // Create a new container
```
* **Lists:** Lists of data that can be stored via another persistent data type.
```java
// Storing a list of strings in a container by verbosely creating
// a list data type wrapping the string data type.
container.set(key, PersistentDataType.LIST.listTypeFrom(PersistentDataType.STRING), List.of("a", "list", "of", "strings"));
// Storing a list of strings in a container by using the API
// provided pre-definitions of commonly used list types.
container.set(key, PersistentDataType.LIST.strings(), List.of("a", "list", "of", "strings"));
// Retrieving a list of strings from the container.
List<String> strings = container.get(key, PersistentDataType.LIST.strings());
```
### Boolean
The `Boolean` PDC type exists for convenience; you cannot make more complex types distill to a `Boolean`.
## Custom data types
Implement your own `PersistentDataType` for complex data. The `PersistentDataType` handles "deconstructing" a complex data type into natively supported types and vice versa.
**Example (UUID):**
```java
@NullMarked
public class UUIDDataType implements PersistentDataType<byte[], UUID> {
public static final UUIDDataType INSTANCE = new UUIDDataType();
private UUIDDataType() {} // Singleton
@Override
public Class<byte[]> getPrimitiveType() {
return byte[].class;
}
@Override
public Class<UUID> getComplexType() {
return UUID.class;
}
@Override
public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) {
ByteBuffer bb = ByteBuffer.allocate(Long.BYTES * 2);
bb.putLong(complex.getMostSignificantBits());
bb.putLong(complex.getLeastSignificantBits());
return bb.array();
}
@Override
public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
ByteBuffer bb = ByteBuffer.wrap(primitive);
long firstLong = bb.getLong();
long secondLong = bb.getLong();
return new UUID(firstLong, secondLong);
}
}
```
**Usage:**
```java
container.set(key, UUIDDataType.INSTANCE, uuid);
```
## Read-only containers
`ItemStack` and `OfflinePlayer` provide read-only views of their PDC. `OfflinePlayer` cannot be modified due to disk read operations. Mutable objects generally need to be re-saved, so read-only "views" are preferred.
```java
NamespacedKey key = ...;
ItemStack item = ...;
PersistentDataContainerView pdcView = item.getPersistentDataContainer();
String value = pdcView.getOrDefault(key, PersistentDataType.STRING, "<null>");
player.sendPlainMessage(value);
```
**Note:** PDC-view support for `ItemStack` was introduced in 1.21.1. Use `ItemMeta` for older versions.
## Storing on different objects
**Caution:** Data is **not** copied across holders automatically. Manual copying is required when 'moving' between `PersistentDataHolder`s. Placing an `ItemStack` as a Block (with a `TileState`) does **not** copy PDC data.
Objects that implement `PersistentDataHolder` have a PDC fetched with `PersistentDataHolder#getPersistentDataContainer()`.
### ItemStack
* Prior to 1.21.1, the PDC was accessed via `ItemMeta` (overhead).
* 1.21.1+ exposes a read-only view via `ItemStack#getPersistentDataContainer()`.
* 1.21.4+ simplifies edits using `ItemStack#editPersistentDataContainer(java.util.function.Consumer)`.
```java
ItemStack itemStack = ...;
itemStack.editPersistentDataContainer(pdc -> {
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
});
```
### Chunk
`Chunk#getPersistentDataContainer()`
### World
`World#getPersistentDataContainer()`
### Entity
`Entity#getPersistentDataContainer()`
### TileState
Requires casting the block's state to a `TileState` extension (only works for blocks with block entities).
```java
Block block = ...;
if (block.getState() instanceof Chest chest) {
chest.getPersistentDataContainer().set(key, PersistentDataType.STRING, "I love tacos!");
chest.update();
}
```
### Structure
`Structure#getPersistentDataContainer()`
### GeneratedStructure
`GeneratedStructure#getPersistentDataContainer()`
### Raid
`Raid#getPersistentDataContainer()`
### OfflinePlayer
`OfflinePlayer` exposes a read-only PDC via `OfflinePlayer#getPersistentDataContainer()`.
### ItemMeta
`ItemMeta#getPersistentDataContainer()`
---
# Particles
This guide explains how to spawn different types of particles. If a particle isn't mentioned, it likely has no special behavior.
**Methods for Spawning Particles:**
1. **ParticleBuilder (Preferred):** Reusable, offers improved readability, and includes the `receivers()` method for greater control over receivers.
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(2, 0.2, 2)
.count(14)
.receivers(32, true)
.spawn();
```
* `ParticleBuilder.receivers(32, true)` selects all players within 32 blocks (sphere). `false` selects players in a cube.
2. **spawnParticle() Methods:**
* `World.spawnParticle()`: Spawns the particle for all players.
* `Player.spawnParticle()`: Spawns the particle only for the player.
## count argument behavior
When spawning particles, the Minecraft client behaves differently based on the `count` argument:
* **`count = 0`**: A singular particle spawns at the provided location without modification. The offset values are multiplied by the `extra` argument.
* **`count > 0`**: `count` number of particles are spawned. New offset values are generated using a Gaussian (normal) distribution, multiplied by the `extra` argument.
## Directional particles
These particles have an initial velocity when spawned. Effective speed varies between particles.
```java
Particle.FLAME.builder()
.location(someLocation)
.offset(0.5, 0.5, 0.5)
.count(8)
.extra(0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.FLAME, someLocation, 8, 0.5, 0.5, 0.5, 0);
```
**Caution:** Leaving the `extra` parameter unset will default it to `1`, likely resulting in unexpected behavior.
### Random direction
Setting the `count` parameter to anything positive will yield a random direction for the velocity.
```java
Particle.CRIT.builder()
.location(someLocation)
.count(6)
.extra(0.6)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.CRIT, someLocation, 6, 0, 0, 0, 0.6);
```
### Specified direction
To specify the velocity's direction, set the `count` argument to `0` and use the offset arguments as the direction vector.
```java
ParticleBuilder particleBuilder = Particle.CAMPFIRE_SIGNAL_SMOKE.builder()
.location(someLocation)
.offset(0, 1, 0)
.count(0)
.extra(0.1);
Bukkit.getScheduler().runTaskTimer(plugin, () -> particleBuilder.receivers(32, true).spawn(), 0, 4);
// or
Bukkit.getScheduler().runTaskTimer(plugin, () -> someWorld.spawnParticle(Particle.CAMPFIRE_SIGNAL_SMOKE, someLocation, 0, 0, 1, 0, 0.1), 0, 4);
```
To make the smoke go down:
```java
ParticleBuilder particleBuilder = Particle.CAMPFIRE_SIGNAL_SMOKE.builder()
.location(someLocation)
.offset(0, -1, 0)
.count(0)
.extra(0.1);
Bukkit.getScheduler().runTaskTimer(plugin, () -> particleBuilder.receivers(32, true).spawn(), 0, 4);
// or
Bukkit.getScheduler().runTaskTimer(plugin, () -> someWorld.spawnParticle(Particle.CAMPFIRE_SIGNAL_SMOKE, someLocation, 0, 0, -1, 0, 0.1), 0, 4);
```
### List of directional particles
* `BLOCK`
* `BUBBLE`
* `BUBBLE_COLUMN_UP`
* `BUBBLE_POP`
* `CAMPFIRE_COSY_SMOKE`
* `CAMPFIRE_SIGNAL_SMOKE`
* `CLOUD`
* `CRIT`
* `DAMAGE_INDICATOR`
* `DRAGON_BREATH`
* `DUST`
* `DUST_COLOR_TRANSITION`
* `DUST_PLUME`
* `ELECTRIC_SPARK`
* `ENCHANTED_HIT`
* `END_ROD`
* `FIREWORK`
* `FISHING`
* `FLAME`
* `FLASH`
* `GLOW_SQUID_INK`
* `ITEM`
* `LARGE_SMOKE`
* `POOF`
* `REVERSE_PORTAL`
* `SCRAPE`
* `SCULK_CHARGE`
* `SCULK_CHARGE_POP`
* `SCULK_SOUL`
* `SMALL_FLAME`
* `SMOKE`
* `SNEEZE`
* `SNOWFLAKE`
* `SOUL`
* `SOUL_FIRE_FLAME`
* `SPIT`
* `SQUID_INK`
* `TOTEM_OF_UNDYING`
* `TRIAL_SPAWNER_DETECTION`
* `TRIAL_SPAWNER_DETECTION_OMINOUS`
* `WAX_OFF`
* `WAX_ON`
* `WHITE_SMOKE`
## Colored particles
These particles can be colored by passing a `Color` object as the `data` argument.
```java
Particle.ENTITY_EFFECT.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(10)
.data(Color.fromARGB(200, 255, 128, 0))
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.ENTITY_EFFECT, someLocation, 10, 1, 1, 1, Color.fromARGB(200, 255, 128, 0));
```
* Only `ENTITY_EFFECT` supports the alpha channel for translucent particles.
* `FLASH` and `TINTED_LEAVES` ignore the alpha channel.
## Dust particles
Vanilla uses the dust particle for redstone particles. They can have a custom color by passing `Particle.DustOptions` as `data`.
* Scale factor must be in the range of `0.01` to `4.0`.
```java
ParticleBuilder particleBuilder = Particle.DUST.builder().color(Color.BLUE, 2.0f);
for (double i = -1.0; i <= 1.0; i += 0.25) {
particleBuilder.location(someLocation.clone().add(0, i, 0)).receivers(32, true).spawn();
}
// or
for (double i = -1.0; i <= 1.0; i += 0.25) {
someWorld.spawnParticle(Particle.DUST, someLocation.clone().add(0, i, 0), 1, new Particle.DustOptions(Color.BLUE, 2.0f));
}
```
* Adding a size argument controls the dust particle's lifetime (in ticks). Default is a random integer between 8 and 40, multiplied by the scale (minimum of 1).
## Dust transition particles
Dust transition particles work like dust particles, but transition their color from one to another using `Particle.DustTransition`.
```java
Particle.DUST_COLOR_TRANSITION.builder()
.location(someLocation)
.offset(0.5, 0, 0)
.count(3)
.colorTransition(Color.RED, Color.BLUE)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.DUST_COLOR_TRANSITION, someLocation, 3, 0.5, 0, 0, new Particle.DustTransition(Color.RED, Color.BLUE, 1.0f));
```
## Note particles
Note particles use the `offsetX` argument to determine the color. `offsetY` and `offsetZ` are ignored.
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(0.4f, 0, 0)
.count(0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.NOTE, someLocation, 0, 0.4f, 0, 0);
```
### Note particle color picker
`offsetX` values between `-1.0` and `1.0` determine the color. Values outside this range repeat the color pattern.
* `offsetX = 0`
To achieve the Vanilla note particle colors, set the `offsetX` to a fraction of 24.
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(2.0f / 24.0f, 0, 0)
.count(0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.NOTE, someLocation, 0, 2.0f / 24.0f, 0, 0);
```
## Trail particles
Trail particles require you to pass a `Particle.Trail` object as `data`.
```java
Particle.TRAIL.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(8)
.data(new Particle.Trail(someLocation.clone().add(-4, 0, 4), Color.YELLOW, 40))
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.TRAIL, someLocation, 8, 1, 1, 1, new Particle.Trail(someLocation.clone().add(-4, 0, 4), Color.YELLOW, 40));
```
## Converging particles
Converge to a single point (location). Offset arguments determine the relative spawn location.
```java
Particle.ENCHANT.builder()
.location(someLocation)
.offset(-2, 0, 2)
.count(0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.ENCHANT, someLocation, 0, -2, 0, 2);
```
* **Curving:** `ENCHANT`, `NAUTILUS`, `PORTAL`, `VAULT_CONNECTION`
* **Straight:** `OMINOUS_SPAWNING`
### List of converging particles
* `ENCHANT`
* `NAUTILUS`
* `OMINOUS_SPAWNING`
* `PORTAL`
* `VAULT_CONNECTION`
## Material particles
### BlockData
Spawn particles that require `BlockData` by passing `BlockData` as its `data` argument.
```java
Particle.BLOCK_CRUMBLE.builder()
.location(someLocation)
.count(4)
.data(BlockType.GLOWSTONE.createBlockData())
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.BLOCK_CRUMBLE, someLocation, 4, BlockType.GLOWSTONE.createBlockData());
```
* Use `BlockType.createBlockData()`. `Material.createBlockData()` or `Bukkit.createBlockData(Material)` are legacy.
* `BLOCK` is a directional particle; velocity matters.
### ItemStack
Spawn particles that require an `ItemStack` by passing an `ItemStack` as its `data` argument.
```java
Particle.ITEM.builder()
.location(someLocation)
.count(4)
.data(ItemStack.of(Material.DIAMOND_PICKAXE))
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.ITEM, someLocation, 4, ItemStack.of(Material.DIAMOND_PICKAXE));
```
* Use `ItemStack.of(Material)`. `new ItemStack(Material)` is legacy.
* `ITEM` is a directional particle.
## Sculk particles
### Sculk charge
`SCULK_CHARGE` takes a `float` as its `data` argument, representing the particle's "roll" in radians.
```java
Particle.SCULK_CHARGE.builder()
.location(someLocation)
.data((float) Math.toRadians(45))
.extra(0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.SCULK_CHARGE, someLocation, 1, 0, 0, 0, 0, (float) Math.toRadians(45));
```
* `SCULK_CHARGE` is a directional particle.
### Shriek
`SHRIEK` takes an `integer` as its `data` argument, setting the delay (in ticks) before the particle spawns.
```java
Particle.SHRIEK.builder()
.location(someLocation)
.data(20)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.SHRIEK, someLocation, 1, 20);
```
### Vibration
Vibration particles require a `Vibration` object as `data`, with a `Vibration.Destination` (either `BlockDestination` or `EntityDestination`) and a travel time in ticks.
```java
Particle.VIBRATION.builder()
.location(someLocation)
.data(new Vibration(new Vibration.Destination.BlockDestination(otherLocation), 40))
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.VIBRATION, someLocation, 1, new Vibration(new Vibration.Destination.BlockDestination(otherLocation), 40));
```
## Rising particles
These particles use `offsetY` as the particle's y-axis velocity. If `offsetX` AND `offsetZ` are `0`, the particle will have almost no x or z-axis velocity.
**Caution:** `EFFECT` and `INSTANT_EFFECT` are powered particles and use `Particle.Spell` as their data. Due to the low vertical velocity range [-0.056, 0.056] and powered particle calculations, the resulting vertical velocity will always be the opposite of the `power` value's sign.
```java
Particle.GLOW.builder()
.location(someLocation)
.count(0)
.offset(0, 2, 0)
.receivers(32, true)
.spawn();
// or
someWorld.spawnParticle(Particle.GLOW, someLocation, 0, 0, 2, 0);
```
These particles rise up, meaning that the initial velocity will be used only briefly, and the particle will start to travel up after a short time. Therefore, negative vertical velocity will only stop the particle from rising temporarily, while a positive vertical velocity will make the particle rise immediately.
### List of rising particles
* `EFFECT`
* `ENTITY_EFFECT`
* `GLOW`
* `INFESTED`
* `INSTANT_EFFECT`
* `RAID_OMEN`
* `TRIAL_OMEN`
* `WITCH`
## Scalable particles
These particles can be scaled with `offsetX`, while `offsetY` and `offsetZ` are ignored.
* If the final calculated scale is negative, the particle will appear mirrored.
### Sweep attack particles
`SWEEP_ATTACK` particle's scale is calculated as `1.0 - offsetX * 0.5`.
```java
ParticleBuilder sweepAttackParticleBuilder = Particle.SWEEP_ATTACK.builder()
.location(someLocation)
.count(0)
.receivers(32, true);
sweepAttackParticleBuilder.spawn();
Bukkit.getScheduler().runTaskLater(plugin, () -> sweepAttackParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);
// or
someWorld.spawnParticle(Particle.SWEEP_ATTACK, someLocation, 0);
Bukkit.getScheduler().runTaskLater(plugin, () -> someWorld.spawnParticle(Particle.SWEEP_ATTACK, someLocation, 0, -2.0, 0, 0), 10);
```
### Explosion particles
`EXPLOSION` particle's scale is calculated as `2.0 * (1.0 - offsetX * 0.5)`.
```java
ParticleBuilder explosionParticleBuilder = Particle.EXPLOSION.builder()
.location(someLocation)
.offset(1, 0, 0)
.count(0)
.receivers(32, true);
explosionParticleBuilder.spawn();
Bukkit.getScheduler().runTaskLater(plugin, () -> explosionParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);
// or
someWorld.spawnParticle(Particle.EXPLOSION, someLocation, 0, 1, 0, 0);
Bukkit.getScheduler().runTaskLater(plugin, () -> someWorld.spawnParticle(Particle.EXPLOSION, someLocation, 0, -2.0, 0, 0), 10);
```
## Miscellaneous behaviors
Particles with unique spawning behaviors.
### Angry villager particles
`ANGRY_VILLAGER` always spawns `0.5` higher (y-axis) than the supplied location.
### Cloud particles
`CLOUD` and `SNEEZE` move towards the player's y level if within two blocks distance. Their vertical velocity will be greatly reduced upon reaching the player's y level. If the player is moving vertically, the particles will attempt to match the player's vertical velocity.
### Damage indicator particles
`DAMAGE_INDICATOR` adds `1.0` to the provided `offsetY`.
### Dust pillar particles
`DUST_PILLAR` uses `offsetY` for the y-axis velocity, while `offsetX` and `offsetZ` are ignored.
### Dust plume particles
`DUST_PLUME` adds `0.15` to the provided `offsetY`.
### Firefly particles
`FIREFLY` uses `offsetY` as the particle's initial y-axis velocity, but there is a 50% chance for the `offsetY`'s sign to be inverted.
### Powered particles
The powered particles multiply the particle's velocity vector by the supplied argument. The y component of the vector is calculated as `(verticalVelocity - 0.1) * power + 0.1`.
#### List of powered particles
* `EFFECT`
* `INSTANT_EFFECT`
* `DRAGON_BREATH`
### Splash particles
`SPLASH` uses the `offsetX` and `offsetZ` arguments to determine the particle's velocity vector if:
* `offsetY` is `0`
* Either `offsetX` or `offsetZ` are not `0`
---
# Plugin configuration
Configuration files allow users to change certain behavior and functionality of plugins.
## Format
By default, plugins use a YAML configuration format (`.yml` file). Other formats (JSON, TOML) are not natively supported.
YAML uses a tree-like `key: value` pair structure, as seen in `plugin.yml`.
**Example:**
```yaml
root:
one-key: 10
another-key: David
```
Accessing indented values uses dots (`.`). The key for "David" is `root.another-key`.
## Creating a config.yml
Place a `config.yml` file in the `resources` directory to specify default settings.
```
example-plugin/
└── src/
└── main/
└── java/
└── resources/
├── config.yml
└── plugin.yml
```
Save this resource to the plugin's data directory during initialization so users can edit it.
```java
public class TestPlugin extends JavaPlugin {
@Override
public void onEnable() {
saveResource("config.yml", /* replace */ false);
// You can also use this for configuration files:
saveDefaultConfig(); // Saves config.yml if it doesn't exist
// getConfig()...
}
}
```
* The `replace` parameter specifies whether to overwrite an existing file (if set to true, the configuration will be overwritten on every call).
## Getting and setting data
Fetch the plugin's `FileConfiguration` with `JavaPlugin#getConfig()` after saving. Use `#get...(key)` and `#set(key, value)` to fetch and set data.
Basic data types are supported by YAML (e.g., `#getString(key)`, `#getBoolean(key)`). More complex Bukkit data types (e.g., `ItemStack`, `Location`, `Vector`) are also supported.
**Example (Loading a Location):**
```java
public class TestPlugin extends JavaPlugin {
public void teleportPlayer(Player player) {
Location to = getConfig().getLocation("target_location");
player.teleport(to);
}
}
```
These complex types implement `ConfigurationSerializable`. You can use this for custom classes.
```java
public class TeleportOptions implements ConfigurationSerializable {
private int chunkX;
private int chunkZ;
private String name;
public TeleportOptions(int chunkX, int chunkZ, String name) {
// Set the values
}
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("chunk-x", this.chunkX);
data.put("chunk-z", this.chunkZ);
data.put("name", this.name);
return data;
}
public static TeleportOptions deserialize(Map<String, Object> args) {
return new TeleportOptions((int) args.get("chunk-x"), (int) args.get("chunk-z"), (String) args.get("name"));
}
}
```
To register the custom class:
```java
ConfigurationSerialization.registerClass(TeleportOptions.class);
```
**Caution:** If you do not call `ConfigurationSerialization#registerClass(Class)` with Paper plugins, you will not be able to load nor save your custom classes.
**Saving Configs:**
Call `FileConfiguration#save(File/String)` to persist changes to disk.
## Custom configuration files
Split configurations across multiple files using the Bukkit `FileConfiguration` API.
```java
File file = new File(plugin.getDataFolder(), "items.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
// Work with config here
config.save(file);
```
This example reads `items.yml` from the plugin's data directory. The file must exist.
**Blocking I/O:**
Loading and saving files on the main thread will slow down your server. `load` and `save` operations should be executed asynchronously.
## Configurate
Configurate is a third-party library for working with configurations, maintained by the Sponge project. It's used internally by Paper and offers features the `FileConfiguration` API lacks. More information can be found [here](https://github.com/SpongePowered/Configurate).
---
# plugin.yml
The `plugin.yml` file is the main configuration file for your plugin, containing information such as name, version, description, dependencies, permissions, and commands. It's located in the `resources` directory.
```
example-plugin/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
└── java/
└── resources/
└── plugin.yml
```
## Example
```yaml
name: ExamplePlugin
version: 1.0.0
main: io.papermc.testplugin.ExamplePlugin
description: An example plugin
author: PaperMC
website: https://papermc.io
api-version: '1.21.10'
```
## Fields
\* indicates a required field.
### name\*
The name of your plugin. Displayed in the plugin list and log messages (overridden by `prefix` if set).
```yaml
name: ExamplePlugin
```
### version\*
The current version of the plugin. Shown in plugin info messages and server logs.
```yaml
version: 1.0.0
```
### main\*
The main class of your plugin. Extends `JavaPlugin` and is the entry point. Package path and class name of your main class.
```yaml
main: io.papermc.testplugin.ExamplePlugin
```
### description
A short description of your plugin. Shown in plugin info commands.
```yaml
description: An example plugin
```
### author / authors
The author(s) of the plugin. Can be a single author or a list. Shown in plugin info commands.
```yaml
author: PaperMC
authors: [PaperMC, SpigotMC, Bukkit]
```
### contributors
The contributors to the plugin that aren't the managing author(s). Shown in plugin info commands.
```yaml
contributors: [PaperMC, SpigotMC, Bukkit]
```
### website
The website of the plugin (GitHub repository, plugin page). Shown in plugin info commands.
```yaml
website: https://papermc.io
```
### api-version
The Paper API version your plugin uses. Doesnt include minor version until 1.20.5. From 1.20.5 and onward, a minor version is supported. Servers with lower versions refuse to load the plugin. Valid versions are 1.13 - 1.21.10.
```yaml
api-version: '1.21.10'
```
* If not specified, the plugin is loaded as legacy, with a console warning.
### load
Tells the server when to load the plugin: `STARTUP` or `POSTWORLD`. Defaults to `POSTWORLD` if not specified.
```yaml
load: STARTUP
```
### prefix
The prefix of the plugin. Displayed in the log instead of the plugin name.
```yaml
prefix: EpicPaperMCHypePlugin
```
### libraries
A list of libraries your plugin depends on. Downloaded from Maven Central and added to the classpath, removing the need to shade and relocate.
```yaml
libraries:
- com.google.guava:guava:30.1.1-jre
- com.google.code.gson:gson:2.8.6
```
* The central repository is configurable using the `PAPER_DEFAULT_CENTRAL_
```markdown
# PaperMC Knowledge Base
## Project Setup
### Overview
This guide focuses on setting up a Paper plugin development environment using IntelliJ IDEA and Gradle. While tailored for IntelliJ, the principles apply to other IDEs with minor adjustments. Gradle is the preferred build system, though alternatives like Maven can be adapted.
### Creating a New Project
1. **Open IntelliJ IDEA** and select "New Project".
2. **Choose Gradle - Kotlin DSL** as the project type.
3. Click **Create**. This opens the `build.gradle.kts` file for dependency management.
### Adding Paper as a Dependency
Add the Paper repository and dependency to your `build.gradle.kts` (Kotlin), `build.gradle` (Groovy), or `pom.xml` (Maven) file.
#### Gradle (Kotlin)
```kotlin
repositories {
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
```
#### Gradle (Groovy)
```groovy
repositories {
maven {
name = 'papermc'
url = 'https://repo.papermc.io/repository/maven-public/'
}
}
dependencies {
compileOnly 'io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT'
}
```
#### Maven
```xml
<project>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.10-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
```
### Setting up the `src` Directory
1. Create a directory named `src`.
2. Inside `src`, create a directory named `main`.
3. Inside `main`, create two directories: `java` and `resources`.
```
example-plugin/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
├── java/
└── resources/
```
### Setting up the `java` Directory
Place your Java source files in the `java` directory. Organize code into packages.
1. Create packages to organize your code (e.g., `io.papermc.testplugin`).
2. Create Java classes within these packages (e.g., `ExamplePlugin.java`).
```
example-plugin/
├── build.gradle.kts
├── settings.gradle.kts
└── src/
└── main/
├── java/
│ └── io/
│ └── papermc/
│ └── testplugin/
│ └── ExamplePlugin.java
└── resources/
```
### Packages
Packages are used to organize code. Java packages group related classes. Package names should follow a reverse domain name convention (e.g., `io.papermc.testplugin`). If you don't have a domain, use your GitHub username (e.g., `io.github.yourname`).
### The `main` Class
The main class extends `JavaPlugin` and serves as the entry point for the plugin.
```java
package io.papermc.testplugin;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class ExamplePlugin extends JavaPlugin implements Listener {
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage(Component.text("Hello, " + event.getPlayer().getName() + "!"));
}
}
```
### Setting up the `resources` Directory
Place the `plugin.yml` file in the `resources` directory.
### Using the Minecraft Development IntelliJ Plugin
An alternative to manual setup is using the Minecraft Development IntelliJ plugin.
1. **Install the Plugin:** Go to `File > Settings > Plugins`, search for "Minecraft Development" in the Marketplace, and install it. Restart IntelliJ.
2. **Create a New Project:** Go to `File > New > Project...` and select "Minecraft".
3. **Provide Project Information:**
* **Name:** Project name.
* **Location:** Project directory.
* **Platform Type:** "Plugin".
* **Platform:** "Paper".
* **Minecraft Version:** Target Minecraft version.
* **Plugin Name:** Plugin name.
* **Main Class:** Main class extending `JavaPlugin`.
* **Optional Settings:** Author, website, description.
* **Build System:** Gradle (recommended) or Maven.
* **Paper Manifest:** Leave unchecked for now (experimental).
* **Group ID:** Reverse domain name (e.g., `io.github.yourname`).
* **Artifact ID:** Project name.
* **Version:** `1.0-SNAPSHOT`.
* **JDK:** Java 21 or higher.
4. Click **Create**.
### Plugin Remapping
Paper uses a Mojang-mapped runtime since 1.20.5. Spigot/Bukkit plugins are assumed to be Spigot-mapped and will be remapped on first load.
### Mojang Mappings
To indicate your plugin is Mojang-mapped, add the following to your build script:
#### Gradle (Kotlin)
```kotlin
tasks.jar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
// if you have shadowJar configured
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "mojang"
}
}
```
#### Gradle (Groovy)
```groovy
jar {
manifest {
attributes('paperweight-mappings-namespace': 'mojang')
}
}
// if you have shadowJar configured
shadowJar {
manifest {
attributes('paperweight-mappings-namespace': 'mojang')
}
}
```
#### Maven
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<archive>
<manifestEntries>
<paperweight-mappings-namespace>mojang</paperweight-mappings-namespace>
</manifestEntries>
</archive>
</configuration>
</plugin>
```
### Spigot Mappings
To explicitly indicate your plugin is Spigot-mapped, add the following to your build script:
#### Gradle (Kotlin)
```kotlin
tasks.jar {
manifest {
attributes["paperweight-mappings-namespace"] = "spigot"
}
}
// if you have shadowJar configured
tasks.shadowJar {
manifest {
attributes["paperweight-mappings-namespace"] = "spigot"
}
}
```
#### Gradle (Groovy)
```groovy
jar {
manifest {
attributes('paperweight-mappings-namespace': 'spigot')
}
}
// if you have shadowJar configured
shadowJar {
manifest {
attributes('paperweight-mappings-namespace': 'spigot')
}
}
```
#### Maven
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<archive>
<manifestEntries>
<paperweight-mappings-namespace>spigot</paperweight-mappings-namespace>
</manifestEntries>
</archive>
</configuration>
</plugin>
```
### Conclusion
You should now have a project set up with Paper as a dependency. Compile your plugin and run it on a Paper server.
* **Run-Task Gradle Plugin:** Streamlines testing by automatically downloading and running a Paper server.
* **IntelliJ Build Menu:** Compile your plugin using the Gradle GUI "Build" menu. The output JAR will be in the `build/libs` directory.
---
## Reading Stacktraces
### Overview
A stacktrace displays the call stack of a thread in Java, showing the execution path leading to a specific point in the program. It's crucial for debugging.
### What is a Stacktrace?
In Java, a stacktrace reveals the call stack of a thread, detailing the sequence of method calls that led to the current execution point. Stacktraces are printed when an exception isn't handled correctly and are invaluable for debugging. They pinpoint the exact line causing the error and the preceding calls.
### Example
```
[15:20:42 ERROR]: Could not pass event PluginEnableEvent to TestPlugin v1.0
java.lang.NullPointerException: Cannot invoke "Object.toString()" because "player" is null
at io.papermc.testplugin.TestPlugin.onPluginEnable(TestPlugin.java:23) ~[TestPlugin-1.0-SNAPSHOT.jar:?]
at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor1.execute(Unknown Source) ~[?:?]
at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-49]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-49]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:200) ~[paper-1.20.2.jar:git-Paper-49]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:104) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:507) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.v1_20_R2.CraftServer.enablePlugin(CraftServer.java:636) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.craftbukkit.v1_20_R2.CraftServer.enablePlugins(CraftServer.java:547) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.loadWorld0(MinecraftServer.java:636) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.loadLevel(MinecraftServer.java:435) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:308) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1101) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:318) ~[paper-1.20.2.jar:git-Paper-49]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
```
* **Error Context:** The error occurred during `PluginEnableEvent` handling by `TestPlugin`.
* **Exception Cause:** `NullPointerException` due to calling `toString()` on a null "player" object.
* **Error Location:** `TestPlugin.java:23`.
* **Execution Path:** The stacktrace shows the sequence of calls that led to the error, starting from `TestPlugin.onPluginEnable` and traversing through Paper/Bukkit internals. You can ignore server internals typically.
### Omitted Stacktraces
In JDK 5+, the JVM may omit stacktraces for optimized code, leading to `NullPointerException`s without a stacktrace. To resolve this, use the JVM flag:
```
java -XX:-OmitStackTraceInFastThrow -jar paper.jar
```
---
## Particles
### Overview
This guide explains how to spawn different types of particles. If a particle isn't mentioned, it likely has no special behavior.
Particles can be spawned in two ways:
1. **`ParticleBuilder` Class (Preferred):** Reusable, readable, and includes `receivers()` for controlling receivers.
2. **`World.spawnParticle()` and `Player.spawnParticle()`:** Spawn particles for all players or a specific player, respectively.
Example using `ParticleBuilder`:
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(2, 0.2, 2)
.count(14)
.receivers(32, true)
.spawn();
```
`ParticleBuilder.receivers(32, true)` selects players within a 32-block radius (sphere). `false` would select players within a cube.
### `count` Argument Behavior
* **`count = 0`:** Spawns a single particle at the provided location without modification. Offset values are multiplied by the `extra` argument.
* **`count > 0`:** Spawns `count` particles. Offset values are generated using a Gaussian distribution and multiplied by the `extra` argument.
### Directional Particles
These particles have an initial velocity when spawned. Effective speed varies between particles.
Example:
```java
Particle.FLAME.builder()
.location(someLocation)
.offset(0.5, 0.5, 0.5)
.count(8)
.extra(0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.FLAME, someLocation, 8, 0.5, 0.5, 0.5, 0);
```
Leaving `extra` unset defaults to `1`, which may result in unexpected behavior.
#### Random Direction
Setting `count` to a positive value yields a random direction for the velocity.
Example:
```java
Particle.CRIT.builder()
.location(someLocation)
.count(6)
.extra(0.6)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.CRIT, someLocation, 6, 0, 0, 0, 0.6);
```
#### Specified Direction
Set `count` to `0` and use the offset arguments as the direction vector.
Example:
```java
ParticleBuilder particleBuilder = Particle.CAMPFIRE_SIGNAL_SMOKE.builder()
.location(someLocation)
.offset(0, 1, 0)
.count(0)
.extra(0.1);
Bukkit.getScheduler().runTaskTimer(plugin, () -> particleBuilder.receivers(32, true).spawn(), 0, 4);
//or
Bukkit.getScheduler().runTaskTimer(plugin, () -> someWorld.spawnParticle(Particle.CAMPFIRE_SIGNAL_SMOKE, someLocation, 0, 0, 1, 0, 0.1), 0, 4);
```
#### List of Directional Particles
* BLOCK
* BUBBLE
* BUBBLE_COLUMN_UP
* BUBBLE_POP
* CAMPFIRE_COSY_SMOKE
* CAMPFIRE_SIGNAL_SMOKE
* CLOUD
* CRIT
* DAMAGE_INDICATOR
* DRAGON_BREATH
* DUST
* DUST_COLOR_TRANSITION
* DUST_PLUME
* ELECTRIC_SPARK
* ENCHANTED_HIT
* END_ROD
* FIREWORK
* FISHING
* FLAME
* FLASH
* GLOW_SQUID_INK
* ITEM
* LARGE_SMOKE
* POOF
* REVERSE_PORTAL
* SCRAPE
* SCULK_CHARGE
* SCULK_CHARGE_POP
* SCULK_SOUL
* SMALL_FLAME
* SMOKE
* SNEEZE
* SNOWFLAKE
* SOUL
* SOUL_FIRE_FLAME
* SPIT
* SQUID_INK
* TOTEM_OF_UNDYING
* TRIAL_SPAWNER_DETECTION
* TRIAL_SPAWNER_DETECTION_OMINOUS
* WAX_OFF
* WAX_ON
* WHITE_SMOKE
### Colored Particles
These particles can be colored by passing a `Color` object as the `data` argument.
Example:
```java
Particle.ENTITY_EFFECT.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(10)
.data(Color.fromARGB(200, 255, 128, 0))
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.ENTITY_EFFECT, someLocation, 10, 1, 1, 1, Color.fromARGB(200, 255, 128, 0));
```
Only `ENTITY_EFFECT` supports the alpha channel for translucency. `FLASH` and `TINTED_LEAVES` ignore the alpha channel.
### Dust Particles
Vanilla uses dust particles for redstone. Custom colors are set using `Particle.DustOptions` as `data`. The scale factor must be between `0.01` and `4.0`.
Example:
```java
ParticleBuilder particleBuilder = Particle.DUST.builder().color(Color.BLUE, 2.0f);
for (double i = -1.0; i <= 1.0; i += 0.25) {
particleBuilder.location(someLocation.clone().add(0, i, 0)).receivers(32, true).spawn();
}
//or
for (double i = -1.0; i <= 1.0; i += 0.25) {
someWorld.spawnParticle(Particle.DUST, someLocation.clone().add(0, i, 0), 1, new Particle.DustOptions(Color.BLUE, 2.0f));
}
```
A size argument controls the dust particle's lifetime in ticks.
### Dust Transition Particles
Dust transition particles transition their color. A `Particle.DustTransition` is used to specify the transition.
Example:
```java
Particle.DUST_COLOR_TRANSITION.builder()
.location(someLocation)
.offset(0.5, 0, 0)
.count(3)
.colorTransition(Color.RED, Color.BLUE)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.DUST_COLOR_TRANSITION, someLocation, 3, 0.5, 0, 0, new Particle.DustTransition(Color.RED, Color.BLUE, 1.0f));
```
### Note Particles
Note particles use the `offsetX` argument to determine the color. `offsetY` and `offsetZ` are ignored.
Example:
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(0.4f, 0, 0)
.count(0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.NOTE, someLocation, 0, 0.4f, 0, 0);
```
#### Note Particle Color Picker
`offsetX` values between `-1.0` and `1.0` determine the color. Values outside this range repeat the pattern. For Vanilla note particle colors, set `offsetX` to a fraction of 24 (e.g., `2.0f / 24.0f`).
Example:
```java
Particle.NOTE.builder()
.location(someLocation)
.offset(2.0f / 24.0f, 0, 0)
.count(0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.NOTE, someLocation, 0, 2.0f / 24.0f, 0, 0);
```
### Trail Particles
Trail particles require a `Particle.Trail` object as `data`.
Example:
```java
Particle.TRAIL.builder()
.location(someLocation)
.offset(1, 1, 1)
.count(8)
.data(new Particle.Trail(someLocation.clone().add(-4, 0, 4), Color.YELLOW, 40))
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.TRAIL, someLocation, 8, 1, 1, 1, new Particle.Trail(someLocation.clone().add(-4, 0, 4), Color.YELLOW, 40));
```
### Converging Particles
These particles converge to a single point. Offset arguments determine the relative spawn location.
Example:
```java
Particle.ENCHANT.builder()
.location(someLocation)
.offset(-2, 0, 2)
.count(0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.ENCHANT, someLocation, 0, -2, 0, 2);
```
* **Curving:** `ENCHANT`, `NAUTILUS`, `PORTAL`, `VAULT_CONNECTION`
* **Straight:** `OMINOUS_SPAWNING`
#### List of Converging Particles
* ENCHANT
* NAUTILUS
* OMINOUS_SPAWNING
* PORTAL
* VAULT_CONNECTION
### Material Particles
#### BlockData
To spawn particles requiring `BlockData`, use `BlockData` as the `data` argument.
Example:
```java
Particle.BLOCK_CRUMBLE.builder()
.location(someLocation)
.count(4)
.data(BlockType.GLOWSTONE.createBlockData())
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.BLOCK_CRUMBLE, someLocation, 4, BlockType.GLOWSTONE.createBlockData());
```
Using `BlockType.createBlockData()` is preferred over legacy methods.
The `BLOCK` particle is a directional particle, so velocity matters.
#### ItemStack
To spawn particles requiring an `ItemStack`, use an `ItemStack` as the `data` argument.
Example:
```java
Particle.ITEM.builder()
.location(someLocation)
.count(4)
.data(ItemStack.of(Material.DIAMOND_PICKAXE))
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.ITEM, someLocation, 4, ItemStack.of(Material.DIAMOND_PICKAXE));
```
Using `ItemStack.of(Material)` is preferred over legacy methods.
The `ITEM` particle is a directional particle.
### Sculk Particles
#### Sculk Charge
The `SCULK_CHARGE` particle takes a `float` as `data`, representing the particle's roll in radians.
Example:
```java
Particle.SCULK_CHARGE.builder()
.location(someLocation)
.data((float) Math.toRadians(45))
.extra(0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.SCULK_CHARGE, someLocation, 1, 0, 0, 0, 0, (float) Math.toRadians(45));
```
`SCULK_CHARGE` is a directional particle.
#### Shriek
The `SHRIEK` particle takes an `integer` as `data`, representing the delay in ticks before the particle spawns.
Example:
```java
Particle.SHRIEK.builder()
.location(someLocation)
.data(20)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.SHRIEK, someLocation, 1, 20);
```
#### Vibration
Vibration particles require a `Vibration` object as `data`, specifying a location (`Vibration.Destination.BlockDestination`) or entity target (`Vibration.Destination.EntityDestination`). The constructor's second argument is the travel time in ticks.
Example:
```java
Particle.VIBRATION.builder()
.location(someLocation)
.data(new Vibration(new Vibration.Destination.BlockDestination(otherLocation), 40))
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.VIBRATION, someLocation, 1, new Vibration(new Vibration.Destination.BlockDestination(otherLocation), 40));
```
### Rising Particles
These particles use `offsetY` as the y-axis velocity. Setting `offsetX` and `offsetZ` to `0` results in minimal x/z velocity.
Caution: `EFFECT` and `INSTANT_EFFECT` are powered particles.
Example:
```java
Particle.GLOW.builder()
.location(someLocation)
.count(0)
.offset(0, 2, 0)
.receivers(32, true)
.spawn();
//or
someWorld.spawnParticle(Particle.GLOW, someLocation, 0, 0, 2, 0);
```
Initial velocity is used briefly, then the particle rises.
#### List of Rising Particles
* EFFECT
* ENTITY_EFFECT
* GLOW
* INFESTED
* INSTANT_EFFECT
* RAID_OMEN
* TRIAL_OMEN
* WITCH
### Scalable Particles
These particles can be scaled with `offsetX`. `offsetY` and `offsetZ` are ignored.
#### Sweep Attack Particles
The `SWEEP_ATTACK` particle's scale is calculated as `1.0 - offsetX * 0.5`.
Example:
```java
ParticleBuilder sweepAttackParticleBuilder = Particle.SWEEP_ATTACK.builder().location(someLocation).count(0).receivers(32, true);
sweepAttackParticleBuilder.spawn();
Bukkit.getScheduler().runTaskLater(plugin, () -> sweepAttackParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);
//or
someWorld.spawnParticle(Particle.SWEEP_ATTACK, someLocation, 0);
Bukkit.getScheduler().runTaskLater(plugin, () -> someWorld.spawnParticle(Particle.SWEEP_ATTACK, someLocation, 0, -2.0, 0, 0), 10);
```
#### Explosion Particles
The `EXPLOSION` particle's scale is calculated as `2.0 * (1.0 - offsetX * 0.5)`.
Example:
```java
ParticleBuilder explosionParticleBuilder = Particle.EXPLOSION.builder().location(someLocation).offset(1, 0, 0).count(0).receivers(32, true);
explosionParticleBuilder.spawn();
Bukkit.getScheduler().runTaskLater(plugin, () -> explosionParticleBuilder.offset(-2.0, 0, 0).spawn(), 10);
//or
someWorld.spawnParticle(Particle.EXPLOSION, someLocation, 0, 1, 0, 0);
Bukkit.getScheduler().runTaskLater(plugin, () -> someWorld.spawnParticle(Particle.EXPLOSION, someLocation, 0, -2.0, 0, 0), 10);
```
### Miscellaneous Behaviors
#### Angry Villager Particles
The `ANGRY_VILLAGER` particle spawns `0.5` higher (y-axis) than the supplied location.
#### Cloud Particles
The `CLOUD` and `SNEEZE` particles move towards the player's y level if within two blocks. They attempt to match vertical velocity.
#### Damage Indicator Particles
The `DAMAGE_INDICATOR` particle adds `1.0` to the provided `offsetY`.
#### Dust Pillar Particles
The `DUST_PILLAR` particle uses `offsetY` for the y-axis velocity. `offsetX` and `offsetZ` are ignored.
#### Dust Plume Particles
The `DUST_PLUME` particle adds `0.15` to the provided `offsetY`.
#### Firefly Particles
The `FIREFLY` particle uses `offsetY` as the initial y-axis velocity, but there's a 50% chance for the sign to be inverted.
#### Powered Particles
Powered particles multiply the velocity vector by the supplied argument. The y component is calculated as `(verticalVelocity - 0.1) * power + 0.1`.
##### List of Powered Particles
* EFFECT
* INSTANT_EFFECT
* DRAGON_BREATH
#### Splash Particles
The `SPLASH` particle uses `offsetX` and `offsetZ` to determine the velocity vector if `offsetY` is `0` and either `offsetX` or `offsetZ` are not `0`.
---
## plugin.yml
### Overview
The `plugin.yml` file is the primary configuration file for a Paper plugin. It contains metadata about the plugin, its dependencies, permissions, and commands. It resides in the `resources` directory of the project.
### Example
```yaml
name: ExamplePlugin
version: 1.0.0
main: io.papermc.testplugin.ExamplePlugin
description: An example plugin
author: PaperMC
website: https://papermc.io
api-version: '1.21.10'
```
### Fields
*Note: Asterisks (*) indicate required fields.*
#### name*
The name of the plugin. Displayed in plugin lists and log messages (unless `prefix` is set).
```yaml
name: ExamplePlugin
```
#### version*
The plugin's current version. Shown in plugin info messages and server logs.
```yaml
version: 1.0.0
```
#### main*
The main class of the plugin. Extends `JavaPlugin` and serves as the plugin's entry point. It's the package path and class name.
```yaml
main: io.papermc.testplugin.ExamplePlugin
```
#### description
A brief description of the plugin. Shown in plugin info commands.
```yaml
description: An example plugin
```
#### author / authors
The plugin's author(s). Can be a single author or a list.
```yaml
author: PaperMC
authors: [PaperMC, SpigotMC, Bukkit]
```
#### contributors
Contributors to the plugin who aren't managing authors.
```yaml
contributors: [PaperMC, SpigotMC, Bukkit]
```
#### website
The plugin's website (e.g., GitHub repository or plugin page).
```yaml
website: https://papermc.io
```
#### api-version
The Paper API version the plugin uses. Includes minor version support from 1.20.5 onwards. Servers with lower versions refuse to load the plugin. Valid versions are 1.13 - 1.21.10.
```yaml
api-version: '1.21.10'
```
*Note: If unspecified, the plugin loads as a legacy plugin with a console warning.*
#### load
Determines when the server loads the plugin: `STARTUP` or `POSTWORLD`. Defaults to `POSTWORLD`.
```yaml
load: STARTUP
```
#### prefix
A prefix displayed in the log instead of the plugin name.
```yaml
prefix: EpicPaperMCHypePlugin
```
#### libraries
A list of libraries the plugin depends on. Downloaded from Maven Central and added to the classpath, eliminating the need for shading and relocation.
```yaml
libraries:
- com.google.guava:guava:30.1.1-jre
- com.google.code.gson:gson:2.8.6
```
*Note: The central repository is configurable via the `PAPER_DEFAULT_CENTRAL_REPOSITORY` environment variable and `org.bukkit.plugin.java.LibraryLoader.centralURL` system property.*
#### permissions
A list of permissions the plugin uses to restrict access to features.
```yaml
permissions:
permission.node:
description: "This is a permission node"
default: op
children:
permission.node.child: true
another.permission.node:
description: "This is another permission node"
default: notop
```
* **description:** Description of the permission node.
* **default:** Default value (`op`, `notop`, `true`, `false`). Defaults to `default-permission` if unspecified, which defaults to `op`.
* **children:** Inherits the parent permission if set to `true`.
#### default-permission
The default value for permissions without a specified `default` value (`op`,
```markdown
# Reading Stacktraces
## Overview
This page explains what a stacktrace is and how to read it for debugging purposes.
## What is a stacktrace?
* A stacktrace shows the call stack of a thread in Java.
* The call stack represents the path of execution that led to the current point in the program.
* Stacktraces are usually printed to the console when an exception is not handled correctly.
* They are useful for debugging because they show the exact line of code that caused an error and the lines of code that called that line, revealing the path of execution.
## Example
Here's an example of a stacktrace caused by a `NullPointerException`:
```
[15:20:42 ERROR]: Could not pass event PluginEnableEvent to TestPlugin v1.0
java.lang.NullPointerException: Cannot invoke "Object.toString()" because "player" is null
at io.papermc.testplugin.TestPlugin.onPluginEnable(TestPlugin.java:23) ~[TestPlugin-1.0-SNAPSHOT.jar:?]
at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor1.execute(Unknown Source) ~[?:?]
at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-49]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-49]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:200) ~[paper-1.20.2.jar:git-Paper-49]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:104) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:507) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.v1_20_R2.CraftServer.enablePlugin(CraftServer.java:636) ~[paper-1.20.2.jar:git-Paper-49]
at org.bukkit.craftbukkit.v1_20_R2.CraftServer.enablePlugins(CraftServer.java:547) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.loadWorld0(MinecraftServer.java:636) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.loadLevel(MinecraftServer.java:435) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:308) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1101) ~[paper-1.20.2.jar:git-Paper-49]
at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:318) ~[paper-1.20.2.jar:git-Paper-49]
at java.lang.Thread.run(Thread.java:833) ~[?:?]
```
* The error occurred when a `PluginEnableEvent` was being handled by `TestPlugin`.
* The cause was a `NullPointerException` when trying to call `toString()` on a null "player" object.
* The error was thrown at line 23 of `TestPlugin.java`.
* The rest of the stacktrace shows server internals, which can generally be ignored in this case.
## Omitted stacktraces
* In JDK 5, the JVM started omitting stacktraces for certain exceptions when the code was optimized.
* To fix this, you can pass the `-XX:-OmitStackTraceInFastThrow` flag to the JVM:
```
java -XX:-OmitStackTraceInFastThrow -jar paper.jar
```
---
# Recipes
## Overview
Recipes define how to craft specific items in Minecraft. This page covers how to define recipes using plugins.
## ShapedRecipe
* A shaped recipe requires a specific pattern of items in the crafting grid.
* Created using pattern strings and a map of characters to items.
* Pattern strings are 3, 3-character strings representing the rows of the crafting grid.
Example:
```java
public class TestPlugin extends JavaPlugin {
@Override
public void onEnable() {
NamespacedKey key = new NamespacedKey(this, "television");
ItemStack item = ItemStack.of(Material.BLACK_WOOL);
item.setData(DataComponentTypes.ITEM_NAME, Component.text("Television"));
ShapedRecipe recipe = new ShapedRecipe(key, item);
recipe.shape(
"AAA",
"ABA",
"AAA"
);
recipe.setIngredient('A', Material.WHITE_CONCRETE);
recipe.setIngredient('B', Material.BLACK_STAINED_GLASS_PANE);
getServer().addRecipe(recipe);
}
}
```
This recipe requires a black stained glass pane surrounded by white concrete to craft a television.
```
AAA
ABA
AAA
```
**Note:**
* Recipes can be registered at any time, not just in `onEnable`.
* If registering after the plugin has been enabled and players are online, resend recipes to players or use the boolean parameter in `addRecipe` to update players.
**Caution:**
* `Air` cannot be used as a material in a shaped recipe.
## ShapelessRecipe
* A shapeless recipe requires a specific number of items in the crafting grid, but not in a specific pattern.
* Created using a list of items.
Example:
```java
public class TestPlugin extends JavaPlugin {
@Override
public void onEnable() {
NamespacedKey key = new NamespacedKey(this, "WarriorSword");
ItemStack item = ItemStack.of(Material.DIAMOND_SWORD);
ShapelessRecipe recipe = new ShapelessRecipe(key, item);
recipe.addIngredient(3, Material.DIAMOND);
recipe.addIngredient(2, Material.STICK);
getServer().addRecipe(recipe);
}
}
```
This recipe requires 3 diamonds and 2 sticks to craft a diamond sword, without any specific orientation.
Possible crafting grid layouts:
```
DSS | SDS | S D
D | D | D
D | D | D S
```
---
# Registries
## Overview
This page explains the Registry API in Paper, which is used to manage game data.
**Experimental:** The Registry API is currently experimental and subject to change.
## What is a registry?
* A registry holds a set of values of the same type, each identified by a key.
* Example: `ItemType` registry holds all known item types.
* Registries are accessed via the `RegistryAccess` class.
* Registries are defined by the server and sent to the client, allowing servers and plugins to define custom content.
* Notable examples include enchantments and biomes.
## Retrieving values from a registry
* Elements are retrieved using their respective keys.
* Two types of keys:
* `net.kyori.adventure.key.Key`: Represents a namespace and a key.
* `TypedKey`: Wraps an Adventure key and includes the key of the registry it belongs to.
Example (retrieving the Sharpness enchantment):
```java
// Fetch the enchantment registry from the registry access
final Registry<Enchantment> enchantmentRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT);
// Get the sharpness enchantment using its key.
// getOrThrow may be replaced with get if the registry may not contain said value
final Enchantment enchantment = enchantmentRegistry.getOrThrow(TypedKey.create(RegistryKey.ENCHANTMENT, Key.key("minecraft:sharpness")));
// Same as above, but using the instance's method
final Enchantment enchantment = enchantmentRegistry.getOrThrow(RegistryKey.ENCHANTMENT.typedKey(Key.key("minecraft:sharpness")));
// Same as above, but using generated create method
// available for data-driven registries or "writable" ones
// (those bound to a lifecycle event in RegistryEvents).
final Enchantment enchantment = enchantmentRegistry.getOrThrow(EnchantmentKeys.create(Key.key("minecraft:sharpness")));
// Same as above too, but using generated typed keys.
// Only Vanilla entries have generated keys, for custom entries, the above method must be used.
final Enchantment enchantment = enchantmentRegistry.getOrThrow(EnchantmentKeys.SHARPNESS);
```
## Referencing registry values
* Referencing registry entries can be done using different approaches.
* `RegistrySet`: Defines a collection of elements that relate to a registry.
* `RegistryKeySet`: A subtype of `RegistrySet` that holds `TypedKey` instances. It remains valid even if registry values change.
Example (creating a `RegistryKeySet`):
```java
// Create a new registry key set that holds a collection enchantments
final RegistryKeySet<Enchantment> bestEnchantments = RegistrySet.keySet(RegistryKey.ENCHANTMENT,
// Arbitrary keys of enchantments to store in the key set.
EnchantmentKeys.CHANNELING,
EnchantmentKeys.create(Key.key("papermc:softspoon"))
);
```
* `Tag`: Follows the concept of a `RegistryKeySet` but is named and can be referenced.
## Mutating registries
* Paper allows plugins to modify registries.
**Caution:**
* Mutating registries must be done during the server's bootstrap phase.
* Only applicable to Paper plugins.
* Exceptions thrown during this phase will cause the server to shut down.
**Note:**
* Mutating registries is done via the `LifecycleEventManager`.
* The general entry point for mutating registries is the `RegistryEvents` type.
* Modification can take two forms: creating new entries and modifying existing entries.
### Create new entries
* Done via the `compose` lifecycle event on registries.
* The `compose` event is called after a registry's content has been loaded from vanilla sources and datapacks.
Example (creating a new enchantment):
```java
public class TestPluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(BootstrapContext context) {
// Register a new handler for the compose lifecycle event on the enchantment registry
context.getLifecycleManager().registerEventHandler(RegistryEvents.ENCHANTMENT.compose().newHandler(event -> {
event.registry().register(
// The key of the registry
// Plugins should use their own namespace instead of minecraft or papermc
EnchantmentKeys.create(Key.key("papermc:pointy")),
b -> b.description(Component.text("Pointy"))
.supportedItems(event.getOrCreateTag(ItemTypeTagKeys.SWORDS))
.anvilCost(1)
.maxLevel(25)
.weight(10)
.minimumCost(EnchantmentRegistryEntry.EnchantmentCost.of(1, 1))
.maximumCost(EnchantmentRegistryEntry.EnchantmentCost.of(3, 1))
.activeSlots(EquipmentSlotGroup.ANY)
);
}));
}
}
```
### Modifying existing entries
* Useful for plugins that want to change the behavior of Vanilla entries.
* Use the `entryAdd` lifecycle event.
* The event is called for any entry added to a registry.
Example (increasing the maximum level of the Sharpness enchantment):
```java
@Override
public void bootstrap(BootstrapContext context) {
context.getLifecycleManager().registerEventHandler(RegistryEvents.ENCHANTMENT.entryAdd()
// Increase the max level to 20
.newHandler(event -> event.builder().maxLevel(20))
// Configure the handler to only be called for the Vanilla sharpness enchantment.
.filter(EnchantmentKeys.SHARPNESS)
);
}
```
---
# Roadmap
## Overview
This page documents planned API changes and potential deprecations in Paper.
## Future plans
### Interface ItemStacks
* Currently, creating `ItemStack`s using the constructor creates an API representation that delegates to an NMS-backed object.
* Use `ItemStack#of` to get the NMS-backed object directly.
* In the future, `ItemStack` will be converted to an interface, and the constructor will be removed.
#### Precautions
* Avoid directly extending the `ItemStack` class. Custom implementations are not supported and will break.
### ServerPlayer reuse
* **Note:** Only applies to NMS usage, not API.
* Avoid directly storing player (`ServerPlayer`) entity instances.
* Currently, the player instance is reused when switching worlds, but this will be reverted to match Vanilla behavior.
* API entities (wrappers) will continue to function, and their underlying instance will be replaced automatically.
## Deprecation policy
**Caution:** Avoid using deprecated APIs. It may cause instability and performance issues.
* API marked with `@Deprecated` should not be used, as alternative API may be available.
* Deprecated API may be marked for removal in the future.
```java
@Deprecated
public void exampleMethod(); // Example deprecated method
```
### Deprecated for removal
* API may be marked as `@Deprecated` and `forRemoval` with a `@ApiStatus.ScheduledForRemoval` version.
* Removal will only occur within major Minecraft release versions.
* Migrate away from API scheduled for removal.
* Adequate time will be given to allow plugin developers to migrate.
```java
@ApiStatus.ScheduledForRemoval(inVersion = "1.20")
@Deprecated(forRemoval = true)
public void exampleMethod(); // Example method marked for removal in 1.20
```
## Deprecation reasons
Common reasons for API deprecation:
### Old API
* API represents concepts that no longer exist in the core game.
* May not be functional or may behave unexpectedly.
### Duplicate API
* API added by Spigot that clashes with existing Paper API.
* Paper will typically deprecate Spigot's API in favor of its own.
### Obsolete API
* Paper has built new APIs to offer as replacements.
* Obsolete API is expected to function for the far future and may not be scheduled for removal for a long time.
---
# paperweight-userdev
## Overview
`paperweight` is Paper's custom build tooling. The `paperweight-userdev` Gradle plugin provides access to internal code (NMS) during development.
**Note:** This guide uses the Gradle Kotlin DSL and assumes basic Gradle knowledge. See the [example plugin](link to example plugin) for a fully-functioning example.
## Why this is useful
* Only supported way of accessing server internals. Redistributing the server JAR is against the Minecraft EULA.
* Avoids issues with Spigot mappings (pre-1.20.5 Paper), which are a mix of obfuscated and mapped names.
* Allows using fully deobfuscated types, names, and fields during development, then remaps the plugin for use with the obfuscated server.
* Does not apply to reflection. Use a library like [this library](link to reflection library) for non-obfuscated names in reflection.
* As of Minecraft 1.20.5, Paper ships with a Mojang-mapped runtime.
## Adding the plugin
Add the plugin to your `build.gradle.kts` file:
```kotlin
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
```
**Gradle Version:** Use the latest stable version of Gradle. See the [Gradle Wrapper documentation](link to Gradle Wrapper documentation) for upgrading.
**Keep up to date:** The latest version of `paperweight-userdev` supports dev bundles for Minecraft 1.17.1 and newer.
**Support:** Only the latest version of `paperweight-userdev` is officially supported. Ask in the `#build-tooling-help` channel in the [Discord server](link to Discord server) for issues.
**Snapshots:** `paperweight-userdev` SNAPSHOT (pre-release) versions are only available through Paper's Maven repository:
```kotlin
pluginManagement {
repositories {
gradlePluginPortal()
maven("https://repo.papermc.io/repository/maven-public/")
}
}
```
## Adding the dev bundle dependency
Add a dev bundle dependency to your `dependencies` block in `build.gradle.kts`:
```kotlin
dependencies {
// Other Dependencies
paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT")
}
```
**Tip:** Remove any dependency on the Paper API, as the dev bundle includes that.
### Configuring the Java toolchain for userdev setup
If the dev bundle doesn't support the Gradle's Java toolchain, configure `paperweight`'s `javaLauncher` property:
```kotlin
paperweight {
javaLauncher = javaToolchains.launcherFor {
// Example scenario:
// Paper 1.17.1 was originally built with JDK 16
languageVersion = JavaLanguageVersion.of(17)
}
}
```
## Gradle tasks
### reobfJar
* Creates a plugin JAR re-obfuscated to Spigot's runtime mappings.
* Works on standard Paper servers.
* Output is in the `build/libs` folder.
* The JAR whose filename includes `-dev` is Mojang-mapped and will not work on most servers.
#### Shadow
* If the shadow Gradle plugin is applied, `paperweight-userdev` will detect it and use the shaded JAR as input for `reobfJar`.
* The `-dev-all.jar` file in `build/libs` is the shaded, but not re-obfuscated JAR.
Run the `reobfJar` task on the default `build` task:
```kotlin
tasks.assemble {
dependsOn(tasks.reobfJar)
}
```
### 1.20.5 and beyond
* As of 1.20.5, Paper ships with a Mojang-mapped runtime.
* CraftBukkit classes are no longer relocated into a versioned package.
* Plugins must be deobfuscated before loading when necessary.
### Default mappings assumption
* Spigot/Bukkit plugins are assumed to be Spigot-mapped if they don't specify their mappings namespace in the manifest.
* Paper plugins are assumed to be Mojang-mapped if they don't specify their mappings namespace in the manifest.
* Spigot-mapped plugins will need to be deobfuscated on first load.
### Compiling to Mojang mappings
* Preferred option: skips the one-time plugin remapping process and may allow version compatibility across smaller updates.
* Makes the plugin incompatible with Spigot servers.
* Remove all `dependsOn(reobfJar)` lines and add:
```kotlin
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
```
### Compiling to Spigot mappings
* Add:
```kotlin
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.REOBF_PRODUCTION
```
* Useful for plugins that have loaders for both Spigot and Paper.
**Note:** If using Gradle with the Groovy DSL, access the fields via static methods like `getMOJANG_PRODUCTION()`.
---
# Using databases
## Overview
This guide covers using databases in Paper plugins for storing larger amounts of data.
## What is a database?
A database is a collection of information stored electronically on a computer system. The main categories are SQL and NoSQL.
## NoSQL vs SQL
* **NoSQL (Not Only SQL):**
* Schema-less and offers flexible data models.
* Designed to handle large volumes of unstructured or semi-structured data.
* Uses various data models (key-value, document, column-family, graph).
* **SQL:**
* Follows the relational database model.
* Organizes data into structured tables with predefined schemas.
* Uses SQL (Structured Query Language) for interaction.
## File-based vs standalone databases
* **File-based:** Stored in a file on disk, used for smaller databases.
* **Standalone:** Operates in a separate process, used for larger data models.
## File-based databases
* Stored within a single file on disk.
* Easier to set up and use, but offer lesser performance.
* Examples: SQLite and H2.
### Simple SQLite Setup
#### SQLite
Requires a driver to connect/initialize the database.
**Note:** The JDBC Driver is bundled with Paper.
#### Usage
Invoke `Class#forName(String)` on the driver to initialize and create the connection:
```java
public class DatabaseManager {
public void connect() {
Class.forName("org.sqlite.JDBC");
Connection connection = DriverManager.getConnection("jdbc:sqlite:plugins/TestPlugin/database.db");
}
}
```
You then have access to a `Connection` object, which can be used to create a `Statement` and execute SQL queries.
## Standalone databases
* Operate in a separate process.
* Harder to set up, but offer better performance.
* Examples: MySQL, MariaDB, and PostgreSQL.
These databases often have connection pooling for improved performance.
### Simple MySQL Setup
#### MySQL
Requires a running MySQL database.
First, add the dependency to your project:
##### Maven
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
<scope>compile</scope>
</dependency>
```
##### Gradle
```kotlin
dependencies {
implementation("com.zaxxer:HikariCP:4.0.3")
}
```
**Caution:** The Hikari library is not bundled with Paper, so you will need to shade/relocate it using the [Shadow plugin](link to Shadow plugin). Alternatively, use the library loader.
#### Usage
```java
public class DatabaseManager {
public void connect() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase"); // Address of your running MySQL database
config.setUsername("username"); // Username
config.setPassword("password"); // Password
config.setMaximumPoolSize(10); // Pool size defaults to 10
config.addDataSourceProperty("", ""); // MISC settings to add
HikariDataSource dataSource = new HikariDataSource(config);
try (Connection connection = dataSource.getConnection()) {
// Use a try-with-resources here to autoclose the connection.
PreparedStatement sql = connection.prepareStatement("SQL");
// Execute statement
} catch (Exception e) {
// Handle any exceptions that arise from getting / handing the exception.
}
}
}
```
## Security
### SQL Injection
SQL injection is a malicious technique where attackers exploit improper input validation to execute unauthorized SQL commands.
Example:
```java
public void login(String username, String password) {
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
// Execute SQL
}
```
If the user enters `' OR 1=1; --` as their username, the SQL statement becomes:
```sql
SELECT * FROM users WHERE username = '' OR 1=1; -- AND password = 'password'
```
This will return all users in the database.
### Prepared statements
Using prepared statements with `PreparedStatement` helps prevent SQL injection by separating SQL code from user input using placeholders. Always use prepared statements to ensure security and integrity.
---
# paperweight-userdev
## Overview
`paperweight` is Paper's custom build tooling. The `paperweight-userdev` Gradle plugin provides access to internal code (NMS) during development.
**Note:** This guide uses the Gradle Kotlin DSL and assumes basic Gradle knowledge. See the [example plugin](link to example plugin) for a fully-functioning example.
## Why this is useful
* Only supported way of accessing server internals. Redistributing the server JAR is against the Minecraft EULA.
* Avoids issues with Spigot mappings (pre-1.20.5 Paper), which are a mix of obfuscated and mapped names.
* Allows using fully deobfuscated types, names, and fields during development, then remaps the plugin for use with the obfuscated server.
* Does not apply to reflection. Use a library like [this library](link to reflection library) for non-obfuscated names in reflection.
* As of Minecraft 1.20.5, Paper ships with a Mojang-mapped runtime.
## Adding the plugin
Add the plugin to your `build.gradle.kts` file:
```kotlin
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
```
**Gradle Version:** Use the latest stable version of Gradle. See the [Gradle Wrapper documentation](link to Gradle Wrapper documentation) for upgrading.
**Keep up to date:** The latest version of `paperweight-userdev` supports dev bundles for Minecraft 1.17.1 and newer.
**Support:** Only the latest version of `paperweight-userdev` is officially supported. Ask in the `#build-tooling-help` channel in the [Discord server](link to Discord server) for issues.
**Snapshots:** `paperweight-userdev` SNAPSHOT (pre-release) versions are only available through Paper's Maven repository:
```kotlin
pluginManagement {
repositories {
gradlePluginPortal()
maven("https://repo.papermc.io/repository/maven-public/")
}
}
```
## Adding the dev bundle dependency
Add a dev bundle dependency to your `dependencies` block in `build.gradle.kts`:
```kotlin
dependencies {
// Other Dependencies
paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT")
}
```
**Tip:** Remove any dependency on the Paper API, as the dev bundle includes that.
### Configuring the Java toolchain for userdev setup
If the dev bundle doesn't support the Gradle's Java toolchain, configure `paperweight`'s `javaLauncher` property:
```kotlin
paperweight {
javaLauncher = javaToolchains.launcherFor {
// Example scenario:
// Paper 1.17.1 was originally built with JDK 16
languageVersion = JavaLanguageVersion.of(17)
}
}
```
## Gradle tasks
### reobfJar
* Creates a plugin JAR re-obfuscated to Spigot's runtime mappings.
* Works on standard Paper servers.
* Output is in the `build/libs` folder.
* The JAR whose filename includes `-dev` is Mojang-mapped and will not work on most servers.
#### Shadow
* If the shadow Gradle plugin is applied, `paperweight-userdev` will detect it and use the shaded JAR as input for `reobfJar`.
* The `-dev-all.jar` file in `build/libs` is the shaded, but not re-obfuscated JAR.
Run the `reobfJar` task on the default `build` task:
```kotlin
tasks.assemble {
dependsOn(tasks.reobfJar)
}
```
### 1.20.5 and beyond
* As of 1.20.5, Paper ships with a Mojang-mapped runtime.
* CraftBukkit classes are no longer relocated into a versioned package.
* Plugins must be deobfuscated before loading when necessary.
### Default mappings assumption
* Spigot/Bukkit plugins are assumed to be Spigot-mapped if they don't specify their mappings namespace in the manifest.
* Paper plugins are assumed to be Mojang-mapped if they don't specify their mappings namespace in the manifest.
* Spigot-mapped plugins will need to be deobfuscated on first load.
### Compiling to Mojang mappings
* Preferred option: skips the one-time plugin remapping process and may allow version compatibility across smaller updates.
* Makes the plugin incompatible with Spigot servers.
* Remove all `dependsOn(reobfJar)` lines and add:
```kotlin
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
```
### Compiling to Spigot mappings
* Add:
```kotlin
paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.REOBF_PRODUCTION
```
* Useful for plugins that have loaders for both Spigot and Paper.
**Note:** If using Gradle with the Groovy DSL, access the fields via static methods like `getMOJANG_PRODUCTION()`.
---
# Scheduling
## Overview
This page explains how to schedule tasks in Paper plugins.
## What is a tick?
[Content missing - Document incomplete]
```
```markdown
# Scheduling in Bukkit
The `BukkitScheduler` is used to schedule code execution later or repeatedly.
**Note:** This guide is for non-Folia Bukkit servers. Folia servers should use their respective schedulers.
## What is a Tick?
* Every game runs a game loop to execute game logic repeatedly.
* In Minecraft, a single execution of this loop is called a 'tick'.
* Minecraft runs at 20 ticks per second, or one tick every 50 milliseconds.
* If a tick takes longer than 50ms, the server lags.
* A task scheduled to run after 100 ticks will run after 5 seconds (at 20 TPS). If the server is running at 10 TPS, it will take 10 seconds.
## Converting Between Human Units and Minecraft Ticks
* Scheduler methods use ticks as the unit of time for delay or period.
* Conversion formulas:
* `ticks = seconds * 20`
* `seconds = ticks / 20`
* Using `TimeUnit` for readability:
```java
TimeUnit.MINUTES.toSeconds(5) * 20 // Converts 5 minutes to ticks
TimeUnit.SECONDS.toMinutes(ticks / 20) // Converts ticks to minutes
```
* Using `Tick` class from Paper:
```java
Tick.tick().fromDuration(Duration.ofMinutes(5)) // Converts 5 minutes to ticks, yields 6000 ticks
```
## Obtaining the Scheduler
* Use the `getScheduler` method on the `Server` class.
```java
@Override
public void onEnable() {
BukkitScheduler scheduler = this.getServer().getScheduler();
}
```
## Scheduling Tasks
Scheduling a task requires passing the following:
* Your plugin's instance.
* The code to run, as either a `Runnable` or `Consumer<BukkitTask>`.
* The delay in ticks before the first execution.
* The period in ticks between executions for repeating tasks.
## Difference Between Synchronous and Asynchronous Tasks
### Synchronous Tasks (On the Main Thread)
* Executed on the main server thread, which handles all game logic.
* Tasks on the main thread affect server performance.
* Use asynchronous tasks for time-consuming operations like web requests, file access, or database interactions.
### Asynchronous Tasks (Off the Main Thread)
* Executed on separate threads, minimizing impact on server performance.
* **Caution:** Large parts of the Bukkit API are not thread-safe and should not be used in asynchronous tasks if they change or access the world state.
* **Note:** Asynchronous tasks are still started from the main thread and affected by server lag.
* For a scheduler independent of the server, use a `ScheduledExecutorService`. See [this guide](link to guide).
## Different Ways to Schedule Tasks
### Using `Runnable`
* The `Runnable` interface is for simple tasks not requiring a `BukkitTask` instance.
* Can be implemented in a separate class:
```java
// MyRunnableTask.java
public class MyRunnableTask implements Runnable {
private final MyPlugin plugin;
public MyRunnableTask(MyPlugin plugin) {
this.plugin = plugin;
}
@Override
public void run() {
this.plugin.getServer().broadcast(Component.text("Hello, World!"));
}
}
scheduler.runTaskLater(plugin, new MyRunnableTask(plugin), 20);
```
* Or use a lambda expression:
```java
scheduler.runTaskLater(plugin,
/* Lambda: */
() -> {
this.plugin.getServer().broadcast(Component.text("Hello, World!"));
},
/* End of the lambda */
20
);
```
### Using `Consumer<BukkitTask>`
* The `Consumer` interface is for tasks needing a `BukkitTask` instance (e.g., repeated tasks where you want to cancel the task from within).
* Implement it in a separate class:
```java
// MyConsumerTask.java
public class MyConsumerTask implements Consumer<BukkitTask> {
private final UUID entityId;
public MyConsumerTask(UUID uuid) {
this.entityId = uuid;
}
@Override
public void accept(BukkitTask task) {
Entity entity = Bukkit.getServer().getEntity(this.entityId);
if (entity instanceof LivingEntity livingEntity) {
livingEntity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1));
return;
}
task.cancel();
// The entity is no longer valid, there's no point in continuing to run this task
}
}
scheduler.runTaskTimer(plugin, new MyConsumerTask(someEntityId), 0, 20);
```
* Or use a lambda expression:
```java
scheduler.runTaskTimer(plugin,
/* Lambda: */
task -> {
Entity entity = Bukkit.getServer().getEntity(entityId);
if (entity instanceof LivingEntity livingEntity) {
livingEntity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1));
return;
}
task.cancel();
// The entity is no longer valid, there's no point in continuing to run this task
}
/* End of the lambda */
, 0, 20
);
```
### Using `BukkitRunnable`
* `BukkitRunnable` implements `Runnable` and holds a `BukkitTask` instance, allowing you to use `this.cancel()` within the `run()` method.
```java
// CustomRunnable.java
public class CustomRunnable extends BukkitRunnable {
private final UUID entityId;
public CustomRunnable(UUID uuid) {
this.entityId = uuid;
}
@Override
public void run() {
Entity entity = Bukkit.getServer().getEntity(this.entityId);
if (entity instanceof LivingEntity livingEntity) {
livingEntity.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20, 1));
return;
}
this.cancel();
// The entity is no longer valid, there's no point in continuing to run this task
}
}
// Example usage (not shown in the original document but inferred)
new CustomRunnable(someEntityId).runTaskTimer(plugin, 0, 20); // Adds a potion effect until the entity dies.
```
### Using a Delay of 0 Ticks
* A delay of 0 ticks runs the task on the next tick.
* If scheduled during server startup or before the server is enabled, the task executes before the server is enabled.
---
# Using Databases
When storing larger amounts of data inside a plugin, it is recommended to use a database.
## What is a Database?
A database is a collection of information stored electronically on a computer system. The main two categories are SQL and NoSQL.
## NoSQL vs SQL
* **NoSQL (Not Only SQL)**:
* Schema-less and offers flexible data models.
* Designed to handle large volumes of unstructured or semi-structured data.
* Uses data models like key-value, document, column-family, or graph.
* **SQL**:
* Follows the relational database model.
* Organizes data into structured tables with predefined schemas.
* Uses SQL (Structured Query Language) to interact with the database.
## File-based vs Standalone Databases
* **File-based**: Stored in a file on disk, used for smaller databases.
* **Standalone**: Operate in a separate process, used for larger data models.
## File-based Databases
* Stored within a single file on the disk.
* Easier to set up and use, suitable for smaller databases.
* Offer lesser performance than standalone databases.
* Examples: SQLite and H2.
### Simple SQLite Setup
#### SQLite
* Requires a driver to connect to/initialize the database.
* **Note:** The JDBC Driver is bundled with Paper, so you don't need to shade/relocate it.
#### Usage
* Invoke `Class#forName(String)` on the driver to allow it to initialize and then create the connection to the database:
```java
// DatabaseManager.java
public class DatabaseManager {
public void connect() {
try {
Class.forName("org.sqlite.JDBC");
Connection connection = DriverManager.getConnection("jdbc:sqlite:plugins/TestPlugin/database.db");
// You then have access to a Connection object,
// which you can use to create a Statement and execute SQL queries.
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace(); // Handle exceptions appropriately
}
}
}
```
* You then have access to a `Connection` object, which you can use to create a `Statement` and execute SQL queries.
* Learn more about the Java Database Connectivity API [here](link to Java Database Connectivity API).
## Standalone Databases
* Operate in a separate process.
* Harder to set up and use, but offer better performance than file-based databases.
* Examples: MySQL, MariaDB, and PostgreSQL.
* Connectors often have connection pooling:
* Creates a pool of pre-established and reusable database connections.
* Reduces the overhead of creating and tearing down connections repeatedly.
* Improves application performance and scalability.
### Simple MySQL Setup
#### MySQL
* Requires more steps but offers performance benefits for larger databases.
* This is a short setup guide for using the `Hikari` library with MySQL.
* **Note:** Requires a running MySQL database to connect to.
* First, add the dependency to your project:
##### Maven
```xml
<!-- pom.xml -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
<scope>compile</scope>
</dependency>
```
##### Gradle
```kotlin
// build.gradle(.kts)
dependencies {
implementation("com.zaxxer:HikariCP:4.0.3")
}
```
* **Caution:** The Hikari library is not bundled with Paper, so you will need to shade/relocate it. Use the Shadow plugin in Gradle.
* Alternatively, you can use the library loader with your Paper plugin to load the library at runtime. See [here](link to Paper plugin library loader) for more information.
#### Usage
* Once you have the dependency added, you can work with the connector in your code:
```java
// DatabaseManager.java
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DatabaseManager {
private HikariDataSource dataSource;
public void connect() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase"); // Address of your running MySQL database
config.setUsername("username"); // Username
config.setPassword("password"); // Password
config.setMaximumPoolSize(10); // Pool size defaults to 10
config.addDataSourceProperty("cachePrepStmts", "true"); // Example MISC setting
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource = new HikariDataSource(config);
}
public void executeStatement(String sql) {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.execute();
} catch (SQLException e) {
e.printStackTrace(); // Handle any exceptions that arise from getting / handing the exception.
}
}
public void close() {
if (dataSource != null) {
dataSource.close();
}
}
}
```
## Security
### SQL Injection
* SQL injection is a malicious technique that exploits improper input validation to execute unauthorized SQL commands.
* Can cause data breaches or damage to the database.
* **Example Vulnerable Code:**
```java
public void login(String username, String password) {
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
// Execute SQL
}
```
* **Exploit Example:**
If the user enters the following as their username: `' OR 1=1; --`
The SQL statement becomes:
```sql
SELECT * FROM users WHERE username = '' OR 1=1; -- AND password = 'password'
```
This returns all users in the database.
### Prepared Statements
* Using prepared statements with `PreparedStatement`s helps prevent SQL injection.
* Separates SQL code from user input using placeholders.
* Reduces the risk of executing unintended SQL commands.
* **Always use prepared statements for security and data integrity.**
* Read more about SQL injection [here](link to SQL injection resource).
```