网络包初步 1.20.1 Fabric 长线教程计划
本篇教程的视频
(待发布)
本篇教程的源代码
(待发布)
本篇教程目标
通过自定义网络包实现机器的开关
介绍
在前面的教程中,我们已经完成了一个完整的方块实体——矿机
现在,我们来初步了解一下自定义网络包的编写
我们将通过自定义网络包来实现矿机的开关功能,确切来说,我们将在GUI上加一个按钮,点击按钮后,客户端会发送一个自定义网络包到服务端,服务端接收到这个包后,会改变矿机的工作状态,并将新的状态同步给客户端
方块实体
首先,我们要改写一下之前写的PortableOriginiumRigBlockEntity类
添加字段
这里我们先添加一个布尔值类型的字段,将由它来决定机器的开关
1 | private boolean enable = true; |
另外,我们准备两个方法,分别用于获取和设置这个字段
特别的,设置字段方法需要额外设置一个触发数据同步的方法,以保证客户端这边的正常运行
改写 propertyDelegate
1 | this.propertyDelegate = new PropertyDelegate() { |
我们在propertyDelegate中增加了一个用于返回enable的值的语句,不过由于PropertyDelegate只能返回int类型,所以我们这里用三元运算符来判断enable的值,返回1或者0
后面GUI那边需要获取这个数据
改写tick方法
接下来我们在tick方法中增加以下语句
1 | if (!be.getEnable()) { |
这样我们矿机将根据enable的值,设置机器是否能工作,并触发数据同步
数据持久化
这里因为增加了一个新字段,我们也要将其进行数据持久化保存,不然重新进入游戏就默认是开启状态了
1 |
|
自定义网络包
接下来我们就先注册一个自定义网络包,等会到GUI上会用到
这里我们先创建一个ModNetworking
1 | public class ModNetworking { |
我们先创建一个Identifier类型的字段,这个是数据包的ID,因为每个数据包都必须有自己独立的标识,这和那些物品、方块注册一样
1 | public static final Identifier SWITCH_PACKET_ID = new Identifier(TutorialModRe.MOD_ID, "switch_packet"); |
然后,我们写一个register方法,模组主类将调用这个方法
1 | public static void register() { |
这是一个很简单的网络包,简单来说就是当服务端收到SWITCH_PACKET_ID这个包时,执行后面的回调函数
ServerPlayNetworking是Fabric为我们提供的API
在收到包后,我们先从packetByteBuf中读取数据,这里我们读取了一个BlockPos和一个布尔值
而后我们把后续对方块实体的操作通过minecraftServer.execute包起来,扔到服务器执行队列里
这是因为Minecraft的网络回调不一定运行在主线程中,但对方块实体、世界的操作必须在主线程中执行,所以这里的操作能避免线程安全问题
最后,我们判断这个方块实体是否是我们矿机的方块实体,如果是,就调用我们之前写的setEnable方法,设置矿机的开关状态
另外,模组主类的调用不要忘记
1 | ModNetworking.register(); |
自定义GUI开关类
在写GUI相关逻辑之前,我们先创建一个自定义的按钮类,这样我们就可以用自定义的材质做我们的按钮
我们先创建一个SwitchButton类,继承自PressableWidget,并重写相应的方法
1 | public class SwitchButton extends PressableWidget { |
我们先添加四个字段
1 | private static final Identifier SWITCH_ENABLE = new Identifier(TutorialModRe.MOD_ID, "textures/gui/button/switch_enable.png"); |
上面两个是我们按钮的材质,分别是开启和关闭状态的按钮
下面两个是我们自定义的函数式接口,分别用于获取当前按钮的状态,以及点击按钮后执行的操作
接下来改写构造函数
1 | public SwitchButton(int i, int j, BooleanSupplier booleanSupplier, Consumer<Boolean> onToggle) { |
我们可以将前面的k和l两个参数去掉,直接指定我们按钮的大小为16*16,然后将booleanSupplier和onToggle赋值给我们之前定义的字段
然后重写onPress方法
1 |
|
简单来说就是当按钮被点击时,获取当前按钮的状态取反,然后调用onToggle方法,将新的状态传入
另外我们还要重写renderButton方法,这个方法用于渲染按钮
1 |
|
首先要获取当前按钮的状态,根据状态选择不同的材质,然后调用context.drawTexture方法绘制按钮
另外在鼠标悬停时,我们还要绘制一个半透明的白色矩形,来提示玩家这个按钮是可以点击的
最后还有一个appendClickableNarrations方法,这个方法用于为按钮添加可点击的叙述,不过它是在无障碍模式下使用的,我们这里就不写了,直接空着
添加GUI按钮
我们先到PortableOriginiumRigScreenHandler类中添加一个方法,用于获取矿机的开关状态
1 | public boolean isEnabled() { |
另外记得吧构造函数的ArrayPropertyDelegate大小改一下,否则会崩溃
1 | public PortableOriginiumRigScreenHandler(int syncId, PlayerInventory playerInventory, PacketByteBuf buf) { |
接下来我们到PortableOriginiumRigScreen类中添加按钮
1 |
|
这里我们重写了init方法,在里面添加了一个SwitchButton按钮,按钮的位置是相对于GUI的左上角的偏移量
根据handler::isEnabled来获取当前矿机的开关状态,取反后将新的状态写入到PacketByteBuf中,并发送给服务端
服务端接收到这个包后,会调用我们之前写的ModNetworking.register方法中的回调函数,改变矿机的开关状态,并同步给客户端











