minecraft-src/net/minecraft/server/level/ServerPlayerGameMode.java
2025-07-04 03:45:38 +03:00

380 lines
14 KiB
Java

package net.minecraft.server.level;
import com.mojang.logging.LogUtils;
import java.util.Objects;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.InteractionResult.Fail;
import net.minecraft.world.InteractionResult.Success;
import net.minecraft.world.InteractionResult.TryEmptyHandInteraction;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.GameMasterBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public class ServerPlayerGameMode {
private static final Logger LOGGER = LogUtils.getLogger();
protected ServerLevel level;
protected final ServerPlayer player;
private GameType gameModeForPlayer = GameType.DEFAULT_MODE;
@Nullable
private GameType previousGameModeForPlayer;
private boolean isDestroyingBlock;
private int destroyProgressStart;
private BlockPos destroyPos = BlockPos.ZERO;
private int gameTicks;
private boolean hasDelayedDestroy;
private BlockPos delayedDestroyPos = BlockPos.ZERO;
private int delayedTickStart;
private int lastSentState = -1;
public ServerPlayerGameMode(ServerPlayer player) {
this.player = player;
this.level = player.serverLevel();
}
public boolean changeGameModeForPlayer(GameType gameModeForPlayer) {
if (gameModeForPlayer == this.gameModeForPlayer) {
return false;
} else {
this.setGameModeForPlayer(gameModeForPlayer, this.previousGameModeForPlayer);
this.player.onUpdateAbilities();
this.player
.server
.getPlayerList()
.broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player));
this.level.updateSleepingPlayerList();
if (gameModeForPlayer == GameType.CREATIVE) {
this.player.resetCurrentImpulseContext();
}
return true;
}
}
protected void setGameModeForPlayer(GameType gameModeForPlayer, @Nullable GameType previousGameModeForPlayer) {
this.previousGameModeForPlayer = previousGameModeForPlayer;
this.gameModeForPlayer = gameModeForPlayer;
gameModeForPlayer.updatePlayerAbilities(this.player.getAbilities());
}
public GameType getGameModeForPlayer() {
return this.gameModeForPlayer;
}
@Nullable
public GameType getPreviousGameModeForPlayer() {
return this.previousGameModeForPlayer;
}
public boolean isSurvival() {
return this.gameModeForPlayer.isSurvival();
}
/**
* Get if we are in creative game mode.
*/
public boolean isCreative() {
return this.gameModeForPlayer.isCreative();
}
public void tick() {
this.gameTicks++;
if (this.hasDelayedDestroy) {
BlockState blockState = this.level.getBlockState(this.delayedDestroyPos);
if (blockState.isAir()) {
this.hasDelayedDestroy = false;
} else {
float f = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart);
if (f >= 1.0F) {
this.hasDelayedDestroy = false;
this.destroyBlock(this.delayedDestroyPos);
}
}
} else if (this.isDestroyingBlock) {
BlockState blockState = this.level.getBlockState(this.destroyPos);
if (blockState.isAir()) {
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
this.lastSentState = -1;
this.isDestroyingBlock = false;
} else {
this.incrementDestroyProgress(blockState, this.destroyPos, this.destroyProgressStart);
}
}
}
private float incrementDestroyProgress(BlockState state, BlockPos pos, int startTick) {
int i = this.gameTicks - startTick;
float f = state.getDestroyProgress(this.player, this.player.level(), pos) * (i + 1);
int j = (int)(f * 10.0F);
if (j != this.lastSentState) {
this.level.destroyBlockProgress(this.player.getId(), pos, j);
this.lastSentState = j;
}
return f;
}
private void debugLogging(BlockPos pos, boolean terminate, int sequence, String message) {
}
public void handleBlockBreakAction(BlockPos pos, Action action, Direction face, int maxBuildHeight, int sequence) {
if (!this.player.canInteractWithBlock(pos, 1.0)) {
this.debugLogging(pos, false, sequence, "too far");
} else if (pos.getY() > maxBuildHeight) {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
this.debugLogging(pos, false, sequence, "too high");
} else {
if (action == Action.START_DESTROY_BLOCK) {
if (!this.level.mayInteract(this.player, pos)) {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
this.debugLogging(pos, false, sequence, "may not interact");
return;
}
if (this.player.getAbilities().instabuild) {
this.destroyAndAck(pos, sequence, "creative destroy");
return;
}
if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
this.debugLogging(pos, false, sequence, "block action restricted");
return;
}
this.destroyProgressStart = this.gameTicks;
float f = 1.0F;
BlockState blockState = this.level.getBlockState(pos);
if (!blockState.isAir()) {
EnchantmentHelper.onHitBlock(
this.level,
this.player.getMainHandItem(),
this.player,
this.player,
EquipmentSlot.MAINHAND,
Vec3.atCenterOf(pos),
blockState,
item -> this.player.onEquippedItemBroken(item, EquipmentSlot.MAINHAND)
);
blockState.attack(this.level, pos, this.player);
f = blockState.getDestroyProgress(this.player, this.player.level(), pos);
}
if (!blockState.isAir() && f >= 1.0F) {
this.destroyAndAck(pos, sequence, "insta mine");
} else {
if (this.isDestroyingBlock) {
this.player.connection.send(new ClientboundBlockUpdatePacket(this.destroyPos, this.level.getBlockState(this.destroyPos)));
this.debugLogging(pos, false, sequence, "abort destroying since another started (client insta mine, server disagreed)");
}
this.isDestroyingBlock = true;
this.destroyPos = pos.immutable();
int i = (int)(f * 10.0F);
this.level.destroyBlockProgress(this.player.getId(), pos, i);
this.debugLogging(pos, true, sequence, "actual start of destroying");
this.lastSentState = i;
}
} else if (action == Action.STOP_DESTROY_BLOCK) {
if (pos.equals(this.destroyPos)) {
int j = this.gameTicks - this.destroyProgressStart;
BlockState blockStatex = this.level.getBlockState(pos);
if (!blockStatex.isAir()) {
float g = blockStatex.getDestroyProgress(this.player, this.player.level(), pos) * (j + 1);
if (g >= 0.7F) {
this.isDestroyingBlock = false;
this.level.destroyBlockProgress(this.player.getId(), pos, -1);
this.destroyAndAck(pos, sequence, "destroyed");
return;
}
if (!this.hasDelayedDestroy) {
this.isDestroyingBlock = false;
this.hasDelayedDestroy = true;
this.delayedDestroyPos = pos;
this.delayedTickStart = this.destroyProgressStart;
}
}
}
this.debugLogging(pos, true, sequence, "stopped destroying");
} else if (action == Action.ABORT_DESTROY_BLOCK) {
this.isDestroyingBlock = false;
if (!Objects.equals(this.destroyPos, pos)) {
LOGGER.warn("Mismatch in destroy block pos: {} {}", this.destroyPos, pos);
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
this.debugLogging(pos, true, sequence, "aborted mismatched destroying");
}
this.level.destroyBlockProgress(this.player.getId(), pos, -1);
this.debugLogging(pos, true, sequence, "aborted destroying");
}
}
}
public void destroyAndAck(BlockPos pos, int sequence, String message) {
if (this.destroyBlock(pos)) {
this.debugLogging(pos, true, sequence, message);
} else {
this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
this.debugLogging(pos, false, sequence, message);
}
}
/**
* Attempts to harvest a block
*/
public boolean destroyBlock(BlockPos pos) {
BlockState blockState = this.level.getBlockState(pos);
if (!this.player.getMainHandItem().canDestroyBlock(blockState, this.level, pos, this.player)) {
return false;
} else {
BlockEntity blockEntity = this.level.getBlockEntity(pos);
Block block = blockState.getBlock();
if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) {
this.level.sendBlockUpdated(pos, blockState, blockState, 3);
return false;
} else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
return false;
} else {
BlockState blockState2 = block.playerWillDestroy(this.level, pos, blockState, this.player);
boolean bl = this.level.removeBlock(pos, false);
if (bl) {
block.destroy(this.level, pos, blockState2);
}
if (this.player.preventsBlockDrops()) {
return true;
} else {
ItemStack itemStack = this.player.getMainHandItem();
ItemStack itemStack2 = itemStack.copy();
boolean bl2 = this.player.hasCorrectToolForDrops(blockState2);
itemStack.mineBlock(this.level, blockState2, pos, this.player);
if (bl && bl2) {
block.playerDestroy(this.level, this.player, pos, blockState2, blockEntity, itemStack2);
}
return true;
}
}
}
}
public InteractionResult useItem(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand) {
if (this.gameModeForPlayer == GameType.SPECTATOR) {
return InteractionResult.PASS;
} else if (player.getCooldowns().isOnCooldown(stack)) {
return InteractionResult.PASS;
} else {
int i = stack.getCount();
int j = stack.getDamageValue();
InteractionResult interactionResult = stack.use(level, player, hand);
ItemStack itemStack;
if (interactionResult instanceof Success success) {
itemStack = (ItemStack)Objects.requireNonNullElse(success.heldItemTransformedTo(), player.getItemInHand(hand));
} else {
itemStack = player.getItemInHand(hand);
}
if (itemStack == stack && itemStack.getCount() == i && itemStack.getUseDuration(player) <= 0 && itemStack.getDamageValue() == j) {
return interactionResult;
} else if (interactionResult instanceof Fail && itemStack.getUseDuration(player) > 0 && !player.isUsingItem()) {
return interactionResult;
} else {
if (stack != itemStack) {
player.setItemInHand(hand, itemStack);
}
if (itemStack.isEmpty()) {
player.setItemInHand(hand, ItemStack.EMPTY);
}
if (!player.isUsingItem()) {
player.inventoryMenu.sendAllDataToRemote();
}
return interactionResult;
}
}
}
public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) {
BlockPos blockPos = hitResult.getBlockPos();
BlockState blockState = level.getBlockState(blockPos);
if (!blockState.getBlock().isEnabled(level.enabledFeatures())) {
return InteractionResult.FAIL;
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos);
if (menuProvider != null) {
player.openMenu(menuProvider);
return InteractionResult.CONSUME;
} else {
return InteractionResult.PASS;
}
} else {
boolean bl = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
boolean bl2 = player.isSecondaryUseActive() && bl;
ItemStack itemStack = stack.copy();
if (!bl2) {
InteractionResult interactionResult = blockState.useItemOn(player.getItemInHand(hand), level, player, hand, hitResult);
if (interactionResult.consumesAction()) {
CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack);
return interactionResult;
}
if (interactionResult instanceof TryEmptyHandInteraction && hand == InteractionHand.MAIN_HAND) {
InteractionResult interactionResult2 = blockState.useWithoutItem(level, player, hitResult);
if (interactionResult2.consumesAction()) {
CriteriaTriggers.DEFAULT_BLOCK_USE.trigger(player, blockPos);
return interactionResult2;
}
}
}
if (!stack.isEmpty() && !player.getCooldowns().isOnCooldown(stack)) {
UseOnContext useOnContext = new UseOnContext(player, hand, hitResult);
InteractionResult interactionResult2;
if (player.hasInfiniteMaterials()) {
int i = stack.getCount();
interactionResult2 = stack.useOn(useOnContext);
stack.setCount(i);
} else {
interactionResult2 = stack.useOn(useOnContext);
}
if (interactionResult2.consumesAction()) {
CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack);
}
return interactionResult2;
} else {
return InteractionResult.PASS;
}
}
}
/**
* Sets the world instance.
*/
public void setLevel(ServerLevel serverLevel) {
this.level = serverLevel;
}
}