to jump!", with some coloring and styling.
final TextComponent textComponent2 = Component.text()
.content("You're a ")
.color(TextColor.color(0x443344))
.append(Component.text().content("Bunny").color(NamedTextColor.LIGHT_PURPLE))
.append(Component.text("! Press "))
.append(
Component.keybind().keybind("key.jump")
.color(NamedTextColor.LIGHT_PURPLE)
.decoration(TextDecoration.BOLD, true)
.build()
)
.append(Component.text(" to jump!"))
.build();
// now you can send `textComponent2` to something, such as a client
```
## Styling components
Styles are a superset of TextColor and TextDecoration and can be applied to text components.
TextColor represents any color in the RGB spectrum.
You can also use NamedTextColor to choose from the default color palette.
The following TextDecorations are available:
* *Italic*
* **Bold**
* Strikethrough
* Underlined
* Obfuscated
## Events
There are currently two types of events available for text components.
Hover events allow you to show another component, item or entity when a user hovers their mouse over the text.
When a user clicks on the text component, a click event is fired which can perform one of the following actions:
* Open a URL
* Open a file
* Run a command
* Suggest a command
* Change a book's page
* Copy a string to clipboard
* Open a dialog
* Send a custom payload back to the server
## Serializing and deserializing components
Serialization to JSON, legacy, and plain representations is also
supported.
Components can be serialized with [Text Serializers](/adventure/serializer).
## Using components within your application
The way you use components within your application will of course vary
depending on what you're aiming to achieve.
However, the most common task is likely to be sending a component to
some sort of Minecraft client. The method for doing this will depend on
the platform your program is running on, however it is likely to involve
serializing the component to Minecraft's JSON format, and then sending
the JSON through another method provided by the platform.
The text library is platform-agnostic and therefore doesn't provide any
way to send components to clients. Some platforms implement [Adventure natively](/adventure/platform/native), so `Components`
can be directly used with their API. For other platforms (Spigot/Bukkit, BungeeCord, and SpongeAPI 7),
we provide compatibility bridges as [Platforms](/adventure/platform) which can be distributed with your own plugins.
---
# Titles
Displaying titles to players.
## Constructing a Title
Titles are composed of:
* A component used for the main title
* A component used for the subtitle
* Optionally, a `Title.Times` object can be used to determine the fade-in, stay on screen and fade-out durations
**Examples:**
```java
public void showMyTitle(final Audience target) {
final Component mainTitle = Component.text("This is the main title", NamedTextColor.WHITE);
final Component subtitle = Component.text("This is the subtitle", NamedTextColor.GRAY);
// Creates a simple title with the default values for fade-in, stay on screen and fade-out durations
final Title title = Title.title(mainTitle, subtitle);
// Send the title to your audience
target.showTitle(title);
}
public void showMyTitleWithDurations(final Audience target) {
final Title.Times times = Title.Times.times(Duration.ofMillis(500), Duration.ofMillis(3000), Duration.ofMillis(1000));
// Using the times object this title will use 500ms to fade in, stay on screen for 3000ms and then fade out for 1000ms
final Title title = Title.title(Component.text("Hello!"), Component.empty(), times);
// Send the title, you can also use Audience#clearTitle() to remove the title at any time
target.showTitle(title);
}
```
---
# Version history
Pages dedicated to displaying the latest changelogs!
import { CardGrid, LinkCard } from "@astrojs/starlight/components";
These changelogs for individual projects mirror those posted on the GitHub Releases page of each project's repository.
---
# Folia Documentation
import { CardGrid, LinkCard } from "@astrojs/starlight/components";
Folia is a fork of Paper which adds regionized multithreading to the dedicated server.
---
# Administration
import PageCards from "/src/components/PageCards.astro";
Welcome to the Folia administration guide! This guide includes information and tutorials
regarding the administration of a Folia server.
#### Reference
---
# Reference
import PageCards from "/src/components/PageCards.astro";
A reference of how Folia works.
---
# Frequently asked questions
Questions frequently asked by our community, answered by us!
## What server types can benefit from Folia?
Server types that naturally spread players out,
like skyblock or SMP, will benefit the most from Folia. The server
should have a sizeable player count, too.
## What hardware will Folia run best on?
Ideally, at least 16 _cores_ (not threads).
## How to best configure Folia?
First, it is recommended that the world is pre-generated so that the number
of chunk system worker threads required is reduced greatly.
The following is a _very rough_ estimation based on the testing
done before Folia was released on the test server we ran that
had ~330 players peak. So, it is not exact and will require further tuning -
just take it as a starting point.
The total number of cores on the machine available should be
taken into account. Then, allocate threads for:
- netty IO: ~4 per 200-300 players
- chunk system io threads: ~3 per 200-300 players
- chunk system workers if pre-generated, ~2 per 200-300 players
- There is no best guess for chunk system workers if not pre-generated, as
on the test server we ran we gave 16 threads but chunk generation was still
slow at ~300 players.
- GC Settings: ???? But, GC settings _do_ allocate concurrent threads, and you need
to know exactly how many. This is typically through the `-XX:ConcGCThreads=n` flag. Do not
confuse this flag with `-XX:ParallelGCThreads=n`, as parallel GC threads only run when
the application is paused by GC and as such should not be taken into account.
After all of that allocation, the remaining cores on the system until 80%
allocation (total threads allocated < 80% of cpus available) can be
allocated to tickthreads (under global config, `threaded-regions.threads`).
The reason you should not allocate more than 80% of the cores is due to the
fact that plugins or even the server may make use of additional threads
that you cannot configure or even predict.
Additionally, the above is all a rough guess based on player count, but
it is very likely that the thread allocation will not be ideal, and you
will need to tune it based on usage of the threads that you end up seeing.
## What commands does Folia disable?
Folia currently disables a handful of commands. These are them:
- Bossbar commands
- Clone commands
- Data commands
- Datapack
- Debug
- Function
- Item commands
- Loot
- Reload
- Return
- Ride
- Rotate
- Schedule
- Scoreboard
- Spectate
- SpreadPlayers
- Tag
- Team
- TeamMsg
- Tick
- Trigger
- Perf
- SaveAll
- Restart
---
# Overview
An overview of how Folia works.
Described in this document is the abstract overview
of changes done by Folia. Folia splits the chunks within all loaded worlds
into independently ticking regions so that the regions are ticked
independently and in parallel. Described first will be intra region
operations, and then inter region operations.
## Rules for independent regions
In order to ensure that regions are independent, the rules for
maintaining regions must ensure that a ticking region
has no directly adjacent neighbor regions which are ticking.
The following rules guarantee the invariant is upheld:
1. Any ticking region may not grow while it is ticking.
2. Any ticking region must initially own a small buffer of chunks outside
its perimeter.
3. Regions may not _begin_ to tick if they have a neighboring adjacent
region.
4. Adjacent regions must eventually merge to form a single region.
Additionally, to ensure that a region is not composed of independent regions
(which would hinder parallelism), regions composed of more than
one independent area must be eventually split into independent regions
when possible.
Finally, to ensure that ticking regions may store and maintain data
about the current region (i.e. tick count, entities within the region, chunks
within the region, block/fluid tick lists, and more), regions have
their own data object that may only be accessed while ticking the region and
by the thread ticking the region. Also, there are callbacks to merging
or splitting regions so that the data object may be updated appropriately.
The implementation of these rules is described in [Region Logic](/folia/reference/region-logic).
The end result of applying these rules is that a ticking region can ensure that
only the current thread has write access to any data contained within the region,
and that at any given time the number of independent regions is close to maximum.
## Intra-region operations
Intra-region operations refer to any operations that only deal with data
for a single region by the owning region, or to merge/split logic.
### Ticking for independent regions
Independent regions tick independently and in parallel. To tick independently
means that regions maintain their own deadlines for scheduling the next tick. For
example, consider two regions A and B such that A's next tick start is at t=15ms
and B's next tick start is at t=0ms. Consider the following sequence of events:
1. At t = 0ms, B begins to tick.
2. At t = 15ms, A begins to tick.
3. At t = 20ms, B is finished its tick. It is then scheduled to tick again at t = 50ms.
4. At t = 50ms, B begins its 2nd tick.
5. At t = 70ms, B finishes its 2nd tick and is scheduled to tick again at t = 100ms.
6. At t = 95ms, A finishes its _first_ tick. It is scheduled to tick again at t = 95ms.
It is important to note that at no time was B's schedule affected by the fact that
A fell behind its 20TPS target.
To implement the described behavior, each region maintains a repeating
task on a scheduled executor (see `SchedulerThreadPool`) that schedules
tasks according to an earliest-start-time-first scheduling algorithm. The
algorithm is similar to EDF, but schedules according to start time. However,
given that the deadline for each tick is 50ms + the start time, it behaves
identically to the EDF algorithm.
The EDF-like algorithm is selected so that as long as the thread pool is
not maximally utilized, that all regions that take <= 50ms to tick will
maintain 20TPS. However, the scheduling algorithm is neither NUMA aware
nor CPU core aware - it will not make attempts (when n regions > m threads)
to pin regions to certain cores.
Since regions tick independently, they maintain their own tick counters. The
implications of this are described in the next section.
### Tick counters
In standard Vanilla, there are several important tick counters: Current Tick,
Game Time Tick, and Daylight Time Tick. The Current Tick counter is used
for determining the tick number since the server has booted. The Game Time
Tick is maintained per world and is used to schedule block ticks
for redstone, fluids, and other physics events. The Daylight Time Tick
is simply the number of ticks since noon, maintained per world.
In Folia, the Current Tick is maintained per region. The Game Time Tick
is split into two counters: Redstone Time and Global Game Time.
Redstone Time is maintained per region. Global Game Time and
Daylight Time are maintained by the "global region."
At the start of each region tick, the global game time tick and
daylight time tick are copied from the global region and any time
the current region retrieves those values, it will retrieve from
the copy received at the start of tick. This is to ensure that
for any two calls to retrieve the tick number throughout the tick,
that those two calls report the same tick number.
The global game time is maintained for a couple of reasons:
1. There needs to be a counter representing how many ticks a world
has existed for, since the game does track total number of days
the world has gone on for.
2. Significant amounts of new entity AI code uses game time (for
a reason I cannot divine) to store absolute deadlines of tasks.
It is not impossible to write code to adjust the deadlines of
all of these tasks, but the amount of work is significant.
#### Global region
The global region is a single scheduled task that is always scheduled
to run at 20TPS that is responsible for maintaining data that is not
tied to any specific region: game rules, global game time, daylight time,
console command handling, world border, weather, and others. Unlike the other
regions, the global region does not need to perform any special logic
for merging or splitting because it is never split or merged - there is
only one global region at any time. The global region does not own
any region specific data.
#### Merging and splitting region tick times
Since redstone and current ticks are maintained per region, there needs
to be appropriate logic to adjust the tick deadlines used by the block/fluid
tick scheduler and anything else that schedules by redstone/current
absolute tick time so that the relative deadline is unaffected.
When merging a region x (from) into a region y (into or to),
we can either adjust both the deadlines of x and y or just one of x and y.
It is simply easier to adjust one, and arbitrarily the region x is chosen.
Then, the deadlines of x must be adjusted so that considering the current
ticks of y that the relative deadlines remain unchanged.
Consider a deadline d1 = from tick + relative deadline in region x.
We then want the adjusted deadline d2 to be d2 = to tick + relative deadline
in region y, so that the relative tick deadline is maintained. We can
achieve this by applying an offset o to d1 so that d1 + o = d2, and the
offset used is o = tick to - tick from. This offset must be calculated
for redstone tick and current tick separately, since the logic to increase
redstone tick can be turned off by the `Level#tickTime` field.
Finally, the split case is easy - when a split occurs,
the independent regions from the split inherit the redstone/current tick
from the parent region. Thus, the relative deadlines are maintained as there
is no tick number change.
In all cases, redstone or any other events scheduled by current tick
remain unaffected when regions split or merge as the relative deadline
is maintained by applying an offset in the merge case and by copying
the tick number in the split case.
## Inter-region operations
Inter-region operations refer to operations that work with other regions that are not
the current ticking region that are in a completely unknown state. These
regions may be transient, may be ticking, or may not even exist.
### Utilities to assist operations
In order to assist in inter region operations, several utilities are provided.
In NMS, these utilities are the `EntityScheduler`, the `RegionizedTaskQueue`,
the global region task queue, and the region-local data provider
`RegionizedData`. The Folia API has similar analogues, but does not have
a region-local data provider as the NMS data provider holds critical
locks and is invoked in critical areas of code when performing any
callback logic and is thus highly susceptible to fatal plugin errors
involving lengthy I/O or world state modification.
#### `EntityScheduler`
The `EntityScheduler` allows tasks to be scheduled to be executed on the
region that owns the entity. This is particularly useful when dealing
with entity teleportation, as once an entity begins an asynchronous
teleport the entity cannot tick until the teleport has completed, and
the timing is undefined.
#### `RegionizedTaskQueue`
The `RegionizedTaskQueue` allows tasks to be scheduled to be executed on
the next tick of a region that owns a specific location, or creating
such region if it does not exist. This is useful for tasks that may
need to edit or retrieve world/block/chunk data outside the current region.
#### Global region task queue
The global region task queue is simply used to perform edits on data
that the global region owns, such as game rules, day time, weather,
or to execute commands using the console command sender.
#### `RegionizedData`
The `RegionizedData` class allows regions to define region-local data,
which allow regions to store data without having to consider concurrent
data access from other regions. For example, current per region
entity/chunk/block/fluid tick lists are maintained so that regions do not
need to consider concurrent access to these data sets.
The utilities allow various cross-region issues to be resolved in a
simple fashion, such as editing block/entity/world state from any region
by using tasks queues, or by avoiding concurrency issues by using
RegionizedData. More advanced operations such as teleportation,
player respawning, and portalling, all make use of these utilities
to ensure the operation is thread-safe.
### Entity intra- and inter-dimension teleports
Entities need special logic in order to teleport safely between
other regions or other dimensions. In all cases however, the call to
teleport/place an entity must be invoked on the region owning the entity.
The `EntityScheduler` can be used to easily schedule code to execute in such
a context.
#### Simple teleportation
In a simple teleportation, the entity already exists in a world at a location
and the target location and dimension are known.
This operation is split into two parts: transform and async place.
In this case, the transform operation removes the entity from the current
world, then adjusts the position. The async place operation schedules a task
to the target location using the `RegionizedTaskQueue` to add the entity to
the target dimension at the target position.
The various implementation details such as non-player entities being
copied in the transform operation are left out, as those are not relevant
for the high level overview.
Things such as player login and player respawn are generally
considered simple teleportation. The player login case only differs
since the player does not exist in any world at the start, and that the async
transform must additionally find a place to spawn the player.
The player respawn is similar to the player login as the respawn
differs by having the player in the world at the time of respawn.
#### Portal teleport
Portal teleport differs from simple teleportation as portalling does
_not_ know the exact location of the teleport. Thus, the transform step
does not update the entity position, but rather a new operation is inserted
between transform and async place: an async search/create, which is responsible
for finding and/or creating the exit portal.
Additionally, the current Vanilla code can refuse a teleport if the
entity is non-player and the nether exit portal does not already exist. But
since the portal location is only determined by the async place, it is
too late to abort - so, the portal logic has been re-done so that there is no
difference between players and entities. Now both entities and players
create exit portals, whether it be for the nether or end.
#### Shutdown during teleport
Since the teleport happens over multiple steps, the server shutdown
process must deal with uncompleted teleportations manually.
## Server shutdown process
The shutdown process occurs by spawning a separate shutdown thread,
which then runs the shutdown logic:
1. Shutdown the tick region scheduler, stopping any further ticks
2. Halt metrics processing
3. Disable plugins
4. Stop accepting new connections
5. Send disconnect (but do not remove) packets to all players
6. Halt the chunk systems for all worlds
7. Execute shutdown logic for all worlds by finish all pending teleports
for all regions, then saving all chunks in the world, and finally
saving the level data for the world (level.dat and other .dat files).
8. Save all players
9. Shutting down the resource manager
10. Releasing the level lock
11. Halting remaining executors (Util executor, region I/O threads, etc.)
The important differences to Vanilla is that the player kick and
world saving logic is replaced by steps 5-8.
For step 5, the players cannot be kicked before teleportations are finished,
as kicking would save the player dat file. So, save is moved after.
For step 6, the chunk system halt is done before saving so that all chunk
generation is halted. This will reduce the load on the server as it shuts
down, which may be critical in memory-constrained scenarios.
For step 7, teleportations are completed differently depending on the type:
simple or portal.
Simple teleportations are completed by ensuring the addition
of the teleporting entity to the destination chunk specified
by teleportation. This allows the entity to be saved at the target
position, as if the teleportation did complete before shutdown.
Portal teleportations are completed by forcing the addition
of the teleporting entity to the source chunk, from where the entity should
have been teleported _from_. Since the target location is not known, the entity
can only be placed back at the origin (no teleportation). While this behavior
is not ideal, the shutdown logic _must_ account for any broken world state -
which means that finding or creating the target exit portal may not be an option.
The teleportation completion must be performed before the world save so that
the teleport completed entities save.
For step 8, only save players after the teleportations are completed.
The remaining steps are Vanilla.
---
# Region logic
An overview to how Folia's regionizer works.
## Fundamental regionizing logic
## Region
A region is simply a set of owned chunk positions and implementation
defined unique data object tied to that region. It is important
to note that for any non-dead region x, that for each chunk position y
it owns that there is no other non-dead region z such that
the region z owns the chunk position y.
## Regionizer
Each world has its own regionizer. The regionizer is a term used
to describe the logic that the class `ThreadedRegionizer` executes
to create, maintain, and destroy regions. Maintenance of regions is
done by merging nearby regions together, marking which regions
are eligible to be ticked, and finally by splitting any regions
into smaller independent regions. Effectively, it is the logic
performed to ensure that groups of nearby chunks are considered
a single independent region.
## Guarantees the regionizer provides
The regionizer provides a set of important invariants that allows
regions to tick in parallel without race conditions:
### First invariant
The first invariant is simply that any chunk holder that exists
has one, and only one, corresponding region.
### Second invariant
The second invariant is that for every _existing_ chunk holder x that is
contained in a region that every each chunk position within the
"merge radius" of x is owned by the region. Effectively, this invariant
guarantees that the region is not close to another region, which allows
the region to assume while ticking it can create data for chunk holders
"close" to it.
### Third invariant
The third invariant is that a ticking region _cannot_ expand
the chunk positions it owns as it ticks. The third invariant
is important as it prevents ticking regions from "fighting"
over non-owned nearby chunks, to ensure that they truly tick
in parallel, no matter what chunk loads they may issue while
ticking.
To comply with the first invariant, the regionizer will
create "transient" regions _around_ ticking regions. Specifically,
around in this context means close enough that would require a merge,
but not far enough to be considered independent. The transient regions
created in these cases will be merged into the ticking region
when the ticking region finishes ticking.
Both of the second invariant and third invariant combined allow
the regionizer to guarantee that a ticking region may create
and then access chunk holders around it (i.e. sync loading) without
the possibility that it steps on another region's toes.
### Fourth invariant
The fourth invariant is that a region is only in one of four
states: "transient", "ready", "ticking", or "dead."
The "ready" state allows a state to transition to the "ticking" state,
while the "transient" state is used as a state for a region that may
not tick. The "dead" state is used to mark regions which should
not be use.
The states transitions are explained later, as it ties in
with the regionizer's merge and split logic.
## Regionizer implementation
The regionizer implementation is a description of how
the class `ThreadedRegionizer` adheres to the four invariants
described previously.
### Splitting the world into sections
The regionizer does not operate on chunk coordinates, but rather
on "region section coordinates." Region section coordinates simply
represent a grouping of NxN chunks on a grid, where N is some power
of two. The actual number is left ambiguous, as region section coordinates
are only an internal detail of how chunks are grouped.
For example, with N=16 the region section (0,0) encompasses all
chunks x in [0,15] and z in [0,15]. This concept is similar to how
the chunk coordinate (0,0) encompasses all blocks x in [0, 15]
and z in [0, 15]. Another example with N=16, the chunk (17, -5) is
contained within region section (1, -1).
Region section coordinates are used only as a performance
tradeoff in the regionizer, as by approximating chunks to their
region coordinate allows it to treat NxN chunks as a single
unit for regionizing. This means that regions do not own chunks positions,
but rather own region section positions. The grouping of NxN chunks
allows the regionizing logic to be performed only on
the creation/destruction of region sections.
For example with N=16 this means up to NxN-1=255 possible
less operations in areas such as addChunk/region recalculation
assuming region sections are always full.
### Implementation variables
The implementation variables control how aggressively the
regionizer will maintain regions and merge regions.
#### Recalculation count
The recalculation count is the minimum number of region sections
that a region must own to allow it to re-calculate. Note that
a recalculation operation simply calculates the set of independent
regions that exist within a region to check if a split can be
performed.
This is a simple performance knob that allows split logic to be
turned off for small regions, as it is unlikely that small regions
can be split in the first place.
#### Max dead section percent
The max dead section percent is the minimum percent of dead
sections in a region that must exist before a region can run
re-calculation logic.
#### Empty section creation radius
The empty section creation radius variable is used to determine
how many empty region sections are to exist around _any_
region section with at least one chunk.
Internally, the regionizer enforces the third invariant by
preventing ticking regions from owning new region sections.
The creation of empty sections around any non-empty section will
then enforce the second invariant.
#### Region section merge radius
The merge radius variable is used to ensure that for any
existing region section x that for any other region section y within
the merge radius are either owned by region that owns x
or are pending a merge into the region that owns x or that the
region that owns x is pending a merge into the region that owns y.
#### Region section chunk shift
The region section chunk shift is simply log2(grid size N). Thus,
N = 1 << region section chunk shift. The conversion from
chunk position to region section is additionally defined as
region coordinate = chunk coordinate >> region section chunk shift.
### Operation
The regionizer is operated by invoking `ThreadedRegionizer#addChunk(x, z)`
or `ThreadedRegionizer#removeChunk(x, z)` when a chunk holder is created
or destroyed.
Additionally, `ThreadedRegion#tryMarkTicking` can be used by a caller
that attempts to move a region from the "ready" state to the "ticking"
state. It is vital to note that this function will return false if
the region is not in the "ready" state, as it is possible
that even a region considered to be "ready" in the past (i.e. scheduled
to tick) may be unexpectedly marked as "transient." Thus, the caller
needs to handle such cases. The caller that successfully marks
a region as ticking must mark it as non-ticking by using
`ThreadedRegion#markNotTicking`.
The function ThreadedRegion#markNotTicking returns true if the
region was migrated from "ticking" state to "ready" state, and false
in all other cases. Effectively, it returns whether the current region
may be later ticked again.
### Region section state
A region section state is one of "dead" or "alive." A region section
may additionally be considered "non-empty" if it contains
at least one chunk position, and "empty" otherwise.
A region section is considered "dead" if and only if the region section
is also "empty" and that there exist no other "empty" sections within the
empty section creation radius.
The existence of the dead section state is purely for performance, as it
allows the recalculation logic of a region to be delayed until the region
contains enough dead sections. However, dead sections are still
considered to belong to the region that owns them just as alive sections.
### Addition of chunks (`addChunk`)
The addition of chunks to the regionizer boils down to two cases:
#### Target region section already exists and is not empty
In this case, it simply adds the chunk to the section and returns.
#### Target region section does not exist or is empty
In this case, the region section will be created if it does not exist.
Additionally, the region sections in the "create empty radius" will be
created as well.
Then, any region in the create empty radius + merge radius are collected
into a set X. This set represents the regions that need to be merged
later to adhere to the second invariant.
If the set X contains no elements, then a region is created in the ready
state to own all of the created sections.
If the set X contains just 1 region, then no regions need to be merged
and no region state is modified, and the sections are added to this
1 region.
Merge logic needs to occur when there are more than 1 region in the
set X. From the set X, a region x is selected that is not ticking. If
no such x exists, then a region x is created. Every region section
created is added to the set x, as it is the section that is known
to not be ticking - this is done to adhere to the third invariant.
Every region y in the set X that is not x is merged into x if
y is not in the ticking state, otherwise x runs the merge later
logic into y.
### Merge later logic
A merge later operation may only take place from
a non-ticking, non-dead region x into a ticking region y.
The merge later logic relies on maintaining a set of regions
to merge into later per region, and another set of regions
that are expected to merge into this region.
Effectively, a merge into later operation from x into y will add y into x's
merge into later set, and add x into y's expecting merge from set.
When the ticking region finishes ticking, the ticking region
will perform the merge logic for all expecting merges.
### Merge logic
A merge operation may only take place between a dead region x
and another region y which may be either "transient"
or "ready." The region x is effectively absorbed into the
region y, as the sections in x are moved to the region y.
The merge into later is also forwarded to the region y,
such so that the regions x was to merge into later, y will
now merge into later.
Additionally, if there is implementation specific data
on region x, the region callback to merge the data into the
region y is invoked.
The state of the region y may be updated after a merge operation
completes. For example, if the region x was "transient", then
the region y should be downgraded to transient as well. Specifically,
the region y should be marked as transient if region x contained
merge later targets that were not y. The downgrading to transient is
required to adhere to the second invariant.
### Removal of chunks (`removeChunk`)
Removal of chunks from region sections simple updates
the region sections state to "dead" or "alive", as well as the
region sections in the empty creation radius. It will not update
any region state, and nor will it purge region sections.
### Region tick start (`tryMarkTicking`)
The tick start simply migrates the state to ticking, so that
invariants #2 and #3 can be met.
### Region tick end (`markNotTicking`)
At the end of a tick, the region's new state is not immediately known.
First, the region must process its pending merges.
After it processes its pending merges, it must then check if the
region is now pending merge into any other region. If it is, then
it transitions to the transient state.
Otherwise, it will process the removal of dead sections and attempt
to split into smaller regions. Note that it is guaranteed
that if a region can be possibly split, it must remove dead sections,
otherwise, this would contradict the rules used to build the region
in the first place.
---
# Welcome
Documentation for all projects under the PaperMC umbrella, including Paper, Velocity and Folia.
import { CardGrid } from "@astrojs/starlight/components";
import { ContributorList } from "astro-contributors";
import LinkCard from "/src/components/LinkCard.astro";
High performance Minecraft server that aims to fix gameplay and mechanics inconsistencies.
The modern, next-generation Minecraft server proxy.
A fork of Paper which adds regionized multithreading to the dedicated server.
A Java library for server-controllable user interface elements in Minecraft: Java Edition.
A discontinued BungeeCord proxy fork that aimed to improve performance and stability.
Various other documentation and tools.
## [Contribute](https://github.com/PaperMC/docs)
We’d like to extend a warm thank you to all the contributors who have helped bring this documentation site to life.
Whether you've written code, submitted feedback, fixed a typo, or shared your expertise - every contribution is deeply appreciated.
This is very much a team effort, and we’re grateful to have you with us on the journey!
---
# Miscellaneous
import PageCards from "/src/components/PageCards.astro";
Documentation that does not cleanly fit in any other category.
---
# Art assets
The official PaperMC and Velocity logomarks and the terms under which you may use them.
This page provides the official PaperMC and Velocity logomarks and the terms under which you may use them.
Images on this page are available through our CDN, and using the provided URLs is encouraged (but not necessary)
when referencing these assets in your projects, as long as you adhere to the usage guidelines outlined below.
:::caution
The logomarks are subject to their own separate licensing terms and do not inherit any from the
projects they represent.
:::
## PaperMC
You may:
- Use the PaperMC logomark to represent the project in blog posts and other places in order to bring
attention to the project.
- Use the PaperMC logomark to represent Paper-Server in downloads, server selectors, and similar
places.
- Crop out extra transparent canvas space behind the PaperMC logomark, so it fits better next to
other content.
You may not:
- Alter any of the colors used in the PaperMC logomark.
- Change the dimensions of the PaperMC logomark.
- Create modified versions of the PaperMC logomark or derivative works of it.
- Add your own project images or branding to the PaperMC logomark.
- Claim the logomark as your own work or use it as a representation of your own projects.
- Sell the PaperMC logomark on its own or as part of other products without explicit permission.
- Alter the transparency of any elements within the PaperMC logomark.
| Image | URL |
|----------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| 
| `https://assets.papermc.io/brand/papermc_combination_mark_dark.min.svg` |
| 
| `https://assets.papermc.io/brand/papermc_combination_mark_light.min.svg` |
|  | `https://assets.papermc.io/brand/papermc_logo.min.svg` |
|  | `https://assets.papermc.io/brand/papermc_logo.256.png` |
|  | `https://assets.papermc.io/brand/papermc_logo.512.png` |
## Velocity
Please do not edit, recolor, rearrange, or distort the Velocity logo. Resizing the
logo and cropping out any blank space is acceptable.
The logo should not be used in a matter that suggests the Velocity project officially
endorses some product or service. For instance, you may advertise a plugin as being
compatible with Velocity, but you may not make the Velocity logo prominent in that
advertising.
| Image | URL |
|-----------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
|  | `https://assets.papermc.io/brand/velocity_combination_mark_blue.min.svg` |
| 
| `https://assets.papermc.io/brand/velocity_combination_mark_white.min.svg` |
|  | `https://assets.papermc.io/brand/velocity_logo_blue.min.svg` |
| 
| `https://assets.papermc.io/brand/velocity_logo_white.min.svg` |
---
# Contact us
There are many ways to contact us, see here for more information.
## Discord
The PaperMC project handles most communication via Discord. Use the following invite: https://discord.gg/papermc
## Forums
:::caution
After careful consideration and due to limited usage, we’ve made the decision to discontinue the PaperMC forums.
Moving forward, we recommend using Hangar for plugin uploads. For all other community discussions and support,
please join us on Discord.
:::
- [Forums](https://forums.papermc.io)
## Twitter/X
We often tweet out version release notes, update notices, and other information via our Twitter
page.
- [@PaperPowered](https://x.com/PaperPowered)
You should not DM or @ this account for support. It is not checked as regularly as the above
locations.
---
# Downloads Service
Paper provides a downloads service that you can use to access builds.
PaperMC provides a downloads service to facilitate automated downloads access. Full documentation can be
found on the [Downloads Service Docs](https://fill.papermc.io/swagger-ui/index.html#/).
:::danger
We emphatically **do not recommend** using unstable builds or auto-updaters within production environments.
:::
All requests must now include a valid User-Agent header that:
- Clearly identifies your software or company
- Is not generic (e.g. curl, wget, or similar defaults)
- Includes a contact URL or email address (e.g. a homepage, bot info page, or support email)
**Some examples:**
```
mc-image-helper/1.39.11 (https://github.com/itzg/docker-minecraft-server)
nodecraft/packifier/1.0.0 (staff@nodecraft.com)
```
## REST API examples
:::note
We require `jq` to be installed for the examples below. You can install it with `sudo apt-get install jq` on Debian/Ubuntu.
:::
### Getting the latest version
```shell
#!/usr/bin/env sh
PROJECT="paper"
USER_AGENT="cool-project/1.0.0 (contact@me.com)"
LATEST_VERSION=$(curl -s -H "User-Agent: $USER_AGENT" https://fill.papermc.io/v3/projects/${PROJECT} | \
jq -r '.versions | to_entries[0] | .value[0]')
echo "Latest version is $LATEST_VERSION"
```
This will get the latest available Minecraft version for the given project.
### Getting the latest stable build number
```shell replace
#!/usr/bin/env sh
PROJECT="paper"
MINECRAFT_VERSION="\{LATEST_PAPER_RELEASE}"
USER_AGENT="cool-project/1.0.0 (contact@me.com)"
LATEST_BUILD=$(curl -s -H "User-Agent: $USER_AGENT" https://fill.papermc.io/v3/projects/${PROJECT}/versions/${MINECRAFT_VERSION}/builds | \
jq -r 'map(select(.channel == "STABLE")) | .[0] | .id')
if [ "$LATEST_BUILD" != "null" ]; then
echo "Latest stable build is $LATEST_BUILD"
else
echo "No stable build for version $MINECRAFT_VERSION found :("
fi
```
This will get the latest stable build for the given project and Minecraft version, if it's available.
### Downloading the latest stable build
```shell replace
#!/usr/bin/env sh
PROJECT="paper"
MINECRAFT_VERSION="\{LATEST_PAPER_RELEASE}"
USER_AGENT="cool-project/1.0.0 (contact@me.com)"
# First check if the requested version has a stable build
BUILDS_RESPONSE=$(curl -s -H "User-Agent: $USER_AGENT" https://fill.papermc.io/v3/projects/${PROJECT}/versions/${MINECRAFT_VERSION}/builds)
# Check if the API returned an error
if echo "$BUILDS_RESPONSE" | jq -e '.ok == false' > /dev/null 2>&1; then
ERROR_MSG=$(echo "$BUILDS_RESPONSE" | jq -r '.message // "Unknown error"')
echo "Error: $ERROR_MSG"
exit 1
fi
# Try to get a stable build URL for the requested version
PAPERMC_URL=$(echo "$BUILDS_RESPONSE" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".url) // "null"')
FOUND_VERSION="$MINECRAFT_VERSION"
# If no stable build for requested version, find the latest version with a stable build
if [ "$PAPERMC_URL" == "null" ]; then
echo "No stable build for version $MINECRAFT_VERSION, searching for latest version with stable build..."
# Get all versions for the project (using the same endpoint structure as the "Getting the latest version" example)
# The versions are organized by version group, so we need to extract all versions from all groups
# Then sort them properly as semantic versions (newest first)
VERSIONS=$(curl -s -H "User-Agent: $USER_AGENT" https://fill.papermc.io/v3/projects/${PROJECT} | \
jq -r '.versions | to_entries[] | .value[]' | \
sort -V -r)
# Iterate through versions to find one with a stable build
for VERSION in $VERSIONS; do
VERSION_BUILDS=$(curl -s -H "User-Agent: $USER_AGENT" https://fill.papermc.io/v3/projects/${PROJECT}/versions/${VERSION}/builds)
# Check if this version has a stable build
STABLE_URL=$(echo "$VERSION_BUILDS" | jq -r 'first(.[] | select(.channel == "STABLE") | .downloads."server:default".url) // "null"')
if [ "$STABLE_URL" != "null" ]; then
PAPERMC_URL="$STABLE_URL"
FOUND_VERSION="$VERSION"
echo "Found stable build for version $VERSION"
break
fi
done
fi
if [ "$PAPERMC_URL" != "null" ]; then
# Download the latest Paper version
curl -o server.jar $PAPERMC_URL
echo "Download completed (version: $FOUND_VERSION)"
else
echo "No stable builds available for any version :("
exit 1
fi
```
This is the most common use case for the API. It will download the latest stable build for the given project and
Minecraft version. You should always serve & use the stable builds. Experimental builds are prone to error and
do not receive support.
## GraphQL API examples
Fill also supports a GraphQL API, which can be accessed at `https://fill.papermc.io/graphql`.
Fill's GraphQL API uses standard pagination, which you can learn more about [here](https://graphql.org/learn/pagination/).
A built-in GraphQL playground is available at https://fill.papermc.io/graphiql?path=/graphql.
Common API tools such as Postman will introspect the API and provide a UI for building queries.
### Getting the latest version
```graphql
query LatestVersion {
project(key: "paper") {
key
versions(first: 1, orderBy: {direction: DESC}) {
edges {
node {
key
}
}
}
}
}
```
Example response
```json
{
"data": {
"project": {
"key": "paper",
"versions": {
"edges": [
{
"node": {
"key": "1.21.11"
}
}
]
}
}
}
}
```
### Getting the latest stable build number
```graphql
query LatestStableBuild {
project(key: "paper") {
key
versions(first: 1, orderBy: {direction: DESC}) {
edges {
node {
key
builds(filterBy: { channel: STABLE }, first: 1, orderBy: { direction: DESC }) {
edges {
node {
number
}
}
}
}
}
}
}
}
```
Example response
```json
{
"data": {
"project": {
"key": "paper",
"versions": {
"edges": [
{
"node": {
"key": "1.21.10",
"builds": {
"edges": [
{
"node": {
"number": 48
}
}
]
}
}
}
]
}
}
}
}
```
### Getting the latest stable build download URL
```graphql
query LatestStableBuildDownloadURL {
project(key: "paper") {
key
versions(first: 1, orderBy: {direction: DESC}) {
edges {
node {
key
builds(filterBy: { channel: STABLE }, first: 1, orderBy: { direction: DESC }) {
edges {
node {
number
download(key: "server:default") {
name
url
checksums {
sha256
}
size
}
}
}
}
}
}
}
}
}
```
Example response
```json
{
"data": {
"project": {
"key": "paper",
"versions": {
"edges": [
{
"node": {
"key": "1.21.10",
"builds": {
"edges": [
{
"node": {
"number": 48,
"download": {
"name": "paper-1.21.10-48.jar",
"url": "https://fill-data.papermc.io/v1/objects/bfca155b4a6b45644bfc1766f4e02a83c736e45fcc060e8788c71d6e7b3d56f6/paper-1.21.10-48.jar",
"checksums": {
"sha256": "bfca155b4a6b45644bfc1766f4e02a83c736e45fcc060e8788c71d6e7b3d56f6"
},
"size": 54185955
}
}
}
]
}
}
}
]
}
}
}
}
```
---
# Hangar auto-publishing
How to automatically publish your plugin to Hangar on commits.
If you want to automatically publish your plugin to [Hangar](https://hangar.papermc.io/) on pushes, you can use
our [Gradle plugin](https://github.com/HangarMC/hangar-publish-plugin).
After you have added the required `hangarPublish` configuration, you can manually publish it by
running `./gradlew build publishPluginPublicationToHangar`, or have GitHub Actions automatically publish a version on
every commit.
## Prerequisites
### Gradle
Your plugin project needs to use Gradle as its build tooling.
:::tip
If you are using
Maven, [switching to a Gradle setup is easy](https://docs.gradle.org/current/userguide/migrating_from_maven.html) and in
general recommended due to higher configurability and support for other plugins, such as
when [compiling against an unobfuscated Minecraft server](/paper/dev/userdev).
The provided examples use Kotlin DSL, but you can also do the same using Groovy. Online converters (even ChatGPT)
are able to convert the example code.
:::
### Creating the `Snapshot` release channel
The builds script below will publish non-release builds under a `Snapshot` channel. You need to create this channel in
your Hangar project's channel page first.

### Adding the `HANGAR_API_TOKEN` repository secret
First, you need to create a Hangar API token. Go to your Hangar settings in the profile dropdown and click on "Api keys" on the left. Then, tick
the `create_version` permission box, give the key a name and create it. **At the top**, you should be given your secret API
token. **Do not share this with anyone**; you will need it in the next step.
The GitHub Actions workflow provided uses a repository secret to store your Hangar API key. Go to your GitHub project settings,
then click on "Actions" under the Security tab and click the "New repository secret" button. Name the
secret `HANGAR_API_TOKEN` and paste the Hangar API token from the previous step into the Secret field.

## Project files
The files below are simple examples that require little manual changes for you to use, but you can still adapt them
depending on your needs. Take a look at the comments and especially the TODOs to figure out what you still need to
change.
### `gradle.properties`
Create a `gradle.properties` file in your project root directory if it does not already exist. In there, you define the
platform versions your plugin is compatible with. Simply remove the platforms you don't need and put in the correct
versions.
Hangar allows version ranges (such as `1.19-1.20.2`) and wildcards (such as `1.20.x`).
```properties
# Specify the platform versions for Paper and Velocity.
# Hangar also allows version ranges (such as 1.19-1.20.2) and wildcards (such as 1.20.x).
# TODO: Remove the platforms you don't need and put in the correct versions.
paperVersion=1.12.2, 1.16.5, 1.19-1.20.2
velocityVersion=3.2
waterfallVersion=1.20
```
### `build.gradle.kts`
In the plugins block of your `build.gradle.kts` build script, add the publish plugin:
```kotlin title="build.gradle.kts"
plugins {
id("io.papermc.hangar-publish-plugin") version "0.1.2"
}
```
Then you simply need to add the `hangarPublish` configuration block and make sure you do the following:
- If your plugin is not a Paper plugin, or supports Velocity/Waterfall as well, copy the register block with a different
platform and change the property used instead of `paperVersion` (as declared in the `gradle.properties` file).
- Insert the correct project namespace
- Insert your plugin dependencies, if any
- You need to have the `HANGAR_API_TOKEN` repository secret set up if you are using the Actions file below, otherwise add the
API key in some other way.
```kotlin
import io.papermc.hangarpublishplugin.model.Platforms
// ...
hangarPublish {
publications.register("plugin") {
version.set(project.version as String)
channel.set("Snapshot") // We're using the 'Snapshot' channel
// TODO: Edit the project name to match your Hangar project
id.set("hangar-project")
apiKey.set(System.getenv("HANGAR_API_TOKEN"))
platforms {
// TODO: Use the correct platform(s) for your plugin
register(Platforms.PAPER) {
// TODO: If you're using ShadowJar, replace the jar lines with the appropriate task:
// jar.set(tasks.shadowJar.flatMap { it.archiveFile })
// Set the JAR file to upload
jar.set(tasks.jar.flatMap { it.archiveFile })
// Set platform versions from gradle.properties file
val versions: List = (property("paperVersion") as String)
.split(",")
.map { it.trim() }
platformVersions.set(versions)
// TODO: Configure your plugin dependencies, if any
dependencies {
// Example for a dependency found on Hangar
hangar("Maintenance") {
required.set(false)
}
// Example for an external dependency
url("Debuggery", "https://github.com/PaperMC/Debuggery") {
required.set(true)
}
}
}
}
}
}
```
## GitHub Actions workflow
You don't necessarily need to publish via GitHub Actions, but it is an easy way to do so. If you want to use it, create
a `publish.yml` file in the `.github/workflows` directory of your project root folder and make sure
you [add the repository secret](#adding-the-hangar_api_token-repository-secret).
You can add and remove branches to be published by editing the `branches` section.
```yaml
name: Publish to Hangar
on:
push:
branches:
# Add any additional branches you want to automatically publish from
- main # Assuming your main branch is called 'main'
jobs:
publish:
# TODO: Optional, make sure the task only runs on pushes to your repository and doesn't fail on forks. Uncomment the line below and put the repo owner into the quotes
# if: github.repository_owner == ''
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Set up JDK 25
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 25
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Publish
env:
# Make sure you have added a repository secret in the repository's settings
HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }}
run: ./gradlew build publishPluginPublicationToHangar --stacktrace
```
## Optional: Handling multiple channels and an automatic changelog
With the following code, any version that contains a hyphen (`-`) will be published under the `Snapshot` channel
(that you need to create on Hangar) and the others on the `Release` channel.
By editing the `channel.set(...)` line, you can change this to any channel you would like.
For example, you could further split builds depending on the branch you are currently on into `Alpha` builds.
:::caution
Make sure you never publish ongoing development builds to the `Release` channel.
:::
```kotlin
import java.io.ByteArrayOutputStream
// ...
// Helper methods
fun executeGitCommand(vararg command: String): String {
val byteOut = ByteArrayOutputStream()
exec {
commandLine = listOf("git", *command)
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
}
fun latestCommitMessage(): String {
return executeGitCommand("log", "-1", "--pretty=%B")
}
val versionString: String = version as String
val isRelease: Boolean = !versionString.contains('-')
val suffixedVersion: String = if (isRelease) {
versionString
} else {
// Give the version a unique name by using the GitHub Actions run number
versionString + "+" + System.getenv("GITHUB_RUN_NUMBER")
}
// Use the commit description for the changelog
val changelogContent: String = latestCommitMessage()
// If you would like to publish releases with their proper changelogs manually, simply add an if statement with the `isRelease` variable here.
hangarPublish {
publications.register("plugin") {
version.set(suffixedVersion)
channel.set(if (isRelease) "Release" else "Snapshot")
changelog.set(changelogContent)
// ... (see above)
}
}
```
## Optional: Updating the resource page
A notable part of publishing a new version might be updating the resource page (e.g. the plugin's home page) with new content from your plugin's repository.
In this example, we're using a README file, but you can use any text you want, as long as it is in Markdown format.
```kotlin
val pageContent = project.file("README.md").readText()
hangarPublish {
publications.register("plugin") {
// ... (see above)
pages.resourcePage(pageContent)
}
}
```
You can invoke the `syncPluginPublicationMainResourcePagePageToHangar` task to update the resource page on Hangar.
This will not publish a new version, but simply update the page content on Hangar.
If you're using a GitHub Actions workflow, you should also add the task invocation to the `Publish` step in your workflow:
```yaml
- name: Publish
# ...
run: ./gradlew build publishPluginPublicationToHangar syncPluginPublicationMainResourcePagePageToHangar --stacktrace
```
---
# Installing or updating Java
How to install or update to Java 25 on Linux (apt/rpm), Windows, or Mac.
Installing Java is a critical first step to using or developing plugins for Paper and Velocity.
This guide will walk you through the recommended installation steps for most major
platforms.
:::caution[Do not use headless variants of Java!]
There are `headless` variants of Java which usually have a suffix of `-headless` in their package
name. Those variants miss required dependencies for Paper. Therefore, using them is not recommended.
:::
:::tip
This guide focuses on Amazon's Corretto OpenJDK distribution. This is because it offers the best
installation experience on the most platforms. Corretto is, however, not the only OpenJDK vendor to
choose from. Many alternatives exist such as [Eclipse Adoptium](https://adoptium.net/),
[Microsoft](https://www.microsoft.com/openjdk) and
[Azul Zulu](https://www.azul.com/downloads/?package=jdk). Note that the JDK Oracle distributes,
while functionally identical, is **not** recommended due to an extremely unfriendly installer and
previous hostile licensing.
:::
## Linux
### Ubuntu/Debian
Installing Java 25 on Debian-based Linux distributions is very simple. First, ensure your system has
all required tools to successfully install Java.
```bash
sudo apt-get update
sudo apt-get install ca-certificates apt-transport-https gnupg wget
```
Second, import the Amazon Corretto public key and apt repository.
```bash
wget -O - https://apt.corretto.aws/corretto.key | sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | sudo tee /etc/apt/sources.list.d/corretto.list
```
Then, install Java 25 and other dependencies using the following command:
```bash
sudo apt-get update
sudo apt-get install -y java-25-amazon-corretto-jdk libxi6 libxtst6 libxrender1
```
Proceed to [verify your installation](#verifying-installation).
### RPM-based
To install Java 25 on CentOS, RHEL, Fedora, openSUSE, SLES, or any other RPM-based Linux
distribution, execute the following commands depending on your package manager. Once you have
finished, precede to [verify your installation](#verifying-installation).
#### DNF
DNF is used on Fedora, CentOS/RHEL 7+, and related distributions.
```bash
sudo rpm --import https://yum.corretto.aws/corretto.key
sudo curl -Lo /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo
sudo dnf -y install java-25-amazon-corretto-devel
```
#### Zypper
Zypper is used on openSUSE, SLES, and related distributions.
```bash
sudo zypper addrepo https://yum.corretto.aws/corretto.repo
sudo zypper refresh
sudo zypper install java-25-amazon-corretto-devel
```
#### YUM
YUM is used on older releases of CentOS/RHEL, and excessively old releases of Fedora.
```bash
sudo rpm --import https://yum.corretto.aws/corretto.key
sudo curl -Lo /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo
sudo yum -y install java-25-amazon-corretto-devel
```
## Windows 10 & 11
If you're on Windows 10 or 11, installing Java is just like installing any other program. Download
the Amazon Corretto installer from
[their website](https://corretto.aws/downloads/latest/amazon-corretto-25-x64-windows-jdk.msi).
Once you have run the installer, it is safe to click "next" through the whole process. No additional
bloatware or toolbars will be installed, and all the required features are enabled out of the box.
Now, open a command prompt and precede to [verify your installation](#verifying-installation).
## macOS/OS X
If you're on macOS, the best way to manage Java installations is with a tool called
[Homebrew](https://brew.sh). Follow the instructions on their homepage to install it. Then, in your
terminal run the following command:
```bash
brew install openjdk@25
```
Once this command has completed, continue to [verify your installation](#verifying-installation).
## Pterodactyl
:::note
On Pterodactyl versions lower than `1.2.0`, an administrator account is required to change the Java
version. These instructions will not apply.
:::
If you have started a Paper server with an incorrect Java version, Pterodactyl will automatically
prompt you to update like this:

If this does not show up for you, the Java version can be manually changed. Navigate to the
"Startup" tab of your server, select `Java 25` from the "Docker Image"
dropdown as shown in the image below.

:::note
If you don't see `Java 25` in the dropdown, an administrator account is required to update the Paper egg.
:::
## Pufferpanel (Docker)
:::note
You will need **Admin** permissions on the panel to change your Java version.
:::
At the server you want to change the Java version for, go to the **Admin** tab and open
**Edit Server Definition**. In the **Environment** tab, the **Docker Image** field allows
you edit the docker image used.

In this case, you can simply replace the version (the number after the ":") with `25`.
You can find alternative JDK docker images on
[Docker Hub](https://hub.docker.com/search?badges=official).
## Verifying installation
:::note
This section does not apply when running on Pterodactyl or Pufferpanel.
:::
Now that you have installed Java 25, run this command in your terminal to ensure the process was
successful.
```bash
java -version
```
The output should be similar to this. The important parts to look out for is that it starts with
`openjdk 25` and contains `64-Bit` in the last line. If the output you get is similar to
`java: command not found`, try creating a new terminal session.
```
openjdk version "25" 2025-09-16
OpenJDK Runtime Environment (build 25+36-3489)
OpenJDK 64-Bit Server VM (build 25+36-3489, mixed mode, sharing)
```
If your installation has failed, do not hesitate to reach out in the `#paper-help` channel of our
[Discord](https://discord.gg/papermc) for support.
---
# Tools
import PageCards from "/src/components/PageCards.astro";
Collection of useful tools for server admins or plugin developers.
---
# Diff viewer
Multi-file rich diff viewer for GitHub and diff/patch files.
Multi-file rich diff viewer with support for GitHub commits, PRs, and comparisons, as well as
diff and patch files.
The GitHub diff viewer is a great tool for most use cases, but it has a variety of limitations,
especially when dealing with large diffs where the viewer will perform poorly or even crash.
The diffs.dev Diff Viewer aims to solve this problem while also providing a variety of additional
features useful for PaperMC and generally, such as special handling for second-order diffs. We have
also built a browser extension for Firefox and Chrome to streamline opening GitHub diffs in the
viewer.
- https://diffs.dev - The diff viewer itself
- [Chrome Extension](https://chromewebstore.google.com/detail/patch-roulette/feaaoepdocmiibjilhoahgldkaajfnhb)
- [Firefox Add-on](https://addons.mozilla.org/en-US/firefox/addon/patch-roulette/)
- https://github.com/PaperMC/diff-viewer - GitHub repository
---
# Item command converter
A tool to update commands from the old NBT-based item format to item components.
import ItemCommandConverter from "/src/components/tools/ItemCommandConverter.svelte";
In 1.20.5, Mojang has moved from unstructured NBT to the so-called item components. While Minecraft
will automatically update all existing items for you, only Paper will update commands and text
components containing items where possible. This tool helps you convert old item commands to the
new format externally.
Note that you need to select the *Entity Argument* mode if you want to convert entity data outside of
the summon command, as we require the entity type to convert data correctly.
---
# MiniMessage web editor
A web-based editor for creating and previewing MiniMessage-formatted text.
The MiniMessage Web Editor is a web-based editor for creating and previewing MiniMessage-formatted text.
It is hosted here: [MiniMessage Web Editor](https://webui.advntr.dev/).

---
# Start script generator
A start script generator for PaperMC projects.
import StartScriptGenerator from "/src/components/tools/StartScriptGenerator.svelte";
What flags do I choose?
If you're running on a modern JVM, i.e. 17, 21 or higher, try with no flags first.
Modern JVMs are very good at handling various applications with default GC settings.
If you have GC problems or are running an older version of Minecraft/an older version of Java,
try [Aikar's flags](/paper/aikars-flags), which are optimized specifically for Minecraft.
If you're generating a script for starting [Velocity](https://papermc.io/software/velocity), choose "Velocity".
---
# Paper Documentation
import { CardGrid, LinkCard } from "@astrojs/starlight/components";
Paper is a Minecraft: Java Edition game server, designed to greatly improve performance and offer more advanced features and API.
---
# Adding plugins
Plugins are the most powerful way to extend the functionality of Paper beyond the configuration files.
Plugins are the most powerful way to extend the functionality of Paper beyond the configuration
files. Functionality added by plugins can range from making milk restore hunger or dead bushes grow,
to adding entirely new and original game modes or items.
:::danger[Malicious Plugins]
Ensure you fully trust the source of any plugin before installing it. Plugins are given **full and
unrestricted** access to not only your server but also the machine that it runs on. Because of this,
it is imperative that plugins only be installed from trusted sources. Be careful!
:::
## Finding plugins
Before installing a plugin, you'll need to find what you want to install. The best place to find
plugins is [Hangar](https://hangar.papermc.io), Paper's plugin repository, but you can also find
plugins on [Modrinth](https://modrinth.com/discover/plugins?g=categories:paper) or
[BukkitDev](https://dev.bukkit.org/bukkit-plugins). Many plugins may release on [GitHub](https://github.com).
Instead of checking each site directly, you can also use a search engine. Searching for the function you
desire, followed by `Paper plugin` will often yield good results.
## Installing plugins
1. Once you've found the plugin you'd like to install, download it. Ensure the file you have
downloaded ends in `.jar`. Some plugins also distribute as `.zip` files, in which case you will
need to extract the file and locate the `.jar` for your platform, often labeled `bukkit` or
`paper`.
2. Once you have the plugin downloaded locally, locate the `plugins` folder from the root directory
of your Paper server.
3. Drag and drop the plugin file (`.jar`) into the `plugins` folder. If you are using a shared
hosting service, you may need to use their web panel or SFTP to upload the plugin; however, the
procedure will be the same.
4. Restart your server. The plugin should load.
5. Check your work. Once the server has finished loading, run the `/plugins` command in-game or type
`plugins` into the console. You should see your freshly installed plugin listed in green. If it
is not listed or is colored red, continue to [troubleshooting](#troubleshooting). A plugin listed
in red means that it is not currently enabled. For a freshly installed plugin, this often means
that the plugin failed to load.
## Troubleshooting
### Check your logs
The first step to troubleshooting installing plugins is to check the log of your server. Your
server's most recent logs will be stored to the `logs/latest.log` file. You may need to scroll near
the beginning of this file to see when plugins were loaded.
#### Missing dependencies
If you see something like this:
```log
[00:00:00] [Server thread/WARN] Could not load 'plugins/MyAwesomePlugin-1.0.0.jar' in folder 'plugins'
[00:00:00] [Server thread/WARN] org.bukkit.plugin.UnknownDependencyException: Unknown/missing dependency plugins: [Vault]. Please download and install these plugins to run 'MyAwesomePlugin'.
```
This means that the plugin you tried to install is missing a dependency. A dependency, in this case,
is another plugin that you must install for the first to function. While you will get a big scary
error, the important line to look at is:
```log
[00:00:00] [Server thread/WARN] Unknown/missing dependency plugins: [Vault]. Please download and install these plugins to run 'MyAwesomePlugin'.
```
This is telling you that in order to load `MyAwesomePlugin`, you must first install `Vault`.
#### Invalid `plugin.yml`
If you see something closer to this:
```log
[00:00:00] [Server thread/WARN] Could not load 'plugins/MyAwesomePlugin-1.0.0.jar' in folder 'plugins'
[00:00:00] [Server thread/WARN] org.bukkit.plugin.InvalidDescriptionException: Invalid plugin.yml
```
This means that what you have downloaded isn't a valid Paper plugin. This is generally caused by one
of the following:
1. The plugin you downloaded isn't a plugin at all, but instead a mod for Forge, Fabric, or similar.
These will not run on Paper.
2. The plugin failed to download completely. Especially when using tools such as `curl` or `wget`,
you can easily download error pages rather than the plugin you intended. This may also be caused
by a network issue. Attempt to download the plugin again. If you are using FTP (not SFTP or a web
panel) to upload your plugin to a shared hosting service, ensure your FTP client is in `binary`
and not `ASCII` mode. Consult the documentation for your FTP client for details.
#### Ambiguous plugin name
If you see something like this:
```log
[00:00:00] [Server thread/WARN] Ambiguous plugin name `Essentials' for files `plugins/EssentialsX-2.19.4.jar' and `plugins/Essentialsx-2.20.0-dev.jar' in `plugins'
```
This means you have two plugins with the same name, which is not supported. In this case, two
versions of EssentialsX are installed. Both the release `2.19.4`, and a development build of
`2.20.0`. Ensure you only have one version of each plugin installed at one time. Delete the older
version of the duplicate plugin, and restart your server.
To prevent accidentally installing two versions of one plugin while updating, you can use
the `update` folder as described in the [Update Guide](/paper/updating#step-2-update-plugins).
#### Something else
If you see an error, but it isn't similar to one of the above, attempt to read it yourself. While
the full error may be large and scary, you likely only have to read the first one or two lines to
understand what is going on. If you're not sure, do not hesitate to reach out for support on our
[Discord](https://discord.gg/papermc) in the `#paper-help` channel.
### If nothing is logged
If nothing is logged, your server is likely not attempting to load any plugins. The conditions
needed for the server to load a plugin are as follows:
1. The file is at the root of the `plugins` folder, relative to its working directory. This is
usually the same folder as the server JAR file. **Subdirectories of the `plugins` folder will not
be checked.** All plugins must be in the root folder.
2. The file ends in `.jar`. If your plugin does not end in `.jar`, what you have downloaded may not
be a plugin. Note that some plugins distribute multiple JARs as `.zip` files. If this is the
case, you have to extract them before installing the plugin.
If both of these are true, and you still see no logs, please reach out for support on our
[Discord](https://discord.gg/papermc) server in the `#paper-help` channel. We will be happy to
assist you.
---
# Administration
import PageCards from "/src/components/PageCards.astro";
Welcome to the Paper administration guide! This guide includes information and tutorials
regarding the administration of a Paper server.
#### Getting started
#### How-to guides
#### Reference
#### Miscellaneous
---
# Getting started
import PageCards from "/src/components/PageCards.astro";
Guides for getting started with running a Paper server.
---
# How-to guides
import PageCards from "/src/components/PageCards.astro";
How-to guides for setting up specific features of Paper and managing the server.
---
# Miscellaneous
import PageCards from "/src/components/PageCards.astro";
Miscellaneous documentation pertaining to Paper.
---
# Reference
import PageCards from "/src/components/PageCards.astro";
Reference of Paper's configuration options, system properties and other features.
---
# Aikar's flags
Aikar's flags are a set of JVM flags designed to improve the performance of your Paper server.
## Recommended JVM startup flags
:::caution[Script Generator]
**This page only serves as an explanation page.** If you want to generate a start script, please visit
our **[Script Generator](/misc/tools/start-script-gen)**.
:::
```bash
java -Xms10G -Xmx10G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar paper.jar --nogui
```
:::danger[Do not allocate all of your available memory on a shared host!]
When setting the `Xms` and `Xmx` values, if your host says you have 8GB of memory, **do not use 8GB**!
Minecraft (and Java) needs additional memory on top of that `Xmx` parameter. It is recommended to
**reduce your `Xmx` and `Xms` by about 1000-1500MB** to avoid running out of memory or `OOMKiller` killing
your server. This also leaves room for the operating system to use memory too.
Do you have 8GB of memory? Use 6500MB for safety.
_But you may also ask your host if they will cover this overhead for you and
give you 9500M instead. Some hosts will! Just ask._
:::
## Recommended memory
**We recommend using at least 6-10GB**, no matter how few players! If you can't afford 10GB of
memory, give as much as you can, but ensure you leave the operating system some memory too. G1GC
operates better with more memory.
However, more memory does not mean better performance above a certain point. Eventually you will hit
a point of diminishing returns. Going out and getting 32GB of RAM for a server will only waste your
money with minimal returns.
## Java GC logging
Are you having old gen issues with these flags? Add the following flags based on your Java version
to enable GC logging:
**Java 8-10**
```bash
-Xloggc:gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=1M
```
**Java 11+**
```bash
-Xlog:gc*:logs/gc.log:time,uptime:filecount=5,filesize=1M
```
GC logging does not hurt your performance and can be left on at all times. The files will not take
up much space (5MB)
## Technical explanation of the flags
1. **-Xms matching -Xmx - why:** You should never run your server with the case that `Xmx` can run
the system completely out of memory. Your server should always be expected to use the entire
`Xmx`! You should then ensure the OS has extra memory on top of that `Xmx` for non-Minecraft/OS level
things. Therefore, you should never run Minecraft with `Xmx` settings you can't support if Java uses it
all. Now, that means **if `Xms` is lower than `Xmx` you have unused memory**! Unused memory is
wasted memory. G1 operates better with the more memory it's given. G1 adaptively chooses how
much memory to give to each region to optimize pause time. If you have more memory than it needs
to reach an optimal pause time, G1 will simply push that extra into the old generation, and it
will not hurt you. The fundamental idea of improving GC behavior is to ensure short-lived objects
die young and never get promoted. With the more memory G1 has, the better assurance you will get
that objects are not getting prematurely promoted to the old generation. G1 operates differently
than previous collectors and is able to handle larger heaps more efficiently.
If it does not need the memory given to it, it will not use it. The entire engine operates
differently and does not suffer from too large of heaps, and this is industry-wide accepted
information that under G1 to keep Xms and Xmx the same!
2. **UnlockExperimentalVMOptions:** needed for some the below options
3. **G1NewSizePercent:** These are the important ones. You now can specify percentages of an
overall desired range for the new generation. With these settings, we tell G1 to not use its
default 5% for new gen, and instead give it 40%! **Minecraft has an extremely high memory
allocation rate**, ranging to at least 800MB/second on a 30 player server! And this is
mostly short-lived objects (Block Position).
Now, this means Minecraft **really** needs more focus on new gen to be able to even support this
allocation rate. If your new gen is too small, you will be running new gen collections 1-2+
times per second, which is awful. You will have so many pauses that TPS has risk of suffering,
and the server will not be able to keep up with the cost of GCs. Then combine the fact that
objects will now promote faster, resulting in your old gen growing faster. Given more new gen,
we are able to slow down the intervals of young gen collections, resulting in more time for
short-lived objects to die young and overall more efficient GC behavior.
4. **G1MixedGCLiveThresholdPercent:** Controls when to include regions in mixed GCs in the young
GC collection, keeping old gen tidy without doing a normal old gen GC collection. When your
memory is less than this percent, old gen won't even be included in 'mixed' collections. Mixed
are not as heavy as a full old collection, so having small incremental cleanups of old keeps
memory usage light.
Default is 65 to 85 depending on the Java version, we are setting that to 90 to ensure we reclaim garbage
in old gen as fast as possible to retain as much free regions as we can.
5. **G1ReservePercent=20:** Minecraft memory allocation rate in up-to-date versions is really insane. We
run the risk of a dreaded "to-space exhaustion" not having enough memory free to move data
around. This ensures more memory is waiting to be used for this operation. Default is 10, so we
are giving another 10 to it.
6. **MaxTenuringThreshold=1:** Minecraft has a really high allocation rate of memory. Of that
memory, most is reclaimed in the eden generation. However, transient data will overflow into
survivor. Initially played with completely removing survivor and had decent results, but does
result in transient data making its way to old which is not good. Max Tenuring 1 ensures that we
do not promote transient data to old generation, but anything that survives 2 passes of GC is
just going to be assumed as longer-lived.
Doing this greatly reduces pause times in young collections as copying data up to 15 times in
survivor space for a tenured object really takes a lot of time for actually old memory. Ideally
the GC engine would track average age for objects instead and tenure out data faster, but that
is not how it works.
Considering average GC rate is 10s to the upwards of minutes per young collection, this does not
result in any 'garbage' being promoted, and just delays longer lived memory to be collected in
mixed GCs.
7. **SurvivorRatio=32:** Because we drastically reduced MaxTenuringThreshold, we will be reducing
use of survivor space drastically. This frees up more regions to be used by eden instead.
8. **AlwaysPreTouch:** AlwaysPreTouch gets the memory setup and reserved at process start ensuring
it is contiguous, improving the efficiency of it more. This improves the operating systems
memory access speed. Mandatory to use Transparent Huge Pages
9. **+DisableExplicitGC:** Many plugins think they know how to control memory, and try to invoke
garbage collection. Plugins that do this trigger a full garbage collection, triggering a massive
lag spike. This flag disables plugins from trying to do this, protecting you from their bad
code.
10. **MaxGCPauseMillis=200:** This setting controls how much memory is used in between the minimum
and maximum ranges specified for your new generation. This is a "goal" for how long you want
your server to pause for collections. 200 is aiming for at most loss of 4 ticks. This will
result in a short TPS drop, however the server can make up for this drop instantly, meaning it
will have no meaningful impact on your TPS. 200ms is lower than players can recognize. In
testing, having this value constrained to an even lower number results in G1 not recollecting
memory fast enough and potentially running out of old gen triggering a full collection. Just
because this number is 200 does not mean every collection will be 200. It means it can use up to
200 if it really needs it, and we need to let it do its job when there is memory to collect.
11. **+ParallelRefProcEnabled:** Optimizes the GC process to use multiple threads for weak reference
checking. Not sure why this isn't default...
12. **G1RSetUpdatingPauseTimePercent=5:** Default is 10% of time spent during pause updating RSets,
reduce this to 5% to make more of it concurrent to reduce pause durations.
13. **G1MixedGCCountTarget=4:** Default is 8. Because we are aiming to collect slower, with less old
gen usage, try to reclaim old gen memory faster to avoid running out of old.
14. **G1HeapRegionSize=8M+:** Default is auto calculated. Super important for Minecraft, especially
1.15, as with low memory situations, the default calculation will in most times be too low. Any
memory allocation half of this size (4MB) will be treated as "Humongous" and promote straight to
old generation and is harder to free. If you allow Java to use the default, you will be
destroyed with a significant chunk of your memory getting treated as Humongous.
15. **+PerfDisableSharedMem:** Causes GC to write to file system which can cause major latency if
disk IO is high - see https://www.evanjones.ca/jvm-mmap-pause.html
### Transparent huge pages
Controversial feature but may be usable if you cannot configure your host for real HugeTLBFS. Try
adding `-XX:+UseTransparentHugePages` but it's extremely important you also have `AlwaysPreTouch` set.
Otherwise, THP will likely hurt you. We have not measured how THP works for Minecraft or its impact with
`AlwaysPreTouch`, so this section is for the advanced users who want to experiment.
---
# Configuring Anti-Xray
Paper ships an obfuscation-based Anti-Xray system by default. Learn how to configure it here.
> Originally written and maintained by [stonar96](https://github.com/stonar96).
Paper includes an obfuscation-based Anti-Xray with three modes, configurable on a per world basis.
:::note[Per World Configuration]
If you aren't already familiar with per world configuration, please take a moment to familiarize
yourself with the [Configuration Guide](/paper/reference/configuration).
:::
This guide is a step-by-step walk-through for configuring Anti-Xray. For reference documentation,
refer to the Anti-Xray section of the
[Per-World Configuration Reference](/paper/reference/world-configuration#anticheat_anti_xray).
Anti-Xray has three different modes. `engine-mode: 1` replaces specified blocks (`hidden-blocks`) with
other "fake" blocks, `stone` (`deepslate` at y < 0), `netherrack`, or `end_stone` based on the
dimension. In contrast, `engine-mode: 2` will replace both `hidden-blocks` and `replacement-blocks`
with randomly generated `hidden-blocks`. `engine-mode: 3` works similarly to `engine-mode: 2`, but instead of
randomizing every block, it randomizes the block for each layer of a chunk.
The following images[^1] show how each mode will look for a player using Xray with the recommended
configuration in both the overworld and nether.
[^1]:
Image design by `Oberfail`, initially posted in the
[PaperMC Discord](https://discord.gg/papermc).
{/*
Seed: -7943468717341609647
# Overworld:
/tp @p -581.976 67.85076 -4924.106 47 36
# Nether:
/tp @p 789.437 117.38012 -319.064 -137.4 28
*/}


Especially on the client side, `engine-mode: 1` is much less computationally intensive, while
`engine-mode: 2` may better prevent Xray. With `engine-mode: 1`, only ores that are entirely covered
by solid blocks will be hidden. Ores exposed to air in caves or water from a lake will not be
hidden. With `engine-mode: 2`, fake ores obstruct the view of real blocks. If `air` is added to
`hidden-blocks`, `engine-mode: 2` will effectively hide all ores, even those exposed to air. `engine-mode: 3` can reduce network load when joining by a factor of ~2 and helps with chunk packet compression.
:::caution[Anti-Xray Bypasses]
**Range Extension**: While Anti-Xray alone will prevent the majority of users from Xraying on your
server, it is not by any means infallible. Because of how Anti-Xray is (and has to be) implemented,
it is possible to, on a default server, extend the range of real ores you can see by a not
insignificant amount. This can be mitigated by any competent anti-cheat plugin; however, this is not
included out of the box.
**Seed Reversing**: Another attack vector is the deterministic nature of Minecraft's world
generation. If the client is able to obtain the world seed, it is able to know the real location of
every generated ore, completely bypassing Anti-Xray. This can be partially worked around by making
it harder for the client to reverse the world seed with the
[`feature-seeds` configuration](/paper/reference/world-configuration#feature_seeds), in conjunction
with the structure seed options in `spigot.yml`. Note that this is not a complete solution, and it
may still be possible for a client to obtain the server's world seed. Using a different seed for
each world may also be beneficial.
**Ores Exposed to Air**: In `engine-mode: 1`, `engine-mode: 2` and `engine-mode: 3`, it is possible for a client
to view ores that are exposed to air. This can be mitigated in `engine-mode: 2` and `engine-mode: 3` by adding `air` to
the `hidden-blocks` list. However, doing this may cause client performance issues (FPS drops) for
some players.
:::
## Recommended configuration
The recommended configuration for `engine-mode: 1`, `engine-mode: 2` and `engine-mode: 3` is as follows:
:::tip[Spacing]
YAML cares about whitespace! The example configuration below is already formatted correctly. Ensure
formatting and indentation remains unchanged by using the "copy" button in the top right of each
example. Especially ensure that no tabulators are accidentally inserted. Check your editor's options
for using spaces instead of tabulators for indentation. If your configuration file already contains
other important changes, it is recommended to make a backup before editing it.
:::
### `engine-mode: 1`
Default World Configuration
Replace the existing `anticheat.anti-xray` block in `paper-world-defaults.yml` with the following:
```yaml title="paper-world-defaults.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 1
hidden-blocks:
# There's no chance to hide dungeon chests as they are entirely surrounded by air, but buried treasures will be hidden.
- chest
- coal_ore
- deepslate_coal_ore
- copper_ore
- deepslate_copper_ore
- raw_copper_block
- diamond_ore
- deepslate_diamond_ore
- emerald_ore
- deepslate_emerald_ore
- gold_ore
- deepslate_gold_ore
- iron_ore
- deepslate_iron_ore
- raw_iron_block
- lapis_ore
- deepslate_lapis_ore
- redstone_ore
- deepslate_redstone_ore
lava-obscures: false
# As of 1.18 some ores are generated much higher.
# Please adjust the max-block-height setting at your own discretion.
# https://minecraft.wiki/w/Ore might be helpful.
max-block-height: 64
# The replacement-blocks list is not used in engine-mode: 1. Changing this will have no effect.
replacement-blocks: []
update-radius: 2
use-permission: false
```
Nether Configuration
Copy and paste into your `paper-world.yml` within your nether world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_nether/paper-world.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 1
hidden-blocks:
- ancient_debris
- nether_gold_ore
- nether_quartz_ore
lava-obscures: false
max-block-height: 128
# The replacement-blocks list is not used in engine-mode: 1. Changing this will have no effect.
replacement-blocks: []
update-radius: 2
use-permission: false
```
End Configuration
Copy and paste into your `paper-world.yml` within your end world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_end/paper-world.yml"
anticheat:
anti-xray:
enabled: false
```
### `engine-mode: 2`
Default World Configuration
Replace the existing `anticheat.anti-xray` block in `paper-world-defaults.yml` with the following:
```yaml title="paper-world-defaults.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 2
hidden-blocks:
# You can add air here such that many holes are generated.
# This works well against cave finders but may cause client FPS drops for all players.
- air
- copper_ore
- deepslate_copper_ore
- raw_copper_block
- diamond_ore
- deepslate_diamond_ore
- gold_ore
- deepslate_gold_ore
- iron_ore
- deepslate_iron_ore
- raw_iron_block
- lapis_ore
- deepslate_lapis_ore
- redstone_ore
- deepslate_redstone_ore
lava-obscures: false
# As of 1.18 some ores are generated much higher.
# Please adjust the max-block-height setting at your own discretion.
# https://minecraft.wiki/w/Ore might be helpful.
max-block-height: 64
replacement-blocks:
# Chest is a tile entity and can't be added to hidden-blocks in engine-mode: 2.
# But adding chest here will hide buried treasures, if max-block-height is increased.
- chest
- amethyst_block
- andesite
- budding_amethyst
- calcite
- coal_ore
- deepslate_coal_ore
- deepslate
- diorite
- dirt
- emerald_ore
- deepslate_emerald_ore
- granite
- gravel
- oak_planks
- smooth_basalt
- stone
- tuff
update-radius: 2
use-permission: false
```
Nether Configuration
Copy and paste into your `paper-world.yml` within your nether world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_nether/paper-world.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 2
hidden-blocks:
# See note about air and possible client performance issues above.
- air
- ancient_debris
- bone_block
- glowstone
- magma_block
- nether_bricks
- nether_gold_ore
- nether_quartz_ore
- polished_blackstone_bricks
lava-obscures: false
max-block-height: 128
replacement-blocks:
- basalt
- blackstone
- gravel
- netherrack
- soul_sand
- soul_soil
update-radius: 2
use-permission: false
```
End Configuration
Copy and paste into your `paper-world.yml` within your end world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_end/paper-world.yml"
anticheat:
anti-xray:
enabled: false
```
### `engine-mode: 3`
Default World Configuration
Replace the existing `anticheat.anti-xray` block in `paper-world-defaults.yml` with the following:
```yaml title="paper-world-defaults.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 3
hidden-blocks:
# You can add air here such that many holes are generated.
# This works well against cave finders but may cause client FPS drops for all players.
- air
- copper_ore
- deepslate_copper_ore
- raw_copper_block
- diamond_ore
- deepslate_diamond_ore
- gold_ore
- deepslate_gold_ore
- iron_ore
- deepslate_iron_ore
- raw_iron_block
- lapis_ore
- deepslate_lapis_ore
- redstone_ore
- deepslate_redstone_ore
lava-obscures: false
# As of 1.18 some ores are generated much higher.
# Please adjust the max-block-height setting at your own discretion.
# https://minecraft.wiki/w/Ore might be helpful.
max-block-height: 64
replacement-blocks:
# Chest is a tile entity and can't be added to hidden-blocks in engine-mode: 2.
# But adding chest here will hide buried treasures, if max-block-height is increased.
- chest
- amethyst_block
- andesite
- budding_amethyst
- calcite
- coal_ore
- deepslate_coal_ore
- deepslate
- diorite
- dirt
- emerald_ore
- deepslate_emerald_ore
- granite
- gravel
- oak_planks
- smooth_basalt
- stone
- tuff
update-radius: 2
use-permission: false
```
Nether Configuration
Copy and paste into your `paper-world.yml` within your nether world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_nether/paper-world.yml"
anticheat:
anti-xray:
enabled: true
engine-mode: 3
hidden-blocks:
# See note about air and possible client performance issues above.
- air
- ancient_debris
- bone_block
- glowstone
- magma_block
- nether_bricks
- nether_gold_ore
- nether_quartz_ore
- polished_blackstone_bricks
lava-obscures: false
max-block-height: 128
replacement-blocks:
- basalt
- blackstone
- gravel
- netherrack
- soul_sand
- soul_soil
update-radius: 2
use-permission: false
```
End Configuration
Copy and paste into your `paper-world.yml` within your end world folder. See the
[Configuration Guide](/paper/reference/configuration) for more information.
```yml title="world/dimensions/minecraft/the_end/paper-world.yml"
anticheat:
anti-xray:
enabled: false
```
## FAQ, common pitfalls and support
I can still see (some) ores / use X-ray
As described above, there are several reasons why you might still see (some) ores even though you
have enabled Anti-Xray:
* The ores are above the configured `max-block-height` value.
* Anti-Xray cannot hide ores exposed to air or other transparent blocks (in caves for example). In
principle this is also the case for `engine-mode: 2` and `engine-mode: 3`, however, usually the fake ores obstruct the
view of real blocks. Hiding those exposed ores too requires additional plugins.
* The `use-permission` option is enabled and you have the Anti-Xray bypass permission (`paper.antixray.bypass`) or you have
operator status.
* The block type is missing in the configured block lists. This can be the result of using an
outdated configuration file.
I have added fake blocks but X-ray doesn't show them
If you use `engine-mode: 2` or `engine-mode: 3` and you have added fake blocks to the `hidden-blocks` list but you can't
see them in-game using X-ray, this can have the following reasons:
* The added block types are tile entities. Anti-Xray can hide (replace) tile entities (such as
chests), provided that they are not exposed to air or other transparent blocks. However, Anti-Xray
can't place tile entities as fake blocks into the chunk.
* The block is disabled in your client's X-ray mod or not shown by your X-ray resource pack.
It doesn't work below y = 0 or in certain other places.
* Your configuration file is probably outdated and missing important blocks in the
`replacement-blocks` list, such as `deepslate` or biome-specific blocks, such as `basalt`. You
might also want to check if the `hidden-blocks` list includes all important ores and their
`deepslate` variants.
* If it doesn't work above a certain y-level, check your `max-block-height` setting.
It still doesn't work, further troubleshooting
* Make sure to always restart your server after making changes to the Anti-Xray configuration.
Changes won't be applied automatically.
* Do not use the `/reload` command. To apply Anti-Xray configuration changes a restart is required.
* After restarting the server, verify that the configuration is applied correctly by inspecting the
config sections with timings or spark.
How and where do I ask for support if it still doesn't work?
If the above bullet points don't solve your problem or if you have further questions about
Anti-Xray, please don't hesitate to ask us on the [PaperMC Discord](https://discord.gg/papermc)
using the #paper-help channel. Please try to provide as much detail as possible about your problem.
"It doesn't work" isn't very helpful when asking for support. Describe what you want to achieve,
what you have tried, what you expect and what you observe. Ideally include a timings or spark link
and a picture what you observe in-game.
---
# Basic troubleshooting
This guide will help you diagnose your server's problem before reporting it to PaperMC or the plugin's author.
This guide will help you diagnose your server's problem before reporting it to PaperMC or the plugin's author.
:::caution[Stop Your Server And Take A Backup]
Before following this guide, stop your server first. Modifying server files while it is still running will corrupt them. Only a full server shutdown can prevent this.
Also, if you don't follow this guide carefully or make a mistake while following it, you might corrupt your server. It is highly advised to back up your server before following this guide. Archiving your server folder as a .zip is good enough, or if you prefer, use backup software such as [borg](https://www.borgbackup.org/) or [kopia](https://kopia.io/). It would be ideal to create a test server by copying your production server's file, but that's not always possible.
:::
If your server encounters a problem, it will either print an error message on the server console, create a crash report and close itself, or do both.
If your server crashes, the crash report will be saved in the crash-report directory. If your server didn't crash, those error messages will be stored in the log directory along with other messages.
Note that the logs older than the latest will be compressed and not stored as plain text files.
The first thing you have to do is diagnose those messages.
Almost every problem you encounter will print error message lines, which are called a "stack trace", on the server console. Examining the stack trace will help you find out what is causing problems on your server.
The stack trace starts with the error message, exception type, and exception message.
Both error messages and exception messages were put there by the developer of either your plugin or Paper. These messages tell you what problem your server experienced. An exception type like `java.lang.RuntimeException` tells you the type of the exception. This will help the developer (and you) understand the type of problem. A good starting point is to search the exception type and message in the [Paper Discord](https://discord.gg/papermc).
Many lines beginning with `at` may appear beneath the exception message. These are the body of the stack trace. These lines tell you where the problem starts. The top line of the body of the stack trace will tell you exactly where the problem occurred and, if possible, display where it came from.
Issues are often plugin-induced, and are the first possible thing you should check.
# Common issues
## Plugin-induced issues
If you find any plugin's name in a stack trace in your logs, head to [Check Plugin Updates](#check-plugin-updates) and read from there. In most cases, the plugin, whose name is found on the stack trace, is causing the problem.
You can disable all of your plugins by renaming the plugins directory to something else, such as plugins-disabled, or by archiving the plugins directory and deleting it.
After that, try to run your server.
If the problem is resolved after removing the plugins, you know that it was a plugin that caused the issue.
### Binary search
In case you've determined a plugin is causing issues but cannot narrow it down, try a binary search.
1. Split your plugins into two groups. The size of the two groups can be different, but it is ideal if the difference is minimal. Make sure that plugins that depend on each other are grouped together.
2. Disable one of the two groups of plugins. You can disable them by changing their extension from .jar to something else, such as .jar-disabled, or move them outside the plugins directory and into a temporary directory.
3. Run your server and check if the problem still exists. If the problem is resolved, the plugin that caused the issue is one of the disabled plugins. If the problem is not resolved, the plugin that is causing the issue is one of the active plugins.
4. Repeat from the start with the suspect plugin group.
Repeat the steps above with groups that have the plugin that is causing the issue.
:::caution[Library Plugin Dependencies]
Some plugins that you install are not a typical plugin, but a library. These are installed like plugins, however tend to offer few user-facing features and are relied upon by other plugins for their functionality. If you disable a library, plugins that depend on it will not work properly. Common examples of these libraries are ProtocolLib, Vault providers, permission plugins, etc.
:::
### Check plugin updates
There is a chance that your problem is already fixed in the latest release or latest build of the plugin.
Head to your plugin's official download page and check if you are using the latest build or the latest release of the plugin. If not, update it to the latest version and try to run your server again to see if the problem is resolved.
### Update library plugins
Many plugins use library plugins like ProtocolLib, and you have to download them and put them in the plugins directory.
If you don't update them to the latest version or latest build, you might experience problems related to plugins that use the library plugin.
Some library plugins tell their users to use their latest development build for support of the latest Minecraft version. You should look carefully at the requirements of your plugin.
### Check documentation
If you misconfigured your plugin or your server, it can also cause problems on your server. Many plugins provide their own documentation about how to set them up properly. Read those documents carefully and check if there is something wrong with the plugin's configuration.
If your problem is related to a plugin you use, and you still don't know how to solve it, you can try to reach out to the plugin's author. Many plugins have a way to contact their author, like a GitHub issue tracker, Discord support guild, Gitter, IRC, etc.
Below, we list other issues that may happen when running a server.
## Server does not start
When this happens, always check your `latest.log` file in your `logs` folder, you may find your issue listed here. If logs are not generating, check your startup script, as described below:
### Checking your startup script
The recommended way to start a server is via a startup script, that you can generate [here](/misc/tools/start-script-gen). Don't double click the .jar!
If you're on Windows and your terminal disappears quickly after you run, make sure there's a line at the end of the file containing just `pause`.
In case you get an error similar to `Error: Unable to access jarfile server.jar`, make sure that the .jar name in your startup script is the same as the file you downloaded. Note that Windows, by default, hides extensions, so you may need re-enable that in the Folder and Search Options in the file explorer to see the correct name of the file, extension included.
### Failed to bind to port
This may happen in two cases:
1. A server is already running, check your task manager app for Java processes.
2. `server-ip`, in `server.properties`, is configured incorrectly. Note that this option is not a placeholder for your external IP, it controls which network interfaces your server will bind to. Most of the time, it should be left empty.
### Attempted to load chunk saved with newer version
{/* spellchecker:off */}
```
java.lang.RuntimeException: Server attempted to load chunk saved with newer version of minecraft! 3955 > 3465
[18:23:38 WARN]: at net.minecraft.world.level.chunk.storage.ChunkRegionLoader.loadChunk(ChunkRegionLoader.java:149)
[18:23:38 WARN]: at io.papermc.paper.chunk.system.scheduling.ChunkLoadTask$ChunkDataLoadTask.runOffMain(ChunkLoadTask.java:338)
[18:23:38 WARN]: at io.papermc.paper.chunk.system.scheduling.GenericDataLoadTask$ProcessOffMainTask.run(GenericDataLoadTask.java:307)
[18:23:38 WARN]: at ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue$PrioritisedTask.executeInternal(PrioritisedThreadedTaskQueue.java:351)
[18:23:38 WARN]: at ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue.executeTask(PrioritisedThreadedTaskQueue.java:118)
[18:23:38 WARN]: at ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool$PrioritisedThread.pollTasks(PrioritisedThreadPool.java:274)
[18:23:38 WARN]: at ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread.run(PrioritisedQueueExecutorThread.java:50)
```
{/* spellchecker:on */}
That error means that your world was created or opened in a server version that's newer than one you're currently running. Downgrading your world is not supported, so make sure to use the latest supported version of Paper. Even if you haven't joined the server, by loading your world in a newer version, it is upgraded automatically.
:::danger[Forcing the server to try to load a newer world]
The server will start if you use the `-DPaper.ignoreWorldDataVersion=true` flag. However, this is **highly not recommended, completely unsupported and may permanently corrupt your world**. If you're going to attempt this, take a backup.
:::
### Circular plugin loading
```
[15:01:04] [Server thread/ERROR]: [SimpleProviderStorage] Circular plugin loading detected!
[15:01:04] [Server thread/ERROR]: [SimpleProviderStorage] Circular load order:
[15:01:04] [Server thread/ERROR]: [SimpleProviderStorage] Plugin1 -> Plugin2 -> Plugin3 -> Plugin4 -> Plugin1
[15:01:04] [Server thread/ERROR]: [SimpleProviderStorage] Please report this to the plugin authors of the first plugin of each loop or join the PaperMC Discord server for further help.
[15:01:04] [Server thread/ERROR]: [SimpleProviderStorage] If you would like to still load these plugins, acknowledging that there may be unexpected plugin loading issues, run the server with -Dpaper.useLegacyPluginLoading=true
```
That means your plugins are configured in a way such that they want to start before (or after) each other, which is impossible -- one has to go first. Plugins usually have reasons to want to start before each other, so when such a conflict happens, rather than picking randomly and risking issues, the server warns you about the issue and shuts down.
There's often a problematic plugin involved, and to solve this, it's preferable that you report the issue to its authors. Removing it should also fix the issue. As a last resort, you can use the `-Dpaper.useLegacyPluginLoading=true` startup flag, but it may cause hard to debug issues.
### Outdated version of Java
```
Exception in thread "ServerMain" java.lang.UnsupportedClassVersionError: org/bukkit/craftbukkit/Main has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at io.papermc.paperclip.Paperclip.lambda$main$0(Paperclip.java:38)
at java.base/java.lang.Thread.run(Thread.java:842)
```
Your version of Java is outdated, check [our guide on updating it](/misc/java-install). To avoid possibly having to do more tweaks, uninstall your current version of Java, if any.
If you do have the correct version installed, your operating system may be not picking it up. Make sure you've closed and opened your terminal after installing it, and that Java is present in your `PATH` environment variable.
## Server crashes or exits unexpectedly
:::caution[Update!]
Always keep your server up to date (and take a backup before updating). Older versions are known to have on-demand crashes that can be triggered by players at any time.
:::
### Unexpected graceful shutdown
If your server shuts down normally as if you typed `/stop` or pressed a stop button in your panel, enable `debug` in `server.properties`. The next time the server shuts down, you will get a stack trace that will help you debug.
### Watchdog dump ("DO NOT REPORT THIS TO PAPER")
```
[02:04:00] [Paper Watchdog Thread/ERROR]: --- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - 1.21.3-66-afb5b13 (MC: 1.21.3) ---
[02:04:00] [Paper Watchdog Thread/ERROR]: The server has not responded for 10 seconds! Creating thread dump
[02:04:00] [Paper Watchdog Thread/ERROR]: ------------------------------
[02:04:00] [Paper Watchdog Thread/ERROR]: Server thread dump (Look for plugins here before reporting to Paper!):
[02:04:00] [Paper Watchdog Thread/ERROR]: ------------------------------
[02:04:00] [Paper Watchdog Thread/ERROR]: Current Thread: Server thread
[02:04:00] [Paper Watchdog Thread/ERROR]: PID: 129 | Suspended: false | Native: true | State: RUNNABLE
[02:04:00] [Paper Watchdog Thread/ERROR]: Stack:
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.UnixFileDispatcherImpl.write0(Native Method)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.UnixFileDispatcherImpl.write(UnixFileDispatcherImpl.java:65)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:137)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.IOUtil.write(IOUtil.java:102)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.IOUtil.write(IOUtil.java:72)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:300)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.ChannelOutputStream.writeFully(ChannelOutputStream.java:68)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/sun.nio.ch.ChannelOutputStream.write(ChannelOutputStream.java:105)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:125)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/java.io.BufferedOutputStream.implFlush(BufferedOutputStream.java:252)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:240)
[02:04:00] [Paper Watchdog Thread/ERROR]: java.base@21.0.5/java.io.FilterOutputStream.close(FilterOutputStream.java:184)
```
That message shows up when your server is taking very long (10+ seconds) to finish the current tick -- it's not a bug or crash, it's simply warning you of severe lag, that can eventually lead to a crash.
A good rule of thumb is checking the first lines of the stack trace, as that shows where the main thread was stuck at the time it was printed. Many times, that points to the root cause of the issue.
However, sometimes the issue may not be obvious or appear in the stack trace. If possible, analyze a [spark report](#spark-report) for more details, and, if you're in doubt about how to proceed, feel free to visit the [Paper Discord](https://discord.gg/papermc) for help.
### Crash without logs
If you have access to a Linux shell, run `dmesg -T | grep -i killed`. That should show how your server process was killed.
A common cause (but not the only one), if it was killed due to OOM, is that your server panel is configured with a memory limit that's too close to your `-Xmx`. Either reduce `-Xmx` (by 1-2GB is a good initial rule of thumb) or increase/disable the memory limits in your panel.
If you're using a hosting company that only provides you with a panel, you likely won't have the tools to get to the bottom of the problem. You should make a ticket with your host in this case.
## Performance and gameplay issues
Unfortunately, Paper can't replicate Vanilla behavior 100%, but it is a goal (except when it comes to exploits). If you're still experiencing a bug that cannot be reproduced in vanilla **multiplayer**, please apply a [Vanilla-like configuration](/paper/vanilla) and check if there's already an open issue in our GitHub. If not, feel free to create one.
### Strange entity/farm/redstone/spawning behavior
If you copied values of a pre-made configuration or optimization guide, now is a good time to revert the changes. Keep a copy of your current configs if you prefer, and delete the originals so they can re-generate to default values.
In case you're still experiencing such issues with default configurations, try our [Vanilla-like configurations](/paper/vanilla) but do note that this comes at a performance cost.
Also, keep in mind that singleplayer does not behave the same as multiplayer, both when it comes to spawning and certain entity behavior. For example, mobs despawn if they're over 128 blocks away from a player, and this becomes more apparent in multiplayer, especially if you're making farms where monsters go to another dimension via a nether portal. If there are players in the target dimension and they're all very far from the portal, the mob will instantly despawn -- this is intended Vanilla behavior.
### Dupes not working
Paper has some [unsupported settings](/paper/reference/global-configuration#unsupported_settings) that allow certain dupes. However, a few of them cannot be re-introduced because that would break other aspects of the server. Paper also will not re-add dupes that no longer exist in the game.
## Performance issues
### Spark report
Paper has the [spark](https://spark.lucko.me/) profiler built-in, in order to diagnose the root cause of performance issues.
For example, you can generate a report for 300 seconds by running `/spark profiler start --timeout 300`
If you want to diagnose lag spikes that last more than, for example, 100 ms, you can run `/spark profiler start --only-ticks-over 100 --timeout 300`
Look into the [spark docs](https://spark.lucko.me/docs) for a more in-depth guide on how to use it and read reports. If you're still unsure about what's the cause of your problem, you can send us the link via the [Paper Discord](https://discord.gg/papermc).
### High RAM usage
Unless you're experiencing out of memory crashes or bad garbage collection (GC) times, high memory usage is expected.
Java programs store objects in memory, in an area where we call the heap. The heap grows over time, you can set its initial value with `-Xms` and the maximum value with `-Xmx`.
It's normal that the Java process will use the RAM it's given, sometimes using a little more than `-Xmx`. This number won't go down over time, as the garbage collector (GC) rarely returns RAM to the operating system in servers. This is by itself not a problem, in fact it's beneficial: by having more heap memory to work with, GC will have to worry less about disposing of garbage, as that takes valuable processing time. This is not a memory leak and will not cause out of memory crashes, which are commonly caused by not leaving enough RAM for your OS or improperly configured memory limits in a container.
There are several different memory metrics that can be measured. For example, imagine a server in a 16GB container, running with `-Xms1G -Xmx14G`. In this case:
* **Container memory limit:** 16GB
* **Maximum heap:** 14GB
* **Current heap size:** Starts at 1GB and expands quickly. Will be between 1-14GB at any given point
* **Current heap usage:** will be smaller than the number above, and will grow and shrink in a sawtooth pattern under normal conditions
Keep in mind that different tools choose different metrics out of these to display, so the usage meter in your panel, at a glance, might not look like what a plugin will display.
### Low CPU usage
Paper is able to make use of multiple cores, but this does not necessarily mean that you will have several cores at near 100% usage. A major source of load in the server comes from the tick loop, which uses a single thread. Thus, at a certain point, more cores will not give you a performance benefit, so it's advisable to go for a CPU with high single-threaded performance and allocate a sufficient amount of threads to your server (at least 4). In servers with a high core count, this situation can translate to a low CPU usage relative to the total amount of cores.
However, if your server (especially if large) is really adamant on using only a single core, check your panel's CPU allocation setting. In certain panels, a number like `4` doesn't mean it'll use 4 cores, but instead that it will use the one core with ID 4.
---
# Contributing
import PageCards from "/src/components/PageCards.astro";
Welcome to the Paper contributing guide! This guide includes information and tutorials for developers
wishing to contribute to the Paper project.
---
# Events
A guide on how to add new events to Paper.
There are several requirements for events in the Paper API.
:::note
Note that while not all existing events may follow these
guidelines, all new and modified events should adhere to them.
:::
All new events should go in the package (sub-package of) `io.papermc.paper.event`.
### Constructors
All new constructors added should be annotated with
[`@ApiStatus.Internal`](https://javadoc.io/doc/org.jetbrains/annotations/latest/org/jetbrains/annotations/ApiStatus.Internal.html)
to signify that they are not considered API and can change at any time without warning.
Constructors that are being replaced, if they aren't being removed, should be marked with
[`@Deprecated`](jd:java:java.lang.Deprecated) and [`@DoNotUse`](jd:paper:io.papermc.paper.annotation.DoNotUse).
### Mutability
Certain API types are "mutable" which can lead to unexpected behavior within events. Mutable types like
[](jd:paper:org.bukkit.Location) and [](jd:paper:org.bukkit.util.Vector)
should therefore be cloned when returned from a "getter" in an event.
### `HandlerList`
For an event class or any subclass of it to be listened to, a [`HandlerList`](jd:paper:org.bukkit.event.HandlerList)
field must be present with an instance and static method to retrieve it.
See the docs for [](jd:paper:org.bukkit.event.Event) for specifics.
This field should be static and final and named `HANDLER_LIST`.
Also consider not putting a `HandlerList` on every event, just a "common parent" event so that a plugin can listen to the
parent event and capture any child events but also listen to the child event separately.
### Miscellaneous
* New parameters or method returns of type [](jd:paper:org.bukkit.inventory.ItemStack)
should not be [`@Nullable`](https://javadoc.io/doc/org.jspecify/jspecify/latest/org/jspecify/annotations/Nullable.html)
in most case and instead accept an empty itemStack.
---
# Development
import PageCards from "/src/components/PageCards.astro";
Welcome to the Paper development guide! This guide includes information and tutorials for developers
on how to create and expand on Paper plugins.
#### Getting started
#### API
#### Miscellaneous
---
# API
import PageCards from "/src/components/PageCards.astro";
Welcome to the Paper API guide!
This guide includes information for developers about how to use specific parts of the Paper API.
---
# Command API
import PageCards from "/src/components/PageCards.astro";
#### Basics
#### Arguments
#### Miscellaneous
---
# Arguments
import PageCards from "/src/components/PageCards.astro";
---
# Basics
import PageCards from "/src/components/PageCards.astro";
---
# Miscellaneous
import PageCards from "/src/components/PageCards.astro";
---
# Component API
import PageCards from "/src/components/PageCards.astro";
---
# Entity API
import PageCards from "/src/components/PageCards.astro";
---
# Event API
import PageCards from "/src/components/PageCards.astro";
---
# Inventories
import PageCards from "/src/components/PageCards.astro";
---
# Lifecycle API
import PageCards from "/src/components/PageCards.astro";
---
# Chat events
An outline on AsyncChatEvent and how to handle it.
The chat event has evolved a few times over the years.
This guide will explain how to properly use the new [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
and its [](jd:paper:io.papermc.paper.chat.ChatRenderer).
The [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
is an improved version of the old [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
that allows you to render chat messages individually for each player.
:::note[`AsyncChatEvent` vs `ChatEvent`]
The key difference between [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
and [](jd:paper:io.papermc.paper.event.player.ChatEvent) is that
[](jd:paper:io.papermc.paper.event.player.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 [](jd:paper:io.papermc.paper.event.player.ChatEvent).
However, we recommend using [`BukkitScheduler`](/paper/dev/scheduler).
:::
## Understanding the renderer
Before we can start using the new chat event, we need to understand how the new renderer works.
The renderer is Paper's way of allowing plugins to modify the chat message before it is sent to the player.
This is done by using the [](jd:paper:io.papermc.paper.chat.ChatRenderer) interface with its
[](jd:paper:io.papermc.paper.chat.ChatRenderer#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience))
method. Previously, this was done by using the [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
with its [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent#setFormat(java.lang.String)) method.
```java title="ChatRenderer.java"
Component render(Player source, Component sourceDisplayName, Component message, Audience viewer);
```
- The [`render`](jd:paper:io.papermc.paper.chat.ChatRenderer#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience)) method is called when a chat message is sent to the player.
- The `source` parameter is the player that sent the message.
- The `sourceDisplayName` parameter is the display name of the player that sent the message.
- The `message` parameter is the message that was sent.
- The `viewer` parameter is the player that is receiving the message.
:::tip[`ChatRenderer.ViewerUnaware`]
If your renderer does not need to know about the viewer, you can use the
[](jd:paper:io.papermc.paper.chat.ChatRenderer$ViewerUnaware)
interface instead of the [](jd:paper:io.papermc.paper.chat.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 [](jd:paper:io.papermc.paper.chat.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
The first way of using the renderer is by implementing the [](jd:paper:io.papermc.paper.chat.ChatRenderer)
interface in a class. In this example, we will be using our `ChatListener` class.
Next, we need to tell the event to use the renderer by using the
[](jd:paper:io.papermc.paper.event.player.AbstractChatEvent#renderer()) method.
```java title="ChatListener.java"
public class ChatListener implements Listener, ChatRenderer {
@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer(this); // Tell the event to use our renderer
}
@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 don't need to instantiate the class every time the event is called.
In this case, you can use [the singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) to create a single instance of the class.
:::
### Using a lambda expression
Another way of using the renderer is by using a lambda expression.
```java title="ChatListener.java"
public class ChatListener implements Listener {
@EventHandler
public void onChat(AsyncChatEvent event) {
event.renderer((source, sourceDisplayName, message, viewer) -> {
// ...
});
}
}
```
## Rendering the message
Now that we have our renderer, we can start rendering the message.
Let's say we want to render our chat to look like this:

To do this, we need to return a new [](jd:adventure:net.kyori.adventure.text.Component) that contains the message we want to send.
```java title="ChatListener.java"
public class ChatListener implements Listener, ChatRenderer {
// Event logic...
@Override
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
return sourceDisplayName
.append(Component.text(": "))
.append(message);
}
}
```
Now you can see that the message is rendered as we wanted it to be.
## Conclusion
That is all you need to know about the new chat event and its renderer.
Of course there are many more things you can do with components in general.
If you want to learn more about components, you can read the [Component Documentation](/adventure/text/).
---
# Adventure
Documentation for all arguments returning Adventure API objects.
import ComponentMp4 from "./assets/vanilla-arguments/component.mp4?url";
import KeyMp4 from "./assets/vanilla-arguments/key.mp4?url";
import NamedColorMp4 from "./assets/vanilla-arguments/namedcolor.mp4?url";
import StyleMp4 from "./assets/vanilla-arguments/style.mp4?url";
import SignedMessageMp4 from "./assets/vanilla-arguments/signedmessage.mp4?url";
import Video from "/src/components/Video.astro";
These arguments return a class from the `net.kyori` package. They are technically not native to Minecraft or Bukkit, but as Paper includes the Adventure library, they are
widely used in the Paper ecosystem.
## Component argument
:::note
This argument is very technical. Following the same format as the `/tellraw ` command for its second argument, it 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
Commands.argument("input", ArgumentTypes.component())
.executes(context -> {
final Component input = context.getArgument("input", Component.class);
context.getSource().getSender().sendRichMessage("Your message: ",
Placeholder.component("input", input)
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Key argument
The key argument allows a user to put in any artificial (namespaced) key, ensuring its validity. This returns a [](jd:adventure:net.kyori.adventure.key:net.kyori.adventure.key.Key),
which can be used at various other places in the Paper API.
### Example usage
```java
Commands.argument("key", ArgumentTypes.key())
.executes(context -> {
final Key key = context.getArgument("key", Key.class);
context.getSource().getSender().sendRichMessage("You put in !",
Placeholder.unparsed("key", key.asString())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Named color argument
This argument provides the user with the ability to select between the 16 built-in "named" text colors. This argument returns a
[](jd:adventure:net.kyori.adventure.text.format.NamedTextColor),
which you can use for applying a color to components.
### Example usage
```java
Commands.argument("color", ArgumentTypes.namedColor())
.then(Commands.argument("message", StringArgumentType.greedyString())
.executes(context -> {
final NamedTextColor color = context.getArgument("color", NamedTextColor.class);
final String msg = StringArgumentType.getString(context, "message");
context.getSource().getSender().sendMessage(
Component.text(msg).color(color)
);
return Command.SINGLE_SUCCESS;
})
)
```
### In-game preview
## Adventure style argument
:::note
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. Most users
do not know how to use that format and thus its general usage is not advised.
:::
The style argument returns its value in the form of a [](jd:adventure:net.kyori.adventure.text.format.Style) object.
This can be applied to any component using `Component#style(Style)`. Whilst the JSON input allows for the `text` field, its content is completely ignored.
### Example usage
```java
Commands.argument("style", ArgumentTypes.style())
.then(Commands.argument("message", StringArgumentType.greedyString())
.executes(context -> {
final Style style = context.getArgument("style", Style.class);
final String msg = StringArgumentType.getString(context, "message");
context.getSource().getSender().sendRichMessage("Your input: ",
Placeholder.component("input", Component.text(msg).style(style))
);
return Command.SINGLE_SUCCESS;
})
)
```
### In-game preview
## Signed message argument
The signed message argument allows a player to send an argument in the form of a **signed message** to the server. This signed message is a special type - it
allows the server to send that message, without the ability to directly modify it, to any player. The visible difference is that unsigned messages have a white bar at the left,
whilst signed messages don't.
A signed message argument returns a `SignedMessageResolver`. In order to call its `#resolve` method, you have to pass in two parameters:
* The argument name
* The `CommandContext` object
The resolved value is a `CompletableFuture`, whose [](jd:adventure:net.kyori.adventure.chat.SignedMessage)
value you can handle using `thenAccept(Consumer)`. Inside of the consumer, you can send the signed message to players or work with it in other ways.
:::caution
By default, the consumer passed into `thenAccept` is not executed on the main thread, making it unsafe to use most of Paper API within it.
If you need to use the API, you can schedule a task to be run on the next available tick. For this you can use the
[main thread executor](jd:paper:org.bukkit.scheduler.BukkitScheduler#getMainThreadExecutor(org.bukkit.plugin.Plugin)).
You can read up on that [here](/paper/dev/scheduler).
:::
:::note
A non-player sender is not capable of sending a signed message, which means that the resolved `CompletableFuture` will never be completed.
You should make sure that only players can use your argument with `.requires(source -> source.getSender() instanceof Player)`. You may
add a fallback greedy string argument for non-player senders if you want the argument to execute regardless of signing.
:::
### Example usage
```java
Commands.argument("target", ArgumentTypes.player())
.then(Commands.argument("message", ArgumentTypes.signedMessage())
.executes(context -> {
final Player target = context.getArgument("target", PlayerSelectorArgumentResolver.class).resolve(context.getSource()).getFirst();
final SignedMessageResolver resolver = context.getArgument("message", SignedMessageResolver.class);
resolver.resolveSignedMessage("message", context).thenAccept(msg -> {
target.sendMessage(msg, ChatType.CHAT.bind(context.getSource().getSender().name()));
});
return Command.SINGLE_SUCCESS;
})
)
```
### In-game preview
---
# Entities and players
Player and Entity arguments documentation.
import EntityOppedMp4 from "./assets/vanilla-arguments/entity-opped.mp4?url";
import EntityUnoppedMp4 from "./assets/vanilla-arguments/entity-unopped.mp4?url";
import EntitiesMp4 from "./assets/vanilla-arguments/entities.mp4?url";
import PlayerMp4 from "./assets/vanilla-arguments/player.mp4?url";
import PlayersMp4 from "./assets/vanilla-arguments/players.mp4?url";
import PlayerProfilesMp4 from "./assets/vanilla-arguments/playerprofiles.mp4?url";
import Video from "/src/components/Video.astro";
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.
## Entity argument
This argument, after resolving its returning `EntitySelectorArgumentResolver`, returns a list of exactly one, no more and no less, entity. It is safe
to call `List#getFirst()` to retrieve that entity. You can resolve it using [](jd:paper:io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver#resolve(io.papermc.paper.command.brigadier.CommandSourceStack)).
### Example usage
```java
Commands.argument("entity", ArgumentTypes.entity())
.executes(context -> {
final EntitySelectorArgumentResolver resolver = context.getArgument("entity", EntitySelectorArgumentResolver.class);
final Entity entity = resolver.resolve(context.getSource()).getFirst();
context.getSource().getSender().sendRichMessage("Found ",
Placeholder.component("entity_name", entity.teamDisplayName())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
If the executing player doesn't have the `minecraft.command.selector` permission:
If the executing player has the `minecraft.command.selector` permission:
## 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, once again, be resolved using
[](jd:paper:io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver#resolve(io.papermc.paper.command.brigadier.CommandSourceStack)),
which returns a `List`.
### Example usage
```java
Commands.argument("entities", ArgumentTypes.entities())
.executes(context -> {
final EntitySelectorArgumentResolver resolver = context.getArgument("entities", EntitySelectorArgumentResolver.class);
final List entities = resolver.resolve(context.getSource());
final Component entityNames = Component.join(JoinConfiguration.commas(true), entities.stream().map(Entity::teamDisplayName).toList());
context.getSource().getSender().sendRichMessage("Found ",
Placeholder.component("entity_names", entityNames)
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Player argument
The player argument allows to retrieve a `PlayerSelectorArgumentResolver`.
For this "single player" argument, you can safely get the target player by running `PlayerSelectorArgumentResolver.resolve(context.getSource()).getFirst()`,
which returns a [](jd:paper:org.bukkit.entity.Player) object.
### Example usage
This command yeets the targeted player into the air!
```java
Commands.argument("target", ArgumentTypes.player())
.executes(context -> {
final PlayerSelectorArgumentResolver resolver = context.getArgument("target", PlayerSelectorArgumentResolver.class);
final Player target = resolver.resolve(context.getSource()).getFirst();
target.setVelocity(new Vector(0, 3.9, 0));
target.sendRichMessage("Yeeeeeeeeeet");
context.getSource().getSender().sendRichMessage("Yeeted !",
Placeholder.component("target", target.displayName())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## 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(context.getSource())` returns a
`List`, which you can just iterate through.
### Example usage
Extending the "single player" yeet command to support multiple targets can look like this:
```java
Commands.argument("targets", ArgumentTypes.players())
.executes(context -> {
final PlayerSelectorArgumentResolver resolver = context.getArgument("targets", PlayerSelectorArgumentResolver.class);
final List targets = resolver.resolve(context.getSource());
final CommandSender sender = context.getSource().getSender();
for (final Player target : targets) {
target.setVelocity(new Vector(0, 3.9, 0));
target.sendRichMessage("Yeeeeeeeeeet");
sender.sendRichMessage("Yeeted !",
Placeholder.component("target", target.displayName())
);
}
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## 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`. 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. Due to this operation sometimes
taking a bit longer, 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. This is also visible in the in-game preview down below. This behavior is also the reason for `/whitelist add` sometimes.
### Example usage - player lookup command
```java
Commands.argument("profiles", ArgumentTypes.playerProfiles())
.executes(context -> {
final PlayerProfileListResolver resolver = context.getArgument("profiles", PlayerProfileListResolver.class);
final Collection profiles = resolver.resolve(context.getSource());
for (final PlayerProfile profile : profiles) {
context.getSource().getSender().sendPlainMessage("Found " + profile.getName());
}
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
---
# Enums
Documentation for EntityAnchor, GameMode and similar enum value arguments.
import EntityAnchorMp4 from "./assets/vanilla-arguments/entityanchor.mp4?url";
import GameModeMp4 from "./assets/vanilla-arguments/gamemode.mp4?url";
import HeightMapMp4 from "./assets/vanilla-arguments/heightmap.mp4?url";
import ScoreboardDisplaySlotMp4 from "./assets/vanilla-arguments/scoreboarddisplayslot.mp4?url";
import TemplateMirrorMp4 from "./assets/vanilla-arguments/templatemirror.mp4?url";
import TemplateRotationMp4 from "./assets/vanilla-arguments/templaterotation.mp4?url";
import Video from "/src/components/Video.astro";
## Entity anchor argument
The entity anchor argument has two valid inputs: `feet` and `eyes`. The resulting [](jd:paper:io.papermc.paper.entity.LookAnchor) is mainly used for methods like
[](jd:paper:org.bukkit.entity.Entity#lookAt(io.papermc.paper.math.Position,io.papermc.paper.entity.LookAnchor)) or
[](jd:paper:org.bukkit.entity.Player#lookAt(org.bukkit.entity.Entity,io.papermc.paper.entity.LookAnchor,io.papermc.paper.entity.LookAnchor)).
### Example usage
```java
Commands.argument("anchor", ArgumentTypes.entityAnchor())
.executes(context -> {
final LookAnchor anchor = context.getArgument("anchor", LookAnchor.class);
context.getSource().getSender().sendRichMessage("You chose !",
Placeholder.unparsed("anchor", anchor.name())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## GameMode argument
The game mode argument works the same way as the first argument of the Vanilla `/gamemode ` command. It accepts any of the 4 valid game modes, returning
a [](jd:paper:org.bukkit.GameMode) enum to use in code.
### Example usage
```java
Commands.argument("mode", ArgumentTypes.gameMode())
.executes(context -> {
final GameMode mode = context.getArgument("mode", GameMode.class);
final Player player = context.getSource().getPlayerOrThrow();
player.setGameMode(mode);
player.sendRichMessage("Your gamemode has been set to !",
Placeholder.component("mode", Component.translatable(mode))
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## HeightMap argument
The [](jd:paper:org.bukkit.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 ` command. E.g. `world_surface`
would mean that the Y coordinate of the surface of the world on the set X/Z values should be used.
### Example usage
```java
Commands.argument("height_map", ArgumentTypes.heightMap())
.executes(context -> {
final HeightMap heightMap = context.getArgument("height_map", HeightMap.class);
context.getSource().getSender().sendRichMessage("You selected ",
Placeholder.unparsed("selection", heightMap.name())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Scoreboard display slot argument
This argument allows you to retrieve a [](jd:paper:org.bukkit.scoreboard.DisplaySlot) enum value from the user.
### Example usage
```java
Commands.argument("slot", ArgumentTypes.scoreboardDisplaySlot())
.executes(context -> {
final DisplaySlot slot = context.getArgument("slot", DisplaySlot.class);
context.getSource().getSender().sendPlainMessage("You selected: " + slot.toString());
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Template mirror argument
Here, the user has 3 valid input possibilities: `front_back`, `left_right`, and `none`. You can retrieve the result of
the argument as a [](jd:paper:org.bukkit.block.structure.Mirror) enum value.
### Example usage
```java
Commands.argument("mirror", ArgumentTypes.templateMirror())
.executes(context -> {
final Mirror mirror = context.getArgument("mirror", Mirror.class);
context.getSource().getSender().sendPlainMessage("You selected: " + mirror.name());
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Template rotation argument
For the 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 [](jd:paper:org.bukkit.block.structure.StructureRotation) enum value.
### Example usage
```java
Commands.argument("rotation", ArgumentTypes.templateRotation())
.executes(context -> {
final StructureRotation rotation = context.getArgument("rotation", StructureRotation.class);
context.getSource().getSender().sendPlainMessage("You selected: " + rotation.name());
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
---
# Location
BlockPosition, FinePosition and World argument documentation.
import BlockPositionMp4 from "./assets/vanilla-arguments/blockposition.mp4?url";
import FinePositionMp4 from "./assets/vanilla-arguments/fineposition.mp4?url";
import WorldMp4 from "./assets/vanilla-arguments/world.mp4?url";
import Video from "/src/components/Video.astro";
## 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 ` Vanilla command.
In order to retrieve the `BlockPosition` variable from the
[](jd:paper:io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver), we have to resolve it using the command source.
### Example usage
```java
Commands.argument("pos", ArgumentTypes.blockPosition())
.executes(context -> {
final BlockPositionResolver resolver = context.getArgument("pos", BlockPositionResolver.class);
final BlockPosition pos = resolver.resolve(context.getSource());
context.getSource().getSender().sendPlainMessage("Put in " + pos.x() + " " + pos.y() + " " + pos.z());
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## 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 [](jd:paper:io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver). You can resolve that by running `FinePositionResolver#resolve(CommandSourceStack)` to get the resulting
[](jd:paper:io.papermc.paper.math.FinePosition).
### Example usage
```java
Commands.argument("pos", ArgumentTypes.finePosition(true))
.executes(context -> {
final FinePositionResolver resolver = context.getArgument("pos", FinePositionResolver.class);
final FinePosition pos = resolver.resolve(context.getSource());
context.getSource().getSender().sendRichMessage("Position: ",
Placeholder.unparsed("x", String.valueOf(pos.x())),
Placeholder.unparsed("y", String.valueOf(pos.y())),
Placeholder.unparsed("z", String.valueOf(pos.z()))
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## World argument
This argument allows the user to select one of the currently loaded world. You can retrieve the result of that as a generic Bukkit
[](jd:paper:org.bukkit.World) object.
### Example usage
```java
Commands.argument("world", ArgumentTypes.world())
.executes(context -> {
final World world = context.getArgument("world", World.class);
final Player player = context.getSource().getPlayerOrThrow();
if (player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.COMMAND)) {
context.getSource().getSender().sendRichMessage("Successfully teleported to ",
Placeholder.component("player", player.displayName()),
Placeholder.unparsed("world", world.key().asString())
);
return Command.SINGLE_SUCCESS;
} else {
context.getSource().getSender().sendRichMessage("Failed to teleport the player");
return Command.SINGLE_SUCCESS;
}
})
```
### In-game preview
---
# Minecraft-specific
Everything regarding the essential Brigadier arguments.
The [Arguments and Literals](/paper/dev/command-api/basics/arguments-and-literals) page covers the most used, native Brigadier arguments. But Minecraft define a few more which Paper wraps around. These can be accessed
in a static context using the [](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes) class. We will go over all of those in this section.
## Quick overview
A quick overview of all possible arguments is defined here:
| Argument Reference | Resolved Type |
|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| [`entity()`](/paper/dev/command-api/arguments/entity-player#entity-argument) | [](jd:paper:org.bukkit.entity.Entity) |
| [`entities()`](/paper/dev/command-api/arguments/entity-player#entities-argument) | List<[Entity](jd:paper:org.bukkit.entity.Entity)> |
| [`player()`](/paper/dev/command-api/arguments/entity-player#player-argument) | [](jd:paper:org.bukkit.entity.Player) |
| [`players()`](/paper/dev/command-api/arguments/entity-player#players-argument) | List<[Player](jd:paper:org.bukkit.entity.Player)> |
| [`playerProfiles()`](/paper/dev/command-api/arguments/entity-player#player-profiles-argument) | Collection<[PlayerProfile](jd:paper:com.destroystokyo.paper.profile.PlayerProfile)> |
| [`blockPosition()`](/paper/dev/command-api/arguments/location#block-position-argument) | [](jd:paper:io.papermc.paper.math.BlockPosition) |
| [`columnBlockPosition()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#columnBlockPosition()) | [](jd:paper:io.papermc.paper.command.brigadier.argument.position.ColumnBlockPosition) |
| [`blockInWorldPredicate()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#blockInWorldPredicate()) | [](jd:paper:io.papermc.paper.command.brigadier.argument.predicate.BlockInWorldPredicate) |
| [`finePosition(boolean centerIntegers)`](/paper/dev/command-api/arguments/location#fine-position-argument) | [](jd:paper:io.papermc.paper.math.FinePosition) |
| [`columnFinePosition(boolean centerIntegers)`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#columnFinePosition()) | [](jd:paper:io.papermc.paper.command.brigadier.argument.position.ColumnFinePosition) |
| [`rotation()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#rotation()) | [](jd:paper:io.papermc.paper.math.Rotation) |
| [`angle()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#angle()) | `float` |
| [`axes()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#axes()) | Set<[Axis](jd:paper:org.bukkit.Axis)> |
| [`blockState()`](/paper/dev/command-api/arguments/paper#block-state-argument) | [](jd:paper:org.bukkit.block.BlockState) |
| [`itemStack()`](/paper/dev/command-api/arguments/paper#item-argument) | [](jd:paper:org.bukkit.inventory.ItemStack) |
| [`itemPredicate()`](/paper/dev/command-api/arguments/predicate#item-predicate-argument) | [](jd:paper:io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate) |
| [`namedColor()`](/paper/dev/command-api/arguments/adventure#named-color-argument) | [](jd:adventure:net.kyori.adventure.text.format.NamedTextColor) (Adventure) |
| [`hexColor()`](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#hexColor()) | [](jd:adventure:net.kyori.adventure.text.format.TextColor) (Adventure) |
| [`component()`](/paper/dev/command-api/arguments/adventure#component-argument) | [](jd:adventure:net.kyori.adventure.text.Component) (Adventure) |
| [`style()`](/paper/dev/command-api/arguments/adventure#adventure-style-argument) | [](jd:adventure:net.kyori.adventure.text.format.Style) (Adventure) |
| [`signedMessage()`](/paper/dev/command-api/arguments/adventure#signed-message-argument) | [](jd:adventure:net.kyori.adventure.chat.SignedMessage) (Adventure) |
| [`scoreboardDisplaySlot()`](/paper/dev/command-api/arguments/enums#scoreboard-display-slot-argument) | [](jd:paper:org.bukkit.scoreboard.DisplaySlot) |
| [`namespacedKey()`](/paper/dev/command-api/arguments/paper#namespacedkey-argument) | [](jd:paper:org.bukkit.NamespacedKey) |
| [`key()`](/paper/dev/command-api/arguments/adventure#key-argument) | [](jd:adventure:net.kyori.adventure.key:net.kyori.adventure.key.Key) (Adventure) |
| [`integerRange()`](/paper/dev/command-api/arguments/predicate#integer-range-argument) | `Range` |
| [`doubleRange()`](/paper/dev/command-api/arguments/predicate#double-range-argument) | `Range` |
| [`world()`](/paper/dev/command-api/arguments/location#world-argument) | [](jd:paper:org.bukkit.World) |
| [`gameMode()`](/paper/dev/command-api/arguments/enums#gamemode-argument) | [](jd:paper:org.bukkit.GameMode) |
| [`heightMap()`](/paper/dev/command-api/arguments/enums#heightmap-argument) | [](jd:paper:org.bukkit.HeightMap) |
| [`uuid()`](/paper/dev/command-api/arguments/paper#uuid-argument) | `UUID` |
| [`objectiveCriteria()`](/paper/dev/command-api/arguments/paper#objective-criteria-argument) | [](jd:paper:org.bukkit.scoreboard.Criteria) |
| [`entityAnchor()`](/paper/dev/command-api/arguments/enums#entity-anchor-argument) | [](jd:paper:io.papermc.paper.entity.LookAnchor) |
| [ `time(int minTime)`](/paper/dev/command-api/arguments/paper#time-argument) | `Integer` |
| [`templateMirror()`](/paper/dev/command-api/arguments/enums#template-mirror-argument) | [](jd:paper:org.bukkit.block.structure.Mirror) |
| [`templateRotation()`](/paper/dev/command-api/arguments/enums#template-rotation-argument) | [](jd:paper:org.bukkit.block.structure.StructureRotation) |
| [`resource(RegistryKey)`](/paper/dev/command-api/arguments/registry#resource-argument) | registry element |
| [`resourceKey(RegistryKey)`](/paper/dev/command-api/arguments/registry#resource-key-argument) | [](jd:paper:io.papermc.paper.registry.TypedKey) of a registry element |
---
# Paper-specific
Documentation for arguments handling miscellaneous Paper API values.
import BlockStateMp4 from "./assets/vanilla-arguments/blockstate.mp4?url";
import ItemStackMp4 from "./assets/vanilla-arguments/itemstack.mp4?url";
import NamespacedKeyMp4 from "./assets/vanilla-arguments/namespacedkey.mp4?url";
import TimeMp4 from "./assets/vanilla-arguments/time.mp4?url";
import UuidMp4 from "./assets/vanilla-arguments/uuid.mp4?url";
import ObjectiveCriteriaMp4 from "./assets/vanilla-arguments/objectivecriteria.mp4?url";
import Video from "/src/components/Video.astro";
The arguments in this section 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
Commands.argument("state", ArgumentTypes.blockState())
.executes(context -> {
final BlockState state = context.getArgument("state", BlockState.class);
// will be an unplaced snapshot which needs to be copied in order to be used later
context.getSource().getSender().sendRichMessage("You specified a !",
Placeholder.component("block", Component.translatable(state.getType()))
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Item argument
The item argument is the way to retrieve an [item](jd:paper:org.bukkit.inventory.ItemStack) following the same argument format as the Vanilla `/give - []`
command as its second argument. The user may also define components to further customize the `ItemStack`. If you only require an item type, you should instead
check out the [registry arguments](/paper/dev/command-api/arguments/registry).
### Example usage
```java
Commands.argument("item", ArgumentTypes.itemStack())
.executes(context -> {
final ItemStack item = context.getArgument("item", ItemStack.class);
final Player player = context.getSource().getPlayerOrThrow();
player.getInventory().addItem(item);
context.getSource().getSender().sendRichMessage("Successfully gave a
- ",
Placeholder.component("player", player.displayName()),
Placeholder.component("item", Component.translatable(item).hoverEvent(item))
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## NamespacedKey argument
This argument allows the user to provide any artificial (namespaced) key. The return value of this argument is a
[](jd:paper:org.bukkit.NamespacedKey), which makes it useful when dealing with Bukkit API.
### Example usage
```java
Commands.argument("key", ArgumentTypes.namespacedKey())
.executes(context -> {
final NamespacedKey key = context.getArgument("key", NamespacedKey.class);
context.getSource().getSender().sendRichMessage("You put in !",
Placeholder.unparsed("key", key.toString())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Time argument
The time argument allows the user to define a time frame, similar to the Vanilla `/time set
':'false'>",
Placeholder.unparsed("uuid", uuid.toString()),
Formatter.booleanChoice("result", result.hasPlayedBefore())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Objective criteria argument
You can retrieve the argument value as a `Criteria`, which can be used with `Scoreboard` objects.
### Example usage
```java
Commands.argument("criteria", ArgumentTypes.objectiveCriteria())
.executes(context -> {
final Criteria criteria = context.getArgument("criteria", Criteria.class);
context.getSource().getSender().sendRichMessage("Default render type for : ",
Placeholder.unparsed("criteria", criteria.getName()),
Placeholder.unparsed("render_type", criteria.getDefaultRenderType().name())
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
---
# Predicates
Documentation for arguments that allow value validation.
import DoubleRangeMp4 from "./assets/vanilla-arguments/doublerange.mp4";
import IntegerRangeMp4 from "./assets/vanilla-arguments/integerrange.mp4";
import ItemPredicateMp4 from "./assets/vanilla-arguments/itempredicate.mp4";
import Video from "/src/components/Video.astro";
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
Commands.argument("range", ArgumentTypes.doubleRange())
.executes(context -> {
final Range range = context.getArgument("range", DoubleRangeProvider.class).range();
final CommandSender sender = context.getSource().getSender();
for (int i = 0; i < 5; i++) {
sender.sendRichMessage("Is in bounds? ",
Placeholder.unparsed("index", String.valueOf(i)),
Placeholder.unparsed("result", String.valueOf(range.contains((double) i)))
);
}
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
## Integer range argument
This argument works very similarly to the double range argument, with the only difference being that this argument only accepts integers.
### Example usage
```java
Commands.argument("range", ArgumentTypes.integerRange())
.then(Commands.argument("input", IntegerArgumentType.integer())
.executes(context -> {
final Range range = context.getArgument("range", IntegerRangeProvider.class).range();
final int input = IntegerArgumentType.getInteger(context, "input");
final CommandSender sender = context.getSource().getSender();
sender.sendRichMessage(" is ':'is not'/> inside the specified range!",
Placeholder.unparsed("input", String.valueOf(input)),
Formatter.booleanChoice("result", range.contains(input))
);
return Command.SINGLE_SUCCESS;
})
)
```
### In-game preview
## 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
Commands.argument("predicate", ArgumentTypes.itemPredicate())
.executes(context -> {
final ItemStackPredicate predicate = context.getArgument("predicate", ItemStackPredicate.class);
final ItemStack woodenSword = ItemType.WOODEN_SWORD.createItemStack();
context.getSource().getSender().sendRichMessage("Does predicate include a default wooden sword? true':'false'>",
Formatter.booleanChoice("result", predicate.test(woodenSword))
);
return Command.SINGLE_SUCCESS;
})
```
### In-game preview
---
# Registry
Documentation for arguments retrieving registry values.
import ResourceItem from "./assets/registry-arguments/resource-item.mp4";
import ResourceKeyItem from "./assets/registry-arguments/resourcekey-item.mp4";
import EnchantsRegistry from "./assets/registry-arguments/enchants-registry.mp4";
import Video from "/src/components/Video.astro";
Registries in Minecraft hold all sort of information - possible item or block types, enchantments, potion effects, ... and more!
There are two types of registry arguments: `resource` and `resourceKey`.
The main difference between those arguments is the return value: The `resource` argument returns the parsed value, whilst the `resourceKey` only returns a `TypedKey`, which
you can use to retrieve the value yourself.
## Resource argument
Just like any other argument, you can get an `ArgumentType` reference to it using `ArgumentTypes.resource(RegistryKey)`.
They are accessed in a static context using the [](jd:paper:io.papermc.paper.registry.RegistryKey) interface.
Each entry in `RegistryKey` returns a `RegistryKey`. The `` generic parameter here describes the return type. This means that if we were to retrieve
[`RegistryKey.ITEM`](jd:paper:io.papermc.paper.registry.RegistryKey#ITEM), the return type would be an `ItemType`, since it is defined as follows:
```java title="RegistryKey.java"
public sealed interface RegistryKey extends Keyed permits RegistryKeyImpl {
// ...
RegistryKey ITEM = RegistryKeyImpl.create("item");
// ...
}
```
And really, there isn't much more to it. For that exact reason, here is an example on the implementation of such an argument:
```java
Commands.argument("enchantment", ArgumentTypes.resource(RegistryKey.ENCHANTMENT))
.executes(context -> {
final Player player = context.getSource().getPlayerOrThrow();
final ItemStack item = player.getInventory().getItemInMainHand();
if (item.isEmpty()) {
context.getSource().getSender().sendRichMessage(" is not holding any item",
Placeholder.component("player", player.displayName())
);
return Command.SINGLE_SUCCESS;
}
final Enchantment enchantment = context.getArgument("enchantment", Enchantment.class);
item.addUnsafeEnchantment(enchantment, 10);
context.getSource().getSender().sendRichMessage("Enchanted 's - with !",
Placeholder.component("player", player.displayName()),
Placeholder.component("item", Component.translatable(item).hoverEvent(item)),
Placeholder.component("enchantment", enchantment.displayName(10))
);
return Command.SINGLE_SUCCESS;
})
```
We define an `enchantment` argument using the registry key of the enchantment registry and retrieve the value of that using `context.getArgument("enchantment", Enchantment.class)`.
Finally, we enchant the item of the executing player's hand with whatever enchantment the sender chose at level 10 and send a success message.
Here is how it looks in-game:
:::caution
There are certain edge-cases where this argument, due to missing registries on the client, will cause a **Network Protocol Error**.
Currently, the only resource where this is the case, is with the `STRUCTURE` registry key.
```java
// Registering this command will cause clients to not be able to connect to the server.
final LiteralCommandNode 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 with a `resourceKey(...)` argument type and parse the values yourself.
:::
## Resource key argument
For the client, there is barely any difference between using `ArgumentTypes.resource` or `ArgumentTypes.resourceKey`. The only difference is that
using `ArgumentTypes.resourceKey` does not provide **error checking**. We can visualize this using `RegistryKey.ITEM`.
Here is the tab completion when using `ArgumentTypes.resource(RegistryKey.ITEM)`:
And here is the tab completion when using `ArgumentTypes.resourceKey(RegistryKey.ITEM)`:
:::note
In the example given above, due to an unhandled `NullPointerException`, the command does not successfully run. The code for that command is directly trying to use the value retrieved
by the registry access by doing `RegistryAccess.registryAccess().getRegistry(itemKey.registryKey()).get(itemKey.key())`. If you try to do any
operation with the result, it might be null and error.
You should **always** check the result of a registry retrieval operation when it's not already enforced by the argument type. An example for that is given below in the [direct code comparison](#direct-code-comparison).
:::
The resource argument provides a much cleaner user experience, whilst the `resourceKey` argument has one very important use case: You get the raw
`TypedKey` 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.
:::tip
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 snippet on how one could use the `RegistryKey.ITEM` registry with a `resource` argument type:
```java
Commands.argument("item", ArgumentTypes.resource(RegistryKey.ITEM))
.executes(context -> {
final ItemType item = context.getArgument("item", ItemType.class);
final Player player = context.getSource().getPlayerOrThrow();
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 `context.getArgument("item", TypedKey.class)`, we instead use the
[](jd:paper:io.papermc.paper.command.brigadier.argument.RegistryArgumentExtractor) to retrieve our `TypedKey`.
```java
Commands.argument("item", ArgumentTypes.resourceKey(RegistryKey.ITEM))
.executes(context -> {
final TypedKey itemKey = RegistryArgumentExtractor.getTypedKey(context, RegistryKey.ITEM, "item");
final ItemType item = RegistryAccess.registryAccess().getRegistry(itemKey.registryKey()).get(itemKey.key());
if (item == null) {
context.getSource().getSender().sendRichMessage("Please provide a valid item!");
return Command.SINGLE_SUCCESS;
}
context.getSource().getPlayerOrThrow().getInventory().addItem(item.createItemStack());
return Command.SINGLE_SUCCESS;
})
```
### Using a TypedKey
First, in order to get the correct registry, you can run `RegistryAccess#getRegistry(RegistryKey)`. In order to get a `RegistryAccess`, you can just use the static
`RegistryAccess.registryAccess()` method. The `RegistryKey` is retrieved using `TypedKey#registryKey()`.
Now, in order to get the final value `T`, you can run `Registry#get(Key)`, where the key can be retrieved using `TypedKey#key()`. 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 types
You can get a list of all registry key types and their return values on the
[`RegistryKey` Javadocs](jd:paper:io.papermc.paper.registry.RegistryKey).
---
# Suggestions
Documentation about defining custom argument suggestions.
import GiveItemCommandMp4 from "./assets/give-item-command.mp4";
import SelectNameCommandMp4 from "./assets/select-name-command.mp4";
import Video from "/src/components/Video.astro";
Sometimes, you want to send your own suggestions to users. For this, you can use the `suggests(SuggestionProvider)` method when declaring
arguments.
## Examining `SuggestionProvider`
The `SuggestionProvider` interface is defined as follows:
```java title="SuggestionProvider.java"
@FunctionalInterface
public interface SuggestionProvider {
CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException;
}
```
Similar to other classes or interfaces with a `` generic parameter, for Paper, this is usually a `CommandSourceStack`. Furthermore, similar to the `Command` interface,
this is a functional interface, which means that instead of passing in a class which implements this interface, we can just pass a lambda expression or a method reference.
Our lambda consists of two parameters, `CommandContext` and `SuggestionsBuilder`, and expects to have a `CompletableFuture` returned.
A very simple lambda for our `suggests` method might look like this:
```java
Commands.argument("name", StringArgumentType.word())
.suggests((context, builder) -> builder.buildFuture())
```
This example obviously does not suggest anything, as we haven't added any suggestions yet.
## The `SuggestionsBuilder`
The `SuggestionsBuilder` has a few methods we can use to construct our suggestions:
### Input retrieval
The first type of methods we will cover are the input retrieval methods: `getInput()`, `getStart()`, `getRemaining()`, and `getRemainingLowerCase()`.
The following table displays what each returns with the following input typed in the chat bar: `/customsuggestions Asumm13Text`.
| Method | Return Value | Description |
|-------------------------|--------------------------------|----------------------------------------------------------|
| 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
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:
- `LiteralMessage`, which can be used for basic, non-formatted text.
- `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(
Component.text("Suggestion tooltip", NamedTextColor.GREEN)
));
```
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`.
The reason for these two methods is that `SuggestionProvider` expects the return value to be `CompletableFuture`. This for once
allows for constructing your suggestions asynchronously inside a `CompletableFuture.supplyAsync(Supplier)` 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((context, builder) -> {
builder.suggest("first");
builder.suggest("second");
return builder.buildFuture();
})
// Here, most Paper API is not usable
Commands.argument("name", StringArgumentType.word())
.suggests((context, 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 title="GiveItemCommand.java"
public class GiveItemCommand {
public static LiteralCommandNode createCommand() {
// Create new command: /giveitem
return Commands.literal("giveitem")
// 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 defer to separate methods, since otherwise, our command definition would grow too big
// It also allows building optional arguments as shown below.
.suggests(GiveItemCommand::amountSuggestions)
.executes(context -> giveItem(
context,
context.getArgument("item", ItemStack.class),
IntegerArgumentType.getInteger(context, "amount")
))
)
// Add a fallback path in case the amount is not specified which gives 64 items
.executes(context -> giveItem(
context,
context.getArgument("item", ItemStack.class),
64
))
)
.build();
}
private static CompletableFuture amountSuggestions(final CommandContext context, 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 giveItem(
final CommandContext context, final ItemStack item, final int amount
) {
// We know that the executor will be a player, so we can just silently return.
// Alternatively we could use CommandSourceStack#getPlayerOrThrow which will be used
// in later chapters to simplify examples.
if (!(context.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("You do not have enough space in your inventory!");
return Command.SINGLE_SUCCESS;
}
// 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("You have been given x !",
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:
## 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 suggestNamesCommand() {
final List names = List.of("Alex", "Andreas", "Stephanie", "Sophie", "Emily");
return Commands.literal("selectname")
.then(Commands.argument("name", StringArgumentType.word())
.suggests((context, builder) -> {
names.stream()
.filter(entry -> entry.toLowerCase(Locale.ROOT).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:
---
# Arguments and literals
An extensive guide to command arguments and literals.
:::note
In the [command tree docs](/paper/dev/command-api/basics/command-tree) we have looked at the structure of Brigadier commands and how to build up a command tree.
If you haven't finished reading that yet, we strongly recommend doing that before reading about arguments and literals.
:::
## Introduction
Each `.then(...)` method of an `ArgumentBuilder` takes in another `ArgumentBuilder` object. This abstract ArgumentBuilder
has two implementations: `RequiredArgumentBuilder` and `LiteralArgumentBuilder`. When using Brigadier with Paper, we create these objects by running either `Commands.literal(String)`
for the `LiteralArgumentBuilder` or `Commands.argument(String, ArgumentType)` for the `RequiredArgumentBuilder`.
As an explanation to what the difference is, you can picture it like this:
* An argument is a variable input by the user. It is semi-unpredictable, but will always return a valid entry of the object that it is backing.
* A literal is a non-variable input by the user. It is mainly used as a way to define predictable input, since each literal is a new branch on our command tree.
## Literals
In code, literals generally cannot be accessed. Yet, due to the nature of our command tree, we can always know on what literal branch we currently are:
```java
Commands.literal("plant")
.then(Commands.literal("tree")
.executes(context -> {
// Here we are on /plant tree
})
)
.then(Commands.literal("grass")
.executes(context -> {
// Here we are on /plant grass
})
)
```
:::tip
You may notice the usage of the `executes` method. This method declares logic to our branches. If a branch has no `executes` method defined, it will not be executable.
For more information about execution logic, [click here](/paper/dev/command-api/basics/executors).
:::
## Arguments
Arguments are slightly more complex. They also define a new branch in a tree, but they are not directly predictable. Each argument is created using `Commands.argument(String, ArgumentType)`.
That method returns a `RequiredArgumentBuilder`. The `T` type parameter declares the return type of the argument, which you can then use inside your `executes` method. That means that
if you put in an `ArgumentType`, you can retrieve the value of that argument as an integer, requiring no manual parsing! There are a few build-in, primitive argument types
that we can use for arguments:
| Argument Type | Resolved Type | 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
A boolean argument type is used for retrieving, well, a boolean. An example usage for that might be a `/serverflight` command which allows for enabling and disabling server flight
with `/serverflight true` and `/serverflight false`:
```java
Commands.literal("serverflight")
.then(Commands.argument("allow", BoolArgumentType.bool())
.executes(context -> {
boolean allow = context.getArgument("allow", boolean.class);
// Toggle server flying
})
)
```
Here, you can see how one would access an argument in-code. The first parameter for the `Commands.argument(String, ArgumentType)` method takes in the node name. This is not required
by literals, as their name is the same as their value. But here we need a way to access the argument. The parameter of the executes-lambda has a method called
`V getArgument(String, Class)`. The first parameter is the name of the argument we want to retrieve. The second parameter is the return value of the argument. As we are using
a boolean argument, we put in `boolean.class` and retrieve the argument value as such.
### Number arguments
All of the number arguments (like `IntegerArgumentType.integer()`) have three overloads:
| Overload | Description |
|-------------------------------------------------|-----------------------------------------------------------|
| `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 |
This is particularly useful for filtering out too high or too low input. As an example, we can define a `/flyspeed` command. As the
[](jd:paper:org.bukkit.entity.Player#setFlySpeed(float)) method only
accepts floats between -1 and 1, where -1 is an inverse direction, it would make sense to limit the values between 0 and 1 for in-bounds, non-negative speed values.
This can be achieved with the following command tree:
```java
Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0F))
.executes(context -> {
float speed = context.getArgument("speed", float.class);
// Set player's flight speed
return Command.SINGLE_SUCCESS;
})
)
```
:::tip
Some arguments can have special ways of being retrieved. Most notably, all of the Brigadier-provided arguments (the ones mentioned on this page)
have a resolver to get their own argument value. For the float argument, this would look like this:
```java
float speed = FloatArgumentType.getFloat(context, "speed");
```
It generally does not matter whether you use `context.getArgument` or `FloatArgumentType.getFloat`, since it goes through the same logic, but in future documentation,
primitive values might be retrieved using their own parsers.
These parsers for Brigadier-native arguments exist. All of these take in `(CommandContext> context, String name)` as method parameters:
- `BoolArgumentType.getBool`
- `IntegerArgumentType.getInteger`
- `LongArgumentType.getLong`
- `FloatArgumentType.getFloat`
- `DoubleArgumentType.getDouble`
- `StringArgumentType.getString`
:::
Now, if we input a valid float between 0 and 1, the command would execute correctly:

But if we input a too small or too big float, it would throw an error **on the client**:


This is the main advantage of native arguments: The client itself performs simple error checking on the arguments, which makes user experience whilst running a command
way better, as they can see invalid input without sending the command to the server.
### String arguments
There are three string arguments: `word`, `string`, and `greedyString`.
The `word` string argument is the simplest one of these. It only accepts a single word consisting of alphanumerical characters and these special characters: `+`, `-`, `_`, and `.`.
* ✅ `.this_is_valid_input.`
* ❌ `this is invalid input`
* ❌ `"also_invalid"`
* ✅ `-10_numbers_are_valid`
* ❌ `@_@`
The `string` argument is slightly more complicated. If unquoted, it follows the same rules as the `word` argument. Only alphanumerical characters and the mentioned special characters.
But if you put your string into quotes, you can enter any combination of unicode characters you want to. Quotes `"` can be escaped using a backslash `\`.
* ✅ `this_is-valid-input`
* ✅ `"\"quotes\""`
* ❌ `this is invalid input`
* ✅ `"this is valid input again"`
* ✅ `"also_valid"`
* ✅ `"紙の神"`
The `greedyString` argument is the only argument which does not perform any parsing. Due to its "greedy" nature, it does not allow any arguments after its declaration. That also means, that
any input is completely valid and it requires no quotes. In fact, quotes are counted as literal characters.
* ✅ `this_is_valid_input`
* ✅ `this is valid as well input`
* ✅ `"this is valid input again"`
* ✅ `also_valid`
* ✅ `紙の神`
Here you can see the arguments in action:

## Further reference
### Minecraft arguments
Apart from these built-in Brigadier arguments, countless custom arguments are defined by Paper as well. These can be accessed in a static context with the `ArgumentTypes` class. You
can read more about these [here](/paper/dev/command-api/arguments/minecraft).
### Custom arguments
Sometimes you want to define your own, custom arguments. For that you can implement the `CustomArgumentType` interface.
You can read more about these [here](/paper/dev/command-api/basics/custom-arguments).
---
# Command trees
An extensive guide to building up a command tree.
import { Image } from "astro:assets";
import tree from "./assets/forest/tree.png";
import treeDescriptions from "./assets/forest/tree-descriptions.png";
What is a command tree and in what way does it have anything to do with Brigadier? If you are confused, this is the page for you! Here we will take an
extensive look at everything you need to know to understands command tree!
:::note
This is meant as a full-on course about the structure of Brigadier commands. This site provides no information about executing branches or general arguments, only literals.
It is suggested that you look in here if you are new to Brigadier programming.
:::
## What is a tree?
When talking about trees, the first that comes to mind is a tree in the wild. One that may look like this:
So, what does this have to do with commands? Imagine a command that looks like this:
```yml title="A Generic Command"
/customplugin reload
/customplugin tphere
/customplugin killall
```
For the sake of simplicity, we will refer to `/customplugin` (without any arguments) as our "command" or "tree" root. Each argument after our "root" is referred to as a "branch".
You can visualize this on our generic tree like this:
Having to draw a tree each time like this is exhausting and usually does not get to the point though. We can visualize trees using a **tree diagram**. This would look like this:
```d2
style.fill: transparent
direction: down
customplugin -> reload
customplugin -> tphere
customplugin -> killall
```
It is way easier to understand the root/branch relationship of elements now. The root node is the node that is at the top of our hierarchy. In this case, that is the 'customplugin' node.
The other elements have an arrow pointing **at** them, meaning they are a **branch** of our root node, also called a child. Family trees follow a similar structure, as you can see here:
```d2
style.fill: transparent
direction: down
parents -> "first child"
parents -> "second child"
parents -> "third child"
"second child" -> "first grandchild"
"third child" -> "second grandchild"
"third child" -> "third grandchild"
```
It is important to remember that this tree-like structure is not only important for Brigadier. It is a fairly often used concept. Paper's included [Adventure API](/paper/dev/component-api/introduction)
also operates using a tree. Why is this important for using Brigadier though? Well, it allows for explicit command declaration. Being at a node, you know exactly where you are. That means you do not have
to, like in standard Bukkit way, first check whether the amount of arguments is 2 and the first argument is `tphere`. Because you are at that exact `tphere` node, you can just start writing
your logic. If you want to learn more about the execute logic of Brigadier commands, it is suggested that you check out [command executors](/paper/dev/command-api/basics/executors).
## How can we visualize a tree in-code?
We can define our root like this:
```java
LiteralArgumentBuilder root = Commands.literal("customplugin");
```
This method returns a `LiteralArgumentBuilder`, which is a class that allows us to add branches to it using the `.then(...)` method, like this:
```java
LiteralArgumentBuilder root = Commands.literal("customplugin");
root.then(Commands.literal("reload"));
root.then(Commands.literal("tphere"));
root.then(Commands.literal("killall"));
```
Each of these `.then(...)` methods adds a new branch to our root. You may have noticed the repeated use of `Commands.literal(String)` here. It does not only define the root of the tree
of our command, but also our "subcommands" (`reload`, `tphere`, and `killall`). Each "child" literal is referred to as a subcommand of its parent.
## Creating a more advanced command
But what if we want a more complex command? Let's say we want to define the following command:
```yml title="Advanced Command"
/advanced
┣━┳ killall
┃ ┣━━ entities
┃ ┣━━ players
┃ ┗━━ zombies
┗━┳ eat
┣━━ ice-cream
┗━━ main-dish
```
Which allows for the following command executions in-game:
```
/advanced killall entities
/advanced killall players
/advanced killall zombies
/advanced eat ice-cream
/advanced eat main-dish
```
As this is a fairly complicated command, we can visualize it as a tree graph first in order to have a better understanding on what is going on:
```d2
style.fill: transparent
direction: down
advanced -> killall
advanced -> eat
killall -> entities
killall -> players
killall -> zombies
eat -> "ice cream"
eat -> "main dish"
```
Having defined our target command, how can we go about this now? There are a few possible ways, but the simplest one is defining **furthest from root** first. That means we are first
defining the last branches of our entire tree. So those, which have no subcommands:
```java
LiteralArgumentBuilder entities = Commands.literal("entities");
LiteralArgumentBuilder players = Commands.literal("players");
LiteralArgumentBuilder zombies = Commands.literal("zombies");
LiteralArgumentBuilder iceCream = Commands.literal("ice-cream");
LiteralArgumentBuilder mainDish = Commands.literal("main-dish");
```
This grants us the deepest elements in our tree.
```d2
style.fill: transparent
entities
players
zombies
"ice cream"
"main dish"
```
Now, we can define the next layer of literals: So the `killall` and `eat` ones:
```java
LiteralArgumentBuilder killall = Commands.literal("killall");
LiteralArgumentBuilder eat = Commands.literal("eat");
```
Visualized in our tree graph:
```d2
style.fill: transparent
killall
eat
```
With these defined, we can add our child elements to their parent element, like this:
```java
killall.then(entities);
killall.then(players);
killall.then(zombies);
eat.then(iceCream);
eat.then(mainDish);
```
Which gives us this, somewhat tree-like structure:
```d2
style.fill: transparent
direction: down
killall -> entities
killall -> players
killall -> zombies
eat -> "ice cream"
eat -> "main dish"
```
Finally, we can create our **root node** and add our `killall` and `eat` subcommands to it:
```java
LiteralArgumentBuilder advancedCommandRoot = Commands.literal("advanced");
advancedCommandRoot.then(killall);
advancedCommandRoot.then(eat);
```
And this returns the final command tree:
```d2
style.fill: transparent
killall -> entities
killall -> players
killall -> zombies
eat -> "ice cream"
eat -> "main dish"
advanced -> killall
advanced -> eat
```
And we are done!
## Chaining 'then' method calls together
You might have noticed that it feels unnecessarily verbose to have to store every child node in its own variable. But here is where the `.then()` argument
comes to rescue. It returns the same element as it was called on. That means if we were to run this code:
```java
LiteralArgumentBuilder value = killall.then(entities);
if (value == killall) {
logger.info("The return value is the same as killall");
}
```
We would always have "The return value is the same as killall" printed out. And we can take advantage of that by chaining the `then(...)` calls together, like this:
```java
killall.then(entities).then(players).then(zombies);
```
Due to this being hard to read, we should give each branch a new line:
```java
killall
.then(entities)
.then(players)
.then(zombies);
```
With this, we also do not have to store every single literal in its own variable, instead we can directly pass them into the `.then(...)` method:
```java
killall
.then(Commands.literal("entities"))
.then(Commands.literal("players"))
.then(Commands.literal("zombies"));
```
The same can be done for the `eat` subcommand:
```java
eat
.then(Commands.literal("ice-cream"))
.then(Commands.literal("main-dish"));
```
Taking even more advantage of the builder pattern of the `then` method, we can put these chained branches directly on the initial creation of our subcommand. Like this:
```java
LiteralArgumentBuilder eat = Commands.literal("eat")
.then(Commands.literal("ice-cream"))
.then(Commands.literal("main-dish"));
LiteralArgumentBuilder killall = Commands.literal("killall")
.then(Commands.literal("entities"))
.then(Commands.literal("players"))
.then(Commands.literal("zombies"));
LiteralArgumentBuilder advancedCommandRoot = Commands.literal("advanced");
advancedCommandRoot.then(eat);
advancedCommandRoot.then(killall);
```
Now then, you might have noticed that our root node also registers its branches using the `.then(...)` method, meaning we can also combine those:
```java
LiteralArgumentBuilder 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"))
);
```
Which results in the finished command!
:::caution
You have to be extremely careful when nesting branches, as the command turns into a completely different one when you misplace a bracket.
:::
---
# Custom arguments
Guide on custom arguments.
Custom arguments are nothing more than a wrapper around existing argument types, which allow a developer to provide an argument with suggestions and reusable parsing in order to
reduce code repetition.
## Why would you use custom arguments?
As example, if you want to have an argument for a player, which is currently online and an operator, you could use a player argument type, add custom suggestions, and throw a
`CommandSyntaxException` in your `executes(...)` method body. This would look like this:
```java
Commands.argument("player", ArgumentTypes.player())
.suggests((context, 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(context -> {
final Player player = context.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(context.getSource()).getFirst();
if (!player.isOp()) {
throw new SimpleCommandExceptionType(new LiteralMessage(player.getName() + " is not a server operator!")).create();
}
context.getSource().getSender().sendRichMessage("Player is a server operator!",
Placeholder.component("player_name", player.displayName())
);
return Command.SINGLE_SUCCESS;
})
```
As you can see, there is a ton of logic not directly involved with the functionality of the command. And if we want to use this same argument on another node, we have to
copy-paste a lot of code. It goes without saying that this would be incredibly tedious.
The solution to this problem are custom arguments. Before going into detail about them, this is how the argument would look when implemented as a custom argument:
```java title="OppedPlayerArgument.java"
public final class OppedPlayerArgument implements CustomArgumentType {
private static final SimpleCommandExceptionType ERROR_BAD_SOURCE = new SimpleCommandExceptionType(
new LiteralMessage("The source needs to be a valid CommandSourceStack!")
);
private static final DynamicCommandExceptionType ERROR_NOT_OPERATOR = new DynamicCommandExceptionType(name -> {
return new LiteralMessage(name + " is not a server operator!");
});
@Override
public Player parse(final StringReader reader) {
throw new UnsupportedOperationException("This method will never be called.");
}
@Override
public Player parse(final StringReader reader, final S source) throws CommandSyntaxException {
if (!(source instanceof CommandSourceStack stack)) {
throw ERROR_BAD_SOURCE.create();
}
final Player player = this.getNativeType().parse(reader).resolve(stack).getFirst();
if (!player.isOp()) {
throw ERROR_NOT_OPERATOR.create(player.getName());
}
return player;
}
@Override
public ArgumentType getNativeType() {
return ArgumentTypes.player();
}
@Override
public CompletableFuture listSuggestions(final CommandContext context, final 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();
}
}
```
At a first look, that seems like way more code than it was needed to just do the logic in the command tree itself. So what is the advantage?
The answer becomes apparent rather quickly when we look at how the argument is now declared:
```java
Commands.argument("player", new OppedPlayerArgument())
.executes(context -> {
final Player player = context.getArgument("player", Player.class);
context.getSource().getSender().sendRichMessage("Player is a server operator!",
Placeholder.component("player_name", player.displayName())
);
return Command.SINGLE_SUCCESS;
})
```
This is way more readable and easy to understand when using a custom argument. And it is reusable! Hopefully, you now have a basic grasp of **why** you should use custom arguments.
## Examining the `CustomArgumentType` interface
The interface is declared as follows:
```java title="CustomArgumentType.java"
public interface CustomArgumentType extends ArgumentType {
@Override
T parse(StringReader reader) throws CommandSyntaxException;
@Override
default T parse(final StringReader reader, final S source) throws CommandSyntaxException {
return ArgumentType.super.parse(reader, source);
}
ArgumentType getNativeType();
@Override
@ApiStatus.NonExtendable
default Collection getExamples() {
return this.getNativeType().getExamples();
}
@Override
default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) {
return ArgumentType.super.listSuggestions(context, builder);
}
}
```
### Generic types
There are three generic types present in the interface:
- `T`: This is the type of the argument type that is returned when `CommandContext#getArgument` is called.
- `N`: The native type of the argument type which this custom argument extends. Used as the "underlying" argument.
- `S`: A generic type for the command source. Will usually be a `CommandSourceStack`.
### Methods
| Method declaration | Description |
|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ArgumentType getNativeType()` | Here, you declare the underlying argument type, which is used as a base for client-side argument validation. |
| `T parse(StringReader reader) throws CommandSyntaxException` | This method is used if `T parse(StringReader, S)` is not overridden. In here, you can run conversion and validation logic. |
| ` T parse(StringReader reader, S source)` | If overridden, this method will be preferred to `T parse(StringReader)`. It serves the same purpose, but allows including the source in the parsing logic. |
| `Collection getExamples()` | This method should **not** be overridden. It is used internally to differentiate certain argument types while parsing. |
| ` CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder)` | This method is the equivalent of `RequiredArgumentBuilder#suggests(SuggestionProvider)`. You can override this method in order to send your own suggestions to the client. |
### A very basic implementation
```java title="BasicImplementation.java"
public class BasicImplementation implements CustomArgumentType {
@Override
public String parse(final StringReader reader) {
return reader.readUnquotedString();
}
@Override
public ArgumentType getNativeType() {
return StringArgumentType.word();
}
}
```
Notice the use of `reader.readUnquotedString()`. In addition to allowing existing argument types to parse your argument,
you can also manually read input. Here, we read an unquoted string, the same as a word string argument type.
## `CustomArgumentType.Converted`
In case that you need to parse the native type to your new type, you can instead use the `CustomArgumentType.Converted` interface.
This interface is an extension to the `CustomArgumentType` interface, which adds two new, overridable methods:
```java
T convert(N nativeType) throws CommandSyntaxException;
default T convert(final N nativeType, final S source) throws CommandSyntaxException {
return this.convert(nativeType);
}
```
These methods work similarly to the `parse` methods, but they instead provide you with the parsed, native type instead of a `StringReader`.
This reduced the need to manually do string reader operations and instead directly uses the native type's parsing rules.
## Error handling during the suggestions phase
In case you are looking for the ability to make the client show currently typed input as red to display invalid input, it should be noted that this is **not possible** with
custom arguments. The client is only able to validate arguments it knows about and there is no way to throw a `CommandSyntaxException` during the suggestions phase. The only way to
achieve that is by using **literals**, but those cannot be modified dynamically during server runtime.

## Example: Ice-cream argument
A practical example on how you can use a custom argument to your advantage could be a classical enum-type argument. In our case, we use this
`IceCreamFlavor` enum:
```java title="IceCreamFlavor.java"
public enum IceCreamFlavor {
VANILLA,
CHOCOLATE,
STRAWBERRY;
@Override
public String toString() {
return this.name().toLowerCase();
}
}
```
We then can use a converted custom argument type in order to convert between a word string argument and our enum type, like this:
```java title="IceCreamArgument.java"
public class IceCreamArgument implements CustomArgumentType.Converted {
private static final DynamicCommandExceptionType ERROR_INVALID_FLAVOR = new DynamicCommandExceptionType(flavor -> {
return new LiteralMessage(flavor + " is not a valid flavor!");
});
@Override
public IceCreamFlavor convert(final String nativeType) throws CommandSyntaxException {
try {
return IceCreamFlavor.valueOf(nativeType.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException _) {
throw ERROR_INVALID_FLAVOR.create(nativeType);
}
}
@Override
public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) {
for (final IceCreamFlavor flavor : IceCreamFlavor.values()) {
final String name = flavor.toString();
// Only suggest if the flavor name matches the user input
if (name.startsWith(builder.getRemainingLowerCase())) {
builder.suggest(name);
}
}
return builder.buildFuture();
}
@Override
public ArgumentType getNativeType() {
return StringArgumentType.word();
}
}
```
Finally, we can just declare our command like this, and we are done! And again, you can just directly get the argument as a ready `IceCreamFlavor`
type without any additional parsing in the `executes(...)` method, which makes custom argument types very powerful.
```java
Commands.argument("flavor", new IceCreamArgument())
.executes(context -> {
final IceCreamFlavor flavor = context.getArgument("flavor", IceCreamFlavor.class);
context.getSource().getSender().sendRichMessage("YUM! You just had a scoop of !",
Placeholder.unparsed("flavor", flavor.toString())
);
return Command.SINGLE_SUCCESS;
})
```

---
# Executors
A guide to execution logic for Brigadier commands.
:::tip
This page requires knowledge about [Command Trees](/paper/dev/command-api/basics/command-tree) and [Arguments and Literals](/paper/dev/command-api/basics/arguments-and-literals). If you haven't read
through those articles, it is highly recommend to check those out beforehand!
:::
This page is dedicated to the `executes(...)` method from the `ArgumentBuilder` class.
## Examining the executes method
The `executes` method is defined as following:
```java title="ArgumentBuilder.java"
public T executes(Command command);
```
The `Command` interface is declared as a `FunctionalInterface`. That means that instead of putting in a class that implements it, we can just pass in a lambda expression.
```java title="Command.java"
@FunctionalInterface
public interface Command {
int SINGLE_SUCCESS = 1;
int run(CommandContext context) throws CommandSyntaxException;
}
```
Our lambda has one parameter and returns an integer. That is essentially that `run` method defined in that interface. The one parameter, `CommandContext` is the one where
we get all the information about the sender who executed that command and all the command arguments. It has quite a few methods, but the main ones of use for us are
`S getSource()` and `V getArgument(String, Class)`. We have taken a brief look at the `getArgument(...)` in the [Arguments and Literals](/paper/dev/command-api/basics/arguments-and-literals) chapter, but
in a nutshell, this is the method that we can retrieve arguments from. There will be more specific examples later on.
You should mainly notice the generic parameter `S` by the `getSource()` method. That is the type of the source of the command. For the executes method, this type is always a
`CommandSourceStack`. That class itself has these methods: `Location getLocation()`, `CommandSender getSender()`, and `@Nullable Entity getExecutor()`.
The most used method from that is `getSender()`, as that is the command sender who has actually run the command. For the target of a command, you should use `getExecutor()`,
which is relevant, if the command was ran via `/execute as run `. It is not necessarily required, but is seen as good practice.
## Example: Flyspeed command
In the [Arguments and Literals](/paper/dev/command-api/basics/arguments-and-literals) chapter, we have briefly declared the structure for a `/flyspeed` command with the use of a ranged float argument.
But that command does not actually set the flyspeed of the executing player. In order to do that, we'd have to append an executor onto it, like this:
```java {4-6}
Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0F))
.executes(context -> {
CommandSender sender = context.getSource().getSender(); // Retrieve the command sender
// Retrieve the command executor, which may or may not be the same as the sender
Entity executor = context.getSource().getExecutor();
// Check whether the executor is a player, as you can only set a player's flight speed
if (!(executor instanceof Player player)) {
// If a non-player tried to set their own flight speed
sender.sendPlainMessage("Only players can fly!");
return Command.SINGLE_SUCCESS;
}
float speed = FloatArgumentType.getFloat(context, "speed"); // Retrieve the speed argument
// Set the player's speed
player.setFlySpeed(speed);
if (sender == player) {
// If the player executed the command themselves
player.sendPlainMessage("Successfully set your flight speed to " + speed);
} else {
// If the speed was set by a different sender (Like using /execute)
sender.sendRichMessage("Successfully set 's flight speed to ",
Placeholder.component("player_name", player.displayName()),
Placeholder.unparsed("speed", String.valueOf(speed))
);
player.sendPlainMessage("Your flight speed has been set to " + speed);
}
return Command.SINGLE_SUCCESS;
})
)
```
### Explanation
There is a lot to unpack, so let's break it down, top to bottom:
The first lines define a `/flyspeed` command root, with a float argument named "speed", which only allows values between 0 and 1.
Note the highlighted lines. We first retrieve the `CommandSourceStack` from our `CommandContext` and then finally retrieve its sender and executor.
A `CommandSender` is an interface, which declares the `sendMessage(...)`, `getServer()`, and `getName()` methods. It is implemented by all entities, including players,
and the `ConsoleCommandSender`, which is used if a console executes a command.
Next up we check whether our executor object is also an instance of a `Player`. If executor were null, this would be false, which is why we require no null check.
If the expression evaluates as true, we get a new `player` variable, which represents an actual player on the server that the command was executed by.
Then, we set the player's flight speed using the value retrieved from the player-provided float argument and send them a message to confirm the operation.
It is always recommended to send a confirmation message whether the command was successful, because otherwise a player might get confused to why a command is "not working".
If the executor was not a player, we can send a form of error message. In our case, we assume the sender to be a console, as an entity usually does not try to send such
a command.
Finally, we just return from the lambda expression providing a return value. As our command succeeded, we can return `Command.SINGLE_SUCCESS`, whose value is `1`.
Don't forget to close all your braces!
Running the command now works correctly:


We can even run it as another player, using `/execute as`:

### Logic separation
Sometimes, if the command is too big or due to personal preference, you might not want to have your logic code in your executes scope, as it might be unreadable due of the amount of indentations.
In such an event we can, instead of defining our logic directly in the lambda expression, use another method instead. For that we can just call our method in the executes scope. This might look like this:
```java title="FlightSpeedCommand.java"
public class FlightSpeedCommand {
public static LiteralCommandNode createCommand() {
return Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0F))
.executes(context -> setFlySpeed(
context,
FloatArgumentType.getFloat(context, "speed") // this pattern also allows for building optional arguments easily
))
)
.build();
}
private static int setFlySpeed(CommandContext context, float speed) {
CommandSender sender = context.getSource().getSender(); // Retrieve the command sender
Entity executor = context.getSource().getExecutor(); // Retrieve the command executor, which may or may not be the same as the sender
// Check whether the executor is a player, as you can only set a player's flight speed
if (!(executor instanceof Player player)) {
// If a non-player tried to set their own flight speed
sender.sendPlainMessage("Only players can fly!");
return Command.SINGLE_SUCCESS;
}
// Set the player's speed
player.setFlySpeed(speed);
if (sender == player) {
// If the player executed the command themselves
player.sendPlainMessage("Successfully set your flight speed to " + speed);
} else {
// If the speed was set by a different sender (Like using /execute)
sender.sendRichMessage("Successfully set 's flight speed to ",
Placeholder.component("player_name", player.displayName()),
Placeholder.unparsed("speed", String.valueOf(speed))
);
player.sendPlainMessage("Your flight speed has been set to " + speed);
}
return Command.SINGLE_SUCCESS;
}
}
```
As you can see, we have made our command tree way easily readable whilst preserving the same functionality.
---
# Introduction
A guide to Paper's Brigadier command API.
Paper's command system is built on top of Minecraft's Brigadier command system. This system provides a powerful and flexible way to define commands and arguments.
It offers many advantages above the previously widely used 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 the definition of commands usable in datapacks.
- Easier creation of subcommands.
:::note
To see a comparison between the new Brigadier system and the old Bukkit system, [click here](/paper/dev/command-api/misc/comparison-bukkit-brigadier).
:::
## Guide
:::tip
If the Brigadier API seems too complicated, you can start with
[basic commands](/paper/dev/command-api/misc/basic-command). They
provide a simple way of creating commands, with only a small learning curve.
:::
The following sites are worth-while to look through first when learning about Brigadier:
- [The Command Tree](/paper/dev/command-api/basics/command-tree)
- [Arguments and Literals](/paper/dev/command-api/basics/arguments-and-literals)
- [Command Executors](/paper/dev/command-api/basics/executors)
- [Command Registration](/paper/dev/command-api/basics/registration)
- [Command Requirements](/paper/dev/command-api/basics/requirements)
- [Argument Suggestions](/paper/dev/command-api/basics/argument-suggestions)
- [Custom Arguments](/paper/dev/command-api/basics/custom-arguments)
For a reference of more advanced arguments, you should look here:
- [Minecraft Arguments](/paper/dev/command-api/arguments/minecraft)
:::danger[Future pages]
The following pages will be added to the documentation in the future:
- **Tutorial: Creating Utility Commands**
- **The Command Dispatcher**
- **Forks and Redirects**
- **Tutorial: Extending the vanilla execute command**
:::
## Additional support
For support regarding the command API, you can always ask in our [Discord server](https://discord.gg/PaperMC) under the
[`#paper-dev`](https://discord.com/channels/289587909051416579/555462289851940864) channel!
---
# Registration
A guide to registering Brigadier commands.
In the previous chapters, we have taken an extensive look at how Brigadier works, but never actually elaborated on how to register commands. So we will be doing that right here!
## The LifecycleEventManager
In Paper, Brigadier commands are registered using the `LifecycleEventManager`. This is a special class which allows us to register commands in such a way that we never have to
worry about handling various server reload events, like `/reload`. Instead, whatever we register using the `LifecycleEventManager`, will be reregistered each time it is required.
But how does one get access to a `LifecycleEventManager` capable of registering commands? There are two "contexts" in which you can use a LifecycleEventManager. The first one,
and preferred one, is in the `PluginBootstrap` class of our plugin.
### Registering inside the plugin bootstrapper
:::note
This requires you to use a [Paper plugin](/paper/dev/getting-started/paper-plugins) (with a `paper-plugin.yml`).
If you are not using `paper-plugin.yml`, you can instead [register your commands inside your plugin's main class](#registering-inside-the-plugins-main-class).
:::
We can get access to a `LifecycleEventManager` capable of registering commands by running `context.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {})`
inside our bootstrap method, like this:
```java title="ExamplePluginBootstrap.java"
public final class ExamplePluginBootstrap implements PluginBootstrap {
@Override
public void bootstrap(final BootstrapContext context) {
context.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
// register your commands here ...
});
}
}
```
A quick recap on what all of this means:
By running `context.getLifecycleManager()`, we get a `LifecycleEventManager` object. We can call
`LifecycleEventManager#registerEventHandler(LifecycleEventType, LifecycleEventHandler)` on that to register our lifecycle event. The first parameter declares the lifecycle event type we want to register the handler for, the second parameter is an interface that looks like this:
```java
@FunctionalInterface
public interface LifecycleEventHandler {
void run(E event);
}
```
Due to it being a functional interface, we can, instead of implementing it, just pass in a lambda which has one parameter, `E`, and no return value. The `E` generic type is a
`ReloadableRegistrarEvent`, which is thus also the type of our lambda parameter.
The `ReloadableRegistrarEvent` class has two methods: `ReloadableRegistrarEvent.Cause cause()` and `Commands registrar()`. The more relevant method for us is
the `registrar` one. With it we can register our commands.
:::tip
Registering commands in the plugin bootstrapper allows for your commands to be used by datapack functions earlier in the server startup process.
:::
### Registering inside the plugin's main class
Getting access to a `LifecycleEventManager` for commands inside our plugin's main class is very similar to how you access it inside the PluginBootstrap class, with the difference
that instead of getting the `LifecycleEventManager` using the `BootstrapContext` provided to us in the bootstrap method of our PluginBootstrap class, we can just retrieve it using
`Plugin#getLifecycleManager`.
```java title="ExamplePlugin.java"
public final class ExamplePlugin extends JavaPlugin {
@Override
public void onEnable() {
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
// register your commands here ...
});
}
}
```
This follows the same concept as the PluginBootstrap one, just that instead of being given a `LifecycleEventManager`, we are instead given a
`LifecycleEventManager`. This doesn't really matter for our use cases, but you might as well be aware of that.
The rest of the methods works the exact same way as with the `PluginBootstrap` parameterized `LifecycleEventManager`.
## Registering commands using the Commands class
Now that we have access to the instance of a `Commands` class via `event.registrar()` in our event handler, we have access to a few overloads of the `Commands#register`
method.
### Registering a LiteralCommandNode
Most of the time, you will be using a `LiteralArgumentBuilder` to build up your command tree. In order to retrieve a `LiteralCommandNode` from that object, we need to call the
`LiteralArgumentBuilder#build()` method on it:
```java
LiteralArgumentBuilder command = Commands.literal("testcmd")
.then(Commands.literal("argument_one"))
.then(Commands.literal("argument_two"));
LiteralCommandNode buildCommand = command.build();
```
Or in short:
```java
LiteralCommandNode buildCommand = Commands.literal("testcmd")
.then(Commands.literal("argument_one"))
.then(Commands.literal("argument_two"))
.build();
```
Now that we have retrieved our `LiteralCommandNode`, we can register it. For that we have multiple overloads, which optionally allow us to set aliases and/or the description.
Registering our "testcmd" might look like this:
```java
context.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
event.registrar().register(buildCommand);
});
```
### Registering a BasicCommand
A [](jd:paper:io.papermc.paper.command.brigadier.BasicCommand) is a Bukkit-like way of defining commands. Instead of building up a command tree,
we allow all user input and retrieve the arguments as a simple array of strings. This type of commands is particularly useful for very simple, text based commands,
like a `/broadcast` command. You can read up on more details about basic commands [here](/paper/dev/command-api/misc/basic-command).
Assuming you already have your `BasicCommand` object, we can register it like this:
```java
final BasicCommand basicCommand = ...;
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
event.registrar().register("commandname", basicCommand);
});
```
Similar to the `LiteralCommandNode`, we also have overloads for setting various additional information for our command.
## Further reference
* For a quick reference on the LifecycleEventManager, click [here](/paper/dev/lifecycle).
---
# Requirements
A guide to setting requirements for commands.
Sometimes you want to limit a player's ability to use and/or view certain commands or subcommands. Exactly for this purpose,
the `ArgumentBuilder` class has a `requires(Predicate)` method to define a requirement in order to use that specific branch of a command tree.
As always, the generic parameter `S` is just a `CommandSourceStack`, providing us with the executing entity, the command sender, and the location of the command.
## Defining permissions
One of the most common use cases for requirements are permissions. Usually, these are checked on the **command sender**, as that is the actual entity/console/object
which ran the command, even if it is run as someone else (the executor). A simple command with a permission might look like this:
```java
Commands.literal("testcmd")
.requires(source -> source.getSender().hasPermission("permission.test"))
.executes(context -> {
context.getSource().getSender().sendRichMessage("You have permission to run this command!");
return Command.SINGLE_SUCCESS;
})
```
This command requires the `permission.test` permission to be had by a sender.
But you cannot only define permissions, you can also require a sender to be a server operator, like this:
```java
Commands.literal("testcmd")
.requires(source -> source.getSender().isOp())
.executes(context -> {
context.getSource().getSender().sendRichMessage("You are a server operator!");
return Command.SINGLE_SUCCESS;
})
```
## Defining more advanced predicates
You don't have to limit yourself to checking just for permissions - since it is a predicate, any boolean can be returned. For example, you can check for whether
a player has a diamond sword in their inventory:
```java
Commands.literal("givesword")
.requires(source -> source.getExecutor() instanceof Player player && !player.getInventory().contains(Material.DIAMOND_SWORD))
.executes(context -> {
context.getSource().getPlayerOrThrow().getInventory().addItem(ItemStack.of(Material.DIAMOND_SWORD));
return Command.SINGLE_SUCCESS;
})
```
At first glance, this works just fine. But it does have a very big flaw - since the player's client is not aware of the requirement, it still shows the command
as executable, even if the requirement resolves as false. But if the client tries to run the command, the server reports that this command doesn't exist (meaning
the requirement was not met):

How can we solve this? The `Player` interface has a method called [`updateCommands()`](jd:paper:org.bukkit.entity.Player#updateCommands()) just for this usecase. It resends the currently registered commands back to the
client in an attempt to reload commands. For now, we can create a new command with which the player can update its own commands in order to resync its command state:
```java
Commands.literal("reloadcommands")
.executes(context -> {
final Player player = context.getSource().getPlayerOrThrow();
player.updateCommands();
player.sendRichMessage("Successfully updated your commands!");
return Command.SINGLE_SUCCESS;
})
```
### Automating command reloads
Forcing a player to reload their own commands is not a viable option for user experience. For this reason, you can **automate** this behavior. It is safe to call
the update commands method as often as required, but it should generally be avoided as it can cost a great deal of bandwidth. If possible, you should instead place
these in very specific spots. Furthermore, this method is completely thread safe, meaning you are free to call it from an asynchronous context.
## Restricted commands
From 1.21.6 onwards, commands can now be restricted. This feature is used by Vanilla in order to make a player confirm whether they
really want to run a command from a run-command click event. That includes ones on text components or dialog buttons.
All Vanilla commands, which require operator status by default, are restricted:

### Restricting your commands
You can apply the same behavior to your commands by wrapping the predicate inside your `.requires` with `Commands.restricted(...)`.
A simple implementation might look like this:
```java
Commands.literal("test-req")
.requires(Commands.restricted(source -> true))
.executes(context -> {
context.getSource().getSender().sendPlainMessage("You passed!");
return Command.SINGLE_SUCCESS;
})
```

Inside the `.restricted` method you can put any logic which you would put into your `.requires` method.
It is nothing more but a simple wrapper around the usual `.requires` predicate:
```java
Commands.literal("mycommand")
.requires(Commands.restricted(source -> source.getSender().hasPermission("my.custom.permission")
&& source.getExecutor() instanceof Player player
&& player.getGameMode() == GameMode.ADVENTURE))
.executes(context -> {
// Command logic
})
```
---
# Basic commands
An overview of a Bukkit-style command declaration using Brigadier.
For very simple commands Paper has a way to declare Bukkit-style commands by implementing the [](jd:paper:io.papermc.paper.command.brigadier.BasicCommand) interface.
This interface has one method you have to override:
- `void execute(CommandSourceStack source, String[] args)`
And three more, optional methods which you can, but don't have to override:
- `Collection suggest(CommandSourceStack source, String[] args)`
- `boolean canUse(CommandSender sender)`
- `@Nullable String permission()`
## Simple usage
Implementing the execute method, your class might look like this:
```java title="YourCommand.java"
public class YourCommand implements BasicCommand {
@Override
public void execute(final CommandSourceStack source, final String[] args) {
}
}
```
With a `CommandSourceStack` you can retrieve basic information about the sender of the command, the location the command was send from,
and the entity for which the command was executed for. You can find more information on [our page on command executors](/paper/dev/command-api/basics/executors).
## The optional methods
You can freely choose whether to implement either of the mentioned, optional methods. Here is a quick overview on what which one does:
### `suggest(CommandSourceStack, String[])`
This method returns some sort of `Collection` and takes in a `CommandSourceStack` and a `String[] args` as parameters. This is similar to the
`onTabComplete(CommandSender, Command, String, String[])` method of the `TabCompleter` interface, which is used for tab completion on Bukkit commands.
Each entry in the collection that you return will be send to the client to be shown as suggestions the same way as with Bukkit commands.
### `canUse(CommandSender)`
With this method, you can set up a basic `requires` structure from Brigadier commands. [You can read more on that here](/paper/dev/command-api/basics/requirements).
This method returns a `boolean`, which is required to return `true` in order for a command sender to be able to execute that command.
:::note
If you override this method, overriding `permission()` does nothing. This is because the default implementation
uses the return value of `permission()`, which wouldn't be used anymore if you were to override it.
```java title="BasicCommand.java"
default boolean canUse(final CommandSender sender) {
final String permission = this.permission();
return permission == null || sender.hasPermission(permission);
}
```
:::
### `permission()`
With the permission method you can, similar to the `canUse` method, set the permission required to be able to execute and view this command.
## Registering basic commands
Registering a `BasicCommand` is very simple: In your plugin's main class, you can simply call one of the
[`registerCommand(...)`](jd:paper:org.bukkit.plugin.java.JavaPlugin#registerCommand(java.lang.String,io.papermc.paper.command.brigadier.BasicCommand))
methods inside the `onEnable` method.
```java title="ExamplePlugin.java"
public final class ExamplePlugin extends JavaPlugin {
@Override
public void onEnable() {
BasicCommand yourCommand = ...;
this.registerCommand("mycommand", yourCommand);
}
}
```
### Basic commands are functional interfaces
Because you only have to override one method, you can directly pass in a lambda expression. This is not recommended for styling
reasons, as it makes the code harder to read.
```java
@Override
public void onEnable() {
this.registerCommand(
"quickcmd",
(source, args) -> source.getSender().sendRichMessage("Hello!")
);
}
```
## Example: Broadcast command
As an example, we can create a simple broadcast command. We start by declaring creating a class which implements `BasicCommand` and overrides `execute` and `permission`:
```java title="BroadcastCommand.java"
public class BroadcastCommand implements BasicCommand {
@Override
public void execute(final CommandSourceStack source, final String[] args) {
}
@Override
public String permission() {
return "example.broadcast.use";
}
}
```
Our permission is set to `example.broadcast.use`. In order to give yourself that permission, it is suggested that you use a plugin like [LuckPerms](https://luckperms.net) or just give yourself
operator permissions. You can also set this permission to be `true` by default. For this, please check out the [plugin.yml documentation](/paper/dev/plugin-yml).
Now, in our `execute` method, we tell the sender that at least one argument is required in order to send a broadcast in case they defined no
arguments (meaning that `args` has a length of 0):
```java
if (args.length == 0) {
source.getSender().sendRichMessage("You cannot send an empty broadcast!");
return;
}
```
Next, we can retrieve the name of the executor of that command. If we do not find one, we can just get the name of the command sender, like this:
```java
final Component name = Objects.requireNonNullElseGet(source.getExecutor(), source::getSender).name();
```
This makes sure that we cover all cases and even allow the command to work correctly with `/execute as`.
Finally, we can build our broadcast message by joining the arguments to a string and send it via `Bukkit.broadcast(Component)`:
```java
final String message = String.join(" ", args);
final Component broadcastMessage = MiniMessage.miniMessage().deserialize(
"BROADCAST » ",
Placeholder.component("name", name),
Placeholder.unparsed("message", message)
);
Bukkit.broadcast(broadcastMessage);
```
And we are done! As you can see, this is a very simple way to define commands. Here is the final result of our class:
```java title="BroadcastCommand.java"
public class BroadcastCommand implements BasicCommand {
@Override
public void execute(final CommandSourceStack source, final String[] args) {
if (args.length == 0) {
source.getSender().sendRichMessage("You cannot send an empty broadcast!");
return;
}
final Component name = Objects.requireNonNullElseGet(source.getExecutor(), source::getSender).name();
final String message = String.join(" ", args);
final Component broadcastMessage = MiniMessage.miniMessage().deserialize(
"BROADCAST » ",
Placeholder.component("name", name),
Placeholder.unparsed("message", message)
);
Bukkit.getServer().broadcast(broadcastMessage);
}
@Override
public String permission() {
return "example.broadcast.use";
}
}
```
Registering the command looks like this:
```java
@Override
public void onEnable() {
this.registerCommand("broadcast", new BroadcastCommand());
}
```
And this is how it looks like in-game:

### Adding suggestions
Our broadcast command works pretty well, but it is lacking on suggestions. A very common kind of suggestion for text based commands are player names.
In order to suggest player names, we can just map all online players to their name, like this:
```java
@Override
public Collection suggest(final CommandSourceStack source, final String[] args) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
```
This works great, but as you can see here, it will always suggest all players, regardless of user input, which can feel unnatural at times:

In order to fix this, we have to do some changes:
First, we early return what we already have in case there is no arguments, as we cannot filter by input then:
```java
if (args.length == 0) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
```
After this, we can add a `filter` step to our stream, where we filter all names by whether they start with our current input, which is `args[args.length - 1]`:
```java
return Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
.filter(name -> name.toLowerCase().startsWith(args[args.length - 1].toLowerCase()))
.toList();
```
And we are done! As you can see, suggestions still work fine:

But when there is no player who starts with an input, it just suggests nothing:

### Final code
Here is the final code for our whole `BroadcastCommand` class, including the suggestions:
```java title="BroadcastCommand.java"
public class BroadcastCommand implements BasicCommand {
@Override
public void execute(final CommandSourceStack source, final String[] args) {
if (args.length == 0) {
source.getSender().sendRichMessage("You cannot send an empty broadcast!");
return;
}
final Component name = Objects.requireNonNullElseGet(source.getExecutor(), source::getSender).name();
final String message = String.join(" ", args);
final Component broadcastMessage = MiniMessage.miniMessage().deserialize(
"BROADCAST » ",
Placeholder.component("name", name),
Placeholder.unparsed("message", message)
);
Bukkit.getServer().broadcast(broadcastMessage);
}
@Override
public String permission() {
return "example.broadcast.use";
}
@Override
public Collection suggest(final CommandSourceStack source, final 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
A comparison between Brigadier and Bukkit commands.
## Registering commands
### The old Bukkit way
In order to register Bukkit commands programmatically, you would define a class that extends `Command`, and implements the `execute(...)` and `tabComplete(...)`
methods. This might look like this:
```java title="BukkitPartyCommand.java"
public class BukkitPartyCommand extends Command {
public BukkitPartyCommand(final String name, final String description, final String usageMessage, final List aliases) {
super(name, description, usageMessage, aliases);
}
@Override
public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) {
if (args.length == 0) {
sender.sendRichMessage("Please provide a player!");
return false;
}
final Player target = Bukkit.getPlayerExact(args[0]);
if (target == null) {
sender.sendRichMessage("Please provide a valid player!");
return false;
}
target.sendPlainMessage(sender.getName() + " started partying with you!");
sender.sendPlainMessage("You are now partying with " + target.getName() + "!");
return true;
}
@Override
public List tabComplete(final CommandSender sender, final String alias, final String[] args) throws IllegalArgumentException {
if (args.length == 1) {
return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
}
return List.of();
}
}
```
After that, you can define your command like this in your main class:
```java
this.getServer().getCommandMap().register(
this.getName().toLowerCase(),
new BukkitPartyCommand("bukkitparty", "Have a party", "/bukkitparty ", List.of())
);
```
As you can see, you have to do a lot of manual checking in order to register a single, very simple command. But how does
the Brigadier API do it?
### The new Paper way
First, we need to retrieve a `LiteralCommandNode`. That's a special Brigadier class that holds some sort of [command tree](/paper/dev/command-api/basics/command-tree).
In our case, it is the root of our command. We can do that by running `Commands.literal(String literal)`, which returns a
`LiteralArgumentBuilder`, where we can define some arguments and executors. Once we are done, we can call
`LiteralArgumentBuilder#build()` to retrieve our build `LiteralCommandNode`, which we can then register. That sounds complicated at first,
but once you see it in action, it looks less terrifying:
```java
public static LiteralCommandNode createCommand(final String commandName) {
return Commands.literal(commandName)
.then(Commands.argument("target", ArgumentTypes.player())
.executes(context -> {
final PlayerSelectorArgumentResolver resolver = context.getArgument("target", PlayerSelectorArgumentResolver.class);
final Player target = resolver.resolve(context.getSource()).getFirst();
final CommandSender sender = context.getSource().getSender();
target.sendPlainMessage(sender.getName() + " started partying with you!");
sender.sendPlainMessage("You are now partying with " + target.getName() + "!");
return Command.SINGLE_SUCCESS;
}))
.build();
}
```
Each `.then(...)` defines a new branch in our tree, which can either be a literal (`Commands.literal(String)`) or an argument
(`Commands.argument(String, ArgumentType)`). Each branch may or may not define an `.executes(Command)` executor. This is
where all the logic happens.
We will take a closer look at that in different pages, but for now, how do we register it? Paper uses a `LifecycleEventManager` system.
In a nutshell, that is a way to register commands (or tags) that get loaded each time the server reloads its resources, like using `/reload`.
Registering our command in the main class looks like this:
```java
this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> {
event.registrar().register(createCommand("paperparty"), "Have a nice party");
});
```
And we are done! As you can see here, both commands do the same thing:
 
---
# Audiences
How to use Adventure's Audiences.
Audiences wrap a collection of recipients that can receive messages. They can be used to send messages to individual
players, groups of players, or even the entire server (including the console).
## Who is an `Audience`?
All `CommandSender`s are single audiences. This includes players, the console, and command blocks. `Server`, `Team` and
`World` are all forwarding audiences. This means that they are made up of multiple audiences. For example, the server is
made up of all online players and the console.
This means that all the [](jd:adventure:net.kyori.adventure.audience.Audience)
methods are available on [](jd:paper:org.bukkit.command.CommandSender),
[](jd:paper:org.bukkit.Server), [](jd:paper:org.bukkit.scoreboard.Team)
and [](jd:paper:org.bukkit.World).
## `ForwardingAudience`
The [](jd:adventure:net.kyori.adventure.audience.ForwardingAudience)
wraps a collection of [](jd:adventure:net.kyori.adventure.audience.Audience)
instances and forwards messages to all of them. This is useful for sending messages to multiple audiences (players) at once.
```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 `Audience`s do?
Audiences are used for interacting with players. They can be used to send messages, play sounds, show bossbars, and more.
They are mostly used for sending other parts of the API to players. For example, you can send a [](jd:adventure:net.kyori.adventure.text.Component)
to a player using [](jd:adventure:net.kyori.adventure.audience.Audience#sendMessage(net.kyori.adventure.text.Component)).
## Pointers
Audiences can also provide arbitrary information, such as display name or UUID. This is done using the pointer system.
```java
// Get the uuid from an audience member, returning a UUID if available
Optional uuid = audience.get(Identity.UUID);
// Get the display name, returning a default value if not available
Component name = audience.getOrDefault(Identity.DISPLAY_NAME, Component.text(""));
```
---
# Internationalization
How to use Adventure's internationalization.
Generally it's a good idea to support translations in your plugin, especially if you want to
appeal to the largest user base. Adventure makes this simple by adding a server-side
translation layer to almost all text that ends up being displayed to clients.
:::note[Javadocs]
Adventure's Javadocs for all-things translations can be found [here](jd:adventure:net.kyori.adventure.translation.package-summary).
:::
## GlobalTranslator
All translation is done through [](jd:adventure:net.kyori.adventure.translation.GlobalTranslator).
You can render translations yourself and add new sources for translations.
You can add sources to the `GlobalTranslator` by creating instances of [](jd:adventure:net.kyori.adventure.translation.TranslationStore)
or implementing the [](jd:adventure:net.kyori.adventure.translation.Translator) interface yourself.
## Where translations work
Vanilla Minecraft handles translations on the client by using the language files bundled with the client or provided via resource packs. If you don't want to send custom language files
in a resource pack, server-side translations are the only alternative. They work anywhere the component API exists, except for [](jd:paper:org.bukkit.inventory.ItemStack)
display text like the display name or lore. So chat, entity display names, scoreboards, tab lists, etc., all support translations.
## Examples
### `ResourceBundle`
```properties title="src/main/resources/your/plugin/Bundle_en_US.properties"
some.translation.key=Translated Message: {0}
```
```java
TranslationStore.StringBased store = TranslationStore.messageFormat(Key.key("namespace:value"));
ResourceBundle bundle = ResourceBundle.getBundle("your.plugin.Bundle", Locale.US);
store.registerAll(Locale.US, bundle, true);
GlobalTranslator.translator().addSource(store);
```
This creates a new `TranslationStore` under a specified namespace. Then, a [](jd:java:java.util.ResourceBundle)
is created from a bundle located on the classpath with the specified [](jd:java:java.util.Locale).
Finally, that `ResourceBundle` is added to the store. That store is then added as a source to the `GlobalTranslator`.
This makes all the translations available server-side.
Now you can use your translation keys in translatable components.
```java
Component.translatable("some.translation.key", Component.text("The Argument"))
```
This will show to clients using the US English language: `Translated Message: The Argument`.
---
# Introduction
An introduction to how components work.
:::note
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. Components offer a number of benefits over plain text strings which are enumerated below.
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 the only formatting options being
confusing symbols like `§c` and `§k` to control basic colors and styles of text.
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 client's 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](https://mojira.dev/MC-190605#comment-id-606141)
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, it's
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();
```
:::note[In-Depth Documentation]
For complete documentation on the Adventure Component API Paper and Velocity use, please look at the
[Adventure documentation](/adventure).
:::
## 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>This is the parent component; its style is " +
"applied to all children.\nThis is the first child, " +
"which is rendered after the parent!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);
}
}
// ...
import static com.example.paperplugin.util.Components.mm; // replace with your own package
final Component component = mm("Hello World!");
```
We recommend using this format for user-facing input such as commands or configuration values.
:::note[In-Depth Documentation]
MiniMessage is a part of Adventure, and you can find its documentation on [Adventure's documentation](/adventure/minimessage/).
:::
:::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"
}
]
}
```
:::note[In-Depth Documentation]
The JSON format is fully documented on the [Minecraft Wiki](https://minecraft.wiki/w/Text_component_format).
:::
:::tip
There are online tools to make generating this format much easier like [JSON Text Generator](https://minecraft.tools/en/json_text.php).
:::
## Serializers
Paper and Velocity come bundled with different serializers for converting between
[](jd:adventure:net.kyori.adventure.text.Component)s and other forms of serialized text.
### [`GsonComponentSerializer`](jd:adventure:net.kyori.adventure.text.serializer.gson:module-summary)
Converts between `Component`
and JSON-formatted strings with convenience methods to directly deal with Gson's
[`JsonElement`](https://javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/JsonElement.html).
This conversion is lossless and is the preferred form of serialization for components that do not have to be edited by users regularly.
### [`MiniMessage`](jd:adventure:net.kyori.adventure.text.minimessage:module-summary)
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](/adventure/minimessage/api/#getting-started).
### [`PlainTextComponentSerializer`](jd:adventure:net.kyori.adventure.text.serializer.plain:module-summary)
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
[](jd:adventure:net.kyori.adventure.text.TranslatableComponent)s to be serialized
into a default language, but generally this shouldn't be used except in certain circumstances, like logging to a text file.
### [`LegacyComponentSerializer`](jd:adventure:net.kyori.adventure.text.serializer.legacy:module-summary)
:::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
[](jd:adventure:net.kyori.adventure.text.serializer.legacy:net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer#legacySection())
and [](jd:adventure:net.kyori.adventure.text.serializer.legacy:net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer#legacyAmpersand()).
:::
---
# Signed messages
A guide to working with SignedMessage objects.
Since [Minecraft version 1.19](https://minecraft.wiki/w/Java_Edition_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.
:::tip[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/ed783dd244ca0321bbd882c347892874).
:::
## How are signed messages represented in code?
Paper uses Adventure's [](jd:adventure:net.kyori.adventure.chat.SignedMessage)
object to represent a signed message. We differentiate two kinds of signed messages: system messages and non-system messages.
System messages (checked with [](jd:adventure:net.kyori.adventure.chat.SignedMessage#isSystem()))
are messages send by the server, whilst non-system messages are not.
You can also differentiate the **signed plain text** `String` content of the message
([](jd:adventure:net.kyori.adventure.chat.SignedMessage#message()))
from the unsigned, nullable [](jd:adventure:net.kyori.adventure.text.Component)
content ([](jd:adventure:net.kyori.adventure.chat.SignedMessage#unsignedContent())).
## Obtaining a signed message
Signed messages can be obtained in two ways.
1. From an [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent) using
[](jd:paper:io.papermc.paper.event.player.AbstractChatEvent#signedMessage()).
2. From an [](jd:paper:io.papermc.paper.command.brigadier.argument.ArgumentTypes#signedMessage())
Brigadier argument type.
## Using signed messages
You can send signed message objects to an [](jd:adventure:net.kyori.adventure.audience.Audience)
using the [](jd:adventure:net.kyori.adventure.audience.Audience#sendMessage(net.kyori.adventure.chat.SignedMessage,net.kyori.adventure.chat.ChatType$Bound))
method. You can obtain a [](jd:adventure:net.kyori.adventure.chat.ChatType$Bound) object
from the [](jd:adventure:net.kyori.adventure.chat.ChatType) interface.
Deleting messages is much simpler. Adventure provides the [](jd:adventure:net.kyori.adventure.audience.Audience#deleteMessage(net.kyori.adventure.chat.SignedMessage))
or [](jd:adventure:net.kyori.adventure.audience.Audience#deleteMessage(net.kyori.adventure.chat.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 [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent).
:::tip[AsyncChatEvent]
The [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent) is covered in the [chat events](/paper/dev/chat-events)
documentation page. If you want to read up on more detail on the chat renderer, you can do so there.
:::
### In-game preview

### Code
```java title="SignedChatListener.java" showLineNumbers
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 " » ".
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 deleteCrossIcon = 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 = deleteCrossIcon
.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(_ -> Bukkit.getServer().deleteMessage(event.signedMessage())));
// Send the base format but with the delete suffix.
return base.appendSpace().append(deleteCross);
});
}
}
```
---
# Custom events
A guide to show you how to add custom events to your plugin.
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 [](jd:paper:org.bukkit.event.Event).
Each event requires a [](jd:paper:org.bukkit.event.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 [](jd:paper:org.bukkit.event.block.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.
:::
```java title="PaperIsCoolEvent.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.
```java title="PaperIsCoolEvent.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.
```java title="ExamplePlugin.java"
public final 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.
```java title="PaperIsCoolEvent.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 cancel) {
this.cancelled = cancel;
}
}
```
Now, when the event is called, you can check if it is cancelled and act accordingly.
```java title="ExamplePlugin.java"
public final 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, [](jd:paper:org.bukkit.event.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 [](jd:paper:org.bukkit.event.Cancellable#isCancelled()) manually.
```java title="ExamplePlugin.java"
public final 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
How to use a custom InventoryHolder to identify custom inventories.
`InventoryHolder`s are a way to identify your plugin's 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 you're guaranteed to have methods available to handle your inventory.
## Creating a custom holder
The first step is to implement the [](jd:paper:org.bukkit.inventory.InventoryHolder) interface.
We can do this the following way: create a new class that will create our [](jd:paper:org.bukkit.inventory.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 [](jd:paper:org.bukkit.Bukkit#createInventory(org.bukkit.inventory.InventoryHolder,int)) instead and remove the argument.
:::
```java title="MyInventory.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 plugin's main class as it's required by the constructor. If you've used the static method and removed the constructor
argument you don't have to pass it here.
:::
```java
Player 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
[](jd:paper:org.bukkit.inventory.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.
Let's make an inventory that counts the amount of times we clicked a stone inside it.
First, let's modify our `MyInventory` class a little:
```java title="MyInventory.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));
}
/// Called from the event whenever the player clicks the stone.
public void addClick() {
this.clicks++;
this.updateCounter();
}
/// Update the counter item once the stone is clicked.
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` 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
A guide to the ItemStack data component API.
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](#example-cool-sword).
#### 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 ontop 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 = item.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 = item.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 = item.getData(DataComponentTypes.MAX_STACK_SIZE);
```
### Setting a valued data component
```java
// Set a custom model data value on this item
item.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)
item.unsetData(DataComponentTypes.TOOL);
// Reset a component to the default (prototype) value for its item type (e.g. max stack size)
item.resetData(DataComponentTypes.MAX_STACK_SIZE);
```
### Non-valued data components
Some components are only flags and don't carry any sort of value:
```java
// Make the item a glider to be used like elytra (combined with the equippable component)
item.setData(DataComponentTypes.GLIDER);
// Remove the glider flag
item.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.key());
// 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!")));
sword.setData(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments().add(Enchantment.SHARPNESS, 10));
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`](jd:paper:org.bukkit.inventory.ItemStack#matchesWithoutData(org.bukkit.inventory.ItemStack,java.util.Set))
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 is common when writing code. This guide outlines the common ways to debug 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 you've done before, as it's 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 you're 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 plugin's 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.getSLF4JLogger().debug("SuperDuperBad Thing has happened");
```
:::note[Logger Levels]
In some consoles, using the `warn` or `error` 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 isn't 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...`:

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`:

Finally, copy the command line arguments from the window, and paste these into your server's startup script.
These will go after the `java` command and before `-jar`. Once you have done this, you can click `OK`. For example:
```shell replace
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar paper-\{LATEST_PAPER_RELEASE}.jar --nogui
```
Once your server is running, you can use the bug icon in the top right to connect your debugger to the server:

#### Using the debugger
Let's 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.sendPlainMessage("You are in the nether!");
}
}
```
You can add a breakpoint to the line by clicking on the line number:

This will pause the code when it reaches that line. You can then use the debugger to inspect the values of variables:

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 [JPenilla's Gradle plugin](https://github.com/jpenilla/run-task) to run the server directly from the IDE.
See [here](https://github.com/jpenilla/run-task#basic-usage) for instructions on how to set up the plugin.
### Preventing timeouts while paused
While the server is paused at a breakpoint, it doesn't tick. If you stay paused for a while, two timeouts can interrupt your debugging session.
#### The watchdog
The watchdog can't tell a paused server apart from a frozen one. With the default configuration, it starts printing thread dumps
after 10 seconds without a tick and stops the server after 60 seconds.
To prevent this, start your server with the [`disable.watchdog`](/paper/reference/system-properties#disablewatchdog) system property:
```shell replace
java -Ddisable.watchdog=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar paper-\{LATEST_PAPER_RELEASE}.jar --nogui
```
If you run the server with the Gradle plugin, add the flag to the `runServer` task instead:
```kotlin title="build.gradle.kts"
tasks {
runServer {
jvmArgs("-Ddisable.watchdog=true")
}
}
```
:::caution
Only disable the watchdog on a development server. On a production server, it is what detects and reports a real freeze.
:::
#### Client timeouts
The Vanilla client disconnects after 30 seconds without receiving anything from the server, so you get kicked while the server is paused.
This timeout is part of the client and can't be changed from the server. If you need to stay connected,
install a client mod that raises it, such as [untimeout](https://modrinth.com/mod/untimeout).
---
# Dialog API
A guide to the dialog API introduced in 1.21.7.
import Dependency from "/src/components/Dependency.astro";
import Video from "/src/components/Video.astro";
import { LATEST_ADVENTURE_API_RELEASE } from "/src/utils/versions";
import DialogShowcaseMp4 from "./assets/dialogs/dialog-showcase.mp4?url";
import InputDialogShowcaseMp4 from "./assets/dialogs/input-dialog-showcase.mp4?url";
[Dialogs](https://minecraft.wiki/w/Dialog) are a feature added to Minecraft in the [1.21.6](https://minecraft.wiki/w/Java_Edition_1.21.6)
update. Paper released developer API for creating custom dialogs in 1.21.7. This section is meant
as an introduction to this API and what you can and cannot do with dialogs.
## What is a dialog?
Dialogs are a way for servers to send custom in-game menus to clients. They allow for displaying various information
or provide an easy way to gather user input.
Dialogs can be shown to players during the configuration phase or normal gameplay, which makes them a very
versatile tool. A simple dialog might look like this:

The dialog shown here is a **confirmation**-type dialog, which just means it always contains two buttons, one meant for
confirmation and one meant for refusal.
## Showing dialogs
Dialogs can be shown in-game using the `/dialog show