Library / SDK
dmulloy2/ProtocolLib avatar
dmulloy2/ProtocolLib

ProtocolLib: a packet level API for Minecraft plugins that outlives obfuscation

Provides read and write access to the Minecraft protocol with Bukkit.

1,330 stars338 forksJavaGPL-2.0

At a glance

What is it?
ProtocolLib gives Bukkit and Spigot plugins an event based API over Minecraft packets, with an index based read and write system that hides the obfuscated server classes. The approach works by deducing names through reflection rather than hard coding them, which is what lets one build survive Minecraft version changes.
Who is it for?
ProtocolLib fits plugin authors whose feature genuinely needs the wire protocol, such as reading a field the server API does not expose, cancelling a packet, or injecting one, and it fits anyone who wants a stable surface that does not break on every Minecraft release.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem is that CraftBukkit classes change on every release

The project states the problem in plain terms. Certain tasks are impossible with the standard Bukkit API and may require working with, and even modifying, Minecraft directly. A common technique is to modify incoming and outgoing packets, or to inject custom packets into the stream. The project calls that technique cumbersome, and notes that most implementations break as soon as a new version of Minecraft is released, mostly because of obfuscation.

The second half of the problem is harder. Different plugins that use this technique may hook into the same classes, with unpredictable outcomes. More often this causes plugins to crash, but it can also lead to more subtle bugs.

That second point is the one that gets a plugin server manager into trouble. A packet hook is not an isolated call into a library, it is a claim on a piece of shared mutable state, namely the server's own network pipeline. Two plugins claiming the same path will interfere, and the failure mode depends on load order and on which of them happens to run first.

The audience for ProtocolLib is therefore quite specific. It is a plugin author who has already established that the Bukkit API cannot express the requirement, and who wants a supported way to express it that will not have to be rewritten for every Minecraft version. It is not a general purpose plugin toolkit, and treating it as one is how projects end up depending on a networking library for something a single event handler would have done.

An event API plus an index based read and write system

ProtocolLib's answer is described as an event API, much like Bukkit, that lets plugins monitor, modify or cancel packets sent and received. The claim attached to it is that the API also hides the gritty, obfuscated classes behind a simple index based read and write system, so you no longer have to reference CraftBukkit.

Those are two different features and it is worth separating them. The event API is the familiar part: you register a listener for a packet type and get called back with an event you can cancel. The index based read and write system is the part that addresses obfuscation. A packet field is addressed by position rather than by name, so a field whose underlying member is renamed between Minecraft versions is still field zero of that packet type.

The accessors follow that model. A packet exposes typed accessors such as getStrings(), getDoubles(), getFloat(), getBlockPositionCollectionModifier() and getVectors(), and each of them is read or written with an index.

java
PacketContainer fakeExplosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
fakeExplosion.getDoubles()
    .write(0, player.getLocation().getX())
    .write(1, player.getLocation().getY())
    .write(2, player.getLocation().getZ());
fakeExplosion.getFloat().write(0, 3.0F);

The alternative the documentation contrasts this with is the raw approach, where you construct a NMS class and push it through the player handle directly. The comparison is fair: the indexed version does not name a single obfuscated member.

Reflection is what buys the version tolerance

The compatibility section explains the mechanism, and it is the most important paragraph in the project documentation.

The stated goal was maximum compatibility with CraftBukkit, and the author's expectation is that ProtocolLib will not need updating for anything but bug fixes and new features. The reason given is reflection: essentially, no name is hard coded. Every field, method and class is deduced by looking at field types, package names or parameter types, and the documentation remarks that this is remarkably consistent across different versions.

Read that carefully, because it is a claim about the shape of the Minecraft code rather than about Minecraft itself. If a class can be identified by its field types and its package, then a rename does not break the identification. A change in the order of fields would break an index based accessor, and a change in a field's type would break the deduction. The consistency the author relies on is empirical, observed across versions, not guaranteed by Mojang.

That is why the reflection approach and the index based API are the same decision. Reflection finds the members; the index tells you which member you meant once it has been found. Neither part would be enough on its own.

It also explains why the project ships a compatibility artifact for Spigot. The mapping from a logical packet type to a concrete implementation class differs between the Paper and Spigot server internals, and the same logical API has to land on different classes in each.

Registering a listener that cancels or rewrites a packet

Wiring the library into a plugin takes three steps.

First, get a reference to the protocol manager in onLoad() or onEnable().

java
private ProtocolManager protocolManager;

public void onLoad() {
    protocolManager = ProtocolLibrary.getProtocolManager();
}

Second, register a listener for a packet type with a priority. The example that disables all sound effects is a server side listener for a packet sent by the server to a client.

java
protocolManager.addPacketListener(new PacketAdapter(
    this,
    ListenerPriority.NORMAL,
    PacketType.Play.Server.NAMED_SOUND_EFFECT
) {
    @Override
    public void onPacketSending(PacketEvent event) {
        event.setCancelled(true);
    }
});

Third, and this is the part that requires the index based system, read and modify the packet contents. The chat censoring example reads the message from string index zero and cancels the packet when it contains a blocked word.

java
PacketContainer packet = event.getPacket();
String message = packet.getStrings().read(0);

Sending a packet follows the same pattern. You build a PacketContainer for a type, fill it through the accessors, and hand it to the manager with sendServerPacket. The full example in the documentation writes a fake explosion to a player using getDoubles(), getFloat(), getBlockPositionCollectionModifier() and getVectors(), with no NMS type named anywhere.

The direction of the callback matters when you wire this up. A packet sent by the server to a client arrives through the sending callback, and a packet sent by a client to the server through the receiving callback, so a listener registered for the wrong one will never fire.

Three artifacts for Paper, for Spigot and for your build

Getting the dependency right is more involved than a single jar name suggests, and the project spells out the distinction.

