Protocol Control - A minimal packet manipulation library

This is a discussion topic for the Ore project, Protocol Control. View the full project on Ore for downloads and more information.

Protocol Control

Under development and not stable!

A minimal packet manipulation library for Sponge.



Note: If you do not have Gradle installed then use ./gradlew for Unix systems or Git Bash and gradlew.bat for Windows systems in place of any ‘gradle’ command.

In order to build ProtocolControl you simply need to run the gradle command. You can find the compiled JAR file in ./build/libs labeled similarly to ‘protocolcontrol-x.x.x-SNAPSHOT.jar’.


Using ProtocolControl in your plugin requires you to use ForgeGradle and add ProtocolControl as a compile-time dependency.

repositories {
  maven { url "" }

dependencies {
  compile "com.ichorpowered:protocolcontrol:0.0.2"


To get started, you will need to get the instance of the ProtocolService from the ServiceManager.

Optional<ProtocolService> protocolService = Sponge.getServiceManager().provide(ProtocolService.class);

Creating a Listener

To create a packet listener, you need to add the @Subscribe annotation on your listener methods, with a parameter for PacketEvent with a generic argument of the Packet<?> type you are listening for.

public void onLoginEvent(PacketEvent<SPacketLoginSuccess> event) {

You can register the listener by passing in the listener object to ProtocolEvent#register. You can also unregister the listener with ProtocolEvent#unregister.

The ProtocolEvent is acquired from ProtocolService#events.

Manipulating a Packet

The event has a method to get the ChannelProfile the packet is going to or coming from and the PacketDirection.

You have access to the original instance of the packet as well as a way to override the packet.

public void onLoginEvent(PacketEvent<SPacketLoginSuccess> event) {
  SPacketLoginSuccess packet = event.packet(); // Gets the packet instance.
  event.packet(...); // Override the packet instance.

You can also prevent a packet from reaching its destination by cancelling the event.

public void onLoginEvent(PacketEvent<SPacketLoginSuccess> event) {
  if (!event.cancel()) event.cancel(true); // If the packet is not already cancelled, cancel it.

Wrapping a Packet

PacketRemapper provides an optional wrapper for your packet, which provides methods to get and set the fields for a packet. This is particularly useful but not limited to packets that do not provide convenient getters or setters. You just need to know the field type and the index for the field of that type in order to use them.

public void onLoginEvent(PacketEvent<SPacketLoginSuccess> event) {
  PacketRemapper.Wrapper<SPacketLoginSuccess> wrapper = this.remapper.wrap(event.packet());
  GameProfile profile = wrapper.<GameProfile>get(GameProfile.class, 0); // Gets the game profile.
  wrapper.<GameProfile>set(GameProfile.class, 0, ...); // Sets the game profile.

The PacketRemapper is acquired from ProtocolService#remapper.

Sending a Packet

You can send a new packet to the client or to the server using the ChannelProfile#send method.

public void onPlayerMove(final MoveEntityEvent event, final @First Player player) {
  final Location<World> location = player.getLocation();
  final ChannelProfile profile = this.protocolChannel.profile(player.getUniqueId());
  try {
    final PacketRemapper.Wrapped<SPacketBlockChange> blockChange = this.remapper.wrap(new SPacketBlockChange());
    blockChange.set(BlockPos.class, 0, new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
    blockChange.set(IBlockState.class, 0, (IBlockState) BlockTypes.WATER.getDefaultState());

    profile.send(PacketDirection.OUTGOING, blockChange.packet()); // Sends the block change packet to the client.
  } catch(Throwable throwable) {

The ProtocolChannel is acquired from ProtocolService#channels. You can then grab the players ChannelProfile using ProtocolChannel#profile.

1 Like

A new version has been released for Protocol Control, it is available for download here.

Protocol Control v0.0.1

These builds are still unstable and experimental!

This is the first release of Protocol Control fixing significant problems since the first snapshot. This release contains some breaking changes, which are likely to occur again in future experimental releases. It is highly recommended to update.


  • Fixes protocol injection lifecycle, ensuring the service is provided early and injection of the channels occur slightly later.
  • Fixes packet remapper setters using the incorrect MethodHandle to update fields.
  • Fixes packets sending to the wrong channel when using ChannelProfile#send.


  • Adds ProtocolChannel providing a way to acquire ChannelProfiles. Useful for sending packets through ChannelProfile#send.
  • Adds improved safety checks on API and slight changes to error handling.


  • Adds usage documentation for all implemented features.
  • Relocates the kyori dependencies, avoiding any conflicting versions from other plugins.

A new version has been released for Protocol Control, it is available for download here.

Protocol Control v0.0.2

These builds are still unstable and experimental!

This release of Protocol Control aims to clean up a few minor design errors and improve documentation. This release contains some breaking changes. It is highly recommended to update.


  • Adds and updates javadocs over all areas of the project.
  • Adds more exception handling and further details to exception handling over all areas of the project.
  • Adds a condition in the PacketHandler to make sure there are active subscribers listening before handling packets.
  • Renames ProtocolChannel#clear to ProtocolChannel#remove.
  • Removes visibility of the enable, disable and setup methods in classes where they do not need to be public.
  • Removes the property to hold ChannelProfiles as weak values in the channels map.


  • Adds a new maven repository, with documented usage, for publishing builds such as these, including source and javadoc artifacts.
  • Adds a .editorconfig for contributors.