Paper-Specific Features
Relevant source files
The following files were used as context for generating this wiki page:- Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperListeners.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/VoidWorldGenerator.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperNexAuth.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperPlatformHandle.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperBootstrap.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/common/config/ConfigurationKeys.java
- Plugin/src/main/java/xyz/xreatlabs/nexauth/common/AuthenticNexAuth.java
The Paper module of NexAuth provides the core authentication functionality for standalone Minecraft servers running on Paper, Spigot, or their forks. It operates independently of any proxy like Velocity or BungeeCord. This implementation directly hooks into the server's lifecycle, from player login to world management, to create a secure authentication environment.
This module leverages the Paper/Spigot API for event handling and world manipulation, while also using the PacketEvents library for low-level packet interception. This combination allows NexAuth to control the player's state before they are fully authenticated, providing features like premium user verification, inventory hiding, and a secure "limbo" state.
Core Components
The Paper implementation is structured around several key classes that manage the plugin's lifecycle and interaction with the server.
| Class | Description | Source File |
|---|---|---|
PaperBootstrap | The main entry point of the plugin. It handles loading, dependency management, and server compatibility checks. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperBootstrap.java |
PaperNexAuth | The central class for the Paper implementation. It initializes all major components like event listeners, command managers, and configuration. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperNexAuth.java |
PaperPlatformHandle | An adapter that implements the PlatformHandle interface, bridging common NexAuth logic with the Paper/Bukkit API for server operations. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperPlatformHandle.java |
PaperListeners | Handles high-level Bukkit player events (PlayerJoinEvent, etc.) and low-level packet events for the authentication process. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperListeners.java |
VoidWorldGenerator | A custom chunk generator for creating empty "limbo" worlds. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/VoidWorldGenerator.java |
Blockers | A listener class registered to prevent unauthenticated players from performing actions (e.g., moving, chatting). | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperNexAuth.java:136 |
InventoryPacketListener | Intercepts packets to hide a player's inventory until they have successfully authenticated. | Plugin/src/main/java/xyz/xreatlabs/nexauth/paper/PaperNexAuth.java:132-135 |
Component Relationships
The following diagram illustrates how the main components of the Paper module interact.
Initialization and Bootstrap
The plugin loading sequence begins with PaperBootstrap. It performs critical startup checks and initialization.
- Server Check: It verifies that the server is a compatible Adventure-API distribution (like Paper) and is not running behind a proxy. If the server is in online mode or has proxy forwarding enabled, the plugin will disable itself with an error.
- Library Loading: It uses
PaperLibraryManagerorBukkitLibraryManagerto download and load required dependencies from thelibraries.jsonfile. - Instantiation: It creates an instance of
PaperNexAuth, which then takes over the main enabling logic.
Sources: PaperBootstrap.java:27-52, PaperNexAuth.java:99-112
This diagram shows the initialization flow.
Player Authentication Flow
NexAuth on Paper uses a combination of standard Bukkit events and low-level packet interception to handle player authentication securely.
1. Pre-Login and Session Verification
When a player attempts to connect, PaperListeners intercepts the process at both the packet and event level.
- Packet Interception (
LOGIN_START): TheasyncPacketReceivemethod inPaperListenerscatches the initial login packet.- If the player is a premium user (and not from Floodgate), it initiates the Mojang session verification flow. It sends an
EncryptionRequestpacket to the client. - The client responds with an
EncryptionResponsepacket. - NexAuth decrypts the response, verifies the nonce, and contacts
sessionserver.mojang.comviahasJoinedto confirm the player's session is valid. - If the session is valid, a fake
LoginStartpacket is re-injected into the server to continue the login process with the player's correct premium UUID.
- If the player is a premium user (and not from Floodgate), it initiates the Mojang session verification flow. It sends an
- Event Handling (
AsyncPlayerPreLoginEvent): For offline-mode players, this event is used to fetch the user's data from the database and set their correct offline-mode UUID before they fully log in.
Sources: PaperListeners.java:114-121, 203-300
The following sequence diagram details the premium user authentication flow.
Sources: PaperListeners.java:203-300
2. Post-Login and World Placement
Once the initial login is handled, standard Bukkit events are used to place the player in the correct world and manage their state.
PlayerSpawnLocationEvent: This event is used to determine the player's initial spawn point. ThechooseServermethod is called to select either a limbo world (if unauthenticated) or a lobby world (if authenticated, e.g., via session). If the player was already in a valid world before a reload/rejoin, their previous location can be cached and restored.PlayerJoinEvent: When the player officially joins the server,onPostLoginis triggered. At this stage, security measures are applied:- The player's inventory is hidden using
InventoryPacketListener. - The player is made invisible.
- The authentication process is started (e.g., prompting for a password or 2FA).
- The player's inventory is hidden using
AuthenticatedEvent: After a player successfully authenticates, an internal event is fired. This triggers the reversal of the security measures: the player becomes visible again, and their inventory is revealed.
Sources: PaperListeners.java:124-128, 137-164, PaperNexAuth.java:118-126
Unauthenticated Player Restrictions
To secure the server, NexAuth heavily restricts what unauthenticated players can do.
Inventory Hiding
A key feature is packet-level inventory hiding, which prevents players from seeing or interacting with their items before logging in. This is managed by the InventoryPacketListener class, which is registered if the server is detected as Paper, Spigot, or CraftBukkit.
- Hiding: On
PlayerJoinEvent,inventoryListener.hideInventory(player)is called. This likely involves sending packets to the client that clear their inventory view. - Revealing: Upon successful authentication,
inventoryListener.revealInventory(player)is called to restore the player's inventory view.
This feature is controlled by the hide-player-inventory configuration key.
// In PaperNexAuth.java
// Hiding inventory on join
onJoin(event) -> inventoryListener.hideInventory(event.getPlayer());
// Revealing inventory on authentication
provider.subscribe(provider.getTypes().authenticated, event -> {
// ...
if (inventoryListener != null) {
inventoryListener.revealInventory(player);
}
});
Sources: PaperNexAuth.java:124, 132-135, ConfigurationKeys.java:160-167
Action Blocking
The Blockers class is registered as an event listener to prevent various actions, such as movement, chatting (except for auth commands), dropping items, and more. While the source for Blockers.java is not provided in this context, its registration implies its function.
Sources: PaperNexAuth.java:136
Limbo World Management
For unauthenticated players, NexAuth creates or uses designated "limbo" worlds. This ensures players are isolated in a secure, minimal environment.
World Creation and Configuration
The PaperPlatformHandle is responsible for managing these worlds.
- When a limbo world is requested via
getServer(name, true), it first checks if the world is already loaded. - If not, it creates a new
WorldCreator. - Crucially, it sets the generator to
nexauth:void, which corresponds to theVoidWorldGenerator. - After creation, it configures game rules to disable the day-night cycle and insomnia, sets a fixed spawn point, and ensures the spawn is kept in memory.
// In PaperPlatformHandle.java
@Override
public World getServer(String name, boolean limbo) {
// ...
var creator = new WorldCreator(name);
if (limbo) {
creator.generator("nexauth:void");
}
world = Bukkit.createWorld(creator);
if (limbo) {
world.setSpawnLocation(new Location(world, 0.5, world.getHighestBlockYAt(0, 0) + 1, 0.5));
world.setKeepSpawnInMemory(true);
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
world.setGameRule(GameRule.DO_INSOMNIA, false);
}
// ...
}
Sources: PaperPlatformHandle.java:60-83
Void World Generator
The VoidWorldGenerator is a simple custom generator that creates an almost entirely empty world. It places a single barrier block at 0, 69, 0 in the first chunk (0,0) to serve as a platform for the player to stand on. This minimalist approach reduces server load for limbo worlds.
// In VoidWorldGenerator.java
public class VoidWorldGenerator extends ChunkGenerator {
@Override
public void generateSurface(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) {
if (chunkX == 0 && chunkZ == 0) {
chunkData.setBlock(0, 69, 0, Material.BARRIER);
}
super.generateSurface(worldInfo, random, chunkX, chunkZ, chunkData);
}
}
Sources: VoidWorldGenerator.java:15-22
The registration of this custom generator happens in PaperBootstrap, which allows the nexauth:void key to be used in WorldCreator.
Sources: PaperBootstrap.java:21-24