# Table of Contents
- [FAQ | packetevents](#faq-packetevents)
- [A Developer's Introduction | packetevents](#a-developer-s-introduction-packetevents)
- [Development Setup | packetevents](#development-setup-packetevents)
- [The Problem of Bundling | packetevents](#the-problem-of-bundling-packetevents)
- [Prerequisites | packetevents](#prerequisites-packetevents)
- [Advanced PacketEvents Example: Combining our Knowledge | packetevents](#advanced-packetevents-example-combining-our-knowledge-packetevents)
- [What is PacketEvents? | packetevents](#what-is-packetevents-packetevents)
- [Introduction to Packet Listeners | packetevents](#introduction-to-packet-listeners-packetevents)
- [Sending and Simulating Packets | packetevents](#sending-and-simulating-packets-packetevents)
- [Creating a PacketEvents Instance | packetevents](#creating-a-packetevents-instance-packetevents)
- [A Beginner's Guide to Bundling | packetevents](#a-beginner-s-guide-to-bundling-packetevents)
- [Tweaking PacketEvents | packetevents](#tweaking-packetevents-packetevents)
---
# FAQ | packetevents
[Skip to content](https://docs.packetevents.com/faq/#_top)
FAQ
===
Do not worry if you haven’t grasped all the answers just yet. This section assumes a certain level of familiarity with the library. Consider it a TL;DR to get oriented, and feel free to navigate directly to the detailed guide if needed.
Does PacketEvents have a Discord Server?
----------------------------------------
[Section titled “Does PacketEvents have a Discord Server?”](https://docs.packetevents.com/faq/#does-packetevents-have-a-discord-server)
Yes. You can ask for support on the [discord server](https://discord.com/invite/DVHxPPxHZc)
. If you wish to report a bug, please resort to our [GitHub issues section](https://github.com/retrooper/packetevents/issues)
.
Is PacketEvents free software?
------------------------------
[Section titled “Is PacketEvents free software?”](https://docs.packetevents.com/faq/#is-packetevents-free-software)
Yes. PacketEvents is free software. Learn more about what that means [here](https://gnu.org/philosophy/free-sw.html)
. Moreover, PacketEvents is licensed under the GPLv3.
Should I shade the PacketEvents API into my plugin?
---------------------------------------------------
[Section titled “Should I shade the PacketEvents API into my plugin?”](https://docs.packetevents.com/faq/#should-i-shade-the-packetevents-api-into-my-plugin)
No. TODO elaborate.
Why do I get a “NoClassDefFoundError” when using the PacketEvents API in my plugin?
-----------------------------------------------------------------------------------
[Section titled “Why do I get a “NoClassDefFoundError” when using the PacketEvents API in my plugin?”](https://docs.packetevents.com/faq/#why-do-i-get-a-noclassdeffounderror-when-using-the-packetevents-api-in-my-plugin)
### Cause #1
[Section titled “Cause #1”](https://docs.packetevents.com/faq/#cause-1)
Your server probably can’t find the PacketEvents API. You can fix this by shading the dependency into your plugin or downloading the plugin version from [SpigotMC](https://spigotmc.org/resources/80279/)
or [Modrinth](https://modrinth.com/plugin/packetevents)
on your server.
### Cause #2
[Section titled “Cause #2”](https://docs.packetevents.com/faq/#cause-2)
The version of PacketEvents your plugin is using is not compatible with the version that is being shaded into your plugin or that is present on the server.
---
# A Developer's Introduction | packetevents
[Skip to content](https://docs.packetevents.com/introduction/a-developers-introduction/#_top)
A Developer's Introduction
==========================
PacketEvents Platform-Agnostic Replacements
-------------------------------------------
[Section titled “PacketEvents Platform-Agnostic Replacements”](https://docs.packetevents.com/introduction/a-developers-introduction/#packetevents-platform-agnostic-replacements)
PacketEvents is designed to support multiple platforms. Thus, you’re provided with alternatives to platform-specific features. You do not have to use our cross-platform features, but they may be useful if you’re targeting multiple Minecraft platforms. These features behave consistently regardless of where the project is deployed.
For example, on Bukkit‑based platforms, you typically work with a [`Player`](https://jd.papermc.io/paper/1.21.11/org/bukkit/entity/Player.html)
instance to access or modify data about a player. PacketEvents essentially replaces this with its own [`User`](https://javadocs.packetevents.com/com/github/retrooper/packetevents/protocol/player/User.html)
type, which represents a network connection in a cross-platform way. The [`User`](https://javadocs.packetevents.com/com/github/retrooper/packetevents/protocol/player/User.html)
API provides information about a connected client while also allowing you to perform actions on that connected client, such as sending them a text message or a title. Another example is entity types. Bukkit defines their own [`EntityType`](https://jd.papermc.io/paper/1.21.11/org/bukkit/entity/EntityType.html)
class, whereas PacketEvents provides a separate [`EntityType`](https://javadocs.packetevents.com/com/github/retrooper/packetevents/protocol/entity/type/EntityType.html)
that works across all supported platforms.
Converter System
----------------
[Section titled “Converter System”](https://docs.packetevents.com/introduction/a-developers-introduction/#converter-system)
To help bridge the gap between Minecraft platform implementations and the PacketEvents library, we provide a converter system. The converter allows you to translate between PacketEvents types and platform‑specific types when necessary. This is useful when you are integrating PacketEvents into a codebase that is intended to run on all supported platforms, or if you plan to support multiple platforms in the future.
We’ll start with a practical problem. How do we convert a Bukkit ItemStack to a PacketEvents ItemStack? Let’s assume we’ re targeting Bukkit-based platforms. Follow along with the code below:
...import com.github.retrooper.packetevents.protocol.item.ItemStack;import io.github.retrooper.packetevents.util.SpigotConversionUtil;...
// Here's your Bukkit ItemStackorg.bukkit.inventory.ItemStack bukkitItemStack = ...;
// Here's your converted PacketEvents ItemStackItemStack packetEventsItemStack = SpigotConversionUtil.fromBukkitItemStack(bukkitItemStack);
So, that was quite simple. How do we do the reverse? How can we convert a PacketEvents ItemStack into a Bukkit ItemStack? Here’s how we can do it:
...import com.github.retrooper.packetevents.protocol.item.ItemStack;import io.github.retrooper.packetevents.util.SpigotConversionUtil;...
// Here's your PacketEvents ItemStackItemStack packetEventsItemStack = ...;
// Here's your converted Bukkit ItemStackorg.bukkit.inventory.ItemStack bukkitItemStack = SpigotConversionUtil.toBukkitItemStack(packetEventsItemStack);
You’ve probably got the hang of it now. Conversion is relatively simple in PacketEvents. You may need to convert from time to time. When sending packets with PacketEvents, you must use our types. When processing packets with PacketEvents, you will interact with our types. If you plan on interacting with Bukkit, you may have to convert between types.
Entity ID System
----------------
[Section titled “Entity ID System”](https://docs.packetevents.com/introduction/a-developers-introduction/#entity-id-system)
In the Minecraft protocol, each entity is represented by a numerical identifier (ID). When an entity is spawned, it is assigned an ID equal to the `current_entity_counter + 1`. This ensures that every entity receives a unique identifier. So, why is this relevant, you may ask? When processing packets pertaining to entities, you will receive an Entity ID. But you may wish to access other information about the entity, such as the type of entity, name, location, and more. How do we do that? You can leverage the Bukkit API! You need to convert the entity ID into a Bukkit [`Entity`](https://jd.papermc.io/paper/1.21.11/org/bukkit/entity/Entity.html)
.
Here, again, you can make use of the conversion system. Here’s an example that works on Bukkit-based platforms.
int entityId = ...; // Received an Entity ID from PacketEvents
World world = null; // Can be null! (Providing a world can allow for faster search time)// Retrieve Bukkit EntityEntity bukkitEntity = SpigotConversionUtil.getEntityById(world, entityId);
// Now, we can access some data using Bukkit.String entityName = bukkitEntity.getName();
---
# Development Setup | packetevents
[Skip to content](https://docs.packetevents.com/introduction/development-setup/#_top)
Development Setup
=================
Maven and Gradle
----------------
[Section titled “Maven and Gradle”](https://docs.packetevents.com/introduction/development-setup/#maven-and-gradle)
You can add PacketEvents as a dependency using [Apache Maven](https://maven.apache.org/)
or [Gradle](https://gradle.org/)
(recommended). These build tools can help you build your project, which may rely on various other dependencies, with ease. If you’re new to Minecraft development, please choose a tool (we prefer Gradle) and familiarize yourself with it. We suggest you follow this [guide](https://docs.gradle.org/current/userguide/getting_started_eng.html)
.
Modules
-------
[Section titled “Modules”](https://docs.packetevents.com/introduction/development-setup/#modules)
PacketEvents supports multiple Minecraft platforms, so you’ll have to specify which one you’re targeting. Here are the modules that we support:
### Supported Modules
[Section titled “Supported Modules”](https://docs.packetevents.com/introduction/development-setup/#supported-modules)
* `spigot`
* `velocity`
* `bungeecord`
* `fabric`
* `sponge`
Maven/Gradle Buildscript
------------------------
[Section titled “Maven/Gradle Buildscript”](https://docs.packetevents.com/introduction/development-setup/#mavengradle-buildscript)
* [Maven](https://docs.packetevents.com/introduction/development-setup/#tab-panel-0)
* [Gradle](https://docs.packetevents.com/introduction/development-setup/#tab-panel-1)
codemc-releases https://repo.codemc.io/repository/maven-releases/ codemc-snapshots https://repo.codemc.io/repository/maven-snapshots/ com.github.retrooper packetevents-INSERT_MODULE_HERE 2.11.2 provided
repositories { maven { url = uri("https://repo.codemc.io/repository/maven-releases/") } maven { url = uri("https://repo.codemc.io/repository/maven-snapshots/") } // your other repositories...}
dependencies { compileOnly("com.github.retrooper:packetevents-INSERT_MODULE_HERE:2.11.2") // your other dependencies...}
---
# The Problem of Bundling | packetevents
[Skip to content](https://docs.packetevents.com/introduction/the-problem-of-bundling/#_top)
The Problem of Bundling
=======================
Before we embark on our development journey, we must make some important decisions. These decisions may impact your project’s success, marketability, and most importantly, size.
If you haven’t guessed it yet, we’re going to discuss a concept known as bundling (often referred to as shading). Bundling is the process of storing a project’s dependencies in the distribution file. When publishing your mod or plugin on a forum, you have the option to bundle PacketEvents into your project distribution (or output) file. The benefits are straightforward: end-users won’t have to proactively install PacketEvents (as a dependency), thereby simplifying the installation process.
We understand that bundling is appealing to many end-users—it makes projects easier to set up. However, bundling comes with important downsides, which is why, from an official standpoint, we discourage it. Bundling places the responsibility of keeping PacketEvents up-to-date on you, the developer. If your users encounter issues or update to a new Minecraft version, they may need a newer version of PacketEvents. They cannot update it themselves; they must wait for you to release a new version of your project that includes the updated library. Based on anonymized data, we suspect that one reason users remain on outdated versions of PacketEvents is because plugin developers choose to bundle it, unintentionally delaying access to the latest improvements. Additionally, bundling complicates responsibility whenever software malfunctions. We frequently see the following scenario:
In cases like this, there is often a significant delay before the PacketEvents team receives the error. If the issue originated in PacketEvents, the user would normally report it to us directly. This indirect reporting chain complicates communication—we may need follow‑up information, and the developer may need to reconnect us with the user.
Overall, our assessment is the following: whether you choose to bundle PacketEvents depends on your priorities. If you prioritize marketability, you may prefer bundling PacketEvents with your project because this simplifies installation for most users. However, if your priority is user experience, you should avoid bundling. Imagine a new Minecraft update releases while you are on vacation and unable to update your plugin. If PacketEvents runs separately from your project, your users can update it on their own, and in many cases your plugin will continue to function without requiring an immediate update from you. While we cannot guarantee complete backwards compatibility, we make a strong effort to minimize breaking changes in our library.
---
# Prerequisites | packetevents
[Skip to content](https://docs.packetevents.com/introduction/prerequisites/#_top)
Prerequisites
=============
Networking and TCP/IP
---------------------
[Section titled “Networking and TCP/IP”](https://docs.packetevents.com/introduction/prerequisites/#networking-and-tcpip)
Previously, we mentioned that PacketEvents is a library that facilitates the transmission and processing of packets on Minecraft. Thus, some networking knowledge is paramount. Minecraft Java Edition (unlike Bedrock Edition) relies on the TCP/IP protocol, and thus it would be great to have some knowledge about that. In summary, there are some promises that are made to you, as a developer, when working with the TCP/IP protocol. First, the order in which packets are sent is the same order in which packets are received.
Another promise that is made to you is that dropped packets are rescheduled. TCP/IP is know for its high reliability on packet delivery. This is a promise that other protocols, such as UDP/IP, do not deliver on.
Minecraft Protocol
------------------
[Section titled “Minecraft Protocol”](https://docs.packetevents.com/introduction/prerequisites/#minecraft-protocol)
Some knowledge of the [Minecraft Protocol](https://minecraft.wiki/w/Java_Edition_protocol/Packets)
is necessary. PacketEvents simply implements the protocol; it is not here to explain all of it to you. The protocol changes nearly each Minecraft update. It’s quite difficult to write documentation for a protocol that changes all the time. If you want your software operational after Minecraft updates, you will have to update PacketEvents and possibly follow up with the Minecraft Protocol changes. The community maintains a [wiki](https://minecraft.wiki/w/Java_Edition_protocol/Packets)
that covers the Minecraft protocol with high accuracy.
Threading & Concurrency
-----------------------
[Section titled “Threading & Concurrency”](https://docs.packetevents.com/introduction/prerequisites/#threading--concurrency)
Experience with threading and concurrency is also essential. Threading allows developers to execute tasks in parallel. Here’s an analogy we tend to use:
Not only is concurrency important in the kitchen, but it’s even more important in software development. Minecraft leverages concurrency to a degree. In Minecraft, network communication processing is handled by one (or multiple) worker thread(s). Logic, such as NPC behavior and world generation, is handled by the so-called ‘main thread.’ Since we’re dealing with a multi-threaded system, you’re going to have to design your software with caution. Multi-threading has its benefits (performance-wise), but it can cause developers to run into many issues. Suppose, for instance, two threads attempt to modify/access a particular piece of information at the same time. You may need to account for that. Thus, we suggest that you familiarize yourself with concurrency & threading in Java before interacting with our library.
---
# Advanced PacketEvents Example: Combining our Knowledge | packetevents
[Skip to content](https://docs.packetevents.com/processing-and-sending/advanced-packetevents-example-combining-our-knowledge/#_top)
Advanced PacketEvents Example: Combining our Knowledge
======================================================
In this example, we will develop a Minecraft Bukkit plugin that will spawn a client-sided Armor Stand, providing a click counter for each player.
First, we’ll spawn an Armor Stand for the client whenever they join the server. Next, we’ll wait for the client to send an “Interact” packet. We’ll check if the client had interacted with our client-sided Armor Stand. If so, we’ll increment the click counter for that player.
Previously, we mentioned that packets provide direct communication with the client, allowing us to give each user a unique experience. “Client-sided entity” in this context merely means that an entity is spawned for a particular client. Most importantly, the server is not informed of said entity, thus it will only be visible to the users you present it to.
import com.github.retrooper.packetevents.event.PacketListener;import com.github.retrooper.packetevents.event.PacketReceiveEvent;import com.github.retrooper.packetevents.event.UserLoginEvent;import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;import com.github.retrooper.packetevents.protocol.packettype.PacketType;import com.github.retrooper.packetevents.protocol.player.User;import com.github.retrooper.packetevents.protocol.world.Location;import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;import io.github.retrooper.packetevents.util.SpigotConversionUtil;import io.github.retrooper.packetevents.util.SpigotReflectionUtil;import org.bukkit.entity.Player;import org.jspecify.annotations.NullMarked;import org.jspecify.annotations.Nullable;
import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;
@NullMarkedpublic class ExampleListener implements PacketListener {
private @Nullable FakeArmorStand fakeArmorStand = null;
@Override public void onUserLogin(UserLoginEvent event) { User user = event.getUser(); Player player = event.getPlayer();
// Create the Armor Stand (if we haven't already) if (this.fakeArmorStand == null) { // Generate a random UUID UUID uuid = UUID.randomUUID(); // Generate an Entity ID int entityId = SpigotReflectionUtil.generateEntityId();
this.fakeArmorStand = new FakeArmorStand(uuid, entityId); }
// Spawn the Armor Stand at the user's current location Location spawnLocation = SpigotConversionUtil.fromBukkitLocation(player.getLocation()); this.fakeArmorStand.spawn(user, spawnLocation); }
@Override public void onPacketReceive(PacketReceiveEvent event) { User user = event.getUser(); if (event.getPacketType() != PacketType.Play.Client.INTERACT_ENTITY) { return; } // They interacted with an entity. WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event); // Retrieve that entity's ID int entityId = packet.getEntityId();
// Check if the client interacted with the Armor Stand if (this.fakeArmorStand != null && entityId == this.fakeArmorStand.entityId) { // Increment their clicks int clicks = this.fakeArmorStand.clicks.getOrDefault(user.getUUID(), 0) + 1; this.fakeArmorStand.clicks.put(user.getUUID(), clicks); user.sendMessage("You now have " + clicks + " clicks on the Armor Stand!"); } }
private static class FakeArmorStand {
private final int entityId; private final UUID uuid;
// track their clicks private final Map clicks = new ConcurrentHashMap<>();
public FakeArmorStand(UUID uuid, int entityId) { this.uuid = uuid; this.entityId = entityId; }
public void spawn(User user, Location location) { WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity( this.entityId, this.uuid, EntityTypes.ARMOR_STAND, location, location.getYaw(), // Head yaw 0, // No additional data null // We won't specify any initial velocity ); user.sendPacket(packet); } }}
---
# What is PacketEvents? | packetevents
[Skip to content](https://docs.packetevents.com/#_top)
What is PacketEvents?
=====================
Minecraft Protocol Library
--------------------------
[Section titled “Minecraft Protocol Library”](https://docs.packetevents.com/#minecraft-protocol-library)
PacketEvents is a protocol library tailored to Minecraft Java Edition, designed to facilitate the processing and transmission of packets. The library was founded by [retrooper](https://github.com/retrooper)
on April 17, 2020. The project, PacketEvents, relies on Netty, a network application framework. This strategic choice imbues PacketEvents with versatility, allowing developers to use it on a range of Minecraft platforms powered by Netty, such as _Paper, Spigot, Velocity, BungeeCord, Fabric, and Sponge_.
### A New Beginning
[Section titled “A New Beginning”](https://docs.packetevents.com/#a-new-beginning)
Our goal with _PacketEvents 2.0_ was to make code efficient and portable. We wanted developers to write code once and run it on multiple platforms. Since PacketEvents depends on Netty, it’s possible for developers to write their own Minecraft server implementation without worrying too much about networking.
### The PacketEvents Team
[Section titled “The PacketEvents Team”](https://docs.packetevents.com/#the-packetevents-team)
| Role | Member |
| --- | --- |
| Project Lead | [retrooper](https://github.com/retrooper) |
| Project Maintainer | [booky](https://github.com/booky10) |
What People Have to Say About PacketEvents
------------------------------------------
[Section titled “What People Have to Say About PacketEvents”](https://docs.packetevents.com/#what-people-have-to-say-about-packetevents)
### Selected Reviews
[Section titled “Selected Reviews”](https://docs.packetevents.com/#selected-reviews)

---
# Introduction to Packet Listeners | packetevents
[Skip to content](https://docs.packetevents.com/processing-and-sending/master/#_top)
Introduction to Packet Listeners
================================
Interception
------------
[Section titled “Interception”](https://docs.packetevents.com/processing-and-sending/master/#interception)
In networking, we typically distinguish between packets sent to the client and packets sent to the server. There are certain packets that only the client can parse, and there exist some that can only be parsed by the server. It’s important that we send the right packets to avoid running into issues. If we fail to notice this difference, the worst that could happen is (a) the client gets kicked because it fails to parse a packet or (b) the client crashes, also failing to parse a packet.
All communication between the client and the server is through packets. PacketEvents allows us to read what’s being sent between the client and server. But it doesn’t end there. We also have the opportunity to alter data being sent. We will expand on more interesting features offered by PacketEvents at a later point. Now, are you ready to intercept some packets?
Here’s an example of how we could intercept a health-related Minecraft packet. Take a moment to read it through, and see if you understand what’s going on.
import com.github.retrooper.packetevents.event.PacketListener;import com.github.retrooper.packetevents.event.PacketSendEvent;import com.github.retrooper.packetevents.protocol.packettype.PacketType;import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;import org.jspecify.annotations.NullMarked;
@NullMarkedpublic class ExampleListener implements PacketListener {
@Override public void onPacketSend(PacketSendEvent event) { if (event.getPacketType() == PacketType.Play.Server.UPDATE_HEALTH) { // health of the connected player was updated! WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth(event); float health = packet.getHealth(); } }}
As you may have guessed, the packet processor above has a condition that checks whether the ‘Update Health’ (also known as ‘Set Health’) packet was sent. If that condition holds true, then the code parses the packet, obtaining the ‘health.’ At the time of writing, here’s how the Minecraft Protocol Wiki describes this packet:

The packet is sent by the server to the client, and it tells the client what its health should be. If we decided we wanted to trick the client (for educational purposes) and provide it with a false health value (such as 15), we could easily do so like this:
import com.github.retrooper.packetevents.event.PacketListener;import com.github.retrooper.packetevents.event.PacketSendEvent;import com.github.retrooper.packetevents.protocol.packettype.PacketType;import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateHealth;import org.jspecify.annotations.NullMarked;
@NullMarkedpublic class ExampleListener implements PacketListener {
@Override public void onPacketSend(PacketSendEvent event) { if (event.getPacketType() == PacketType.Play.Server.UPDATE_HEALTH) { // health of the connected player was updated! WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth(event); packet.setHealth(15.0F); // range depends on maximum health of entity event.markForReEncode(true); // tells packetevents to modify the packet } }}
As shown, packet modification is also simple to implement with PacketEvents. Let’s code a (slightly) more useful task using PacketEvents. The following code intercepts the ‘Interact Entity’ packet, which is sent whenever the client interacts with or attacks an entity, and then sends a message to the client informing them that they have attacked an entity:
import com.github.retrooper.packetevents.event.PacketListener;import com.github.retrooper.packetevents.event.PacketReceiveEvent;import com.github.retrooper.packetevents.protocol.packettype.PacketType;import com.github.retrooper.packetevents.protocol.player.User;import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;import io.github.retrooper.packetevents.util.SpigotConversionUtil;import net.kyori.adventure.text.Component;import net.kyori.adventure.text.event.HoverEvent;import net.kyori.adventure.text.format.NamedTextColor;import net.kyori.adventure.text.format.TextDecoration;import org.bukkit.entity.Entity;import org.jspecify.annotations.NullMarked;
@NullMarkedpublic class ExampleListener implements PacketListener {
@Override public void onPacketReceive(PacketReceiveEvent event) { User user = event.getUser(); // Whenever the player sends an entity interaction packet. if (event.getPacketType() == PacketType.Play.Client.INTERACT_ENTITY) { WrapperPlayClientInteractEntity packet = new WrapperPlayClientInteractEntity(event); WrapperPlayClientInteractEntity.InteractAction action = packet.getAction(); if (action == WrapperPlayClientInteractEntity.InteractAction.ATTACK) { int entityID = packet.getEntityId();
// Find the Bukkit entity (WARNING: unsafe method!) Entity entity = SpigotConversionUtil.getEntityById(null, entityID);
// Create a chat component with the Adventure API Component message = Component.text("You attacked an entity.") .hoverEvent(HoverEvent.hoverEvent( HoverEvent.Action.SHOW_TEXT, Component.text("Entity Name: " + entity.getName()) .color(NamedTextColor.GREEN) .decorate(TextDecoration.BOLD) .decorate(TextDecoration.ITALIC) ));
// Send it to the cross-platform user user.sendMessage(message); } } }}
As you may have noticed, PacketEvents provided us with an entity ID. We used the conversion system to convert it into a Bukkit entity so we can assess the name of the entity. Lastly, we sent the message using the Adventure API (which is embedded in PacketEvents). The Adventure API is also available on all Paper-based software, you can learn more about it [here](https://docs.papermc.io/adventure/)
. If you’re confused about the entity IDs and the conversion system, refer to [this](https://docs.packetevents.com/processing-and-sending/introduction/a-developers-introduction#entity-id-system)
.
### What’s Next?
[Section titled “What’s Next?”](https://docs.packetevents.com/processing-and-sending/master/#whats-next)
We’ve successfully written a packet listener! What’s next? We need to register the listener:
* [Normal](https://docs.packetevents.com/processing-and-sending/master/#tab-panel-2)
* [If Bundling](https://docs.packetevents.com/processing-and-sending/master/#tab-panel-3)
import com.github.retrooper.packetevents.PacketEvents;import com.github.retrooper.packetevents.event.EventManager;import com.github.retrooper.packetevents.event.PacketListenerPriority;import org.bukkit.plugin.java.JavaPlugin;
public class YourBukkitPlugin extends JavaPlugin {
@Override public void onLoad() { EventManager events = PacketEvents.getAPI().getEventManager(); events.registerListener(new ExampleListener(), PacketListenerPriority.NORMAL); }}
import com.github.retrooper.packetevents.PacketEvents;import com.github.retrooper.packetevents.event.PacketListenerPriority;import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;import org.bukkit.plugin.java.JavaPlugin;
public class YourBukkitPlugin extends JavaPlugin {
@Override public void onLoad() { // Building, loading, and initializing the library is necessary when bundling (or shading). PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); PacketEvents.getAPI().load();
// Register our listener PacketEvents.getAPI().getEventManager().registerListener(new ExampleListener(), PacketListenerPriority.NORMAL); }
@Override public void onEnable() { // Initialize the library! PacketEvents.getAPI().init(); }
@Override public void onDisable() { // Clean-up process PacketEvents.getAPI().terminate(); }}
---
# Sending and Simulating Packets | packetevents
[Skip to content](https://docs.packetevents.com/sending-and-simulating-packets/#_top)
Sending and Simulating Packets
==============================
Where Do We Start?
------------------
[Section titled “Where Do We Start?”](https://docs.packetevents.com/sending-and-simulating-packets/#where-do-we-start)
PacketEvents provides two core capabilities that you will use throughout your project: sending packets and receiving packets. PacketEvents provides many other features and utilities, some of which we may explore later, but for now, understanding how to send and receive packets is enough to get started.
Sending Packets
---------------
[Section titled “Sending Packets”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-packets)
Sending packets is another feature provided by PacketEvents. Firstly, you need to know what kind of packet you want to send to the client. The next step is finding the appropriate wrapper in PacketEvents. You’ll then create an instance of the wrapper, and finally, you’ll send it to the client using our API.
We’ll illustrate how you can send packets using an example. Let’s send the client an “Update Health” packet, resetting their food and health values. Information about this specific packet is available [here](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol)
.
### Sending Packets using the PacketEvents Cross-Platform User
[Section titled “Sending Packets using the PacketEvents Cross-Platform User”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-packets-using-the-packetevents-cross-platform-user)
User user = ...;// Create the packet (feed it all the data it needs).WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth( 20f, // Health (0-20) 20, // Food (0-20) 5f); // Food saturation (0-5)// Finally, send it to the user.user.sendPacket(packet);
### Sending Packets using the Bukkit (or Velocity) Player
[Section titled “Sending Packets using the Bukkit (or Velocity) Player”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-packets-using-the-bukkit-or-velocity-player)
Player player = ...;// Create the packet (feed it all the data it needs).WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth( 20f, // Health (0-20) 20, // Food (0-20) 5f); // Food saturation (0-5)// Finally, send it to the player.PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet);
### Sending Packets using the BungeeCord ProxiedPlayer
[Section titled “Sending Packets using the BungeeCord ProxiedPlayer”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-packets-using-the-bungeecord-proxiedplayer)
ProxiedPlayer player = ...;// Create the packet (feed it all the data it needs).WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth( 20f, // Health (0-20) 20, // Food (0-20) 5f); // Food saturation (0-5)// Finally, send it to the player.PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet);
Simulating Packets
------------------
[Section titled “Simulating Packets”](https://docs.packetevents.com/sending-and-simulating-packets/#simulating-packets)
Packet simulation is also supported in PacketEvents. Now, what do we mean by “packet simulation”? From the server’s perspective, sending packets means that the server is the one sending information to the client. It therefore follows that the reception of packets, in this context, means that the server is receiving information from a client. PacketEvents allows us to “trick” the server and send a fake packet on behalf of a client. Be cautious, readers! Using this feature can break the functionality of other plugins on your server; hence, you should avoid it whenever possible.
We’ll demonstrate how you can simulate packets using an example. Let’s simulate an Animation (Swing Arm) packet, making it appear like the client swung their arm. More information about this specific packet is available [here](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol#Swing_Arm)
.
### Simulating Packets using the PacketEvents Cross-Platform User
[Section titled “Simulating Packets using the PacketEvents Cross-Platform User”](https://docs.packetevents.com/sending-and-simulating-packets/#simulating-packets-using-the-packetevents-cross-platform-user)
User user = ...;// Create the packet (feed it all the data it needs).WrapperPlayClientAnimation packet = new WrapperPlayClientAnimation( InteractionHand.MAIN_HAND);// Finally, fake the incoming packet.user.receivePacket(packet);
### Simulating Packets using the Bukkit (or Velocity) Player
[Section titled “Simulating Packets using the Bukkit (or Velocity) Player”](https://docs.packetevents.com/sending-and-simulating-packets/#simulating-packets-using-the-bukkit-or-velocity-player)
Player player = ...;// Create the packet (feed it all the data it needs).WrapperPlayClientAnimation packet = new WrapperPlayClientAnimation( InteractionHand.MAIN_HAND);// Finally, fake the incoming packet.PacketEvents.getAPI().getPlayerManager().receivePacket(player, packet);
### Simulating Packets using the BungeeCord ProxiedPlayer
[Section titled “Simulating Packets using the BungeeCord ProxiedPlayer”](https://docs.packetevents.com/sending-and-simulating-packets/#simulating-packets-using-the-bungeecord-proxiedplayer)
ProxiedPlayer player = ...;WrapperPlayClientAnimation packet = new WrapperPlayClientAnimation( InteractionHand.MAIN_HAND);// Finally, fake the incoming packet.PacketEvents.getAPI().getPlayerManager().receivePacket(player, packet);
Sending and Simulating Packets… Silently?
-----------------------------------------
[Section titled “Sending and Simulating Packets… Silently?”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-and-simulating-packets-silently)
The title might seem a tad bit confusing, so we’ll clarify what we mean by that. Above, you not only learned how to send packets, but also how to fake incoming packets. These features are useful to many developers, yet still might cause some issues (E.g. StackOverflowException). You might want to differentiate between the packets that you caused and the packets that you did not cause. The simplest way of doing so is by sending or simulating packets “silently”. This will still perform the action, but it will not trigger your packet listener. Yet again, this is something you want to be cautious with. This feature can break compatibility with other plugins, thus please avoid it if possible.
### Sending Packets Silently
[Section titled “Sending Packets Silently”](https://docs.packetevents.com/sending-and-simulating-packets/#sending-packets-silently)
User user = ...;// Create the packet (feed it all the data it needs).WrapperPlayServerUpdateHealth packet = new WrapperPlayServerUpdateHealth( 20d, // Health (0-20) 20, // Food (0-20) 5d); // Food saturation (0-5)// Finally, send it to the user without triggering their listeners.user.sendPacketSilently(packet);
### Simulating Packets Silently
[Section titled “Simulating Packets Silently”](https://docs.packetevents.com/sending-and-simulating-packets/#simulating-packets-silently)
User user = ...;// Create the packet (feed it all the data it needs).WrapperPlayClientAnimation packet = new WrapperPlayClientAnimation( InteractionHand.MAIN_HAND);// Finally, fake the incoming packet without triggering their listeners.user.receivePacketSilently(packet);
---
# Creating a PacketEvents Instance | packetevents
[Skip to content](https://docs.packetevents.com/setup-when-bundling/master/#_top)
Creating a PacketEvents Instance
================================
Do You Need a PacketEvents Instance?
------------------------------------
[Section titled “Do You Need a PacketEvents Instance?”](https://docs.packetevents.com/setup-when-bundling/master/#do-you-need-a-packetevents-instance)
Many features offered by PacketEvents are accessible through a PacketEvents instance. Depending on your setup, you may have to create your own PacketEvents instance as soon as your mod/plugin launches. If you’re bundling PacketEvents in your distribution file, then you need to create a PacketEvents instance. If you’re not bundling, then do not create an instance. The user will have to proactively install PacketEvents, and PacketEvents will create an instance for you.
If you’re not bundling, you can ignore this page.
The PacketEvents instance allows you to access functionality within our API. This instance can be created using a PacketEventsBuilder. Each platform has its own PacketEventsBuilder, and each builder carries its own implementation of the PacketEvents library.
Basic Example
-------------
[Section titled “Basic Example”](https://docs.packetevents.com/setup-when-bundling/master/#basic-example)
The PacketEvents instance provides two methods for initialization: `load` and `init`. You must call `load` before calling `init`. PacketEvents also provides a termination method, `terminate`, which should be called when your plugin shuts down.
Below is an example of how to properly initialize and terminate PacketEvents in the `Main` class on Bukkit‑based platforms:
import com.github.retrooper.packetevents.PacketEvents;import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;import org.bukkit.plugin.java.JavaPlugin;
public class YourBukkitPlugin extends JavaPlugin {
@Override public void onLoad() { // Building, loading, and initializing the library is necessary when bundling. PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); PacketEvents.getAPI().load();
/* * MORE CODE MAY APPEAR HERE (such as registering listeners) */ }
@Override public void onEnable() { // Initialize the library! PacketEvents.getAPI().init(); }
@Override public void onDisable() { // Clean-up process PacketEvents.getAPI().terminate(); }}
Below is an example of initialization and termination of PacketEvents for a non-Bukkit-based platform, like Velocity:
import com.github.retrooper.packetevents.PacketEvents;import com.google.common.eventbus.Subscribe;import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;import com.velocitypowered.api.plugin.Plugin;import com.velocitypowered.api.plugin.PluginContainer;import com.velocitypowered.api.plugin.annotation.DataDirectory;import com.velocitypowered.api.proxy.ProxyServer;import io.github.retrooper.packetevents.velocity.factory.VelocityPacketEventsBuilder;import jakarta.inject.Inject;import org.slf4j.Logger;
import java.nio.file.Path;
@Plugin(...)public class YourVelocityPlugin {
private final ProxyServer server; private final Logger logger; private final PluginContainer pluginContainer; private final Path dataDirectory;
@Inject public YourVelocityPlugin( final ProxyServer server, final Logger logger, final PluginContainer pluginContainer, final @DataDirectory Path dataDirectory ) { this.server = server; this.logger = logger; this.pluginContainer = pluginContainer; this.dataDirectory = dataDirectory; }
@Subscribe public void onProxyInitialize(final ProxyInitializeEvent event) { PacketEvents.setAPI(VelocityPacketEventsBuilder.build(this.server, this.pluginContainer, this.logger, this.dataDirectory)); PacketEvents.getAPI().load();
/* * MORE CODE MAY APPEAR HERE (such as registering listeners) */ PacketEvents.getAPI().init(); }
@Subscribe public void onProxyShutdown(ProxyShutdownEvent event) { PacketEvents.getAPI().terminate(); }}
---
# A Beginner's Guide to Bundling | packetevents
[Skip to content](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#_top)
A Beginner's Guide to Bundling
==============================
So, you’ve decided you want to bundle PacketEvents in your distribution file. If you do not wish to bundle, please skip this section. There are a few things you need to do to avoid running into issues when bundling. Here’s a summary of the steps:
1. #### Setup Maven/Gradle
You will need to set up Maven/Gradle in a particular way if you desire to bundle PacketEvents.
2. #### Add Necessary Soft Dependencies
You must configure your plugin in such a way that it loads after a certain set of plugins. This way, you avoid running into issues when bundling.
3. #### Relocate PacketEvents
You need to relocate PacketEvents’ packages to avoid version conflicts. Do not assume that you’re the only one bundling.
4. #### Minimize PacketEvents (not recommended)
You may optionally minimize PacketEvents to reduce the size of your project. This process will remove classes within PacketEvents that are unused.
### 1\. Setup Maven/Gradle for Bundling
[Section titled “1. Setup Maven/Gradle for Bundling”](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#1-setup-mavengradle-for-bundling)
* [Maven](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-6)
* [Gradle](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-7)
codemc-releases https://repo.codemc.io/repository/maven-releases/ codemc-snapshots https://repo.codemc.io/repository/maven-snapshots/ com.github.retrooper packetevents-INSERT_MODULE_HERE 2.11.2 compile
repositories { maven { url = uri("https://repo.codemc.io/repository/maven-releases/") } maven { url = uri("https://repo.codemc.io/repository/maven-snapshots/") } // your other repositories...}
dependencies { implementation("com.github.retrooper:packetevents-INSERT_MODULE_HERE:2.11.2") // your other dependencies...}
### 2\. Add Necessary Soft Dependencies
[Section titled “2. Add Necessary Soft Dependencies”](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#2-add-necessary-soft-dependencies)
If you’re developing for Bukkit-based platforms, this section was written for you. It’s very important that you configure your `plugin.yml` in such a way that it has the following plugins as soft dependencies:
softdepend:- ProtocolLib- ProtocolSupport- ViaVersion- ViaBackwards- ViaRewind- Geyser-Spigot
The reason we’re adding these plugins as soft dependencies is so that your plugin loads after these plugins are loaded (if present on the server). The PacketEvents injector requires these plugins to be loaded before it starts the network injection process. Failure to add these soft dependencies can lead to exceptions and software malfunctions.
### 3\. Relocate PacketEvents
[Section titled “3. Relocate PacketEvents”](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#3-relocate-packetevents)
We need to relocate PacketEvents’ packages. This step is required so that we can avoid any version conflicts. It’s possible that PacketEvents is already present on your server; thus, we must account for version conflicts. This can be achieved by configuring the [Maven Shade Plugin for Apache Maven](https://maven.apache.org/plugins/maven-shade-plugin/)
or [Shadow Plugin for Gradle](https://gradleup.com/shadow/)
.
* [Maven](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-8)
* [Gradle](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-9)
org.apache.maven.plugins maven-shade-plugin 3.6.1 package shade false false com.github.retrooper my.domain.myplugin.libs.packetevents io.github.retrooper my.domain.myplugin.libs.packetevents net.kyori my.domain.myplugin.libs.kyori com.google.gson my.domain.myplugin.libs.gson
plugins { id("com.gradleup.shadow") version "9.3.1" // your other plugins...}
tasks.shadowJar { relocate("com.github.retrooper", "my.domain.myplugin.libs.packetevents") relocate("io.github.retrooper", "my.domain.myplugin.libs.packetevents") relocate("net.kyori", "my.domain.myplugin.libs.kyori") relocate("com.google.gson", "my.domain.myplugin.libs.gson")}
### 4\. Minimize PacketEvents (optional)
[Section titled “4. Minimize PacketEvents (optional)”](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#4-minimize-packetevents-optional)
Last but not least, you can minimize PacketEvents. Unused PacketEvents classes will be discarded from your distribution file, allowing you to save a little space. Please note that this is **not recommended** and sometimes cause some classloading issues to occur.
* [Maven](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-4)
* [Gradle](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#tab-panel-5)
shade false true false
tasks.shadowJar { minimize()}
### Testing
[Section titled “Testing”](https://docs.packetevents.com/setup-when-bundling/a-beginners-guide-to-bundling/#testing)
Now, you’ve hopefully successfully relocated PacketEvents. Please ensure that your output or distribution file contains PacketEvents packages, which contain ‘`com.github.retrooper`’ and ‘`io.github.retrooper`’. The package containing ‘`com.github.retrooper`’ refers to the PacketEvents cross-platform API. These are API features that work on every platform. The package that contains ‘`io.github.retrooper`’ refers to the implementation of PacketEvents. Without an implementation, PacketEvents cannot function.
---
# Tweaking PacketEvents | packetevents
[Skip to content](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#_top)
Tweaking PacketEvents
=====================
Theory of Settings
------------------
[Section titled “Theory of Settings”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#theory-of-settings)
Settings in your PacketEvents instance dictate the way the library behaves. We’ve listed all the settings below with a brief description of what they affect.
### Check for Updates
[Section titled “Check for Updates”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#check-for-updates)
The ‘[checkForUpdates](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#checkForUpdates(boolean))
’ setting controls whether PacketEvents should compare the local version against the latest release using GitHub’s API. PacketEvents will then inform server administrators through the console whether the installation is up-to-date, outdated, or identified as a development build. It’s strongly recommended to keep this enabled so administrators are notified whenever a new update becomes available.
### Re-Encode By Default
[Section titled “Re-Encode By Default”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#re-encode-by-default)
The ‘[reEncodeByDefault](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#reEncodeByDefault(boolean))
’ setting determines whether PacketEvents listeners automatically have permission to modify packet data. When this setting is enabled, listeners can change data without explicitly marking the event as modified (or as re-encoded). When it is disabled, each event must be manually marked as modified before any packet modifications will be applied. If you’re bundling PacketEvents, we suggest you toggle this setting off to improve PacketEvents’ performance. If you’re not bundling PacketEvents, you will find this setting on by default to remain in line with backwards compatibility. The default value of this setting may change in the future.
### Downsample Colors
[Section titled “Downsample Colors”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#downsample-colors)
TODO
### Custom Resource Provider
[Section titled “Custom Resource Provider”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#custom-resource-provider)
TODO
### Debug
[Section titled “Debug”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#debug)
The ‘[debug](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#debug(boolean))
’ setting controls whether PacketEvents should print debug messages to the console. When enabled, information about connecting clients may be logged.
### Full Stack Trace
[Section titled “Full Stack Trace”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#full-stack-trace)
The ‘[fullStackTrace](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#fullStackTrace(boolean))
’ setting controls whether PacketEvents should print full stack traces whenever an exception occurs. It’s generally best to disable this in production to avoid exception spam, which can negatively impact server performance.
### Kick if Terminated
[Section titled “Kick if Terminated”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#kick-if-terminated)
The ‘[kickIfTerminated](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#kickIfTerminated(boolean))
’ configuration option controls whether PacketEvents should block players from joining the server while its instance is terminated or otherwise inactive. We recommend keeping this enabled as an additional safeguard to prevent players from connecting before PacketEvents is fully operational.
### Kick on Packet Exception
[Section titled “Kick on Packet Exception”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#kick-on-packet-exception)
The ‘[kickOnPacketException](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#kickOnPacketException(boolean))
’ setting controls whether PacketEvents should disconnect a client when it fails to process data received from that client. When this option is enabled, PacketEvents proactively kicks any client that sends invalid data.
### Timestamp Mode
[Section titled “Timestamp Mode”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#timestamp-mode)
The ‘[timestampMode](https://javadocs.packetevents.com/com/github/retrooper/packetevents/settings/PacketEventsSettings.html#timeStampMode(com.github.retrooper.packetevents.util.TimeStampMode))
’ setting controls the level of precision used when recording event timestamps. Every event in PacketEvents includes a timestamp, and this option lets developers choose the unit of time used for those measurements. In essence, it determines how precise each event’s timestamp should be. The available options are `MILLIS`, which records timestamps in milliseconds, `NANO`, which records them in nanoseconds, and `NONE`, which disables timestamping entirely.
Default Values for PacketEvents Settings
----------------------------------------
[Section titled “Default Values for PacketEvents Settings”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#default-values-for-packetevents-settings)
| Setting | Default Value |
| --- | --- |
| `reEncodeByDefault` | `true` |
| `checkForUpdates` | `true` |
| `downsampleColors` | `false` |
| `debug` | `false` |
| `fullStackTrace` | `false` |
| `kickOnPacketException` | `true` |
| `kickIfTerminated` | `true` |
| `timestampMode` | `MILLIS` |
Practical Guide to Settings
---------------------------
[Section titled “Practical Guide to Settings”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#practical-guide-to-settings)
Here is how you access the current PacketEvents settings. Configuration should be done prior to loading the PacketEvents instance.
PacketEventsSettings settings = instance.getSettings();// e.g. disable checking for updatessettings.checkForUpdates(false);// you can also modify multiple settings at once:settings.checkForUpdates(true).debug(false).timeStampMode(TimeStampMode.MILLIS);
End Result
----------
[Section titled “End Result”](https://docs.packetevents.com/setup-when-bundling/tweaking-packetevents/#end-result)
import com.github.retrooper.packetevents.PacketEvents;import com.github.retrooper.packetevents.util.TimeStampMode;import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;import org.bukkit.plugin.java.JavaPlugin;
public class YourBukkitPlugin extends JavaPlugin {
@Override public void onLoad() { // Create our PacketEvents instance PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
// Configure PacketEvents settings PacketEvents.getAPI().getSettings().checkForUpdates(true).debug(false) .timeStampMode(TimeStampMode.MILLIS);
// Load PacketEvents PacketEvents.getAPI().load();
/* * MORE CODE MAY APPEAR HERE (such as registering listeners) */ }
@Override public void onEnable() { // Initialize the library! PacketEvents.getAPI().init(); }
@Override public void onDisable() { // Clean-up process PacketEvents.getAPI().terminate(); }}
---