黎雨轩
本帖最后由 黎雨轩 于 2022-9-10 22:29 编辑

这应该是一个关于Java的问题

是这样的,1.19.2之前的版本如果想要使TileEntity实现tick,只需要在你mod里TileEntity的子类覆写tick函数即可。

1.19.2实现方式变了,首先你绑定BlockEntity(TileEntity改成了BlockEntity)的方块得实现EntityBlock接口,覆写getTicker,其函数签名如下
  1. @Nullable
  2.    default <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) {
  3.       return null;
  4.    }
复制代码
返回的BlockEntityTicker是一个函数式接口:
  1. @FunctionalInterface
  2. public interface BlockEntityTicker<T extends BlockEntity> {
  3.    void tick(Level pLevel, BlockPos pPos, BlockState pState, T pBlockEntity);
  4. }
复制代码



这是我覆写的getTicker:
  1. @Nullable
  2.     @Override
  3.     public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
  4.         return blockEntityType == BlockEntityTypesLibSC.BE_MINE_ORE.get() ? BlockEntityMindOre::tick : null;
  5.     }
复制代码
返回的是我方块实体里面的一个静态方法
  1. public static void tick(Level level, BlockPos blockPos, BlockState blockState, BlockEntityMindOre blockEntity) {
  2.         if (level.isClientSide) {
  3.             if (blockEntity.heartBeatTimer == MAX_TIME) {
  4.                 int x = blockPos.getX();
  5.                 int y = blockPos.getY();
  6.                 int z = blockPos.getZ();
  7.                 double volumeModified;
  8.                 Player player = level.getNearestPlayer(x, y, z, MAX_DISTANCE, false);
  9.                 if (player != null) {
  10.                     volumeModified = 1 - Math.sqrt(player.distanceToSqr(x, y, z)) / MAX_DISTANCE;
  11.                     SaintChapter.LOGGER.info(String.format("Mind Ore Info:\ndistanceSqr: %f; distance: %f; volumeModifier: %f", player.distanceToSqr(x, y, z), Math.sqrt(player.distanceToSqr(x, y, z)),  volumeModified));
  12.                     level.playSound(player, blockPos, SoundsLibSC.HEART_BEAT.get(), SoundSource.BLOCKS, (float) (3 + 7 * volumeModified), 1f);
  13.                 }
  14.                 blockEntity.heartBeatTimer = 0;
  15.             }
  16.             blockEntity.heartBeatTimer++;
  17.         }
  18.     }
复制代码
函数的第四个参数是BlockEntityMindOre  ,是BlockEntity的子类,满足泛型条件。
但是IDE里面会报错:Incompatible types: T is not convertible to BlockEntityMindOre

2442954615
非常Ok 但是我也不知道

秦千久
看 Ticker 的定义:
  1. @FunctionalInterface
  2. public interface BlockEntityTicker<T extends BlockEntity> {
  3.    void tick(Level pLevel, BlockPos pPos, BlockState pState, T pBlockEntity);
  4. }
复制代码

再看该方法的返回值泛型:
  1. <T extends BlockEntity> BlockEntityTicker<T>
复制代码

此时返回值期望的是 BlockEntityTicker<T extends BlockEntity>,但你返回的是 BlockEntityTicker<BlockEntityMindOre>,T 的范围比 BlockEntityMindOre 大,需要强制转型,所以会提示你 Incompatible types: T is not convertible to BlockEntityMindOre。
解决办法:把 tick 的最后一个参数的类型变成 BlockEntity,然后在方法内强转成 BlockEntityMindOre。
或者你可以尝试让 getTicker 的返回值类型变为 BlockEntityTicker<BlockEntityMindOre>。

黎雨轩
秦千久 发表于 2022-9-11 11:20
看 Ticker 的定义:

再看该方法的返回值泛型:

感谢大佬耐心指导。

我实现的getTicker返回时已经判断了
  1. blockEntityType == BlockEntityTypesLibSC.BE_MINE_ORE.get()
复制代码

这里的BlockEntityTypesLibSC.BE_MINE_ORE.get()是获取注册表注册的方块实体对象。
这就已经确定该函数式接口被调用时,传入的参数一定是BlockEntityType<BlockEntityMineOre>
本人java经验不足,以为编译器能够认识到这个已知条件。。

我发现原版提供了一个欺骗编译器的方法
  1. @Nullable
  2.     protected static <E extends BlockEntity, A extends BlockEntity> BlockEntityTicker<A> createTickerHelper(BlockEntityType<A> serverType, BlockEntityType<E> clientType, BlockEntityTicker<? super E> ticker) {
  3.         return clientType == serverType ? (BlockEntityTicker<A>)ticker : null;
  4.     }
复制代码

我觉得很有意思

秦千久
黎雨轩 发表于 2022-9-11 12:05
感谢大佬耐心指导。

我实现的getTicker返回时已经判断了

也不算欺骗,这就是强制类型转换。

Ph-苯
是这样的,1.19.2之前的版本如果想要使TileEntity实现tick,只需要在你mod里TileEntity的子类覆写tick函数即可。
其实不是1.19.2,是1.17的某个快照……