User Tools

Site Tools


tutorial:networking

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:networking [2020/12/10 19:13] – Rewrite for networking v1 - v0 has been moved i509vcbtutorial:networking [2024/04/15 02:17] (current) – [Advanced Networking topics] solidblock
Line 1: Line 1:
-====== Page Information ====== +**Note: **This page has replaced the old networking page. 
- +It is recommended to use the new networking API described on this page.
-This page has replaced the old networking page. +
-It is recommended to use the new networking api described on this page.+
 The old page can be [[tutorial:legacy:networking-v0|found here]]. The old page can be [[tutorial:legacy:networking-v0|found here]].
  
Line 15: Line 13:
 Say you had a wand which highlights the block you are looking at to all nearby players. Say you had a wand which highlights the block you are looking at to all nearby players.
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-class HighlightingWandItem extends Item {+public class HighlightingWandItem extends Item {
     public HighlightingWand(Item.Settings settings) {     public HighlightingWand(Item.Settings settings) {
         super(settings)         super(settings)
Line 51: Line 49:
 [[Minecraft's Logical Sides|{{tutorial:sides.png?700}}]] [[Minecraft's Logical Sides|{{tutorial:sides.png?700}}]]
  
-The diagram above shows that the game client and dedicated server art separate systems bridged together using packets. This packet bridge does not only exist between a game client and dedicated server, but also between your client and another client connected over LAN. The packet bridge is also present even in singleplayer! This is because the game client will spin up a special integrated server instance to run the game on. The key difference between the three types of connections that are shown in the table below:+The diagram above shows that the game client and dedicated server are separate systems bridged together using packets. This packet bridge does not only exist between a game client and dedicated server, but also between your client and another client connected over LAN. The packet bridge is also present even in singleplayer! This is because the game client will spin up a special integrated server instance to run the game on. The key difference between the three types of connections that are shown in the table below:
  
 ^ Connection Type               ^ Access to game client       ^ ^ Connection Type               ^ Access to game client       ^
Line 58: Line 56:
 | Singleplayer (or LAN host)    | Yes -> Full access          | | Singleplayer (or LAN host)    | Yes -> Full access          |
  
-It may seem complicated to have to communication with the server in three different ways. However you don't need to communicate in three different ways with the game client. Since all three connection types communicate with the game client using packets, you only need to communicate with the game client like you are always running on a dedicated server. Connection to server over LAN or Singleplayer can be also be treated like the server is a remote dedicated server; so your game client cannot directly access the server instance.+It may seem complicated to have communication with the server in three different ways. Howeveryou don't need to communicate in three different ways with the game client. Since all three connection types communicate with the game client using packets, you only need to communicate with the game client like you are always running on a dedicated server. Connection to server over LAN or Singleplayer can be also be treated like the server is a remote dedicated server; so your game client cannot directly access the server instance.
  
 ===== An introduction to networking ===== ===== An introduction to networking =====
Line 76: Line 74:
         // BAD CODE: DON'T EVER DO THIS!         // BAD CODE: DON'T EVER DO THIS!
         ClientBlockHighlighting.highlightBlock(MinecraftClient.getInstance(), target);         ClientBlockHighlighting.highlightBlock(MinecraftClient.getInstance(), target);
-        return TypedActionResult.success(user.getHandStack(hand));+        return TypedActionResult.success(user.getStackInHand(hand));
     }     }
 </code> </code>
  
-Next we need to send the packet to the game client. First you need to define an ''Identifier'' used to identify your packet. For this example our Identifier will be ''wiki_example:highlight_block''. In order to send the packet to the game client, you need to specify which player's game client you want to packet to be received by. Since the action is occurring on the logical server, we may upcast the ''player'' to a ''ServerPlayerEntity''.+Nextwe need to send the packet to the game client. Firstyou need to define an ''Identifier'' used to identify your packet. For this example our Identifier will be ''wiki_example:highlight_block''. In order to send the packet to the game client, you need to specify which player's game client you want the packet to be received by. Since the action is occurring on the logical server, we may upcast the ''player'' to a ''ServerPlayerEntity''. 
 +<code java> 
 +public class TutorialNetworkingConstants { 
 +    // Save the id of the packet so we can reference it later 
 +    public static final Identifier HIGHLIGHT_PACKET_ID = new Identifier("wiki_example", "highlight_block"); 
 +
 +</code>
  
 To send the packet to the player, we will use some of the methods inside of ''ServerPlayNetworking''. We will use the following method inside of that class: To send the packet to the player, we will use some of the methods inside of ''ServerPlayNetworking''. We will use the following method inside of that class:
Line 91: Line 95:
 The player in this method is the player the packet will be sent to. The channel name is the ''Identifier'' you decided on earlier to identify your packet. The ''PacketByteBuf'' is what stores the data for the packet. We will return later to writing data to the packet's payload via the buf. The player in this method is the player the packet will be sent to. The channel name is the ''Identifier'' you decided on earlier to identify your packet. The ''PacketByteBuf'' is what stores the data for the packet. We will return later to writing data to the packet's payload via the buf.
  
-Since we are not writing any data to the packet for now, we will send the packet with an empty payload. A buf with an empty payload may be created using ''PacketByteBufs.empty()''.+Since we are not writing any data to the packetfor now, we will send the packet with an empty payload. A buf with an empty payload may be created using ''PacketByteBufs.empty()''.
  
 <code java [enable_line_numbers="true", highlight_lines_extra="2"]> <code java [enable_line_numbers="true", highlight_lines_extra="2"]>
     ....     ....
-    ServerPlayNetworking.send((ServerPlayerEntity) user, ModNetworkingConstants.HIGHLIGHT_PACKET_ID, PacketByteBufs.empty());+    ServerPlayNetworking.send((ServerPlayerEntity) user, TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, PacketByteBufs.empty());
     return TypedActionResult.success(user.getHandStack(hand));     return TypedActionResult.success(user.getHandStack(hand));
 } }
Line 105: Line 109:
  
 To receive a packet from a server on the game client, your mod needs to specify how it will handle the incoming packet. To receive a packet from a server on the game client, your mod needs to specify how it will handle the incoming packet.
-In your client entrypoint, you will register the receiver for your packet using ''PlayClientNetworking.registerGlobalReceiver(Identifier channelName, ChannelHandler channelHandler)''+In your client entrypoint, you will register the receiver for your packet using ''ClientPlayNetworking.registerGlobalReceiver(Identifier channelName, ChannelHandler channelHandler)''
  
-The ''Identifier'' should match the same Identifier you use to send the packet to the client. The ''ChannelHandler'' is the functional interface you will use to implement how the packet is handled. **Note the ''ChannelHandler'' should be the one that is a nested interface of ''PlayClientNetworking''**+The ''Identifier'' should match the same Identifier you use to send the packet to the client. The ''ChannelHandler'' is the functional interface you will use to implement how the packet is handled. **Note the ''ChannelHandler'' should be the one that is a nested interface of ''ClientPlayNetworking''**
  
 The example below implements the play channel handler as a lambda: The example below implements the play channel handler as a lambda:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-ClientPlayNetworking.registerGlobalReceiver(ModNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {+ClientPlayNetworking.registerGlobalReceiver(TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {
     ...     ...
 }); });
 </code> </code>
  
-However you cannot draw the highlight box immediately. This is because the receiver is called on the netty event loop. The event loop runs on another thread, and you must draw the highlight box on the render thread.+Howeveryou cannot draw the highlight box immediately. This is because the receiver is called on the netty event loop. The event loop runs on another thread, and you must draw the highlight box on the render thread.
  
 In order to draw the highlight box, you need to schedule the task on the game client. This may be done with the ''client'' field that is provided in the channel handler. Typically you will run the task on the client by using the ''execute'' method: In order to draw the highlight box, you need to schedule the task on the game client. This may be done with the ''client'' field that is provided in the channel handler. Typically you will run the task on the client by using the ''execute'' method:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-ClientPlayNetworking.registerGlobalReceiver(ModNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {+ClientPlayNetworking.registerGlobalReceiver(TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {
     client.execute(() -> {     client.execute(() -> {
         // Everything in this lambda is run on the render thread         // Everything in this lambda is run on the render thread
Line 130: Line 134:
 </code> </code>
  
-You may have noticed you are not told where the block to highlight is. You can write this data to the packet byte buf. Instead of sending ''PacketByteBufs.empty()'' to the game client in your item's ''use'' method, instead you will create a new packet byte buf and send that instead.+You may have noticed you are not told where the block to highlight is. You can write this data to the packet byte buf. Instead of sending ''PacketByteBufs.empty()'' to the game client in your item's ''use'' method, insteadyou will create a new packet byte buf and send that instead.
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
Line 136: Line 140:
 </code> </code>
  
-Next you need to write the data to the packet byte buf. It should be noted that you must read data in the same order you write it.+Nextyou need to write the data to the packet byte buf. It should be noted that you must read data in the same order you write it.
  
 <code java [enable_line_numbers="true", highlight_lines_extra="3"]> <code java [enable_line_numbers="true", highlight_lines_extra="3"]>
Line 144: Line 148:
 </code> </code>
  
-Afterwards you will send the ''buf'' field through the ''send'' method.+Afterwardsyou will send the ''buf'' field through the ''send'' method.
  
 To read this block position on the game client, you can use ''PacketByteBuf.readBlockPos()''. To read this block position on the game client, you can use ''PacketByteBuf.readBlockPos()''.
Line 152: Line 156:
 In the end, the client's handler would look like this: In the end, the client's handler would look like this:
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-ClientPlayNetworking.registerGlobalReceiver(ModNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {+ClientPlayNetworking.registerGlobalReceiver(TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, (client, handler, buf, responseSender) -> {
     // Read packet data on the event loop     // Read packet data on the event loop
     BlockPos target = buf.readBlockPos();     BlockPos target = buf.readBlockPos();
Line 165: Line 169:
 ==== Sending packets to the server and receiving packets on the server ==== ==== Sending packets to the server and receiving packets on the server ====
  
-Sending packets to a server and receiving a packet on the server are very similar to how you would on the client. However there are a few key differences.+Sending packets to a server and receiving a packet on the server is very similar to how you would on the client. Howeverthere are a few key differences.
  
 Firstly sending a packet to the server is done through ''ClientPlayNetworking.send''. Receiving a packet on the server is similar to receiving a packet on the client, using the ''ServerPlayNetworking.registerGlobalReceiver(Identifier channelName, ChannelHandler channelHandler)'' method. The ''ChannelHandler'' for the server networking also passes the ''ServerPlayerEntity'' (player) who sent the packet through the ''player'' parameter. Firstly sending a packet to the server is done through ''ClientPlayNetworking.send''. Receiving a packet on the server is similar to receiving a packet on the client, using the ''ServerPlayNetworking.registerGlobalReceiver(Identifier channelName, ChannelHandler channelHandler)'' method. The ''ChannelHandler'' for the server networking also passes the ''ServerPlayerEntity'' (player) who sent the packet through the ''player'' parameter.
  
-===== The concept of tracking and why you only see the highlighted block =====+===== The concept of tracking and why only you see the highlighted block =====
  
-Now that the highlighting wand properly uses networking so the dedicated server does not crash, you invite your friend back on the server to show off the highlighting wand. You use the wand and the block is highlighted on your client and the server does not crash. However your friend does not see the highlighted block. This is intentional with the code that you already have here. To solve this issue let us take a look at the item's use code:+Now that the highlighting wand properly uses networking so the dedicated server does not crash, you invite your friend back on the server to show off the highlighting wand. You use the wand and the block is highlighted on your client and the server does not crash. Howeveryour friend does not see the highlighted block. This is intentional with the code that you already have here. To solve this issue let us take a look at the item's use code:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
Line 183: Line 187:
         buf.writeBlockPos(target);         buf.writeBlockPos(target);
  
-        ServerPlayNetworking.send((ServerPlayerEntity) user, ModNetworkingConstants.HIGHLIGHT_PACKET_ID, buf);+        ServerPlayNetworking.send((ServerPlayerEntity) user, TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, buf);
         return TypedActionResult.success(user.getHandStack(hand));         return TypedActionResult.success(user.getHandStack(hand));
     }     }
Line 192: Line 196:
 Since we know where the highlight will occur, we can use ''PlayerLookup.tracking(ServerWorld world, BlockPos pos)'' to get a collection of all players who can see that position in the world. Then you would simply iterate through all players in the returned collection and send the packet to each player: Since we know where the highlight will occur, we can use ''PlayerLookup.tracking(ServerWorld world, BlockPos pos)'' to get a collection of all players who can see that position in the world. Then you would simply iterate through all players in the returned collection and send the packet to each player:
  
-<code java [enable_line_numbers="true", highlight_lines_extra="2,3"]>+<code java [enable_line_numbers="true", highlight_lines_extra="10,11,12,13"]>
     public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {     public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
         // Verify we are processing the use action on the logical server         // Verify we are processing the use action on the logical server
Line 204: Line 208:
         // Iterate over all players tracking a position in the world and send the packet to each player         // Iterate over all players tracking a position in the world and send the packet to each player
         for (ServerPlayerEntity player : PlayerLookup.tracking((ServerWorld) world, target)) {         for (ServerPlayerEntity player : PlayerLookup.tracking((ServerWorld) world, target)) {
-            ServerPlayNetworking.send(player, ModNetworkingConstants.HIGHLIGHT_PACKET_ID, buf);+            ServerPlayNetworking.send(player, TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID, buf);
         }         }
  
Line 211: Line 215:
 </code> </code>
  
-After this change, when you use the wand, you friend should also see the highlighted block on their own client.+After this change, when you use the wand, your friend should also see the highlighted block on their own client.
  
-====== Advanced Networking topics ====== 
  
-The Networking system Fabric API supplies is very flexible and supports additional features other than just sending and receiving simple packetsAs some of these more advanced topics are longhere are links to their specific pages:+===== Networking in 1.20.5 ===== 
 +Since 1.20.5, the logic of networking is totally rewrited. In 1.20.5you have to define a custom ''Payload''. First, define the payload that includes a ''BlockPos'':
  
-^ Networking Topic ^ Description ^ +<code java> 
-| [[tutorial:networking:connection_events|Connection Network connection events]] | Events related to the the lifecycle of a connection to a client or server | +public record BlockHighlightPayload(BlockPos blockPos) implements CustomPayload { 
-| [[tutorial:networking:channel_events|Channel registration events]] | Events related to a server of client declaring the ability to receive a packet on a channel of a specific name | +  public static final Id<BlockHighlightPayload> ID = CustomPayload.id("tutorial:block_highlight"); 
-| [[tutorial:networking:login|Login phase networking]]| Sending requests to a client during loginand allowing delay of login for a short amount of time | +  public static final PacketCodec<PacketByteBuf, BlockHighlightPayload> CODEC = PacketCodec.tuple(BlockPos.PACKET_CODEC, BlockHighlightPayload::blockPos, BlockHighlightPayload::new)
-| [[tutorial:networking:dynamic_handlers|Dynamic registration of channel handlers]]| Allowing for a connection to receive a packet with a special handler |+  // or you can also write like this: 
 +  // public static final PacketCodec<PacketByteBuf, BlockHighlightPayload> CODEC = PacketCodec.of((value, buf) -> buf.writeBlockPos(value.blockPos), buf -> new BlockHighlightPayload(buf.readBlockPos()));
  
 +  @Override
 +  public Id<? extends CustomPayload> getId() {
 +    return ID;
 +  }
 +}
 +</code>
 +
 +And then, register the receiver like this:
 +<code java>
 +PayloadTypeRegistry.playS2C().register(BlockHighlightPayload.ID, BlockHighlightPayload.CODEC);
 +ClientPlayNetworking.registerGlobalReceiver(BlockHighlightPayload.ID, (payload, context) -> {
 +  context.client().execute(() -> {
 +    ClientBlockHighlighting.highlightBlock(client, target);
 +  });
 +});
 +</code>
 +
 +Now, on the server side, you can send the packet to players like this:
 +<code java>
 +    public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
 +        if (world.isClient()) return super.use(world, user, hand);
 +
 +        // ...
 +
 +        for (ServerPlayerEntity player : PlayerLookup.tracking((ServerWorld) world, target)) {
 +            ServerPlayNetworking.send(player, new BlockHighlightPayload(blockPos));
 +        }
 +
 +        return TypedActionResult.success(user.getHandStack(hand));
 +    }
 +</code>
tutorial/networking.1607627605.txt.gz · Last modified: 2020/12/10 19:13 by i509vcb