For Server Developers

Paper Plugin Development Best Practices

Async vs. sync, the persistent data container, modern commands, and how to test a plugin without a live server.

Paper has become the dominant Bukkit-API server implementation for a reason — better performance, an actively maintained fork, and an API that keeps evolving. Here's what matters if you're building on it seriously.

Event-Driven Architecture

Everything in a Paper plugin should be a reaction to an event, not a polling loop. Register listeners with explicit priorities (`LOWEST` → `MONITOR`) and understand that `MONITOR`-priority listeners should never mutate the event — they're for observing final outcomes, not changing them. Get priority ordering wrong and two plugins that should cooperate will silently fight each other.

Async vs. Sync — The Rule That Actually Matters

Anything touching the Bukkit API (blocks, entities, inventories, world state) must run on the main thread. Anything slow — database calls, HTTP requests, file I/O — must not. The `BukkitScheduler` gives you both `runTaskAsynchronously` and `runTask`; the standard pattern is: do the slow work async, then hop back to the main thread with a follow-up sync task to actually touch the world. Blocking the main thread, even briefly, shows up directly as server-wide lag.

Folia Changes This

Folia's regionized multithreading splits the world into independently-ticked regions, each with its own thread. Plugins built assuming a single main thread need Folia-aware scheduling (region schedulers, entity schedulers) to run correctly — a growing consideration as more networks adopt it for scale.

Persistent Data Containers (PDC) Over Raw NBT

The PDC is the supported way to attach custom data to items, entities, and blocks without fighting Mojang's internal NBT format directly. One common gotcha: data isn't automatically copied between holders — cloning an ItemStack doesn't always carry PDC tags the way developers expect, and it's a frequent source of "why did my custom item lose its data" bugs.

Modern Commands: Brigadier, Not String Parsing

Paper's Brigadier-based command API (built on Mojang's own command framework) replaces manual string-splitting with typed argument parsing and built-in tab completion. One side effect worth knowing: registering commands this way can affect `/reload` behavior — another good reason to avoid relying on `/reload` in production at all, and restart cleanly instead.

Configuration

Bukkit's `FileConfiguration` covers basic YAML config needs; for more complex, versioned, or validated config, `ConfigurationSerializable` or a library like Configurate gives you type safety and migration paths as your config schema evolves across plugin versions.

Testing Without a Live Server

MockBukkit lets you unit-test plugin logic against a simulated server environment — catching regressions before they ever touch a production world, and making it realistic to run CI against plugin code the way you would any other software project.

Common Pitfalls

Recent API Shifts Worth Tracking

Paper's move to Mojang mappings, the Brigadier command API, the registries/lifecycle API for plugin bootstrapping, and a new Dialog API for structured UI are all relatively recent — if your reference material predates them, expect some real API drift.