Skip to content

Commit

Permalink
Quick Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Kooperlol committed May 15, 2024
1 parent 43ac816 commit 81329d4
Showing 1 changed file with 259 additions and 1 deletion.
260 changes: 259 additions & 1 deletion src/main/java/codes/kooper/blockify/utils/MiningUtils.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,265 @@
package codes.kooper.blockify.utils;

import codes.kooper.blockify.Blockify;
import codes.kooper.blockify.events.BlockifyBreakEvent;
import codes.kooper.blockify.models.View;
import codes.kooper.blockify.types.BlockifyBlockStage;
import codes.kooper.blockify.types.BlockifyPosition;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockBreakAnimation;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class MiningUtils {
private final ConcurrentHashMap<BlockifyPosition, BlockifyBlockStage> blockStages;

public MiningUtils() {
this.blockStages = new ConcurrentHashMap<>();
}

/**
* Handle custom digging
*
* @param player Player who is digging
* @param view View of the player
* @param actionType DiggingAction
* @param blockData BlockData of the block
* @param position BlockifyPosition
*/
public void handleCustomDigging(Player player, View view, DiggingAction actionType, BlockData blockData, BlockifyPosition position) {
// Affect player with mining fatigue
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_DIGGING, Integer.MAX_VALUE, -1, true, false, false)));

// Update block stage periodically
if (!blockStages.containsKey(position)) {
blockStages.put(position, new BlockifyBlockStage((byte) 0, System.currentTimeMillis()));
}
if (blockStages.get(position).getTask() == 0) {
blockStages.get(position).setTask(Bukkit.getScheduler().runTaskTimerAsynchronously(Blockify.getInstance(), () -> {
if (player.isOnline() && blockStages.containsKey(position)) {
updateBlockStage(player, position, blockData, view);
}
}, 0, 1).getTaskId());
}

// Check if cancelled digging
if (actionType == DiggingAction.CANCELLED_DIGGING && blockStages.containsKey(position)) {
Bukkit.getScheduler().cancelTask(blockStages.get(position).getTask());
blockStages.get(position).setTask(0);
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> player.removePotionEffect(PotionEffectType.SLOW_DIGGING));
return;
}

// Check if player can instantly break block (CREATIVE)
if (actionType == DiggingAction.START_DIGGING && player.getGameMode() == GameMode.CREATIVE) {
actionType = DiggingAction.FINISHED_DIGGING;
blockStages.get(position).setStage((byte) 9);
}

// Block break functionality
if (actionType == DiggingAction.FINISHED_DIGGING && blockStages.get(position).getStage() >= 9) {
breakCustomBlock(player, position, blockData, view);
}
}

/**
* Handle normal digging
*
* @param player Player who is digging
* @param view View of the player
* @param actionType DiggingAction
* @param blockData BlockData of the block
* @param position BlockifyPosition
*/
public void handleNormalDigging(Player player, View view, DiggingAction actionType, BlockData blockData, BlockifyPosition position) {
// Block break functionality
if (actionType == DiggingAction.FINISHED_DIGGING || canInstantBreak(player, blockData)) {
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> {
// Call BlockifyBreakEvent
BlockifyBreakEvent ghostBreakEvent = new BlockifyBreakEvent(player, position.toPosition(), blockData, view, view.getStage());
ghostBreakEvent.callEvent();
// If block is not cancelled, break the block, otherwise, revert the block
if (!ghostBreakEvent.isCancelled()) {
Blockify.getInstance().getBlockChangeManager().sendBlockChange(view.getStage(), view.getStage().getAudience(), position, Material.AIR.createBlockData());
view.setBlock(position, Material.AIR.createBlockData());
} else {
player.sendBlockChange(position.toLocation(player.getWorld()), blockData);
}
});
}
}

/**
* Check if player can instantly break block
*
* @param player Player who is digging
* @param blockData BlockData of the block
* @return boolean
*/
public boolean canInstantBreak(Player player, BlockData blockData) {
return blockData.getDestroySpeed(player.getInventory().getItemInMainHand(), true) >= blockData.getMaterial().getHardness() * 30 || player.getGameMode() == GameMode.CREATIVE;
}

/**
* Update block stage
*
* @param player Player who is digging
* @param position BlockifyPosition
* @param blockData BlockData of the block
* @param view View of the player
*/
public void updateBlockStage(Player player, BlockifyPosition position, BlockData blockData, View view) {
// Check if block stage exists, if not, return
if (!blockStages.containsKey(position) || blockStages.get(position) == null) return;
// Get block stage and check if it is null, if so, remove it from the map and return
BlockifyBlockStage blockStage = blockStages.get(position);
if (blockStage == null) {
blockStages.remove(position);
return;
}
// If the time difference between the last updated time and the current time is greater than a 1/9th of the mining time, update the block stage
if (System.currentTimeMillis() - blockStage.getLastUpdated() > (calculateMiningTimeInMilliseconds(blockData, player) / 9) * view.getStage().getAudience().getMiningSpeed(player)) {
// Increment block stage
blockStage.setStage((byte) (blockStage.getStage() + 1));
// Update last updated time to current time
blockStage.setLastUpdated(System.currentTimeMillis());
// If block stage is greater than or equal to 9, break the block
if (blockStage.getStage() >= 9) {
breakCustomBlock(player, position, blockData, view);
player.spawnParticle(Particle.BLOCK_CRACK, position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5, 10, 0, 0, 0, blockData);
player.playSound(player.getLocation(), blockData.getSoundGroup().getBreakSound(), 1, 1);
}
}
// Send block break animation packet
WrapperPlayServerBlockBreakAnimation wrapperPlayServerBlockBreakAnimation = new WrapperPlayServerBlockBreakAnimation(new Random().nextInt(999999999) + 1000, new Vector3i(position.getX(), position.getY(), position.getZ()), blockStages.get(position).getStage());
PacketEvents.getAPI().getPlayerManager().sendPacket(player, wrapperPlayServerBlockBreakAnimation);
}

