User Tools

Site Tools


zh_cn: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
zh_cn:tutorial:networking [2022/01/28 07:18] – [Advanced Networking topics] solidblockzh_cn:tutorial:networking [2024/07/05 10:58] (current) solidblock
Line 1: Line 1:
 **注意:**本页已经取代旧版页面。建议使用本页描述的新的网络API。旧的页面参见[[tutorial:legacy:networking-v0|此处(英文)]]。 **注意:**本页已经取代旧版页面。建议使用本页描述的新的网络API。旧的页面参见[[tutorial:legacy:networking-v0|此处(英文)]]。
 +
 +对于 1.20.5 引入的新的网络通信 API,请参见 [[#1.20.5 中的网络通信]]。
  
 ====== 网络通信 ====== ====== 网络通信 ======
Line 41: Line 43:
 要修复崩溃,你需要了解 Minecraft 如何在客户端和专用服务器之间通信(communication)。 要修复崩溃,你需要了解 Minecraft 如何在客户端和专用服务器之间通信(communication)。
  
-[[Minecraft 的逻辑|{{tutorial:sides.png?700}}]]+[[Minecraft 的逻辑|{{tutorial:sides.png?700}}]]
  
 从上面这张图可以看到,游戏客户端和专用服务器是相互分离的系统,并使用数据包(packets,注意不是 datapack)桥接在一起。这个数据包桥(packet bridge)不仅存在于游戏客户端和专用服务器之间,还存在于您的客户端和通过 LAN 连接的另一个客户端之间。注意即使是单人游戏也有数据包桥,这是因为游戏客户端会启动一个特殊的集成服务器实例来运行游戏。下表显示了三种连接类型之间的主要区别: 从上面这张图可以看到,游戏客户端和专用服务器是相互分离的系统,并使用数据包(packets,注意不是 datapack)桥接在一起。这个数据包桥(packet bridge)不仅存在于游戏客户端和专用服务器之间,还存在于您的客户端和通过 LAN 连接的另一个客户端之间。注意即使是单人游戏也有数据包桥,这是因为游戏客户端会启动一个特殊的集成服务器实例来运行游戏。下表显示了三种连接类型之间的主要区别:
Line 72: Line 74:
 </code> </code>
  
-接下来,我们需要将数据包发送到游戏客户端。首先需要定义一个用于识别数据包的 ''Identifier''。对于本例,我们的标识符为 ''wiki_example:highlight_block''。为将数据包发送到游戏客户端,需要指定要哪个玩家的游戏客户端接收数据包。由于该操作发生在逻辑服务器上,所以可以将 ''player'' 向上强转为 ''ServerPlayerEntity''+接下来,我们需要将数据包发送到游戏客户端。首先需要定义一个用于识别数据包的 ''Identifier''。对于本例,我们的标识符为 ''tutorial:highlight_block''。为将数据包发送到游戏客户端,需要指定要哪个玩家的游戏客户端接收数据包。由于该操作发生在逻辑服务器上,所以可以将 ''player'' 向上强转为 ''ServerPlayerEntity'' 
 +<code java> 
 +public class TutorialNetworkingConstants { 
 +    // 存储数据包的 id 以便后面引用 
 +    public static final Identifier HIGHLIGHT_PACKET_ID = Identifier.of("tutorial", "highlight_block"); 
 +
 +</code>
  
 要将数据包发送到玩家,我们使用 ''ServerPlayerNetworking'' 中的一些方法。我们使用该类中的以下方法: 要将数据包发送到玩家,我们使用 ''ServerPlayerNetworking'' 中的一些方法。我们使用该类中的以下方法:
Line 81: Line 89:
 </code> </code>
  
-数据包将会被发送到此方法中的玩家。通道名称是你之前决定用来识别数据包的 ''Identifier''。''PacketByteBuf'' 用于存储数据包的数据。We will return later to writing data to the packet's payload via the buf.+数据包将会被发送到此方法中的玩家。通道名称是你之前决定用来识别数据包的 ''Identifier''。''PacketByteBuf'' 用于存储数据包的数据。然后我们返回,以通过 buf 将数据写入数据包的有效负载。
  
 由于我们没有向数据包写入任何数据,所以现在,我们将发送带有空有效负载的数据包。可以使用 ''PacketByteBufs.empty()'' 创建带有空有效负载的 buf。 由于我们没有向数据包写入任何数据,所以现在,我们将发送带有空有效负载的数据包。可以使用 ''PacketByteBufs.empty()'' 创建带有空有效负载的 buf。
Line 201: Line 209:
  
 这样修改之后,当你使用魔杖时,你的朋友也应该在他们自己的客户端上看到高亮方块。 这样修改之后,当你使用魔杖时,你的朋友也应该在他们自己的客户端上看到高亮方块。
-====== 高级网络主题 ====== 
  
-网络系统 Fabric API 提供的功能非常灵活并支持除了发送和接收简单数据包之外其他功能由于其中一些更高级的主题很长以下是指向其特页面链接+===== 1.20.5 中的网络通信 ===== 
 +自 1.20.5 开始网络通信逻辑被大改在 1.20.5 ,''RegistryByteBuf'' 现在用于游玩阶段网络,你需要定义个 ''Payload''。首先,定义一个包含了 ''BlockPos'' 的 ''Payload''
  
-^ 网络通信主题 ^ 描述 ^ +<code java> 
-| [[zh_cn:tutorial:networking:connection_events|Connection Network connection events]] | 与客户端或服务器连接的生命周期相关的事件 | +public record BlockHighlightPayload(BlockPos blockPos) implements CustomPayload { 
-| [[zh_cn:tutorial:networking:channel_events|Channel registration events]] | 与客户端或服务器相关的事件声明在特定名称的通道上接收数据包的能力 | +  public static final Id<BlockHighlightPayload> ID = new CustomPayload.Id<>(TutorialNetworkingConstants.HIGHLIGHT_PACKET_ID); 
-| [[zh_cn:tutorial:networking:login|Login phase networking]]| 在登录期间向客户端发送请求;并允许在短时间内延迟登录 | +  public static final PacketCodec<PacketByteBuf, BlockHighlightPayload> CODEC = PacketCodec.tuple(BlockPos.PACKET_CODEC, BlockHighlightPayload::blockPos, BlockHighlightPayload::new); 
-| [[zh_cn:tutorial:networking:dynamic_handlers|Dynamic registration of channel handlers]]| 允许连接接收带有特殊处理器的数据包 |+  // 者,你也可以这样写: 
 +  // 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> 
 + 
 +然后,像这样注册 receiver: 
 +<code java> 
 +PayloadTypeRegistry.playS2C().register(BlockHighlightPayload.ID, BlockHighlightPayload.CODEC); 
 +ClientPlayNetworking.registerGlobalReceiver(BlockHighlightPayload.ID, (payload, context) -> { 
 +  context.client().execute(() -> { 
 +    ClientBlockHighlighting.highlightBlock(client, target); 
 +  }); 
 +}); 
 +</code> 
 + 
 +现在,在服务器一端你可以像这样把数据包发送给玩家: 
 +<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>
zh_cn/tutorial/networking.1643354288.txt.gz · Last modified: 2022/01/28 07:18 by solidblock