Vamos a ver como interceptar y enviar paquetes crudos del servidor de Minecraft Spigot. (Concepto avanzado)
¿Qué es un paquete?
Este es un tema muy extenso, así que voy a explicarlo MUY por encima.
En Minecraft existe el servidor, que es lo que estamos modificando, y el cliente, que es el juego en un ordenador. Para comunicarse el cliente con el servidor, se usa lo que son paquetes. Un paquete no es nada más que, pues podríamos imaginarnos como una caja de cartón que contiene información dentro y esta se envía al servidor o el servidor lo envía al cliente. Por ejemplo, cuando tú te mueves dentro del juego, tu cliente envía paquetes al servidor que te estás moviendo y el servidor te envía paquetes de cómo los otros jugadores se están moviendo.

Esquema del funcionamiento de paquetes entre cliente y servidor
¿Por qué usar paquetes?
Es cierto que la API de Spigot ya contiene una gran parte implementada de las funciones necesarias para desarrollar la mayoría de modificaciones en el servidor. Pero hay veces que la API de Spigot no es suficiente y necesitamos interactuar de una forma más cruda con el cliente y el servidor. Por ejemplo, si queremos crear un jugador falso (o también conocido como NPC), esto no se puede hacer con la API de Spigot. Necesitamos engañar al cliente con la existencia otro jugador extra en el servidor que realmente no existe, simplemente lo estamos emulando con paquetes generados por nosotros. Justamente este tema es bastante complicado y no lo vamos a tratar aquí, pero si queremos una cosa más simple.
Problemática de paquetes
Los paquetes que se usan en el servidor de Minecraft justamente se encuentran dentro del código de Minecraft. Eso que implica que si creamos un plugin para una versión en específico y queremos moverlo a otra versión o que se pueda usar otra versión, esto complica mucho más las cosas, ya que el package es diferente. ¿Cómo solucionamos esto?
ProtocolLib
En Minecraft hay una librería muy famosa llamada Protocol Lib que nos ayuda a escuchar paquetes que envía el cliente al servidor o enviar paquetes que no existen en realidad del servidor al cliente sin preocuparnos de la versión del servidor, de la definición de paquetes ni nada por el estilo.
Vamos a empezar por incluir esta dependencia en nuestro proyecto de Gradle. Para ello nos iremos a su repositorio de GitHub y pondremos el repositorio que contiene la librería y la implementación de la librería.
repositories {
// ...
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
// ...
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
}
Luego en el plugin.yml
hará falta declarar que protocolib
es una dependencia, ya que sin esta dependencia instalada aparte en el servidor, nuestro código fallará.
# ...
depend:
- ProtocolLib
Una vez instalado el ProtocolLib, hemos de buscar el paquete que necesitamos enviar al cliente. Para ello existe una web llamada wiki.vg que contiene cada uno de los paquetes de Minecraft. (Si usas una versión antigua de Minecraft, clica aquí).
Enviar paquetes
En mi caso, quiero falsificar la animación de la mano del jugador, y hacer creer al cliente que ha interactuado con algo. Para llevar a cabo esto, el paquete que voy a utilizar es el Entity Animation
.
Sabiendo más o menos el nombre (nunca es exacto), podemos usar protocollib
para enviar este paquete. Primero de todo necesitamos un PacketContainer
que incluya el tipo de paquete que queremos. El tipo de paquete lo podemos encontrar en PacketType
(PacketType.Play.Client y PacketType.Play.Server). En mi caso, es el paquete ANIMATION
.
PacketContainer paquete = new PacketContainer(PacketType.Play.Server.ANIMATION);
A continuación toca rellenar la información del paquete siguiendo la información en wiki.vg.

Captura de pantalla del paquete "Entity Animation" en wiki.vg
Field Name
, Field Type
y Notes
veremos los parámetros que tiene el paquete. En este caso, en particular, el paquete tiene dos parámetros. Uno que es el ID de la entidad, en este caso el ID del jugador. Y el segundo es un número que podemos ver abajo que indica el tipo de animación. En este caso yo quiero la animación de Swing Main Arm, con lo cual usaré el número 0.
PacketContainer paquete = new PacketContainer(PacketType.Play.Server.ANIMATION);
paquete.getIntegers()
.write(0, player.getEntityId());
paquete.getIntegers()
.write(1, 0);
Como vemos, el paquete tiene diferentes métodos que nos dejan conseguir el tipo de variable que requiere el paquete en este caso. Si nos fijamos en el "field type", el paquete requiere un int, que es la idea del jugador. Lo escribo en la primera posición con la idea del jugador.
Una vez puestos todos los parámetros del paquete, solo hace falta enviarlo en protocol.lib. Esto también es muy sencillo. Simplemente, hay que conseguir una instancia de protocollib
y llamar al método sendServerPacket
.
PacketContainer paquete = new PacketContainer(PacketType.Play.Server.ANIMATION);
paquete.getIntegers()
.write(0, player.getEntityId());
paquete.getIntegers()
.write(1, 0);
ProtocolLibrary.getProtocolManager().sendServerPacket(player, paquete);
Y ya estaría. Si probamos el código, por ejemplo, corriendo un comando, veremos como el brazo del jugador se mueve 😄.