mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-31 12:56:28 +00:00
重构挖掘
This commit is contained in:
@@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@@ -183,7 +184,7 @@ public final class CraftEngineBlocks {
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
}
|
||||
if (sendParticles) {
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), 2001, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
}
|
||||
block.setType(Material.AIR, applyPhysics);
|
||||
return true;
|
||||
|
||||
@@ -7,15 +7,14 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.BlockSettings;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
@@ -38,7 +37,6 @@ import org.bukkit.event.world.GenericGameEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BlockEventListener implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
@@ -95,7 +93,7 @@ public class BlockEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) // I forget why it's LOW before
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerBreak(BlockBreakEvent event) {
|
||||
org.bukkit.block.Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
@@ -106,9 +104,8 @@ public class BlockEventListener implements Listener {
|
||||
if (!state.isEmpty()) {
|
||||
Location location = block.getLocation();
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
// double check to prevent dupe
|
||||
// if simply adventure check, player would be survival mode for the moment
|
||||
if (serverPlayer.isAdventureMode() && !serverPlayer.canBreak(LocationUtils.toBlockPos(location))) {
|
||||
// double check adventure mode to prevent dupe
|
||||
if (!FastNMS.INSTANCE.mayBuild(serverPlayer.serverPlayer()) && !serverPlayer.canBreak(LocationUtils.toBlockPos(location))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,15 +130,19 @@ public class BlockEventListener implements Listener {
|
||||
// play sound
|
||||
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
if (player.getGameMode() == GameMode.CREATIVE) {
|
||||
if (player.getGameMode() == GameMode.CREATIVE || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
Key itemId = Optional.ofNullable(itemInHand).map(Item::id).orElse(ItemKeys.AIR);
|
||||
// do not drop if it's not the correct tool
|
||||
if (!state.settings().isCorrectTool(itemId) || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
BlockSettings settings = state.settings();
|
||||
if (settings.requireCorrectTool()) {
|
||||
if (itemInHand == null) return;
|
||||
if (!settings.isCorrectTool(itemInHand.id()) &&
|
||||
(!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().handle()))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// drop items
|
||||
ContextHolder.Builder builder = ContextHolder.builder();
|
||||
|
||||
@@ -133,6 +133,7 @@ public class BukkitCustomBlock extends CustomBlock {
|
||||
if (settings.burnable()) {
|
||||
Reflections.method$FireBlock$setFlammable.invoke(Reflections.instance$Blocks$FIRE, mcBlock, settings.burnChance(), settings.fireSpreadChance());
|
||||
}
|
||||
Reflections.field$BlockStateBase$requiresCorrectToolForDrops.set(mcBlockState, settings.requireCorrectTool());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to init block settings", e);
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -76,7 +77,7 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, previousState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
@@ -69,7 +70,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, currentState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.chunk.Palette;
|
||||
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
|
||||
import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
|
||||
@@ -221,7 +222,7 @@ public class PacketConsumers {
|
||||
try {
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
int eventId = buf.readInt();
|
||||
if (eventId != 2001) return;
|
||||
if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return;
|
||||
BlockPos blockPos = buf.readBlockPos(buf);
|
||||
int state = buf.readInt();
|
||||
boolean global = buf.readBoolean();
|
||||
@@ -1118,17 +1119,19 @@ public class PacketConsumers {
|
||||
if (Config.enableSoundSystem()) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(blockState);
|
||||
if (BukkitBlockManager.instance().isBlockSoundRemoved(blockOwner)) {
|
||||
player.startMiningBlock(world, pos, blockState, false, null);
|
||||
player.startMiningBlock(pos, blockState, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.isMiningBlock() || player.shouldSyncAttribute()) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.stopMiningBlock();
|
||||
} else {
|
||||
player.setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (player.isAdventureMode()) {
|
||||
if (Config.simplyAdventureCheck()) {
|
||||
if (Config.simplifyAdventureCheck()) {
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
if (!player.canBreak(pos, state.vanillaBlockState().handle())) {
|
||||
player.preventMiningBlock();
|
||||
@@ -1141,7 +1144,7 @@ public class PacketConsumers {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.startMiningBlock(world, pos, blockState, true, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
player.startMiningBlock(pos, blockState, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
} else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.abortMiningBlock();
|
||||
|
||||
@@ -9,20 +9,21 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||
import net.momirealms.craftengine.core.block.BlockSettings;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.PackedBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
@@ -62,6 +63,7 @@ public class BukkitServerPlayer extends Player {
|
||||
private boolean swingHandAck;
|
||||
private float miningProgress;
|
||||
|
||||
private int lastSuccessfulBreak;
|
||||
private int resentSoundTick;
|
||||
private int resentSwingTick;
|
||||
|
||||
@@ -73,6 +75,8 @@ public class BukkitServerPlayer extends Player {
|
||||
private final Map<Integer, List<Integer>> furnitureView = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, Object> entityTypeView = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean clientSideCanBreak = true;
|
||||
|
||||
public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) {
|
||||
this.channel = channel;
|
||||
this.plugin = plugin;
|
||||
@@ -108,8 +112,8 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public boolean shouldSyncAttribute() {
|
||||
long current = System.currentTimeMillis();
|
||||
if (current - this.lastAttributeSyncTime > 10000) {
|
||||
long current = gameTicks();
|
||||
if (current - this.lastAttributeSyncTime > 100) {
|
||||
this.lastAttributeSyncTime = current;
|
||||
return true;
|
||||
}
|
||||
@@ -209,13 +213,7 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public int gameTicks() {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
return (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to get current tick", e);
|
||||
}
|
||||
return FastNMS.INSTANCE.field$MinecraftServer$currentTick();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -266,6 +264,11 @@ public class BukkitServerPlayer extends Player {
|
||||
this.plugin.networkManager().sendPacket(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPackets(List<Object> packet, boolean immediately) {
|
||||
this.plugin.networkManager().sendPackets(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivePacket(Object packet) {
|
||||
this.plugin.networkManager().receivePacket(this, packet);
|
||||
@@ -319,45 +322,75 @@ public class BukkitServerPlayer extends Player {
|
||||
if (this.isDestroyingBlock) {
|
||||
this.tickBlockDestroy();
|
||||
}
|
||||
// if it's not destroying custom blocks, we do predict
|
||||
if (Config.predictBreaking()) {
|
||||
if (!this.isDestroyingCustomBlock && (gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) {
|
||||
this.predictNextBlockToMine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroyProgress(Object blockState, BlockPos pos) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
return (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(blockState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to get destroy progress for player " + platformPlayer().getName());
|
||||
return 0f;
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
|
||||
}
|
||||
|
||||
private void predictNextBlockToMine() {
|
||||
double range = getInteractionRange() + Config.extendedInteractionRange();
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
if (result == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Block hitBlock = result.getHitBlock();
|
||||
if (hitBlock == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int stateId = BlockStateUtils.blockDataToId(hitBlock.getBlockData());
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startMiningBlock(org.bukkit.World world, BlockPos pos, Object state, boolean custom, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
public void startMiningBlock(BlockPos pos, Object state, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
// instant break
|
||||
boolean custom = immutableBlockState != null;
|
||||
if (custom && getDestroyProgress(state, pos) >= 1f) {
|
||||
assert immutableBlockState != null;
|
||||
// not an instant break on client side
|
||||
PackedBlockState vanillaBlockState = immutableBlockState.vanillaBlockState();
|
||||
// if it's not an instant break on client side, we should resend level event
|
||||
if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) {
|
||||
try {
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to send level event packet", e);
|
||||
}
|
||||
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
|
||||
WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
}
|
||||
//ParticleUtils.addBlockBreakParticles(world, LocationUtils.toBlockPos(pos), state);
|
||||
return;
|
||||
}
|
||||
setCanBreakBlock(!custom);
|
||||
// if it's a custom one, we prevent it, otherwise we allow it
|
||||
setClientSideCanBreakBlock(!custom);
|
||||
// set some base info
|
||||
setDestroyPos(pos);
|
||||
setDestroyedState(state);
|
||||
setIsDestroyingBlock(true, custom);
|
||||
}
|
||||
|
||||
private void setCanBreakBlock(boolean canBreak) {
|
||||
@Override
|
||||
public void setClientSideCanBreakBlock(boolean canBreak) {
|
||||
try {
|
||||
if (this.clientSideCanBreak == canBreak && !shouldSyncAttribute()) {
|
||||
return;
|
||||
}
|
||||
this.clientSideCanBreak = canBreak;
|
||||
if (canBreak) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object serverPlayer = serverPlayer();
|
||||
@@ -379,8 +412,7 @@ public class BukkitServerPlayer extends Player {
|
||||
} else {
|
||||
Object fatiguePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$mining_fatigue, entityID(), (byte) 9, -1, false, false, false);
|
||||
Object hastePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$haste, entityID(), (byte) 0, -1, false, false, false);
|
||||
sendPacket(fatiguePacket, true);
|
||||
sendPacket(hastePacket, true);
|
||||
sendPackets(List.of(fatiguePacket, hastePacket), true);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -390,17 +422,26 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public void stopMiningBlock() {
|
||||
setCanBreakBlock(true);
|
||||
setClientSideCanBreakBlock(true);
|
||||
setIsDestroyingBlock(false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preventMiningBlock() {
|
||||
setCanBreakBlock(false);
|
||||
setClientSideCanBreakBlock(false);
|
||||
setIsDestroyingBlock(false, false);
|
||||
abortMiningBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
this.swingHandAck = false;
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetEffect(Object mobEffect) throws ReflectiveOperationException {
|
||||
Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect);
|
||||
Object packet;
|
||||
@@ -412,24 +453,13 @@ public class BukkitServerPlayer extends Player {
|
||||
sendPacket(packet, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
abortDestroyProgress();
|
||||
}
|
||||
|
||||
private void tickBlockDestroy() {
|
||||
// prevent server from taking over breaking blocks
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// if player swings hand is this tick
|
||||
if (!this.swingHandAck) return;
|
||||
this.swingHandAck = false;
|
||||
int currentTick = gameTicks();
|
||||
// optimize break speed, otherwise it would be too fast
|
||||
if (currentTick - this.lastSuccessfulBreak <= 5) return;
|
||||
try {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
double range = getInteractionRange();
|
||||
@@ -444,8 +474,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
Object blockPos = LocationUtils.toBlockPos(hitPos);
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
int currentTick = (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
|
||||
// send hit sound if the sound is removed
|
||||
if (currentTick - this.lastHitBlockTime > 3) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(this.destroyedState);
|
||||
Object soundType = Reflections.field$BlockBehaviour$soundType.get(blockOwner);
|
||||
@@ -457,9 +487,14 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
// accumulate progress (custom blocks only)
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
// prevent server from taking over breaking custom blocks
|
||||
Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
// check item in hand
|
||||
Item<ItemStack> item = this.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
if (item != null) {
|
||||
Material itemMaterial = item.getItem().getType();
|
||||
// creative mode + invalid item in hand
|
||||
if (canInstabuild() && (itemMaterial == Material.DEBUG_STICK
|
||||
|| itemMaterial == Material.TRIDENT
|
||||
|| (VersionHelper.isVersionNewerThan1_20_5() && itemMaterial == MaterialUtils.MACE)
|
||||
@@ -468,37 +503,65 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
float progressToAdd = (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(this.destroyedState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
float progressToAdd = getDestroyProgress(this.destroyedState, hitPos);
|
||||
int id = BlockStateUtils.blockStateToId(this.destroyedState);
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
// double check custom block
|
||||
if (customState != null && !customState.isEmpty()) {
|
||||
if (!customState.settings().isCorrectTool(item == null ? ItemKeys.AIR : item.id())) {
|
||||
progressToAdd *= customState.settings().incorrectToolSpeed();
|
||||
BlockSettings blockSettings = customState.settings();
|
||||
if (blockSettings.requireCorrectTool()) {
|
||||
if (item != null) {
|
||||
// it's correct on plugin side
|
||||
if (blockSettings.isCorrectTool(item.id())) {
|
||||
// but not on serverside
|
||||
if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) {
|
||||
// we fix the speed
|
||||
progressToAdd = progressToAdd * (10f / 3f);
|
||||
}
|
||||
} else {
|
||||
// not a correct tool on plugin side and not a correct tool on serverside
|
||||
if (!blockSettings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.destroyedState)) {
|
||||
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// item is null, but it requires correct tool, then we reset the speed
|
||||
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
// accumulate progress
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
// broadcast changes
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
|
||||
// can break now
|
||||
if (this.miningProgress >= 1f) {
|
||||
if (isAdventureMode() && Config.simplyAdventureCheck()) {
|
||||
// for simplified adventure break, switch mayBuild temporarily
|
||||
if (isAdventureMode() && Config.simplifyAdventureCheck()) {
|
||||
// check the appearance state
|
||||
if (canBreak(hitPos, customState.vanillaBlockState().handle())) {
|
||||
player.setGameMode(GameMode.SURVIVAL);
|
||||
// Error might occur so we use try here
|
||||
try {
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, true);
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
} finally {
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal break check
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
}
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, blockPos, id, false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
this.stopMiningBlock();
|
||||
// send break particle + (removed sounds)
|
||||
sendPacket(FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(WorldEvents.BLOCK_BREAK_EFFECT, blockPos, id, false), false);
|
||||
this.lastSuccessfulBreak = currentTick;
|
||||
this.destroyPos = null;
|
||||
this.setIsDestroyingBlock(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,8 +570,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) throws ReflectiveOperationException {
|
||||
Object packet = Reflections.constructor$ClientboundBlockDestructionPacket.newInstance(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) {
|
||||
Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
for (org.bukkit.entity.Player other : player.getWorld().getPlayers()) {
|
||||
Location otherLocation = other.getLocation();
|
||||
double d0 = (double) hitPos.x() - otherLocation.getX();
|
||||
@@ -522,52 +585,22 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public double getInteractionRange() {
|
||||
try {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object attributeInstance = Reflections.method$ServerPlayer$getAttribute.invoke(serverPlayer(), Reflections.instance$Holder$Attribute$block_interaction_range);
|
||||
if (attributeInstance == null) return 4.5d;
|
||||
return (double) Reflections.method$AttributeInstance$getValue.invoke(attributeInstance);
|
||||
} else {
|
||||
return 4.5d;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to get interaction range for player " + platformPlayer().getName(), e);
|
||||
return 4.5d;
|
||||
}
|
||||
return FastNMS.INSTANCE.getInteractionRange(serverPlayer());
|
||||
}
|
||||
|
||||
public void setIsDestroyingBlock(boolean value, boolean custom) {
|
||||
if (value) {
|
||||
this.isDestroyingBlock = true;
|
||||
this.isDestroyingCustomBlock = custom;
|
||||
this.swingHandAck = true;
|
||||
this.miningProgress = 0;
|
||||
} else {
|
||||
this.isDestroyingBlock = false;
|
||||
this.swingHandAck = false;
|
||||
if (this.destroyPos != null) {
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to set isDestroyingCustomBlock", e);
|
||||
}
|
||||
}
|
||||
this.destroyPos = null;
|
||||
this.miningProgress = 0;
|
||||
this.destroyedState = null;
|
||||
this.isDestroyingCustomBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortDestroyProgress() {
|
||||
this.swingHandAck = false;
|
||||
public void setIsDestroyingBlock(boolean is, boolean custom) {
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos == null) return;
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to abort destroyProgress", e);
|
||||
this.isDestroyingBlock = is;
|
||||
this.isDestroyingCustomBlock = custom && is;
|
||||
if (is) {
|
||||
this.swingHandAck = true;
|
||||
} else {
|
||||
this.swingHandAck = false;
|
||||
this.destroyedState = null;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
this.destroyPos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2418,6 +2418,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$requiresCorrectToolForDrops = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 5
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$canOcclude = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 6
|
||||
@@ -2925,11 +2931,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$destroyProgressStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -2937,11 +2943,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayerGameMode, int.class, 1
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayerGameMode, int.class, 1
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$delayedTickStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -3009,11 +3015,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
ReflectionUtils.getDeclaredMethod(
|
||||
clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
)
|
||||
);
|
||||
// public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredMethod(
|
||||
// clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -3022,11 +3028,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ServerboundSwingPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -4955,11 +4961,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ClientboundLevelEventPacket$eventId = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
|
||||
Reference in New Issue
Block a user