/**
* Breaks a custom block
*
* @param player Player who is breaking the block
* @param position BlockifyPosition
* @param blockData BlockData of the block
* @param view View of the player
*/
public void breakCustomBlock(Player player, BlockifyPosition position, BlockData blockData, View view) {
// Run synchronously as using Spigot API
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> {
// Call BlockifyBreakEvent
BlockifyBreakEvent ghostBreakEvent = new BlockifyBreakEvent(player, position.toPosition(), blockData, view, view.getStage());
ghostBreakEvent.callEvent();
// If block stage exists, cancel the task and remove it from the map
if (blockStages.containsKey(position)) {
Bukkit.getScheduler().cancelTask(blockStages.get(position).getTask());
blockStages.remove(position);
}
// Remove mining fatigue effect
player.removePotionEffect(PotionEffectType.SLOW_DIGGING);
// If block is not cancelled, break the block, otherwise, revert the block
if (!ghostBreakEvent.isCancelled()) {
Blockify.getInstance().getBlockChangeManager().sendBlockChange(view.getStage(), view.getStage().getAudience(), position, Material.AIR.createBlockData());
view.setBlock(position, Material.AIR.createBlockData());
} else {
player.sendBlockChange(position.toLocation(player.getWorld()), blockData);
}
});
}

/**
* Calculates the total mining time in milliseconds.
* Referenced from <a href="https://minecraft.fandom.com/wiki/Breaking#Calculation">Minecraft Fandom</a>.
*
* @param block BlockData of the block
* @param player Player who is mining
* @return double Time to break block in milliseconds
*/
private double calculateMiningTimeInMilliseconds(BlockData block, Player player) {
double speedMultiplier = 1.0;
// Check if player is using the preferred tool
boolean isPreferredTool = block.isPreferredTool(player.getInventory().getItemInMainHand());
// Check if player can harvest the block
boolean canHarvest = isPreferredTool && block.requiresCorrectToolForDrops();
// If player is using the preferred tool, get the speed multiplier, otherwise, set it to 1.0
if (isPreferredTool) {
speedMultiplier = getToolSpeed(player.getInventory().getItemInMainHand(), block);
if (!canHarvest) {
speedMultiplier = 1.0;
}
}

// Check if player is flying, if so, divide the speed multiplier by 5
if (player.isFlying()) {
speedMultiplier /= 5;
}

// Calculate the damage based on the speed multiplier and block hardness
double damage = speedMultiplier / block.getMaterial().getHardness();

// If player can harvest the block, divide the damage by 30, otherwise, divide it by 100
if (canHarvest) {
damage /= 30;
} else {
damage /= 100;
}

// Check if player can instantly break the block
if (damage > 1) {
return 0;
}

// Calculate the mining time in ticks and convert it to milliseconds
double ticks = Math.round(1 / damage);
double seconds = ticks / 20;
return seconds * 1000;
}

/**
* Get the tool speed.
* Referenced from <a href="https://minecraft.fandom.com/wiki/Breaking#Speed">Minecraft Fandom</a>.
*
* @param item ItemStack of the tool
* @param blockData BlockData of the block
* @return int Speed of the tool
*/
private int getToolSpeed(ItemStack item, BlockData blockData) {
if (item.getType().name().contains("SWORD") && blockData.getMaterial() == Material.COBWEB) {
return 15;
}
return switch (item.getType()) {
case WOODEN_PICKAXE, WOODEN_SHOVEL, WOODEN_AXE, WOODEN_SWORD -> 2;
case STONE_PICKAXE, STONE_SHOVEL, STONE_AXE, STONE_SWORD -> 4;
case IRON_PICKAXE, IRON_SHOVEL, IRON_AXE, IRON_SWORD -> 6;
case GOLDEN_PICKAXE, GOLDEN_SHOVEL, GOLDEN_AXE, GOLDEN_SWORD -> 12;
case DIAMOND_PICKAXE, DIAMOND_SHOVEL, DIAMOND_AXE, DIAMOND_SWORD -> 8;
case SHEARS -> {
if (blockData.getMaterial().name().contains("LEAVES")) {
yield 15;
} else if (blockData.getMaterial().name().contains("WOOL")) {
yield 5;
} else if (blockData.getMaterial().name().contains("CARPET")) {
yield 5;
}
yield switch (blockData.getMaterial()) {
case VINE, GLOW_LICHEN -> 1;
case COBWEB -> 15;
default -> 2;
};
}
default -> 1;
};
}

}
}

0 comments on commit 81329d4

Please sign in to comment.