NeoForge-1.20.4Mod开发教程之方块的数据同步

本篇教程参考为Flandre芙兰的NeoForge1.20.4Mod开发的视频,这篇教程是我边学边总结的,与其说是教程更像我的学习笔记,如有不清楚的地方,大家可以在评论区提问。在我力所能及的范围内,我都会解答的。

 

同样的还是先从创建方块和方块实体开始,这期方块的功能是向最近的玩家每隔一定时间播放僵尸的声音

创建ZombieBlock类

public class ZombieBlock extends BaseEntityBlock {
 ​
     public ZombieBlock() {
         super(Properties.ofFullCopy(Blocks.STONE));
    }
 ​
     @Override
     protected MapCodec<? extends BaseEntityBlock> codec() {
         return null;
    }
 ​
     @Override
     public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
         return new ZombieBlockEntity(pos, state);
    }
 ​
     public @NotNull RenderShape getRenderShape(BlockState state) {
         return RenderShape.MODEL;
    }
 ​
     @Override
     public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, @NotNull BlockState state, @NotNull BlockEntityType<T> blockEntityType) {//和上期不同的是需要返回客户端的逻辑
         return level.isClientSide ? createTickerHelper(blockEntityType, ModBlockEntities.ZOMBIE_BLOCK_ENTITY.get(), ZombieBlockEntity::clientTick):
                                     createTickerHelper(blockEntityType,ModBlockEntities.ZOMBIE_BLOCK_ENTITY.get(), ZombieBlockEntity::serverTick);
 ​
    }
 ​
 }

 

创建ZombieBlockEntity类

public class ZombieBlockEntity extends BlockEntity {
     private static final int MAX_TIME = 5*20;
     private boolean flag  = false;//通过改变flag状态来决定是否播放声音
     private int timer = 0;
 ​
     public ZombieBlockEntity(BlockPos pos, BlockState blockState) {
         super(ModBlockEntities.ZOMBIE_BLOCK_ENTITY.get(), pos, blockState);
    }
 ​
     public static void clientTick(Level level, BlockPos pos, BlockState state, ZombieBlockEntity zombieBlockEntity) {//客户端播放声音
         if (level.isClientSide&&zombieBlockEntity.flag) {
             var player = level.getNearestPlayer(pos.getX(), pos.getY(), pos.getZ(), 10,false);
             level.playSound(player,pos, SoundEvents.ZOMBIE_AMBIENT, SoundSource.NEUTRAL, 1.0F, 1.0F);
             zombieBlockEntity.flag = false;
        }
    }
 ​
     public static void serverTick(Level level, BlockPos pos, BlockState state, ZombieBlockEntity zombieBlockEntity) {//服务端计时
         if (level.isClientSide) {
             return;
        }
         zombieBlockEntity.timer++;
         if (zombieBlockEntity.timer >= MAX_TIME) {
            zombieBlockEntity.flag = true;
            level.sendBlockUpdated(pos,level.getBlockState(pos),level.getBlockState(pos),3);//服务器告诉客户端BlockEntity状态改变,进行同步
            zombieBlockEntity.timer = 0;
        }
    }
 ​
     @Override
     public Packet<ClientGamePacketListener> getUpdatePacket(){//返回一个用于网络同步的包,包含方块实体NBT数据(由getUpdateTag返回)
         return ClientboundBlockEntityDataPacket.create(this);
    }
 ​
     @Override
     public void onDataPacket(@NotNull Connection net, ClientboundBlockEntityDataPacket packet){//服务端发送客户端包到客户端后,客户端接收到后调用这个方法。这里拿到标签,并调用 handleUpdateTag 来处理内容
         if (packet.getTag() != null) {
             handleUpdateTag(packet.getTag());
        }
    }
 ​
     @Override
     public @NotNull CompoundTag getUpdateTag() {//每次需要发送整个方块实体数据到客户端时会调用(例如区块加载或sendBlockUpdated)
         CompoundTag compoundNBT = super.getUpdateTag();
         compoundNBT.putBoolean("flag",flag);
         return compoundNBT;
    }
 ​
     @Override
     public void handleUpdateTag(CompoundTag tag){//客户端拿到同步的数据标签后从tag读取flag并更新本地实体状态
         flag = tag.getBoolean("flag");
    }
 }

 

整体执行顺序(流程)

  1. 服务端 tick 每 tick 增加计时器

  2. 当时间到达 5 秒时

    • 服务端设置 flag=true

    • 调用 sendBlockUpdated 向客户端发送包

  3. Minecraft 内部发送包给客户端

    • getUpdatePacket 打包 NBT

    • 客户端调用 onDataPacket

    • 再调用 handleUpdateTag 读取 flag

  4. 在客户端的 clientTick

    • 检测到 flag 是 true

    • 播放僵尸声音

    • 重置 flag

© 版权声明
THE END
喜欢就支持一下吧
点赞5赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容