Architectury provides basic abstraction over Fabric and Forge's networking apis.
When you join a world in singleplayer, Minecraft fires a local integrated server. This is to separate the client and the server (logical server) logics, and to allow Opening to LAN.
Each type of message is differentiated by a ResourceLocation
, start by statically declaring one, so we can use it later.
public static final ResourceLocation EXAMPLE_PACKET_ID = new ResourceLocation("examplemod", "example_packet");
Now, depending on the direction of the transmission, we will register the packet receiving handler in a different location.
When we send a packet from the client to the server, we will register the handler on the common side.
When we send a packet from the server to the client, we will register the handler on the client side.
// We are using S2C here for an example, use C2S instead if this is from the client to the server NetworkManager.registerReceiver(NetworkManager.Side.S2C, EXAMPLE_PACKET_ID, (buf, context) -> { Player player = context.getPlayer(); // Logic });
FriendlyPacketBuf buf = new FriendlyPacketBuf(Unpooled.buffer()); NetworkManager.sendToServer(EXAMPLE_PACKET_ID, buf);
FriendlyPacketBuf buf = new FriendlyPacketBuf(Unpooled.buffer()); NetworkManager.sendToPlayer(player, EXAMPLE_PACKET_ID, buf);
The packet buffer supplied is a stream of bytes sent over the network, you must read it in order of how you sent it.
Let say, we want to send a block position and an item stack to the server. In between creating the packet buf and sending it, we can write our data to it.
FriendlyPacketBuf buf = new FriendlyPacketBuf(Unpooled.buffer()); buf.writeBlockPos(pos); buf.writeItem(stack); NetworkManager.sendToServer(EXAMPLE_PACKET_ID, buf);
And then on the server, we can read it.
NetworkManager.registerReceiver(NetworkManager.Side.C2S, EXAMPLE_PACKET_ID, (buf, context) -> { BlockPos pos = buf.readBlockPos(); ItemStack stack = buf.readItem(); });
First, we need to register a message channel for our packets to go through.
public static final NetworkChannel CHANNEL = NetworkChannel.create(new ResourceLocation("examplemod", "networking_channel"));
Then, we will create a message class like this:
public class ExampleMessage { public ExampleMessage(FriendlyPacketBuf buf) { // Decode data into a message } public ExampleMessage() { // Message creation } public void encode(FriendlyPacketBuf buf) { // Encode data into the buf } public void apply(Supplier<PacketContext> contextSupplier) { // On receive } }
After this, we will register the message into the channel.
CHANNEL.register(ExampleMessage.class, ExampleMessage::encode, ExampleMessage::new, ExampleMessage::apply);
For any data we want to add to the message, we will want to add fields, then add logic to encode, and decode it over the network.
Let say, we want to send a block position and an item stack to the server.
public class ExampleMessage { public final BlockPos pos; public final ItemStack stack; public ExampleMessage(FriendlyPacketBuf buf) { this(buf.readBlockPos(), buf.readItem()); } public ExampleMessage(BlockPos pos, ItemStack stack) { this.pos = pos; this.stack = stack; } public void encode(FriendlyPacketBuf buf) { buf.writeBlockPos(pos); buf.writeItem(stack); } public void apply(Supplier<PacketContext> contextSupplier) { // Do logic here } }