323 lines
15 KiB
Java
323 lines
15 KiB
Java
package net.minecraft.server.level;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import net.minecraft.world.MenuProvider;
|
|
import net.minecraft.world.item.UseOnContext;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.InteractionResultHolder;
|
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.block.JigsawBlock;
|
|
import net.minecraft.world.level.block.StructureBlock;
|
|
import net.minecraft.world.level.block.CommandBlock;
|
|
import java.util.Objects;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockBreakAckPacket;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.world.level.GameType;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
public class ServerPlayerGameMode {
|
|
private static final Logger LOGGER;
|
|
public ServerLevel level;
|
|
public ServerPlayer player;
|
|
private GameType gameModeForPlayer;
|
|
private boolean isDestroyingBlock;
|
|
private int destroyProgressStart;
|
|
private BlockPos destroyPos;
|
|
private int gameTicks;
|
|
private boolean hasDelayedDestroy;
|
|
private BlockPos delayedDestroyPos;
|
|
private int delayedTickStart;
|
|
private int lastSentState;
|
|
|
|
public ServerPlayerGameMode(final ServerLevel xd) {
|
|
this.gameModeForPlayer = GameType.NOT_SET;
|
|
this.destroyPos = BlockPos.ZERO;
|
|
this.delayedDestroyPos = BlockPos.ZERO;
|
|
this.lastSentState = -1;
|
|
this.level = xd;
|
|
}
|
|
|
|
public void setGameModeForPlayer(final GameType bjq) {
|
|
(this.gameModeForPlayer = bjq).updatePlayerAbilities(this.player.abilities);
|
|
this.player.onUpdateAbilities();
|
|
this.player.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.UPDATE_GAME_MODE, new ServerPlayer[] { this.player }));
|
|
this.level.updateSleepingPlayerList();
|
|
}
|
|
|
|
public GameType getGameModeForPlayer() {
|
|
return this.gameModeForPlayer;
|
|
}
|
|
|
|
public boolean isSurvival() {
|
|
return this.gameModeForPlayer.isSurvival();
|
|
}
|
|
|
|
public boolean isCreative() {
|
|
return this.gameModeForPlayer.isCreative();
|
|
}
|
|
|
|
public void updateGameMode(final GameType bjq) {
|
|
if (this.gameModeForPlayer == GameType.NOT_SET) {
|
|
this.gameModeForPlayer = bjq;
|
|
}
|
|
this.setGameModeForPlayer(this.gameModeForPlayer);
|
|
}
|
|
|
|
public void tick() {
|
|
++this.gameTicks;
|
|
if (this.hasDelayedDestroy) {
|
|
final BlockState byg2 = this.level.getBlockState(this.delayedDestroyPos);
|
|
if (byg2.isAir()) {
|
|
this.hasDelayedDestroy = false;
|
|
}
|
|
else {
|
|
final float float3 = this.incrementDestroyProgress(byg2, this.delayedDestroyPos, this.delayedTickStart);
|
|
if (float3 >= 1.0f) {
|
|
this.hasDelayedDestroy = false;
|
|
this.destroyBlock(this.delayedDestroyPos);
|
|
}
|
|
}
|
|
}
|
|
else if (this.isDestroyingBlock) {
|
|
final BlockState byg2 = this.level.getBlockState(this.destroyPos);
|
|
if (byg2.isAir()) {
|
|
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
|
|
this.lastSentState = -1;
|
|
this.isDestroyingBlock = false;
|
|
}
|
|
else {
|
|
this.incrementDestroyProgress(byg2, this.destroyPos, this.destroyProgressStart);
|
|
}
|
|
}
|
|
}
|
|
|
|
private float incrementDestroyProgress(final BlockState byg, final BlockPos fk, final int integer) {
|
|
final int integer2 = this.gameTicks - integer;
|
|
final float float6 = byg.getDestroyProgress(this.player, this.player.level, fk) * (integer2 + 1);
|
|
final int integer3 = (int)(float6 * 10.0f);
|
|
if (integer3 != this.lastSentState) {
|
|
this.level.destroyBlockProgress(this.player.getId(), fk, integer3);
|
|
this.lastSentState = integer3;
|
|
}
|
|
return float6;
|
|
}
|
|
|
|
public void handleBlockBreakAction(final BlockPos fk, final ServerboundPlayerActionPacket.Action a, final Direction fp, final int integer) {
|
|
final double double6 = this.player.getX() - (fk.getX() + 0.5);
|
|
final double double7 = this.player.getY() - (fk.getY() + 0.5) + 1.5;
|
|
final double double8 = this.player.getZ() - (fk.getZ() + 0.5);
|
|
final double double9 = double6 * double6 + double7 * double7 + double8 * double8;
|
|
if (double9 > 36.0) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, false, "too far"));
|
|
return;
|
|
}
|
|
if (fk.getY() >= integer) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, false, "too high"));
|
|
return;
|
|
}
|
|
if (a == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) {
|
|
if (!this.level.mayInteract(this.player, fk)) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, false, "may not interact"));
|
|
return;
|
|
}
|
|
if (this.isCreative()) {
|
|
if (!this.level.extinguishFire(null, fk, fp)) {
|
|
this.destroyAndAck(fk, a, "creative destroy");
|
|
}
|
|
else {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, true, "fire put out"));
|
|
}
|
|
return;
|
|
}
|
|
if (this.player.blockActionRestricted(this.level, fk, this.gameModeForPlayer)) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, false, "block action restricted"));
|
|
return;
|
|
}
|
|
this.level.extinguishFire(null, fk, fp);
|
|
this.destroyProgressStart = this.gameTicks;
|
|
float float14 = 1.0f;
|
|
final BlockState byg15 = this.level.getBlockState(fk);
|
|
if (!byg15.isAir()) {
|
|
byg15.attack(this.level, fk, this.player);
|
|
float14 = byg15.getDestroyProgress(this.player, this.player.level, fk);
|
|
}
|
|
if (!byg15.isAir() && float14 >= 1.0f) {
|
|
this.destroyAndAck(fk, a, "insta mine");
|
|
}
|
|
else {
|
|
if (this.isDestroyingBlock) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(this.destroyPos, this.level.getBlockState(this.destroyPos), ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, false, "abort destroying since another started (client insta mine, server disagreed)"));
|
|
}
|
|
this.isDestroyingBlock = true;
|
|
this.destroyPos = fk.immutable();
|
|
final int integer2 = (int)(float14 * 10.0f);
|
|
this.level.destroyBlockProgress(this.player.getId(), fk, integer2);
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, true, "actual start of destroying"));
|
|
this.lastSentState = integer2;
|
|
}
|
|
}
|
|
else if (a == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) {
|
|
if (fk.equals(this.destroyPos)) {
|
|
final int integer3 = this.gameTicks - this.destroyProgressStart;
|
|
final BlockState byg15 = this.level.getBlockState(fk);
|
|
if (!byg15.isAir()) {
|
|
final float float15 = byg15.getDestroyProgress(this.player, this.player.level, fk) * (integer3 + 1);
|
|
if (float15 >= 0.7f) {
|
|
this.isDestroyingBlock = false;
|
|
this.level.destroyBlockProgress(this.player.getId(), fk, -1);
|
|
this.destroyAndAck(fk, a, "destroyed");
|
|
return;
|
|
}
|
|
if (!this.hasDelayedDestroy) {
|
|
this.isDestroyingBlock = false;
|
|
this.hasDelayedDestroy = true;
|
|
this.delayedDestroyPos = fk;
|
|
this.delayedTickStart = this.destroyProgressStart;
|
|
}
|
|
}
|
|
}
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, true, "stopped destroying"));
|
|
}
|
|
else if (a == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) {
|
|
this.isDestroyingBlock = false;
|
|
if (!Objects.equals(this.destroyPos, fk)) {
|
|
ServerPlayerGameMode.LOGGER.warn("Mismatch in destroy block pos: " + this.destroyPos + " " + fk);
|
|
this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1);
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(this.destroyPos, this.level.getBlockState(this.destroyPos), a, true, "aborted mismatched destroying"));
|
|
}
|
|
this.level.destroyBlockProgress(this.player.getId(), fk, -1);
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, true, "aborted destroying"));
|
|
}
|
|
}
|
|
|
|
public void destroyAndAck(final BlockPos fk, final ServerboundPlayerActionPacket.Action a, final String string) {
|
|
if (this.destroyBlock(fk)) {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, true, string));
|
|
}
|
|
else {
|
|
this.player.connection.send(new ClientboundBlockBreakAckPacket(fk, this.level.getBlockState(fk), a, false, string));
|
|
}
|
|
}
|
|
|
|
public boolean destroyBlock(final BlockPos fk) {
|
|
final BlockState byg3 = this.level.getBlockState(fk);
|
|
if (!this.player.getMainHandItem().getItem().canAttackBlock(byg3, this.level, fk, this.player)) {
|
|
return false;
|
|
}
|
|
final BlockEntity bwi4 = this.level.getBlockEntity(fk);
|
|
final Block bpe5 = byg3.getBlock();
|
|
if ((bpe5 instanceof CommandBlock || bpe5 instanceof StructureBlock || bpe5 instanceof JigsawBlock) && !this.player.canUseGameMasterBlocks()) {
|
|
this.level.sendBlockUpdated(fk, byg3, byg3, 3);
|
|
return false;
|
|
}
|
|
if (this.player.blockActionRestricted(this.level, fk, this.gameModeForPlayer)) {
|
|
return false;
|
|
}
|
|
bpe5.playerWillDestroy(this.level, fk, byg3, this.player);
|
|
final boolean boolean6 = this.level.removeBlock(fk, false);
|
|
if (boolean6) {
|
|
bpe5.destroy(this.level, fk, byg3);
|
|
}
|
|
if (this.isCreative()) {
|
|
return true;
|
|
}
|
|
final ItemStack bek7 = this.player.getMainHandItem();
|
|
final ItemStack bek8 = bek7.copy();
|
|
final boolean boolean7 = this.player.canDestroy(byg3);
|
|
bek7.mineBlock(this.level, byg3, fk, this.player);
|
|
if (boolean6 && boolean7) {
|
|
bpe5.playerDestroy(this.level, this.player, fk, byg3, bwi4, bek8);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public InteractionResult useItem(final Player ayg, final Level bjt, final ItemStack bek, final InteractionHand ajh) {
|
|
if (this.gameModeForPlayer == GameType.SPECTATOR) {
|
|
return InteractionResult.PASS;
|
|
}
|
|
if (ayg.getCooldowns().isOnCooldown(bek.getItem())) {
|
|
return InteractionResult.PASS;
|
|
}
|
|
final int integer6 = bek.getCount();
|
|
final int integer7 = bek.getDamageValue();
|
|
final InteractionResultHolder<ItemStack> ajj8 = bek.use(bjt, ayg, ajh);
|
|
final ItemStack bek2 = ajj8.getObject();
|
|
if (bek2 == bek && bek2.getCount() == integer6 && bek2.getUseDuration() <= 0 && bek2.getDamageValue() == integer7) {
|
|
return ajj8.getResult();
|
|
}
|
|
if (ajj8.getResult() == InteractionResult.FAIL && bek2.getUseDuration() > 0 && !ayg.isUsingItem()) {
|
|
return ajj8.getResult();
|
|
}
|
|
ayg.setItemInHand(ajh, bek2);
|
|
if (this.isCreative()) {
|
|
bek2.setCount(integer6);
|
|
if (bek2.isDamageableItem() && bek2.getDamageValue() != integer7) {
|
|
bek2.setDamageValue(integer7);
|
|
}
|
|
}
|
|
if (bek2.isEmpty()) {
|
|
ayg.setItemInHand(ajh, ItemStack.EMPTY);
|
|
}
|
|
if (!ayg.isUsingItem()) {
|
|
((ServerPlayer)ayg).refreshContainer(ayg.inventoryMenu);
|
|
}
|
|
return ajj8.getResult();
|
|
}
|
|
|
|
public InteractionResult useItemOn(final Player ayg, final Level bjt, final ItemStack bek, final InteractionHand ajh, final BlockHitResult cvd) {
|
|
final BlockPos fk7 = cvd.getBlockPos();
|
|
final BlockState byg8 = bjt.getBlockState(fk7);
|
|
if (this.gameModeForPlayer == GameType.SPECTATOR) {
|
|
final MenuProvider ajl9 = byg8.getMenuProvider(bjt, fk7);
|
|
if (ajl9 != null) {
|
|
ayg.openMenu(ajl9);
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
return InteractionResult.PASS;
|
|
}
|
|
else {
|
|
final boolean boolean9 = !ayg.getMainHandItem().isEmpty() || !ayg.getOffhandItem().isEmpty();
|
|
final boolean boolean10 = ayg.isSecondaryUseActive() && boolean9;
|
|
if (!boolean10) {
|
|
final InteractionResult aji11 = byg8.use(bjt, ayg, ajh, cvd);
|
|
if (aji11.consumesAction()) {
|
|
return aji11;
|
|
}
|
|
}
|
|
if (bek.isEmpty() || ayg.getCooldowns().isOnCooldown(bek.getItem())) {
|
|
return InteractionResult.PASS;
|
|
}
|
|
final UseOnContext bfw11 = new UseOnContext(ayg, ajh, cvd);
|
|
if (this.isCreative()) {
|
|
final int integer12 = bek.getCount();
|
|
final InteractionResult aji12 = bek.useOn(bfw11);
|
|
bek.setCount(integer12);
|
|
return aji12;
|
|
}
|
|
return bek.useOn(bfw11);
|
|
}
|
|
}
|
|
|
|
public void setLevel(final ServerLevel xd) {
|
|
this.level = xd;
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
}
|
|
}
|