Skip to main content
Runway uses PacketEvents to intercept and modify Minecraft network packets before they reach the client. This allows the plugin to format text with MiniMessage and apply placeholder replacements across all game UI elements.

Architecture

All listeners in Runway:
  • Extend AbstractListener, which extends PacketEvents’ SimplePacketListenerAbstract
  • Use PacketListenerPriority.NORMAL priority
  • Have access to ProcessHandler for text formatting and placeholder resolution
  • Can be individually enabled/disabled via configuration

Base Class Structure

public abstract class AbstractListener extends SimplePacketListenerAbstract {
    protected final ProcessHandler handler;
    protected final Config config;

    public AbstractListener(ProcessHandler handler, ConfigManager configManager) {
        super(PacketListenerPriority.NORMAL);
        this.handler = handler;
        this.config = configManager.config();
    }
}
Source: AbstractListener.java:9-19

Registration

Listeners are registered in the plugin’s onEnable() method using PacketEvents’ EventManager:
EventManager manager = PacketEvents.getAPI().getEventManager();
manager.registerListeners(
    new SystemChatListener(processHandler, configManager),
    new TablistListener(processHandler, configManager),
    new InventoryListener(processHandler, configManager),
    new TitleListener(processHandler, configManager),
    new ScoreboardListener(processHandler, configManager),
    new ItemListener(processHandler, configManager)
);
PacketEvents.getAPI().init();
Source: Runway.java:50-58

Available Listeners

System Chat

Intercepts chat messages and system notifications

Tablist

Formats player list headers and footers

Title

Handles title and subtitle text formatting

Inventory

Formats inventory titles and item display

Scoreboard

Processes scoreboard objective and score text

Item

Formats individual item names and lore

How Listeners Work

  1. Packet Interception: PacketEvents captures outgoing server packets before they’re sent to the client
  2. Type Checking: Each listener checks if the packet type matches what it handles
  3. Config Validation: Verifies the listener is enabled in the configuration
  4. Processing: Extracts text components and passes them to ProcessHandler
  5. Modification: Updates the packet with formatted text
  6. Transmission: The modified packet is sent to the client

PacketEvents Integration

Runway initializes PacketEvents during the plugin load phase:
@Override
public void onLoad() {
    PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
    PacketEvents.getAPI().getSettings()
        .reEncodeByDefault(true)
        .bStats(false)
        .checkForUpdates(false);
    PacketEvents.getAPI().load();
}
Source: Runway.java:24-31

Key Settings

  • reEncodeByDefault(true): Ensures packet modifications are properly encoded
  • bStats(false): Disables metrics collection
  • checkForUpdates(false): Disables update checking

Common Patterns

All listeners follow this pattern:
@Override
public void onPacketPlaySend(PacketPlaySendEvent e) {
    // 1. Check if listener is enabled and packet type matches
    if (!config.getOrDefault("listeners.example", true) ||
        e.getPacketType() != PacketType.Play.Server.EXAMPLE) return;

    // 2. Get the player and create packet wrapper
    Player player = e.getPlayer();
    WrapperPlayServerExample packet = new WrapperPlayServerExample(e);
    
    // 3. Process and update the text component
    packet.setMessage(handler.processComponent(packet.getMessage(), player));
}

Performance Considerations

  • Listeners use early returns to avoid unnecessary processing
  • Config checks happen before packet deserialization
  • Text processing is only applied when listeners are enabled
  • PacketEvents handles packet re-encoding efficiently

Build docs developers (and LLMs) love