Skip to content

Commit

Permalink
BlockChangeManager optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Kooperlol committed Jun 12, 2024
1 parent 9ed2d3f commit de07bec
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 18 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group = 'codes.kooper'
version = '1.1.5-beta'
version = '1.1.6-beta'

dependencies {
implementation 'org.projectlombok:lombok:1.18.28'
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/codes/kooper/blockify/Blockify.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import codes.kooper.blockify.protocol.PlayerInfoAdapter;
import codes.kooper.blockify.utils.MiningUtils;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import lombok.Getter;
import org.bukkit.plugin.java.JavaPlugin;
Expand All @@ -19,6 +20,7 @@ public final class Blockify extends JavaPlugin {
private StageManager stageManager;
private BlockChangeManager blockChangeManager;
private MiningUtils miningUtils;
private ServerVersion serverVersion;

@Override
public void onLoad() {
Expand All @@ -32,6 +34,7 @@ public void onLoad() {
@Override
public void onEnable() {
new Metrics(this, 21782);
serverVersion = PacketEvents.getAPI().getServerManager().getVersion();
getLogger().info("Blockify has been enabled!");

stageManager = new StageManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Getter
public class BlockChangeManager {
Expand Down Expand Up @@ -147,15 +148,13 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
}

// Send multiple block changes to the players
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (Player onlinePlayer : audience.getOnlinePlayers()) {
Location playerLocation = onlinePlayer.getLocation();
if (playerLocation.getWorld() != stage.getWorld()) continue;

// The chunk index is used to keep track of the current chunk being sent
AtomicInteger chunkIndex = new AtomicInteger(0);
// Create an array of chunks to send from the block changes map
List<BlockifyChunk> chunksToSend = new ArrayList<>(List.of(blockChanges.keySet().toArray(new BlockifyChunk[0])));
chunksToSend.sort((chunk1, chunk2) -> {
Comparator<BlockifyChunk> comparator = (chunk1, chunk2) -> {
// Get distance from chunks to player
int x = playerLocation.getBlockX() / 16;
int z = playerLocation.getBlockZ() / 16;
Expand All @@ -170,14 +169,17 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B

// Compare distances and return accordingly
return Integer.compare(distanceSquared1, distanceSquared2);
});
};
Queue<BlockifyChunk> chunksToSend = new PriorityQueue<>(comparator);
chunksToSend.addAll(blockChanges.keySet());

// Create a task to send a chunk to the player every tick
blockChangeTasks.put(onlinePlayer, Bukkit.getScheduler().runTaskTimer(Blockify.getInstance(), () -> {
// Check if player is online, if not, cancel the task
if (!onlinePlayer.isOnline()) {
blockChangeTasks.computeIfPresent(onlinePlayer, (key, task) -> {
task.cancel();
executorService.shutdown();
return null;
});
return;
Expand All @@ -186,24 +188,24 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
// Loop through chunks per tick
for (int i = 0; i < stage.getChunksPerTick(); i++) {
// If the chunk index is greater than the chunks to send length
if (chunkIndex.get() >= chunksToSend.size()) {
if (chunksToSend.isEmpty()) {
// Safely cancel the task and remove it from the map
blockChangeTasks.computeIfPresent(onlinePlayer, (key, task) -> {
task.cancel();
executorService.shutdown();
return null; // Remove the task
});
return;
}

// Get the chunk from the chunks to send array
BlockifyChunk chunk = chunksToSend.get(chunkIndex.get());
chunkIndex.getAndIncrement();
BlockifyChunk chunk = chunksToSend.poll();

// Check if the chunk is loaded; if not, return
if (!stage.getWorld().isChunkLoaded(chunk.x(), chunk.z())) return;

// Send the chunk packet to the player
Bukkit.getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> sendChunkPacket(stage, onlinePlayer, chunk, blockChanges));
executorService.submit(() -> sendChunkPacket(stage, onlinePlayer, chunk, blockChanges));
}
}, 0L, 1L));
}
Expand All @@ -220,7 +222,7 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
*/
public void sendChunkPacket(Stage stage, Player player, BlockifyChunk chunk, ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges) {
// Get the user from PacketEvents API
User user = PacketEvents.getAPI().getPlayerManager().getUser(player);
final User user = PacketEvents.getAPI().getPlayerManager().getUser(player);

// Loop through the chunks y positions
for (int chunkY = stage.getMinPosition().getY() >> 4; chunkY <= stage.getMaxPosition().getY() >> 4; chunkY++) {
Expand Down Expand Up @@ -250,7 +252,8 @@ public void sendChunkPacket(Stage stage, Player player, BlockifyChunk chunk, Con
// Send the packet to the player
WrapperPlayServerMultiBlockChange.EncodedBlock[] encodedBlocksArray = encodedBlocks.toArray(new WrapperPlayServerMultiBlockChange.EncodedBlock[0]);
WrapperPlayServerMultiBlockChange wrapper = new WrapperPlayServerMultiBlockChange(new Vector3i(chunk.x(), chunkY, chunk.z()), true, encodedBlocksArray);
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> user.sendPacket(wrapper));
if (user == null || !player.isOnline()) return;
user.sendPacket(wrapper);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/codes/kooper/blockify/models/View.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class View {
private final ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blocks;
private final Stage stage;
private final String name;
private boolean breakable;
private boolean breakable, placeable;
private Pattern pattern;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockChange;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;

Expand Down Expand Up @@ -51,7 +52,7 @@ public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> new BlockifyInteractEvent(player, position.toPosition(), blockData, view, view.getStage()).callEvent());

// Check if block is breakable, if not, send block change packet to cancel the break
if (!view.isBreakable()) {
if (!view.isBreakable() || blockData.getMaterial() == Material.BEDROCK) {
WrapperPlayServerBlockChange wrapperPlayServerBlockChange = new WrapperPlayServerBlockChange(new Vector3i(position.getX(), position.getY(), position.getZ()), SpigotConversionUtil.fromBukkitBlockData(blockData).getGlobalId());
PacketEvents.getAPI().getPlayerManager().sendPacket(player, wrapperPlayServerBlockChange);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import codes.kooper.blockify.types.BlockifyPosition;
import com.github.retrooper.packetevents.event.SimplePacketListenerAbstract;
import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent;
import com.github.retrooper.packetevents.event.simple.PacketPlaySendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockChange;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

Expand Down Expand Up @@ -45,4 +47,29 @@ public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
}
}

@Override
public void onPacketPlaySend(PacketPlaySendEvent event) {
if (event.getPacketType() == PacketType.Play.Server.BLOCK_CHANGE) {
WrapperPlayServerBlockChange wrapper = new WrapperPlayServerBlockChange(event);
Player player = (Player) event.getPlayer();

// Get the stages the player is in. If the player is not in any stages, return.
List<Stage> stages = Blockify.getInstance().getStageManager().getStages(player);
if (stages == null || stages.isEmpty()) {
return;
}

BlockifyPosition position = new BlockifyPosition(wrapper.getBlockPosition().getX(), wrapper.getBlockPosition().getY(), wrapper.getBlockPosition().getZ());
for (Stage stage : stages) {
for (View view : stage.getViews()) {
if (view.hasBlock(position)) {
if (wrapper.getBlockState().getType().getName().equalsIgnoreCase(view.getBlock(position).getMaterial().name())) continue;
event.setCancelled(true);
return;
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public static BlockifyPosition fromLocation(Location location) {
return new BlockifyPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ());
}

/**
* Create a new BlockifyPosition
*
* @param vector The vector to create the BlockifyPosition from
*/
public static BlockifyPosition fromVector(Vector vector) {
return new BlockifyPosition(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
}

/**
* Creates new BlockifyPositions
*
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/codes/kooper/blockify/utils/BlockUtils.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package codes.kooper.blockify.utils;

import codes.kooper.blockify.types.BlockifyPosition;
import org.bukkit.Location;
import org.bukkit.block.data.Ageable;
import org.bukkit.block.data.BlockData;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BlockUtils {
Expand Down Expand Up @@ -35,6 +38,32 @@ public static Set<BlockifyPosition> getBlocksBetween(BlockifyPosition pos1, Bloc
return positions;
}

/**
* Get all the locations between two locations.
* Call this method asynchronously if you are going to be getting a large amount of locations.
*
* @param loc1 The first location.
* @param loc2 The second location.
* @return A list of all the locations between the two locations.
*/
public static List<Location> getLocationsBetween(Location loc1, Location loc2) {
List<Location> locations = new ArrayList<>();
int minX = Math.min(loc1.getBlockX(), loc2.getBlockX());
int minY = Math.min(loc1.getBlockY(), loc2.getBlockY());
int minZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ());
int maxX = Math.max(loc1.getBlockX(), loc2.getBlockX());
int maxY = Math.max(loc1.getBlockY(), loc2.getBlockY());
int maxZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ());
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
locations.add(new Location(loc1.getWorld(), x, y, z));
}
}
}
return locations;
}

/**
* Set the age of a block.
*
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/codes/kooper/blockify/utils/MiningUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import codes.kooper.blockify.types.BlockifyBlockStage;
import codes.kooper.blockify.types.BlockifyPosition;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBlockBreakAnimation;
Expand All @@ -15,6 +16,7 @@
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
Expand Down Expand Up @@ -119,7 +121,11 @@ public void handleNormalDigging(Player player, View view, DiggingAction actionTy
* @return boolean
*/
public boolean canInstantBreak(Player player, BlockData blockData) {
return blockData.getDestroySpeed(player.getInventory().getItemInMainHand(), true) >= blockData.getMaterial().getHardness() * 30 || player.getGameMode() == GameMode.CREATIVE;
if (Blockify.getInstance().getServerVersion().isOlderThan(ServerVersion.V_1_20)) {
return calculateMiningTimeInMilliseconds(blockData, player) <= 50;
} else {
return blockData.getDestroySpeed(player.getInventory().getItemInMainHand(), true) >= blockData.getMaterial().getHardness() * 30 || player.getGameMode() == GameMode.CREATIVE;
}
}

/**
Expand Down Expand Up @@ -229,13 +235,17 @@ 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());
// Efficiency level
int efficiencyLevel = player.getInventory().getItemInMainHand().getEnchantmentLevel(Enchantment.DIG_SPEED);
// 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;
} else if (efficiencyLevel > 0) {
speedMultiplier += Math.pow(efficiencyLevel, 2) + 1;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Blockify
version: '1.1.5-beta'
version: '1.1.6-beta'
main: codes.kooper.blockify.Blockify
api-version: '1.20'
api-version: '1.19'
depend:
- packetevents

0 comments on commit de07bec

Please sign in to comment.