====== Networking ======
Architectury provides basic abstraction over Fabric and Forge's networking apis.
===== Differences between Server and Logical Server =====
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.
====== Receiver Callback ======
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");
===== Registering the Handler =====
Now, depending on the direction of the transmission, we will register the packet receiving handler in a different location.
=== Client -> Logical Server ===
When we send a packet from the client to the server, we will register the handler on the common side.
=== Logical Server -> Client ===
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
});
===== Sending the Packet =====
=== Client -> Logical Server ===
FriendlyPacketBuf buf = new FriendlyPacketBuf(Unpooled.buffer());
NetworkManager.sendToServer(EXAMPLE_PACKET_ID, buf);
=== Logical Server -> Client ===
FriendlyPacketBuf buf = new FriendlyPacketBuf(Unpooled.buffer());
NetworkManager.sendToPlayer(player, EXAMPLE_PACKET_ID, buf);
===== Handling the Packet =====
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();
});
====== Message Channel ======
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 contextSupplier) {
// On receive
}
}
After this, we will register the message into the channel.
CHANNEL.register(ExampleMessage.class, ExampleMessage::encode, ExampleMessage::new, ExampleMessage::apply);
===== Attaching more data with the message =====
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 contextSupplier) {
// Do logic here
}
}