Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ plugins {
}

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

dependencies {
implementation 'org.projectlombok:lombok:1.18.28'
compileOnly "io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT"
compileOnly 'com.github.retrooper.packetevents:spigot:2.3.0'
}
version = '1.0.0-alpha'

repositories {
mavenCentral()
Expand All @@ -22,7 +16,21 @@ repositories {
name = "sonatype"
url = "https://oss.sonatype.org/content/groups/public/"
}
maven { url 'https://repo.codemc.io/repository/maven-releases/' }
maven {
name = "codemc"
url "https://repo.codemc.io/repository/maven-releases/"
}
maven {
name = "jitpack"
url = 'https://jitpack.io'
}
}

dependencies {
implementation 'org.projectlombok:lombok:1.18.28'
compileOnly "io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT"
compileOnly 'com.github.retrooper:packetevents-spigot:2.4.0'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
}

def targetJavaVersion = 17
Expand All @@ -33,14 +41,13 @@ java {
}

tasks.withType(JavaCompile).configureEach {
options.annotationProcessorPath = configurations.compileClasspath
options.annotationProcessorPath = configurations.compileClasspath
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release = targetJavaVersion
options.release.set(targetJavaVersion)
}
}

tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
options.deprecation = true
options.compilerArgs += '-Xlint:deprecation'
}
Expand Down
7 changes: 1 addition & 6 deletions src/main/java/codes/kooper/blockify/Blockify.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import codes.kooper.blockify.protocol.BlockPlaceAdapter;
import codes.kooper.blockify.protocol.ChunkLoadAdapter;
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;
Expand All @@ -19,15 +18,12 @@
public final class Blockify extends JavaPlugin {
private StageManager stageManager;
private BlockChangeManager blockChangeManager;
private MiningUtils miningUtils;
private ServerVersion serverVersion;

@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().getSettings().reEncodeByDefault(false)
.checkForUpdates(true)
.bStats(false);
PacketEvents.getAPI().getSettings().reEncodeByDefault(false).checkForUpdates(true);
PacketEvents.getAPI().load();
}

Expand All @@ -39,7 +35,6 @@ public void onEnable() {

stageManager = new StageManager();
blockChangeManager = new BlockChangeManager();
miningUtils = new MiningUtils();

getServer().getPluginManager().registerEvents(new StageBoundListener(), this);

Expand Down
102 changes: 66 additions & 36 deletions src/main/java/codes/kooper/blockify/managers/BlockChangeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
import codes.kooper.blockify.types.BlockifyChunk;
import codes.kooper.blockify.types.BlockifyPosition;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.chunk.BaseChunk;
import com.github.retrooper.packetevents.protocol.world.chunk.Column;
import com.github.retrooper.packetevents.protocol.world.chunk.LightData;
import com.github.retrooper.packetevents.protocol.world.chunk.impl.v_1_18.Chunk_v1_18;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerMultiBlockChange;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
Expand Down Expand Up @@ -204,7 +209,7 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
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));
Bukkit.getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> sendChunkPacket(onlinePlayer, chunk, blockChanges));
}
}, 0L, 1L));
}
Expand All @@ -214,49 +219,74 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
* Sends a chunk packet to the player.
* Call Asynchronously
*
* @param stage the stage
* @param player the player
* @param chunk the chunk
* @param blockChanges the block changes
*/
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);
public void sendChunkPacket(Player player, BlockifyChunk chunk, ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges) {
User packetUser = PacketEvents.getAPI().getPlayerManager().getUser(player);
ConcurrentHashMap<BlockifyPosition, BlockData> blockData = blockChanges.get(chunk);
int ySections = packetUser.getTotalWorldHeight() >> 4;
Map<BlockData, WrappedBlockState> blockDataToState = new HashMap<>();
List<BaseChunk> chunks = new ArrayList<>();
Chunk bukkitChunk = player.getWorld().getChunkAt(chunk.x(), chunk.z());
ChunkSnapshot chunkSnapshot = bukkitChunk.getChunkSnapshot();
int maxHeight = player.getWorld().getMaxHeight();
int minHeight = player.getWorld().getMinHeight();

// Loop through the chunks y positions
for (int chunkY = stage.getMinPosition().getY() >> 4; chunkY <= stage.getMaxPosition().getY() >> 4; chunkY++) {
// Create a list of encoded blocks for PacketEvents wrapper
List<WrapperPlayServerMultiBlockChange.EncodedBlock> encodedBlocks = new ArrayList<>();
for (int i = 0; i < ySections; i++) {
Chunk_v1_18 baseChunk = new Chunk_v1_18();

// Loop through the blocks to send
for (Map.Entry<BlockifyPosition, BlockData> entry : blockChanges.get(chunk).entrySet()) {
BlockifyPosition position = entry.getKey();
int blockY = position.getY();
// Set block data for the chunk section
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
int worldY = (i << 4) + y + minHeight;
BlockifyPosition position = new BlockifyPosition(x + (chunk.x() << 4), worldY, z + (chunk.z() << 4));

// Check if the block is in the current y section
if (blockY >> 4 == chunkY) {
BlockData blockData = entry.getValue();
// Get the block data ID
blockDataToId.computeIfAbsent(blockData, bd -> WrappedBlockState.getByString(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), bd.getAsString(false)).getGlobalId());

int id = blockDataToId.get(blockData);
int x = position.getX() & 0xF;
int y = position.getY();
int z = position.getZ() & 0xF;
// Add the encoded block to the list
encodedBlocks.add(new WrapperPlayServerMultiBlockChange.EncodedBlock(id, x, y, z));
if (blockData.containsKey(position)) {
BlockData data = blockData.get(position);
WrappedBlockState state = blockDataToState.computeIfAbsent(data, SpigotConversionUtil::fromBukkitBlockData);
baseChunk.set(x, y, z, state);
} else if (worldY >= minHeight && worldY < maxHeight) {
BlockData defaultData = chunkSnapshot.getBlockData(x, worldY, z);
WrappedBlockState defaultState = SpigotConversionUtil.fromBukkitBlockData(defaultData);
baseChunk.set(x, y, z, defaultState);
}
}
}
}

// Check if there are no encoded blocks to send
if (encodedBlocks.isEmpty()) continue;
// Set biome data for the chunk section
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
for (int l = 0; l < 4; l++) {
int id = baseChunk.getBiomeData().palette.stateToId(1);
baseChunk.getBiomeData().storage.set(k << 2 | l << 2 | j, id);
}
}
}

