Proper code formatting is essential for maintaining Paper’s codebase. This guide covers all formatting requirements, including Paper-specific markers, imports, and style conventions.
Paper Code Markers
All modifications to Vanilla Minecraft files must be marked with Paper comments. These markers help track changes across files and maintain them long-term.
Paper code markers are required for all Vanilla file modifications. They remain crucial even a decade later for understanding what changed and why.
Every Paper modification needs a descriptive comment:
// Paper start - <COMMIT DESCRIPTION>
The description should explain:
- Why the change was made
- What it was before
- What the change does
You can add additional details after a semicolon (;) or on the next line.
Single-Line Changes
For one-line modifications, add the marker at the end:
entity.getWorld().dontBeStupid(); // Paper - Move away from beStupid()
Multi-Line Changes
For multi-line modifications, wrap the code with start/end markers:
// Paper start - Use plugin-set spawn
// entity.getWorld().explode(entity.getWorld().getSpawn());
Location spawnLocation = ((CraftWorld) entity.getWorld()).getSpawnLocation();
entity.getWorld().explode(new BlockPosition(spawnLocation.getX(), spawnLocation.getY(), spawnLocation.getZ()));
// Paper end - Use plugin-set spawn
The end marker should use the same description as the start marker for easy matching.
Complete Example
entity.getWorld().dontBeStupid(); // Paper - Move away from beStupid()
entity.getFriends().forEach(Entity::explode);
entity.updateFriends();
// Paper start - Use plugin-set spawn
// entity.getWorld().explode(entity.getWorld().getSpawn());
Location spawnLocation = ((CraftWorld) entity.getWorld()).getSpawnLocation();
entity.getWorld().explode(new BlockPosition(spawnLocation.getX(), spawnLocation.getY(), spawnLocation.getZ()));
// Paper end - Use plugin-set spawn
Import Guidelines
Paper has specific rules for imports in Vanilla Minecraft classes to prevent patch conflicts.
Use Fully Qualified Names (FQN)
When adding new types to a Vanilla class, use the fully qualified class name instead of adding imports to the top of the file.
Correct approach:
import net.minecraft.server.MinecraftServer;
// Don't add new imports here
public class SomeVanillaClass {
public final org.bukkit.Location newLocation; // Paper - add location
}
Why? This prevents future patch conflicts in the import section.
When to Add Imports
You can add an import with a comment if:
- The type is used a significant number of times
- Using FQN would make the code too verbose
For only a couple of uses, always prefer FQN.
Java Style Guide
Paper generally follows the Oracle Java style (default in most IDEs) with a few modifications:
Line Length
- 80 characters is not a hard limit
- Go over 80 lines if it improves readability
- Exception: Some Spigot-related files have stricter limits
Match Surrounding Code
When in doubt, or if the surrounding code uses a clearly different style, match the existing style.
Avoid var Keyword
Usage of the var keyword is discouraged.
Why?
- Makes patch files harder to read
- Can cause confusion during updates due to changed return types
Exception: Use var only when:
- The line would be extremely long without it
- It’s filled with hard-to-parse generics
- The base type is already obvious from context
Example of acceptable var usage:
// Acceptable - extremely verbose generic type
var complexMap = new ConcurrentHashMap<ResourceLocation, Map<String, List<ComplexType>>>();
// Not acceptable - simple type
var player = event.getPlayer(); // Use Player player = ...
Nullability Annotations
Paper is transitioning between nullability annotation libraries:
For New Classes You Create
Use org.jspecify.annotations:
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked // Types are non-null by default
public class MyNewClass {
private @Nullable String optionalValue;
private String requiredValue; // Non-null by default
public @Nullable String getOptional() {
return this.optionalValue;
}
}
For Existing Classes
Keep using org.jetbrains.annotations:
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ExistingClass {
public void doSomething(@NotNull Player player, @Nullable String message) {
// ...
}
}
These will be migrated to JSpecify later. Don’t convert them yourself.
API Validation with Preconditions
When validating API inputs or object state, use the Preconditions class from Guava.
Checking Method Arguments
Use Preconditions.checkArgument() to validate method parameters:
@Override
public void sendMessage(Player player, Component message) {
Preconditions.checkArgument(player != null, "player cannot be null");
Preconditions.checkArgument(player.isOnline(), "player %s must be online", player.getName());
Preconditions.checkArgument(message != null, "message cannot be null");
// rest of code
}
Do not use Preconditions.checkNotNull()—it throws NullPointerException, making it harder to distinguish between internal errors and invalid arguments.
Throws: IllegalArgumentException when conditions fail
Checking Object State
Use Preconditions.checkState() to validate object state inside methods:
private Player player;
@Override
public void sendMessage(Component message) {
Preconditions.checkArgument(message != null, "message cannot be null");
Preconditions.checkState(this.player != null, "player cannot be null");
Preconditions.checkState(this.player.isOnline(), "player %s must be online", this.player.getName());
// rest of code
}
Throws: IllegalStateException when conditions fail
Preconditions Best Practices
- Use
checkArgument() for validating input parameters
- Use
checkState() for validating object state
- Include descriptive error messages
- Use format strings (
%s) for dynamic values
Obfuscation Helpers
Obfuscation helpers improve readability for unmapped variables or poorly-named parameters.
Purpose
- Make code more readable
- Aid future maintenance
- Should be easy for the JVM to inline
Example
double d0 = entity.getX(); final double fromX = d0; // Paper - OBFHELPER
double d1 = entity.getZ(); final double fromZ = d1; // Paper - OBFHELPER
// ...
this.someMethod(fromX, fromZ); // Paper
Use your best judgment and do what fits best in your situation. The goal is always readability and maintainability.
Before submitting your PR, verify:
Common Mistakes
Missing Paper Markers
// ❌ Wrong - no Paper marker
public final Location spawnLocation;
// ✅ Correct
public final org.bukkit.Location spawnLocation; // Paper - custom spawn location
Adding Unnecessary Imports
// ❌ Wrong - new import added
import org.bukkit.Location;
public class VanillaClass {
public final Location spawn; // Paper
}
// ✅ Correct - FQN used
public class VanillaClass {
public final org.bukkit.Location spawn; // Paper - custom spawn
}
Mismatched Start/End Markers
// ❌ Wrong - descriptions don't match
// Paper start - Use plugin-set spawn
Location spawn = getCustomSpawn();
// Paper end - Custom spawn location
// ✅ Correct - identical descriptions
// Paper start - Use plugin-set spawn
Location spawn = getCustomSpawn();
// Paper end - Use plugin-set spawn
Using checkNotNull
// ❌ Wrong - throws NullPointerException
Preconditions.checkNotNull(player, "player cannot be null");
// ✅ Correct - throws IllegalArgumentException
Preconditions.checkArgument(player != null, "player cannot be null");
IDE Configuration
Most Java IDEs are pre-configured with Oracle Java style. For best results:
- IntelliJ IDEA: Use the default Java code style
- Eclipse: Use the default Java formatter
- VS Code: Install the Java extension pack
Paper may apply minor formatting fixes during PR review, but following these guidelines reduces review time.