Skip to content

Repository files navigation

PacketAPI

Modern, versioned access to the Minecraft protocol through direct NMS (CraftPlayer.getHandle(), ServerPlayer.connection.send). Menu/container packets live in PacketUxUi; sign input lives in SignGUI. Optional Netty intercept uses an instance-scoped injection path that refuses to silently overwrite another PacketAPI copy.

Codacy Badge

What it is

  • Per-version paperweight adapters (1.16 → 26.2) sharing one NMS implementation when the packet layout does not break
  • Typed PacketWrap around raw net.minecraft.network.protocol.Packet handles
  • Raw NMS first: every packet is observable as Object once injected — no per-packet wrapper allocation
  • Cached PacketType identity per class (PacketKind, domain, direction) for O(1) hot-path dispatch
  • Versioned views only where layout breaks: move, interact, dig (PlayerAction), use-item
  • Packet domains, cases, and exemptions so PacketAPI does not fight companion libraries (SignGUI, PacketUxUi)
  • PacketSendEvent / PacketReceiveEvent plus NMS player events (NmsPlayerJoinEvent, NmsSwingEvent, …)
  • Expanded facades: packets, players, connections, metadata, protocol, classifier, cases
  • Folia / Paper / Canvas region-safe schedulers
  • Ownership-aware PacketListenerBus.ensureInjected (single inject entry point) that detects foreign shaded copies via pipeline markers and backs off instead of hijacking
  • No duplication of PacketUxUi window click / open / set-slot / cursor / book packets

Add the dependency

Latest release: 1.0.2 — hosted on Reposilite.

Maven (pom.xml):

<repositories>
<repository>
<id>mastersmp-releases</id>
<url>http://repo.mastersmp.net/releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.opmasterleo</groupId>
<artifactId>PacketAPI</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>

Gradle (Groovy) (build.gradle):

repositories {
maven {
name ="mastersmp-releases"
url = uri("http://repo.mastersmp.net/releases")
isAllowInsecureProtocol =true
}
}
dependencies {
implementation("net.opmasterleo:PacketAPI:1.0.2")
}

Gradle (Kotlin DSL) (build.gradle.kts):

repositories {
maven {
name ="mastersmp-releases"
url = uri("http://repo.mastersmp.net/releases")
isAllowInsecureProtocol =true
}
}
dependencies {
implementation("net.opmasterleo:PacketAPI:1.0.2")
}

Send a packet

importnet.opmasterleo.packet.PacketAPI;
importnet.opmasterleo.packet.PacketAPIConfig;
importnet.opmasterleo.packet.event.PacketReceiveEvent;
importnet.opmasterleo.packet.event.PacketSendEvent;
importnet.opmasterleo.packet.packet.OwnedPacketPolicy;
importnet.opmasterleo.packet.packet.PacketExemption;
importnet.opmasterleo.packet.packet.PacketExemptions;
importnet.kyori.adventure.text.Component;
PacketAPIapi = PacketAPI.bootstrap(plugin, PacketAPIConfig.builder()
.autoInjectOnJoin(true)
.registerExemption(PacketExemption.builder("MyMenus")
.domains(net.opmasterleo.packet.packet.PacketDomain.UI_CONTAINER)
.nameContains("OpenScreen")
.build())
.build());
api.packets().setHealth(player, 20f, 20, 5f);
api.packets().keepAlive(player, System.currentTimeMillis());
api.cases().when(packet, view -> {
if (view.isUiOwned()) return;
if (view.isEntityLifecycle()) { /* ... */ }
});
@EventHandlerpublicvoidonReceive(PacketReceiveEventevent) {
if (event.getDomain() == net.opmasterleo.packet.packet.PacketDomain.UI_SIGN) {
return; // SignGUI owns this path
}
}

Built-in exemptions (disable with respectExemptionsOnSend(false)):

  • PacketExemptions.SIGN_GUI — sign editor packets
  • PacketExemptions.PACKET_UX_UI — menus, containers, slots, books, cursors

Listen without touching Netty (enable autoInjectOnJoin or call api.listeners().ensureInjected(player)):

importnet.opmasterleo.packet.event.PacketSendEvent;
importnet.opmasterleo.packet.event.PacketReceiveEvent;
importnet.opmasterleo.packet.event.NmsPlayerJoinEvent;
importnet.opmasterleo.packet.nms.packet.PacketKind;
importnet.opmasterleo.packet.nms.packet.PacketViews;
@EventHandlerpublicvoidonOutbound(PacketSendEventevent) {
// Raw NMS handle — zero-copyObjectraw = event.getPacket();
if (event.packetType().kind() == PacketKind.MOVE_PLAYER) {
PacketViews.MoveViewmove = PacketAPI.get().packets().parseMove(raw);
doublex = move.x();
}
}
@EventHandlerpublicvoidonInbound(PacketReceiveEventevent) {
if (event.packetType().kind() == PacketKind.INTERACT) {
PacketViews.InteractViewinteract = PacketAPI.get().packets().parseInteract(event.getPacket());
}
}
@EventHandlerpublicvoidonPacket(PacketSendEventevent) {
if (event.getPacketName().contains("SetHealth")) {
event.setCancelled(true);
}
}
@EventHandlerpublicvoidonJoin(NmsPlayerJoinEventevent) {
ObjectserverPlayer = event.getPlayerHandle();
}

Send any raw NMS packet (no factory required):

api.packets().send(player, myNmsPacket);

PacketAPI does not generate typed wrappers for every Clientbound* / Serverbound* class. Completeness comes from Netty interception + raw handles; use parseMove / parseInteract / parsePlayerAction / parseUseItem only for the hot serverbound families whose fields drift across versions.

Optional Netty filters (single entry point — never call low-level inject helpers):

api.listeners().addOutbound(player, (viewer, packet) -> OutboundPacketResult.keep());
// Conflict / ownership probe for status/debug:InjectionProbe.Resultprobe = api.detectInjectionConflicts(player);
Stringstatus = api.injectionStatus(player);

Build / publish

./gradlew shadowJar
./gradlew publish # Reposilite releases
./gradlew publishSnapshot # Reposilite snapshots

About

Modern access to the Minecraft protocol

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages