Minecraft 同步区块实体取消区块破坏事件

问题描述 投票:0回答:1

在 Minecraft Forge 1.19.4 中,我有一个使用方法处理的 BlockEvent.BreakEvent 事件。在这个方法中,在特定条件下,我取消了块销毁。然而,由于该事件仅在服务器端触发,因此副作用是该块消失并在客户端重新出现,并且与该块关联的块实体变得不对齐。即使我尝试通过发送数据包将其与服务器到客户端同步对齐,它也没有效果,因为同步发生在实际破坏和重建块之前调用的方法中,无论是服务器端还是客户端。 我的代码在这里:

@Mod.EventBusSubscriber(modid = DoorLockPadMod.MODID)
public class EventHandlers {
@SubscribeEvent
    public static void onBlockDestroyed(@NotNull BlockEvent.BreakEvent event) {
        Player playerEntity = event.getPlayer();
        Level level = playerEntity.getLevel();
        BlockPos blockPos = event.getPos();
        BlockState blockState = level.getBlockState(blockPos);
        FunctionUtils.setLevel(level);
        if (blockState.getBlock() instanceof DoorBlock) {
            BlockPos padBlockPos = FunctionUtils.checkIfAssignedDoor(blockPos, null);
            if (padBlockPos != null) {
                PadBlockEntity padBlockEntity = (PadBlockEntity) level.getBlockEntity(padBlockPos);
                BlockState padBlockState = level.getBlockState(padBlockPos);
                if (padBlockEntity != null && (!padBlockEntity.isDoorLocked() || playerEntity.isCreative())) {
                    padBlockEntity.setRelatedDoor(null);
                    padBlockEntity.setPassword(null);
                    level.sendBlockUpdated(padBlockPos, padBlockState, padBlockState, 2);
                    FunctionUtils.removeDoorAssignedPadBlockByPadBlock(padBlockPos);
                } else event.setCanceled(true);
            }
        } else if (blockState.getBlock() instanceof PadBlock) {
            PadBlockEntity padBlockEntity = (PadBlockEntity) level.getBlockEntity(blockPos);
            if (padBlockEntity != null && (!padBlockEntity.isDoorLocked() || playerEntity.isCreative())) {
                FunctionUtils.removeDoorAssignedPadBlockByPadBlock(blockPos);
            } else if (padBlockEntity != null) event.setCanceled(true);
        }
    }
}

此客户端 BlockEntity 在 blockPos 处具有与服务器端 BlockEntity 不同的值。

PadBlockEntity类的代码为:

public class PadBlockEntity extends BlockEntity {
    private BlockPos pos;
    private BlockPos supportBlockPos;
    private String password;
    private boolean isDoorLocked;
    private String relatedDoor;
    private int ticks;

    public PadBlockEntity(BlockPos pos, BlockState state) {
        super(DoorLockPadMod.PAD_BLOCK_ENTITY.get(), pos, state);
        this.pos = pos;
        this.isDoorLocked = true;
        this.ticks = 0;
    }

    public BlockPos getPos() {
        return pos;
    }

    public void setPos(BlockPos pos) {
        this.pos = pos;
    }

    public BlockPos getSupportBlockPos() {
        return supportBlockPos;
    }

    public void setSupportBlockPos(BlockPos supportBlockpos) {
        this.supportBlockPos = supportBlockpos;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isDoorLocked() {
        return isDoorLocked;
    }

    public void setDoorLocked(boolean doorLocked) {
        isDoorLocked = doorLocked;
    }

    public String getRelatedDoor() {
        return relatedDoor;
    }

    public void setRelatedDoor(String relatedDoor) {
        this.relatedDoor = relatedDoor;
    }

    @Override
    protected void saveAdditional(CompoundTag nbt) {
        super.saveAdditional(nbt);
        if (this.getPassword() != null)
            nbt.putString("password", this.getPassword());
        nbt.putLong("pos", this.getPos().asLong());
        nbt.putLong("supportBlockPos", this.getSupportBlockPos().asLong());
        if (this.getRelatedDoor() != null)
            nbt.putString("relatedDoor", this.getRelatedDoor());
    }

    @Override
    public void load(@NotNull CompoundTag nbt) {
        super.load(nbt);
        this.password = nbt.getString("password");
        this.pos = BlockPos.of(nbt.getLong("pos"));
        this.supportBlockPos = BlockPos.of(nbt.getLong("supportBlockPos"));
        this.relatedDoor = nbt.getString("relatedDoor");
        if (StringUtils.isEmpty(this.relatedDoor)) {
            this.isDoorLocked = true;
            this.ticks = 0;
        }
    }

    @NotNull
    @Override
    public CompoundTag getUpdateTag() {
        super.getUpdateTag();
        return saveWithoutMetadata();
    }

    @Override
    public void handleUpdateTag(CompoundTag nbt) {
        super.handleUpdateTag(nbt);
        this.load(nbt);
    }

    @Override
    public Packet<ClientGamePacketListener> getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }

    public static <T extends BlockEntity> void tick(Level level, BlockPos blockPos, BlockState state,
                                                    T blockEntity) {
        if (level.isClientSide && blockEntity instanceof PadBlockEntity padBlockEntity) {
            if (!padBlockEntity.isDoorLocked()) {
                padBlockEntity.ticks++;
            }

            if (padBlockEntity.ticks == 200) {
                padBlockEntity.setDoorLocked(true);
                FunctionUtils.sendToServer(padBlockEntity);
                padBlockEntity.ticks = 0;
            }
        }
    }
}

还有其他解决办法吗?

我尝试过使用sendBlockUpdate,但在我看来,在事件结束之前该块实际上并未被销毁,因此这会影响尚未修改的块实体。我尝试创建一个数据包来同步服务器和客户端,但出于同样的原因,我认为它不起作用。在整个方法中,块尚未被破坏和重新插入,并且块实体保持不变。

java minecraft forge
1个回答
0
投票

我已经解决了!实际上,Forge 在出于我不知道的原因取消块销毁事件后,仅在客户端重置块实体。我保存了旧的块实体,并在看到块实体已在客户端设置后立即将其重新插入。下面是我的意思的代码片段。

@Mod.EventBusSubscriber(modid = DoorLockPadMod.MODID)
public class EventHandlers {
private static BlockEntity oldBlockEntity;
...
@SubscribeEvent
    public static void onBlockDestroyed(@NotNull BlockEvent.BreakEvent event) {
    ...
    else if (padBlockEntity != null) {
                event.setCanceled(true);
                oldBlockEntity = padBlockEntity;
            }
    ...
    }

    @SubscribeEvent
    public static void onTick(TickEvent.ClientTickEvent event) {
        if (oldBlockEntity != null) {
            ClientLevel level = Minecraft.getInstance().level;
            BlockPos blockPos = oldBlockEntity.getBlockPos();
            if (level != null && level.getBlockEntity(blockPos) != null) {
                level.setBlockEntity(oldBlockEntity);
                oldBlockEntity = null;
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.