Packets (Messages)
Minecraft Packets (called Messages in Void) describe the data that is sent between the player and server. These messages contain information about player actions, world events, and other game mechanics.
Typically, you describe existing packets in game, so you can read, modify, or cancel them.
However you are not limited to this, you can also define your own packets for your own minecraft mod or plugin.
Packets can be defined with IMinecraftClientboundPacket<TPacket>
or IMinecraftServerboundPacket<TPacket>
interface. If your packet is same for both client and server, you can use IMinecraftPacket<TPacket>
interface.
Defining Packets
Section titled “Defining Packets”Your packet definition must specify how to Decode and Encode the packet data.
In this example, we will define a Set Held Item (clientbound) packet.
This packet contains one integer property - slot
, which is the index of the item in the player hotbar.
public class SetHeldItemClientboundPacket : IMinecraftClientboundPacket<SetHeldItemClientboundPacket>{ public required int Slot { get; set; }
public void Encode(ref MinecraftBuffer buffer, ProtocolVersion protocolVersion) { buffer.WriteVarInt(Slot); }
public static SetHeldItemClientboundPacket Decode(ref MinecraftBuffer buffer, ProtocolVersion protocolVersion) { int slot = buffer.ReadVarInt();
return new SetHeldItemClientboundPacket { Slot = slot }; }
public void Dispose() { GC.SuppressFinalize(this); }}
Registering Packets
Section titled “Registering Packets”Before receiving or sending packets, you need to register them specifying packet ids for each game protocol version.
Packets registrations are made for each game phase, so you need to register them in the correct phase. Common phases are Handshake
, Login
, Configuration
and Play
.
[Subscribe]public void OnPhaseChanged(PhaseChangedEvent @event){ if (@event.Phase is not Phase.Play) return;
@event.Player.RegisterPacket<SetHeldItemClientboundPacket>([ new(0x4F, ProtocolVersion.MINECRAFT_1_20_2), new(0x51, ProtocolVersion.MINECRAFT_1_20_3), new(0x53, ProtocolVersion.MINECRAFT_1_20_5), new(0x63, ProtocolVersion.MINECRAFT_1_21_2), new(0x62, ProtocolVersion.MINECRAFT_1_21_5) ]);}
Receiving Packets
Section titled “Receiving Packets”Now that we have defined our packet, we can receive it with MessageReceivedEvent
event.
[Subscribe]public void OnMessageReceived(MessageReceivedEvent @event){ // Check if the message is a SetHeldItemClientboundPacket if (@event.Message is not SetHeldItemClientboundPacket packet) return;
// Print the slot value sent by server Console.WriteLine($"Received {nameof(SetHeldItemClientboundPacket)} with Slot: {packet.Slot}");}
There is also a MessageSentEvent
event that is triggered when the packet is already sent to the receiver.
[Subscribe]public void OnMessageSent(MessageSentEvent @event){ // Check if the message is a SetHeldItemClientboundPacket if (@event.Message is not SetHeldItemClientboundPacket packet) return;
// Print the slot value sent to the player Console.WriteLine($"Sent {nameof(SetHeldItemClientboundPacket)} with Slot: {packet.Slot}");}
Sending Packets
Section titled “Sending Packets”There are 3 ways to send packets in Void:
- Directly to the
IPlayer
instance - To the
INetworkChannel
of server or player - To the
ILink
connection between the server and player
Sending Packets to the Player
Section titled “Sending Packets to the Player”You can send packets to the player with SendPacketAsync
method on IPlayer
instance.
await player.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
Sending Packets to the Server
Section titled “Sending Packets to the Server”You can send packets to the server with ILink.ServerChannel
instance.
await player.GetLink().ServerChannel.SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
Sending Packets to the ILink
Section titled “Sending Packets to the ILink”You can send packets to the link with ILink.SendPacketAsync
method.
ILink
will automatically determine the destination of the packet based on the packet interface.
- If the packet has
IMinecraftClientboundPacket<TPacket>
interface, it will be sent to the client. - If the packet has
IMinecraftServerboundPacket<TPacket>
interface, it will be sent to the server. - If the packet has both interfaces, it will be sent only to the client.
- If the packet has neither interface,
InvalidOperationException
will be thrown.
await player.GetLink().SendPacketAsync(new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
When you want to explicitly send a packet to the server or client, SendPacketAsync
has overload that specifies the Side of destination.
await player.GetLink().SendPacketAsync(Side.Client, new SetHeldItemClientboundPacket { Slot = slot }, cancellationToken);
Complete Example
Section titled “Complete Example”Check out complete example for inventory service plugin that includes both clientbound and serverbound set held item packets.
Cancelling Packets
Section titled “Cancelling Packets”You can cancel packets in MessageReceivedEvent
event.
Set IEvent.Result
value to true
to prevent sending packet to receiver.
[Subscribe]public void OnMessageReceived(MessageReceivedEvent @event){ if (@event.Message is not SetHeldItemClientboundPacket packet) return;
// Cancel the SetHeldItemClientboundPacket packet @event.Result = true;}