Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-269785

Block breaking particles on beds are misaligned when broken by creative mode players

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • None
    • 1.20.4, 24w14a
    • None
    • Community Consensus
    • Particles

      The normal behaviour can be observed when breaking a bed in survival or adventure mode. When you break a bed, the block breaking effects are played for both halves no matter in which orientation or order you break it in.

      Compare that to creative mode, where if you break the bed's top half, it behaves normally, however when you break the bottom half, the particle effects are played twice for the head, and no times for the foot.

      Doing my own investigations, the problem appears to come from this method in the bed block class (cleaned up a bit for readability) (using yarn mappings):

      @Override
      public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
          BedPart part = state.get(PART);
          BlockPos otherHalfPos = pos.offset(getDirectionTowardsOtherPart(part, state.get(FACING)));
          BlockState otherHalfState = world.getBlockState(otherHalfPos);
          if (!world.isClient && player.isCreative() && part == BedPart.FOOT && otherHalfState.isOf(this) && otherHalfState.get(PART) == BedPart.HEAD) {
              world.setBlockState(otherHalfPos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL | Block.SKIP_DROPS);
              world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, otherHalfPos, Block.getRawIdFromState(otherHalfState));
          }
          return super.onBreak(world, pos, state, player);
      }

       

      The purpose of this code appears to be to remove the head of the bed when the foot is broken so it doesn't drop and item. At first it seemed like the problem was entirely here, but after digging ruther it seems a little more complicated than that.

      The problem seems t be in two parts:
      1. The effect for the head of the block isn't being played (world.syncWorldEvent does nothing here)
      2. Breaking the head of the block here somehow triggers getStateForNeighborUpdate to be called on the foot of the bed, which does its validation and causes the foot to break and play its effects, then its effects are repeated by the onBreak method.

      After some trial and error, I landed on this that should patch both issues.

      The significant changes are:
      1. move the client check down so spawnBreakParticles is called for the head on both client and server
      2. skip the call to spawnBreakParticles for the foot to avoid doubling up with the call that comes from elsewhere.

      @Override
      public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
          BedPart part = state.get(PART);
          BlockPos otherHalfPos = pos.offset(getDirectionTowardsOtherPart(part, state.get(FACING)));
          BlockState otherHalfState = world.getBlockState(otherHalfPos);
          // moved client side check down as we need this to run on both sides 
          if (/*!world.isClient &&*/ player.isCreative() && part == BedPart.FOOT && otherHalfState.isOf(this) && otherHalfState.get(PART) == BedPart.HEAD) {
              if (!world.isClient) {
                  world.setBlockState(otherHalfPos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL | Block.SKIP_DROPS);
              }
              spawnBreakParticles(world, player, otherHalfPos, otherHalfState);
              
              // contents of the super method (to preserve previous behaviour - would be good to have a way to call this without the spawnBreakParticles call)
              if (state.isIn(BlockTags.GUARDED_BY_PIGLINS)) {
                  PiglinBrain.onGuardedBlockInteracted(player, false);
              }
              world.emitGameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Emitter.of(player, state));
              return state;
          } else {
              // moved super call to an else condition
              return super.onBreak(world, pos, state, player);
          }
      }

       

       

       

            Unassigned Unassigned
            awr_* Sollace
            Votes:
            4 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              CHK: