ProtocolLib

3M Downloads

Applying PacketType.Play.Client.BLOCK_DIG correctly, while switching block and remove lingering animation.

AlbusThePenguin opened this issue ยท 0 comments

commented

Make sure you're doing the following

  • You're using the latest build for your server version
  • This isn't an issue caused by another plugin
  • You've checked for duplicate issues
  • You didn't use /reload

Describe the question
I am trying to create custom block durability using PacketType.Play.Client.BLOCK_DIG & PotionEffectType.SLOW_DIGGING.

I have two concerns I can't seem to figure out.

  1. The animation for the block doesn't go away when I set it to 0 again after a block has been broken and the animation lingers. I use a mining system that regenerate blocks, so if you break one it turns into a non minable different block while a cooldown for the new block to regenerate is up and the block is "regenerated". so when the block has been replaced with a different the animation persist.

  2. If I while holding down the mining button switch block to another one it doesn't start a new event, it breaks both the new and old if I switch before breaking the first OR it breaks the first or the second, a bit unstable and random. And if I break one, and then switch over to the next without letting go of the mouse button it doesn't start the animation which is probably the same issue as mentioned first.

API method(s) used
PacketType, ProtocolManager, ListenerPriority, PacketAdapter, PacketContainer, PacketEvent, BlockPosition.

Expected behavior

  1. I'd like for it to be able to apply the animation so that if I destroy a block the animation goes away from the block in the same location after being replaced.

  2. For players to be able to hold down the mouse button while switching blocks to break, rather than having to stop and start over.

Code
`@Getter
public class BlockBreakTask extends BukkitRunnable {

private final ProtocolManager protocolManager;

private final BlockBreakHandler blockBreakHandler;

private final Map<Player, BlockAnimation> animations = new ConcurrentHashMap<>();

public BlockBreakTask(ProtocolManager protocolManager, BlockBreakHandler blockBreakHandler) {
    this.protocolManager = protocolManager;
    this.blockBreakHandler = blockBreakHandler;
}

@Override
public void run() {
    for(Map.Entry<Player, BlockAnimation> entry : animations.entrySet()) {

        Player player = entry.getKey();

        BlockAnimation animation = entry.getValue();

        double playerBreakSpeed = 25; //Todo: update when break speed enchant is there.

        int hits = animation.getHits();

        int baseRequiredHits = animation.getBaseDurability();

        int required = calculateRequiredHits(playerBreakSpeed, baseRequiredHits);

        int animationStage = calculateAnimationStage(hits, required);

        boolean done = false;

        if(hits >= required) {
            animationStage = 0;
            Location location = animation.getLocation();

            PlayerBreakBlockEvent event = new PlayerBreakBlockEvent(location, player);
            Bukkit.getPluginManager().callEvent(event);

            if(!event.isCancelled()) {
                location.getBlock().breakNaturally();
            }

            done = true;
        }

        sendAnimation(player, animationStage, animation.getBlockPosition());

        if(done) {
            animations.remove(player);
        } else {
            animation.setHits(hits + 1);
            animations.put(player, animation);
        }
    }
}

public void sendAnimation(Player player, int value, BlockPosition position) {
    PacketContainer container = new PacketContainer(PacketType.Play.Server.BLOCK_BREAK_ANIMATION);
    container.getBlockPositionModifier().write(0, position);

    container.getIntegers().write(0, value);
    container.getIntegers().write(1, value);

    protocolManager.sendServerPacket(player, container);
}

private int calculateAnimationStage(int hits, int requiredHits) {
    double progress = (double) hits / requiredHits;
    return (int) (10 * progress);
}

public int calculateRequiredHits(double playerBreakSpeed, int baseRequiredHits) {
    double adjustedHits = baseRequiredHits / Math.log(playerBreakSpeed + 1);

    return Math.max(1, (int) adjustedHits);
}

}`

`@Getter
public class BlockBreakHandler {

private final Nucleus nucleus;

private final Experience experience;

private final BlockManager blockManager;

private final ProtocolManager protocolManager;

private final BlockBreakTask blockBreakTask;

private final ItemUtils itemUtils;

public BlockBreakHandler(Nucleus nucleus, Experience experience, BlockManager blockManager, ProtocolManager protocolManager, ItemUtils itemUtils) {
    this.nucleus = nucleus;
    this.experience = experience;
    this.blockManager = blockManager;
    this.protocolManager = protocolManager;

    this.blockBreakTask = new BlockBreakTask(protocolManager, this);
    this.itemUtils = itemUtils;
}

public void load() {
    this.blockBreakTask.runTaskTimer(nucleus, 0, 0);
}

public void unload() {
    this.blockBreakTask.cancel();
}

public void registerProtocolListener() {
    protocolManager.addPacketListener(new PacketAdapter(nucleus, ListenerPriority.NORMAL, PacketType.Play.Client.BLOCK_DIG) {
        @Override
        public void onPacketReceiving(PacketEvent event) {

            PacketContainer container = event.getPacket();
            EnumWrappers.PlayerDigType type = container.getPlayerDigTypes().read(0);

            Player player = event.getPlayer();

            if(player.getGameMode() == GameMode.CREATIVE) return;

            addSlow(player);

            Location location = getLocation(player);
            if(location == null) return;

            Material material = location.getBlock().getType();

            MaterialData data = blockManager.getMaterials().getOrDefault(material, null);
            if(data == null) return;

            BlockPosition pos = container.getBlockPositionModifier().read(0);
            BlockPosition position = new BlockPosition(pos.getX(), pos.getY(), pos.getZ());

            Skill skill = data.skill();

            int playerLevel = experience.getLevel(player, skill);
            if(playerLevel < data.level()) {
                player.sendMessage("Can't break this...");
                return;
            }

            ItemStack item = player.getInventory().getItemInMainHand();
            PlayerItemType itemType = itemUtils.getPlayerItemType(item);

            if(!data.playerItemTypes().contains(itemType)) {
                player.sendMessage("Wrong break item..");
                return;
            }

            if(type == EnumWrappers.PlayerDigType.START_DESTROY_BLOCK) {
                BlockAnimation animation = blockBreakTask.getAnimations().getOrDefault(player, new BlockAnimation(position, location, data.blockDurability()));
                blockBreakTask.getAnimations().put(player, animation);
            } else if(type == EnumWrappers.PlayerDigType.ABORT_DESTROY_BLOCK || type == EnumWrappers.PlayerDigType.STOP_DESTROY_BLOCK || type == EnumWrappers.PlayerDigType.RELEASE_USE_ITEM || type == EnumWrappers.PlayerDigType.SWAP_HELD_ITEMS) {
                blockBreakTask.getAnimations().remove(player);
                removeSlow(player);
            }
        }
    });
}

public void removeSlow(Player player) {
    Bukkit.getScheduler().runTask(nucleus, () -> player.removePotionEffect(PotionEffectType.SLOW_DIGGING));
}

public void addSlow(Player player) {
    Bukkit.getScheduler().runTask(nucleus, () -> player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_DIGGING, PotionEffect.INFINITE_DURATION, -1, false, false)));
}

private Location getLocation(Player player) {
    Block block = player.getTargetBlockExact(5);
    if (block != null) {
        return block.getLocation();
    }
    return null;
}

}
`
and why is only START_DESTROY_BLOCK and ABORT_DESTROY_BLOCK fired?