ProtocolLib.jar is the primary Paper plugin. Spigot servers must use the compatibility artifact, ProtocolLib-Spigot.jar. The Maven artifact is the platform neutral library shared by both distributions, which is the one you put in your own build.

For a plugin, ProtocolLib is added to the build path and then declared in plugin.yml like any other plugin, as a dependency or a soft dependency.

yaml
depend: [ ProtocolLib ]

The Maven coordinates are net.dmulloy2 with artifactId ProtocolLib, version 5.4.0, and the documented scope is provided.

xml
<dependency>
  <groupId>net.dmulloy2</groupId>
  <artifactId>ProtocolLib</artifactId>
  <version>5.4.0</version>
  <scope>provided</scope>
</dependency>

The Gradle form uses the same coordinates and the same idea.

gradle
repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'net.dmulloy2:ProtocolLib:5.4.0'
}

That compileOnly versus provided choice is the point of both examples. The library is not bundled into your plugin, the server provides it at runtime, and that is what stops two plugins each shipping their own copy of ProtocolLib from hooking the same classes and producing exactly the interference the project set out to prevent.

To build ProtocolLib itself, the project uses Gradle, and the root project folder is where you work.

bash
./gradlew build

The other targets named are clean, test and shadowJar, and shadowJar produces a jar with all dependencies included, ByteBuddy being the one named, for both Paper and Spigot.

A dev build tag, a numbered release, and a plugin server that owns the risk

The release surface is worth reading carefully. The project publishes a Spigot page, a releases page, a dev build tag, JavaDoc and a link to the Minecraft protocol wiki, which is the reference for the packet format itself and is the more useful document when you are working out a new field.

The tags tell you how the project ships. The newest numbered release listed is 5.4.0 from 2025-08-07, and 5.3.0 came on 2024-10-20. Alongside them there is a rolling dev-build tag, dated 2025-08-10 in the listing, which is a moving target rather than a version. The last push to the repository was on 2026-10-06, so work continues between numbered releases and the dev build is where you would look for the current state of the code.

For a plugin author, that gives you a choice between two risk profiles. A numbered release is a fixed artifact with a version you can name in your plugin.yml and in your Maven coordinates, which is what a server operator expects. The dev build is where compatibility fixes for a new Minecraft version land first, and using it means your plugin depends on an artifact that changes under you.

The version you pick also determines which server you can run on, since the Paper and Spigot artifacts differ, and the compatibility between a ProtocolLib build and a given Minecraft version is not documented as a support matrix anywhere in the project description. The practical approach is to pin the numbered release, test on the Minecraft version you target, and read the release notes when you upgrade.

One structural note from the repository layout. Besides src/, there are separate paper/ and spigot/ directories, plus TinyProtocol/, and the top level also carries build.gradle.kts, settings.gradle, gradle.properties, a jitpack.yml and an AGENTS.md. The paper/ and spigot/ split is the packaging difference in the repository itself.

Editorial conclusion

ProtocolLib fits plugin authors whose feature genuinely needs the wire protocol, such as reading a field the server API does not expose, cancelling a packet, or injecting one, and it fits anyone who wants a stable surface that does not break on every Minecraft release. It does not fit a plugin that can be built on the standard Bukkit API, since the project itself says those tasks do not need it, and it does not fit a server operator who wants a dependency with a predictable release cadence, because the newest numbered release listed is 5.4.0 from 2025-08-07 while a rolling dev-build tag is also published. Check three things before you commit. Check the artifact you are actually shipping, since ProtocolLib.jar is the primary Paper plugin, Spigot servers need the compatibility artifact ProtocolLib-Spigot.jar, and the Maven artifact is the platform neutral library shared by both. Check the scope of the dependency in your build, since the documented Maven coordinates use provided scope and the Gradle example uses compileOnly, so the server supplies the jar. And check the version you pin against your target Minecraft release, because the compatibility guarantee is a design property of the reflection layer rather than a stated support matrix. The last push was on 2026-10-06 and the license is GPL-2.0.

Frequently asked questions

What does ProtocolLib do for Minecraft plugins?

It provides an event API, similar to Bukkit, that lets plugins monitor, modify or cancel packets sent and received, and it hides the obfuscated server classes behind an index based read and write system so you no longer have to reference CraftBukkit.

How do I add ProtocolLib as a dependency in my plugin?

Add ProtocolLib.jar to the Java build path and declare depend: [ ProtocolLib ] in plugin.yml, or use the Maven coordinates net.dmulloy2:ProtocolLib with version 5.4.0 and provided scope, or the Gradle form with compileOnly.

Which ProtocolLib artifact should I use on Paper and on Spigot?

ProtocolLib.jar is the primary Paper plugin, Spigot servers must use the compatibility artifact ProtocolLib-Spigot.jar, and the Maven artifact is the platform neutral library shared by both distributions.

How does ProtocolLib survive Minecraft version changes?

The project attributes it to reflection: essentially no name is hard coded, since every field, method and class is deduced by looking at field types, package names or parameter types, which the documentation says is remarkably consistent across versions.

How do I build ProtocolLib from source?

It is built with Gradle, so run ./gradlew build in the root project folder. Other targets named in the documentation are clean, test and shadowJar, the last of which bundles the dependencies, ByteBuddy, into a jar for both Paper and Spigot.

What license is ProtocolLib released under?

GPL-2.0, published as License.txt at the root of the repository. The newest numbered release listed is 5.4.0 from 2025-08-07, and the last push was on 2026-10-06.

Official sources

  1. dmulloy2/ProtocolLib on GitHub
  2. Issues
  3. License: GPL-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dmulloy2-protocollib.svg)](https://hysenlabs.com/projects/dmulloy2-protocollib)

Was this useful?