559 lines
22 KiB
Java
559 lines
22 KiB
Java
package net.minecraft.client.multiplayer;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.primitives.Shorts;
|
|
import com.google.common.primitives.SignedBytes;
|
|
import com.mojang.logging.LogUtils;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.client.ClientRecipeBook;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
|
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
|
|
import net.minecraft.client.multiplayer.prediction.BlockStatePredictionHandler;
|
|
import net.minecraft.client.multiplayer.prediction.PredictiveAction;
|
|
import net.minecraft.client.player.LocalPlayer;
|
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
|
import net.minecraft.client.resources.sounds.SoundInstance;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.NonNullList;
|
|
import net.minecraft.network.HashedStack;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ServerGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ServerboundContainerButtonClickPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundContainerSlotStateChangedPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundPickItemFromBlockPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundPickItemFromEntityPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
|
|
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
|
|
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.stats.StatsCounter;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.InteractionResult.Success;
|
|
import net.minecraft.world.InteractionResult.TryEmptyHandInteraction;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.HasCustomInventoryScreen;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
import net.minecraft.world.inventory.ClickType;
|
|
import net.minecraft.world.inventory.Slot;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.context.UseOnContext;
|
|
import net.minecraft.world.item.crafting.display.RecipeDisplayId;
|
|
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.SoundType;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.EntityHitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.apache.commons.lang3.mutable.MutableObject;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class MultiPlayerGameMode {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private final Minecraft minecraft;
|
|
private final ClientPacketListener connection;
|
|
private BlockPos destroyBlockPos = new BlockPos(-1, -1, -1);
|
|
private ItemStack destroyingItem = ItemStack.EMPTY;
|
|
private float destroyProgress;
|
|
private float destroyTicks;
|
|
private int destroyDelay;
|
|
private boolean isDestroying;
|
|
private GameType localPlayerMode = GameType.DEFAULT_MODE;
|
|
@Nullable
|
|
private GameType previousLocalPlayerMode;
|
|
private int carriedIndex;
|
|
|
|
public MultiPlayerGameMode(Minecraft minecraft, ClientPacketListener connection) {
|
|
this.minecraft = minecraft;
|
|
this.connection = connection;
|
|
}
|
|
|
|
/**
|
|
* Sets player capabilities depending on current gametype.
|
|
*/
|
|
public void adjustPlayer(Player player) {
|
|
this.localPlayerMode.updatePlayerAbilities(player.getAbilities());
|
|
}
|
|
|
|
public void setLocalMode(GameType localPlayerMode, @Nullable GameType previousLocalPlayerMode) {
|
|
this.localPlayerMode = localPlayerMode;
|
|
this.previousLocalPlayerMode = previousLocalPlayerMode;
|
|
this.localPlayerMode.updatePlayerAbilities(this.minecraft.player.getAbilities());
|
|
}
|
|
|
|
/**
|
|
* Sets the game type for the player.
|
|
*/
|
|
public void setLocalMode(GameType type) {
|
|
if (type != this.localPlayerMode) {
|
|
this.previousLocalPlayerMode = this.localPlayerMode;
|
|
}
|
|
|
|
this.localPlayerMode = type;
|
|
this.localPlayerMode.updatePlayerAbilities(this.minecraft.player.getAbilities());
|
|
}
|
|
|
|
public boolean canHurtPlayer() {
|
|
return this.localPlayerMode.isSurvival();
|
|
}
|
|
|
|
public boolean destroyBlock(BlockPos pos) {
|
|
if (this.minecraft.player.blockActionRestricted(this.minecraft.level, pos, this.localPlayerMode)) {
|
|
return false;
|
|
} else {
|
|
Level level = this.minecraft.level;
|
|
BlockState blockState = level.getBlockState(pos);
|
|
if (!this.minecraft.player.getMainHandItem().canDestroyBlock(blockState, level, pos, this.minecraft.player)) {
|
|
return false;
|
|
} else {
|
|
Block block = blockState.getBlock();
|
|
if (block instanceof GameMasterBlock && !this.minecraft.player.canUseGameMasterBlocks()) {
|
|
return false;
|
|
} else if (blockState.isAir()) {
|
|
return false;
|
|
} else {
|
|
block.playerWillDestroy(level, pos, blockState, this.minecraft.player);
|
|
FluidState fluidState = level.getFluidState(pos);
|
|
boolean bl = level.setBlock(pos, fluidState.createLegacyBlock(), 11);
|
|
if (bl) {
|
|
block.destroy(level, pos, blockState);
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the player is hitting a block with an item.
|
|
*/
|
|
public boolean startDestroyBlock(BlockPos loc, Direction face) {
|
|
if (this.minecraft.player.blockActionRestricted(this.minecraft.level, loc, this.localPlayerMode)) {
|
|
return false;
|
|
} else if (!this.minecraft.level.getWorldBorder().isWithinBounds(loc)) {
|
|
return false;
|
|
} else {
|
|
if (this.minecraft.player.getAbilities().instabuild) {
|
|
BlockState blockState = this.minecraft.level.getBlockState(loc);
|
|
this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, loc, blockState, 1.0F);
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
this.destroyBlock(loc);
|
|
return new ServerboundPlayerActionPacket(Action.START_DESTROY_BLOCK, loc, face, i);
|
|
});
|
|
this.destroyDelay = 5;
|
|
} else if (!this.isDestroying || !this.sameDestroyTarget(loc)) {
|
|
if (this.isDestroying) {
|
|
this.connection.send(new ServerboundPlayerActionPacket(Action.ABORT_DESTROY_BLOCK, this.destroyBlockPos, face));
|
|
}
|
|
|
|
BlockState blockState = this.minecraft.level.getBlockState(loc);
|
|
this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, loc, blockState, 0.0F);
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
boolean bl = !blockState.isAir();
|
|
if (bl && this.destroyProgress == 0.0F) {
|
|
blockState.attack(this.minecraft.level, loc, this.minecraft.player);
|
|
}
|
|
|
|
if (bl && blockState.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), loc) >= 1.0F) {
|
|
this.destroyBlock(loc);
|
|
} else {
|
|
this.isDestroying = true;
|
|
this.destroyBlockPos = loc;
|
|
this.destroyingItem = this.minecraft.player.getMainHandItem();
|
|
this.destroyProgress = 0.0F;
|
|
this.destroyTicks = 0.0F;
|
|
this.minecraft.level.destroyBlockProgress(this.minecraft.player.getId(), this.destroyBlockPos, this.getDestroyStage());
|
|
}
|
|
|
|
return new ServerboundPlayerActionPacket(Action.START_DESTROY_BLOCK, loc, face, i);
|
|
});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets current block damage
|
|
*/
|
|
public void stopDestroyBlock() {
|
|
if (this.isDestroying) {
|
|
BlockState blockState = this.minecraft.level.getBlockState(this.destroyBlockPos);
|
|
this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, this.destroyBlockPos, blockState, -1.0F);
|
|
this.connection.send(new ServerboundPlayerActionPacket(Action.ABORT_DESTROY_BLOCK, this.destroyBlockPos, Direction.DOWN));
|
|
this.isDestroying = false;
|
|
this.destroyProgress = 0.0F;
|
|
this.minecraft.level.destroyBlockProgress(this.minecraft.player.getId(), this.destroyBlockPos, -1);
|
|
this.minecraft.player.resetAttackStrengthTicker();
|
|
}
|
|
}
|
|
|
|
public boolean continueDestroyBlock(BlockPos posBlock, Direction directionFacing) {
|
|
this.ensureHasSentCarriedItem();
|
|
if (this.destroyDelay > 0) {
|
|
this.destroyDelay--;
|
|
return true;
|
|
} else if (this.minecraft.player.getAbilities().instabuild && this.minecraft.level.getWorldBorder().isWithinBounds(posBlock)) {
|
|
this.destroyDelay = 5;
|
|
BlockState blockState = this.minecraft.level.getBlockState(posBlock);
|
|
this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, posBlock, blockState, 1.0F);
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
this.destroyBlock(posBlock);
|
|
return new ServerboundPlayerActionPacket(Action.START_DESTROY_BLOCK, posBlock, directionFacing, i);
|
|
});
|
|
return true;
|
|
} else if (this.sameDestroyTarget(posBlock)) {
|
|
BlockState blockState = this.minecraft.level.getBlockState(posBlock);
|
|
if (blockState.isAir()) {
|
|
this.isDestroying = false;
|
|
return false;
|
|
} else {
|
|
this.destroyProgress = this.destroyProgress + blockState.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), posBlock);
|
|
if (this.destroyTicks % 4.0F == 0.0F) {
|
|
SoundType soundType = blockState.getSoundType();
|
|
this.minecraft
|
|
.getSoundManager()
|
|
.play(
|
|
new SimpleSoundInstance(
|
|
soundType.getHitSound(),
|
|
SoundSource.BLOCKS,
|
|
(soundType.getVolume() + 1.0F) / 8.0F,
|
|
soundType.getPitch() * 0.5F,
|
|
SoundInstance.createUnseededRandom(),
|
|
posBlock
|
|
)
|
|
);
|
|
}
|
|
|
|
this.destroyTicks++;
|
|
this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, posBlock, blockState, Mth.clamp(this.destroyProgress, 0.0F, 1.0F));
|
|
if (this.destroyProgress >= 1.0F) {
|
|
this.isDestroying = false;
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
this.destroyBlock(posBlock);
|
|
return new ServerboundPlayerActionPacket(Action.STOP_DESTROY_BLOCK, posBlock, directionFacing, i);
|
|
});
|
|
this.destroyProgress = 0.0F;
|
|
this.destroyTicks = 0.0F;
|
|
this.destroyDelay = 5;
|
|
}
|
|
|
|
this.minecraft.level.destroyBlockProgress(this.minecraft.player.getId(), this.destroyBlockPos, this.getDestroyStage());
|
|
return true;
|
|
}
|
|
} else {
|
|
return this.startDestroyBlock(posBlock, directionFacing);
|
|
}
|
|
}
|
|
|
|
private void startPrediction(ClientLevel level, PredictiveAction action) {
|
|
try (BlockStatePredictionHandler blockStatePredictionHandler = level.getBlockStatePredictionHandler().startPredicting()) {
|
|
int i = blockStatePredictionHandler.currentSequence();
|
|
Packet<ServerGamePacketListener> packet = action.predict(i);
|
|
this.connection.send(packet);
|
|
}
|
|
}
|
|
|
|
public void tick() {
|
|
this.ensureHasSentCarriedItem();
|
|
if (this.connection.getConnection().isConnected()) {
|
|
this.connection.getConnection().tick();
|
|
} else {
|
|
this.connection.getConnection().handleDisconnection();
|
|
}
|
|
}
|
|
|
|
private boolean sameDestroyTarget(BlockPos pos) {
|
|
ItemStack itemStack = this.minecraft.player.getMainHandItem();
|
|
return pos.equals(this.destroyBlockPos) && ItemStack.isSameItemSameComponents(itemStack, this.destroyingItem);
|
|
}
|
|
|
|
/**
|
|
* Syncs the current player item with the server
|
|
*/
|
|
private void ensureHasSentCarriedItem() {
|
|
int i = this.minecraft.player.getInventory().getSelectedSlot();
|
|
if (i != this.carriedIndex) {
|
|
this.carriedIndex = i;
|
|
this.connection.send(new ServerboundSetCarriedItemPacket(this.carriedIndex));
|
|
}
|
|
}
|
|
|
|
public InteractionResult useItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult result) {
|
|
this.ensureHasSentCarriedItem();
|
|
if (!this.minecraft.level.getWorldBorder().isWithinBounds(result.getBlockPos())) {
|
|
return InteractionResult.FAIL;
|
|
} else {
|
|
MutableObject<InteractionResult> mutableObject = new MutableObject<>();
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
mutableObject.setValue(this.performUseItemOn(player, hand, result));
|
|
return new ServerboundUseItemOnPacket(hand, result, i);
|
|
});
|
|
return mutableObject.getValue();
|
|
}
|
|
}
|
|
|
|
private InteractionResult performUseItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult result) {
|
|
BlockPos blockPos = result.getBlockPos();
|
|
ItemStack itemStack = player.getItemInHand(hand);
|
|
if (this.localPlayerMode == GameType.SPECTATOR) {
|
|
return InteractionResult.CONSUME;
|
|
} else {
|
|
boolean bl = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
|
|
boolean bl2 = player.isSecondaryUseActive() && bl;
|
|
if (!bl2) {
|
|
BlockState blockState = this.minecraft.level.getBlockState(blockPos);
|
|
if (!this.connection.isFeatureEnabled(blockState.getBlock().requiredFeatures())) {
|
|
return InteractionResult.FAIL;
|
|
}
|
|
|
|
InteractionResult interactionResult = blockState.useItemOn(player.getItemInHand(hand), this.minecraft.level, player, hand, result);
|
|
if (interactionResult.consumesAction()) {
|
|
return interactionResult;
|
|
}
|
|
|
|
if (interactionResult instanceof TryEmptyHandInteraction && hand == InteractionHand.MAIN_HAND) {
|
|
InteractionResult interactionResult2 = blockState.useWithoutItem(this.minecraft.level, player, result);
|
|
if (interactionResult2.consumesAction()) {
|
|
return interactionResult2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!itemStack.isEmpty() && !player.getCooldowns().isOnCooldown(itemStack)) {
|
|
UseOnContext useOnContext = new UseOnContext(player, hand, result);
|
|
InteractionResult interactionResult3;
|
|
if (player.hasInfiniteMaterials()) {
|
|
int i = itemStack.getCount();
|
|
interactionResult3 = itemStack.useOn(useOnContext);
|
|
itemStack.setCount(i);
|
|
} else {
|
|
interactionResult3 = itemStack.useOn(useOnContext);
|
|
}
|
|
|
|
return interactionResult3;
|
|
} else {
|
|
return InteractionResult.PASS;
|
|
}
|
|
}
|
|
}
|
|
|
|
public InteractionResult useItem(Player player, InteractionHand hand) {
|
|
if (this.localPlayerMode == GameType.SPECTATOR) {
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
this.ensureHasSentCarriedItem();
|
|
MutableObject<InteractionResult> mutableObject = new MutableObject<>();
|
|
this.startPrediction(this.minecraft.level, i -> {
|
|
ServerboundUseItemPacket serverboundUseItemPacket = new ServerboundUseItemPacket(hand, i, player.getYRot(), player.getXRot());
|
|
ItemStack itemStack = player.getItemInHand(hand);
|
|
if (player.getCooldowns().isOnCooldown(itemStack)) {
|
|
mutableObject.setValue(InteractionResult.PASS);
|
|
return serverboundUseItemPacket;
|
|
} else {
|
|
InteractionResult interactionResult = itemStack.use(this.minecraft.level, player, hand);
|
|
ItemStack itemStack2;
|
|
if (interactionResult instanceof Success success) {
|
|
itemStack2 = (ItemStack)Objects.requireNonNullElseGet(success.heldItemTransformedTo(), () -> player.getItemInHand(hand));
|
|
} else {
|
|
itemStack2 = player.getItemInHand(hand);
|
|
}
|
|
|
|
if (itemStack2 != itemStack) {
|
|
player.setItemInHand(hand, itemStack2);
|
|
}
|
|
|
|
mutableObject.setValue(interactionResult);
|
|
return serverboundUseItemPacket;
|
|
}
|
|
});
|
|
return mutableObject.getValue();
|
|
}
|
|
}
|
|
|
|
public LocalPlayer createPlayer(ClientLevel level, StatsCounter statsManager, ClientRecipeBook recipes) {
|
|
return this.createPlayer(level, statsManager, recipes, false, false);
|
|
}
|
|
|
|
public LocalPlayer createPlayer(ClientLevel level, StatsCounter statsManager, ClientRecipeBook recipes, boolean wasShiftKeyDown, boolean wasSprinting) {
|
|
return new LocalPlayer(this.minecraft, level, this.connection, statsManager, recipes, wasShiftKeyDown, wasSprinting);
|
|
}
|
|
|
|
/**
|
|
* Attacks an entity
|
|
*/
|
|
public void attack(Player player, Entity targetEntity) {
|
|
this.ensureHasSentCarriedItem();
|
|
this.connection.send(ServerboundInteractPacket.createAttackPacket(targetEntity, player.isShiftKeyDown()));
|
|
if (this.localPlayerMode != GameType.SPECTATOR) {
|
|
player.attack(targetEntity);
|
|
player.resetAttackStrengthTicker();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles right-clicking an entity, sends a packet to the server.
|
|
*/
|
|
public InteractionResult interact(Player player, Entity target, InteractionHand hand) {
|
|
this.ensureHasSentCarriedItem();
|
|
this.connection.send(ServerboundInteractPacket.createInteractionPacket(target, player.isShiftKeyDown(), hand));
|
|
return (InteractionResult)(this.localPlayerMode == GameType.SPECTATOR ? InteractionResult.PASS : player.interactOn(target, hand));
|
|
}
|
|
|
|
/**
|
|
* Handles right-clicking an entity from the entities side, sends a packet to the server.
|
|
*/
|
|
public InteractionResult interactAt(Player player, Entity target, EntityHitResult ray, InteractionHand hand) {
|
|
this.ensureHasSentCarriedItem();
|
|
Vec3 vec3 = ray.getLocation().subtract(target.getX(), target.getY(), target.getZ());
|
|
this.connection.send(ServerboundInteractPacket.createInteractionPacket(target, player.isShiftKeyDown(), hand, vec3));
|
|
return (InteractionResult)(this.localPlayerMode == GameType.SPECTATOR ? InteractionResult.PASS : target.interactAt(player, vec3, hand));
|
|
}
|
|
|
|
public void handleInventoryMouseClick(int containerId, int slotId, int mouseButton, ClickType clickType, Player player) {
|
|
AbstractContainerMenu abstractContainerMenu = player.containerMenu;
|
|
if (containerId != abstractContainerMenu.containerId) {
|
|
LOGGER.warn("Ignoring click in mismatching container. Click in {}, player has {}.", containerId, abstractContainerMenu.containerId);
|
|
} else {
|
|
NonNullList<Slot> nonNullList = abstractContainerMenu.slots;
|
|
int i = nonNullList.size();
|
|
List<ItemStack> list = Lists.<ItemStack>newArrayListWithCapacity(i);
|
|
|
|
for (Slot slot : nonNullList) {
|
|
list.add(slot.getItem().copy());
|
|
}
|
|
|
|
abstractContainerMenu.clicked(slotId, mouseButton, clickType, player);
|
|
Int2ObjectMap<HashedStack> int2ObjectMap = new Int2ObjectOpenHashMap<>();
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
ItemStack itemStack = (ItemStack)list.get(j);
|
|
ItemStack itemStack2 = nonNullList.get(j).getItem();
|
|
if (!ItemStack.matches(itemStack, itemStack2)) {
|
|
int2ObjectMap.put(j, HashedStack.create(itemStack2, this.connection.decoratedHashOpsGenenerator()));
|
|
}
|
|
}
|
|
|
|
HashedStack hashedStack = HashedStack.create(abstractContainerMenu.getCarried(), this.connection.decoratedHashOpsGenenerator());
|
|
this.connection
|
|
.send(
|
|
new ServerboundContainerClickPacket(
|
|
containerId, abstractContainerMenu.getStateId(), Shorts.checkedCast(slotId), SignedBytes.checkedCast(mouseButton), clickType, int2ObjectMap, hashedStack
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
public void handlePlaceRecipe(int containerId, RecipeDisplayId recipe, boolean useMaxItems) {
|
|
this.connection.send(new ServerboundPlaceRecipePacket(containerId, recipe, useMaxItems));
|
|
}
|
|
|
|
/**
|
|
* GuiEnchantment uses this during multiplayer to tell PlayerControllerMP to send a packet indicating the enchantment action the player has taken.
|
|
*/
|
|
public void handleInventoryButtonClick(int containerId, int buttonId) {
|
|
this.connection.send(new ServerboundContainerButtonClickPacket(containerId, buttonId));
|
|
}
|
|
|
|
/**
|
|
* Used in PlayerControllerMP to update the server with an ItemStack in a slot.
|
|
*/
|
|
public void handleCreativeModeItemAdd(ItemStack stack, int slotId) {
|
|
if (this.minecraft.player.hasInfiniteMaterials() && this.connection.isFeatureEnabled(stack.getItem().requiredFeatures())) {
|
|
this.connection.send(new ServerboundSetCreativeModeSlotPacket(slotId, stack));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends a Packet107 to the server to drop the item on the ground
|
|
*/
|
|
public void handleCreativeModeItemDrop(ItemStack stack) {
|
|
boolean bl = this.minecraft.screen instanceof AbstractContainerScreen && !(this.minecraft.screen instanceof CreativeModeInventoryScreen);
|
|
if (this.minecraft.player.hasInfiniteMaterials() && !bl && !stack.isEmpty() && this.connection.isFeatureEnabled(stack.getItem().requiredFeatures())) {
|
|
this.connection.send(new ServerboundSetCreativeModeSlotPacket(-1, stack));
|
|
this.minecraft.player.getDropSpamThrottler().increment();
|
|
}
|
|
}
|
|
|
|
public void releaseUsingItem(Player player) {
|
|
this.ensureHasSentCarriedItem();
|
|
this.connection.send(new ServerboundPlayerActionPacket(Action.RELEASE_USE_ITEM, BlockPos.ZERO, Direction.DOWN));
|
|
player.releaseUsingItem();
|
|
}
|
|
|
|
public boolean hasExperience() {
|
|
return this.localPlayerMode.isSurvival();
|
|
}
|
|
|
|
/**
|
|
* Checks if the player is not creative, used for checking if it should break a block instantly
|
|
*/
|
|
public boolean hasMissTime() {
|
|
return !this.localPlayerMode.isCreative();
|
|
}
|
|
|
|
/**
|
|
* Checks if the player is riding a horse, used to choose the GUI to open
|
|
*/
|
|
public boolean isServerControlledInventory() {
|
|
return this.minecraft.player.isPassenger() && this.minecraft.player.getVehicle() instanceof HasCustomInventoryScreen;
|
|
}
|
|
|
|
public boolean isAlwaysFlying() {
|
|
return this.localPlayerMode == GameType.SPECTATOR;
|
|
}
|
|
|
|
@Nullable
|
|
public GameType getPreviousPlayerMode() {
|
|
return this.previousLocalPlayerMode;
|
|
}
|
|
|
|
public GameType getPlayerMode() {
|
|
return this.localPlayerMode;
|
|
}
|
|
|
|
/**
|
|
* Return isHittingBlock
|
|
*/
|
|
public boolean isDestroying() {
|
|
return this.isDestroying;
|
|
}
|
|
|
|
public int getDestroyStage() {
|
|
return this.destroyProgress > 0.0F ? (int)(this.destroyProgress * 10.0F) : -1;
|
|
}
|
|
|
|
public void handlePickItemFromBlock(BlockPos pos, boolean includeData) {
|
|
this.connection.send(new ServerboundPickItemFromBlockPacket(pos, includeData));
|
|
}
|
|
|
|
public void handlePickItemFromEntity(Entity entity, boolean includeData) {
|
|
this.connection.send(new ServerboundPickItemFromEntityPacket(entity.getId(), includeData));
|
|
}
|
|
|
|
public void handleSlotStateChanged(int slotId, int containerId, boolean newState) {
|
|
this.connection.send(new ServerboundContainerSlotStateChangedPacket(slotId, containerId, newState));
|
|
}
|
|
}
|