// 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);
if (user == null || !player.isOnline()) return;
ChannelHelper.runInEventLoop(user.getChannel(), () -> user.sendPacket(wrapper));
chunks.add(baseChunk);
}
// TODO: Implement Tile Entities
Column column = new Column(chunk.x(), chunk.z(), true, chunks.toArray(new BaseChunk[0]), null);
LightData lightData = new LightData();
byte[][] emptyLightArray = new byte[ySections][0];
BitSet emptyBitSet = new BitSet();
BitSet lightBitSet = new BitSet();
for (int i = 0; i < ySections; i++) {
emptyBitSet.set(i, true);
}
lightData.setBlockLightArray(emptyLightArray);
lightData.setSkyLightArray(emptyLightArray);
lightData.setBlockLightCount(ySections);
lightData.setSkyLightCount(ySections);
lightData.setBlockLightMask(lightBitSet);
lightData.setSkyLightMask(lightBitSet);
lightData.setEmptyBlockLightMask(emptyBitSet);
lightData.setEmptySkyLightMask(emptyBitSet);
WrapperPlayServerChunkData chunkData = new WrapperPlayServerChunkData(column, lightData);
packetUser.sendPacketSilently(chunkData);
}

}
32 changes: 27 additions & 5 deletions src/main/java/codes/kooper/blockify/protocol/BlockDigAdapter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package codes.kooper.blockify.protocol;

import codes.kooper.blockify.Blockify;
import codes.kooper.blockify.events.BlockifyBreakEvent;
import codes.kooper.blockify.events.BlockifyInteractEvent;
import codes.kooper.blockify.models.Stage;
import codes.kooper.blockify.models.View;
Expand All @@ -11,6 +12,8 @@
import com.github.retrooper.packetevents.protocol.player.DiggingAction;
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;

Expand Down Expand Up @@ -52,11 +55,20 @@ public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
return;
}

// Check if player has custom mining speed, if so, handle custom digging, else handle normal digging
if (view.getStage().getAudience().getMiningSpeed(player) != 1) {
Blockify.getInstance().getMiningUtils().handleCustomDigging(player, view, actionType, blockData, position);
} else {
Blockify.getInstance().getMiningUtils().handleNormalDigging(player, view, actionType, blockData, position);
// Block break functionality
if (actionType == DiggingAction.FINISHED_DIGGING || canInstantBreak(player, blockData)) {
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> {
// Call BlockifyBreakEvent
BlockifyBreakEvent blockifyBreakEvent = new BlockifyBreakEvent(player, position.toPosition(), blockData, view, view.getStage());
blockifyBreakEvent.callEvent();
// If block is not cancelled, break the block, otherwise, revert the block
if (!blockifyBreakEvent.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);
}
});
}

return;
Expand All @@ -66,4 +78,14 @@ public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
}
}

/**
* Check if player can instantly break block
*
* @param player Player who is digging
* @param blockData BlockData of the block
* @return boolean
*/
private boolean canInstantBreak(Player player, BlockData blockData) {
return blockData.getDestroySpeed(player.getInventory().getItemInMainHand(), true) >= blockData.getMaterial().getHardness() * 30 || player.getGameMode() == GameMode.CREATIVE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.github.retrooper.packetevents.event.simple.PacketPlaySendEvent;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

import java.util.List;
Expand Down Expand Up @@ -42,7 +43,11 @@ public void onPacketPlaySend(PacketPlaySendEvent event) {
if (!view.hasChunk(chunkX, chunkZ)) return;
BlockifyChunk blockifyChunk = new BlockifyChunk(chunkX, chunkZ);

Blockify.getInstance().getBlockChangeManager().sendChunkPacket(stage, player, blockifyChunk, view.getBlocks());
// Cancel the packet to prevent the player from seeing the chunk
event.setCancelled(true);

// Send the chunk packet to the player
Bukkit.getServer().getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> Blockify.getInstance().getBlockChangeManager().sendChunkPacket(player, blockifyChunk, view.getBlocks()));
}
}
}
Expand Down
Loading