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