MultiPlayerGameMode.java

net.minecraft.client.multiplayer.MultiPlayerGameMode

信息

  • 全限定名:net.minecraft.client.multiplayer.MultiPlayerGameMode
  • 类型:public class
  • 包:net.minecraft.client.multiplayer
  • 源码路径:src/main/java/net/minecraft/client/multiplayer/MultiPlayerGameMode.java
  • 起始行号:L74
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L75
    • 说明:

      TODO

  • minecraft

    • 类型: Minecraft
    • 修饰符: private final
    • 源码定位: L76
    • 说明:

      TODO

  • connection

    • 类型: ClientPacketListener
    • 修饰符: private final
    • 源码定位: L77
    • 说明:

      TODO

  • destroyBlockPos

    • 类型: BlockPos
    • 修饰符: private
    • 源码定位: L78
    • 说明:

      TODO

  • destroyingItem

    • 类型: ItemStack
    • 修饰符: private
    • 源码定位: L79
    • 说明:

      TODO

  • destroyProgress

    • 类型: float
    • 修饰符: private
    • 源码定位: L80
    • 说明:

      TODO

  • destroyTicks

    • 类型: float
    • 修饰符: private
    • 源码定位: L81
    • 说明:

      TODO

  • destroyDelay

    • 类型: int
    • 修饰符: private
    • 源码定位: L82
    • 说明:

      TODO

  • isDestroying

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L83
    • 说明:

      TODO

  • localPlayerMode

    • 类型: GameType
    • 修饰符: private
    • 源码定位: L84
    • 说明:

      TODO

  • previousLocalPlayerMode

    • 类型: GameType
    • 修饰符: private
    • 源码定位: L85
    • 说明:

      TODO

  • carriedIndex

    • 类型: int
    • 修饰符: private
    • 源码定位: L86
    • 说明:

      TODO

内部类/嵌套类型

构造器

public MultiPlayerGameMode(Minecraft minecraft, ClientPacketListener connection) @ L88

  • 构造器名:MultiPlayerGameMode
  • 源码定位:L88
  • 修饰符:public

参数:

  • minecraft: Minecraft
  • connection: ClientPacketListener

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void adjustPlayer(Player player) @ L93

  • 方法名:adjustPlayer
  • 源码定位:L93
  • 返回类型:void
  • 修饰符:public

参数:

  • player: Player

说明:

TODO

public void setLocalMode(GameType mode, GameType previousMode) @ L97

  • 方法名:setLocalMode
  • 源码定位:L97
  • 返回类型:void
  • 修饰符:public

参数:

  • mode: GameType
  • previousMode: GameType

说明:

TODO

public void setLocalMode(GameType mode) @ L103

  • 方法名:setLocalMode
  • 源码定位:L103
  • 返回类型:void
  • 修饰符:public

参数:

  • mode: GameType

说明:

TODO

public boolean canHurtPlayer() @ L112

  • 方法名:canHurtPlayer
  • 源码定位:L112
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean destroyBlock(BlockPos pos) @ L116

  • 方法名:destroyBlock
  • 源码定位:L116
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean startDestroyBlock(BlockPos pos, Direction direction) @ L148

  • 方法名:startDestroyBlock
  • 源码定位:L148
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos
  • direction: Direction

说明:

TODO

public void stopDestroyBlock() @ L207

  • 方法名:stopDestroyBlock
  • 源码定位:L207
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public boolean continueDestroyBlock(BlockPos pos, Direction direction) @ L224

  • 方法名:continueDestroyBlock
  • 源码定位:L224
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos
  • direction: Direction

说明:

TODO

private void startPrediction(ClientLevel level, PredictiveAction predictiveAction) @ L290

  • 方法名:startPrediction
  • 源码定位:L290
  • 返回类型:void
  • 修饰符:private

参数:

  • level: ClientLevel
  • predictiveAction: PredictiveAction

说明:

TODO

public void tick() @ L298

  • 方法名:tick
  • 源码定位:L298
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

private boolean sameDestroyTarget(BlockPos pos) @ L307

  • 方法名:sameDestroyTarget
  • 源码定位:L307
  • 返回类型:boolean
  • 修饰符:private

参数:

  • pos: BlockPos

说明:

TODO

private void ensureHasSentCarriedItem() @ L312

  • 方法名:ensureHasSentCarriedItem
  • 源码定位:L312
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public InteractionResult useItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult blockHit) @ L320

  • 方法名:useItemOn
  • 源码定位:L320
  • 返回类型:InteractionResult
  • 修饰符:public

参数:

  • player: LocalPlayer
  • hand: InteractionHand
  • blockHit: BlockHitResult

说明:

TODO

private InteractionResult performUseItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult blockHit) @ L334

  • 方法名:performUseItemOn
  • 源码定位:L334
  • 返回类型:InteractionResult
  • 修饰符:private

参数:

  • player: LocalPlayer
  • hand: InteractionHand
  • blockHit: BlockHitResult

说明:

TODO

public InteractionResult useItem(Player player, InteractionHand hand) @ L379

  • 方法名:useItem
  • 源码定位:L379
  • 返回类型:InteractionResult
  • 修饰符:public

参数:

  • player: Player
  • hand: InteractionHand

说明:

TODO

public LocalPlayer createPlayer(ClientLevel level, StatsCounter stats, ClientRecipeBook recipeBook) @ L412

  • 方法名:createPlayer
  • 源码定位:L412
  • 返回类型:LocalPlayer
  • 修饰符:public

参数:

  • level: ClientLevel
  • stats: StatsCounter
  • recipeBook: ClientRecipeBook

说明:

TODO

public LocalPlayer createPlayer(ClientLevel level, StatsCounter stats, ClientRecipeBook recipeBook, Input lastSentInput, boolean wasSprinting) @ L416

  • 方法名:createPlayer
  • 源码定位:L416
  • 返回类型:LocalPlayer
  • 修饰符:public

参数:

  • level: ClientLevel
  • stats: StatsCounter
  • recipeBook: ClientRecipeBook
  • lastSentInput: Input
  • wasSprinting: boolean

说明:

TODO

public void attack(Player player, Entity entity) @ L420

  • 方法名:attack
  • 源码定位:L420
  • 返回类型:void
  • 修饰符:public

参数:

  • player: Player
  • entity: Entity

说明:

TODO

public void spectate(Entity entity) @ L427

  • 方法名:spectate
  • 源码定位:L427
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

public InteractionResult interact(Player player, Entity entity, EntityHitResult hitResult, InteractionHand hand) @ L431

  • 方法名:interact
  • 源码定位:L431
  • 返回类型:InteractionResult
  • 修饰符:public

参数:

  • player: Player
  • entity: Entity
  • hitResult: EntityHitResult
  • hand: InteractionHand

说明:

TODO

public void handleContainerInput(int containerId, int slotNum, int buttonNum, ContainerInput containerInput, Player player) @ L438

  • 方法名:handleContainerInput
  • 源码定位:L438
  • 返回类型:void
  • 修饰符:public

参数:

  • containerId: int
  • slotNum: int
  • buttonNum: int
  • containerInput: ContainerInput
  • player: Player

说明:

TODO

public void handlePlaceRecipe(int containerId, RecipeDisplayId recipe, boolean useMaxItems) @ L478

  • 方法名:handlePlaceRecipe
  • 源码定位:L478
  • 返回类型:void
  • 修饰符:public

参数:

  • containerId: int
  • recipe: RecipeDisplayId
  • useMaxItems: boolean

说明:

TODO

public void handleInventoryButtonClick(int containerId, int buttonId) @ L482

  • 方法名:handleInventoryButtonClick
  • 源码定位:L482
  • 返回类型:void
  • 修饰符:public

参数:

  • containerId: int
  • buttonId: int

说明:

TODO

public void handleCreativeModeItemAdd(ItemStack clicked, int slot) @ L486

  • 方法名:handleCreativeModeItemAdd
  • 源码定位:L486
  • 返回类型:void
  • 修饰符:public

参数:

  • clicked: ItemStack
  • slot: int

说明:

TODO

public void handleCreativeModeItemDrop(ItemStack clicked) @ L492

  • 方法名:handleCreativeModeItemDrop
  • 源码定位:L492
  • 返回类型:void
  • 修饰符:public

参数:

  • clicked: ItemStack

说明:

TODO

public void releaseUsingItem(Player player) @ L504

  • 方法名:releaseUsingItem
  • 源码定位:L504
  • 返回类型:void
  • 修饰符:public

参数:

  • player: Player

说明:

TODO

public void piercingAttack(PiercingWeapon weapon) @ L510

  • 方法名:piercingAttack
  • 源码定位:L510
  • 返回类型:void
  • 修饰符:public

参数:

  • weapon: PiercingWeapon

说明:

TODO

public boolean hasExperience() @ L518

  • 方法名:hasExperience
  • 源码定位:L518
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean hasMissTime() @ L522

  • 方法名:hasMissTime
  • 源码定位:L522
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isServerControlledInventory() @ L526

  • 方法名:isServerControlledInventory
  • 源码定位:L526
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isSpectator() @ L530

  • 方法名:isSpectator
  • 源码定位:L530
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public GameType getPreviousPlayerMode() @ L534

  • 方法名:getPreviousPlayerMode
  • 源码定位:L534
  • 返回类型:GameType
  • 修饰符:public

参数:

说明:

TODO

public GameType getPlayerMode() @ L538

  • 方法名:getPlayerMode
  • 源码定位:L538
  • 返回类型:GameType
  • 修饰符:public

参数:

说明:

TODO

public boolean isDestroying() @ L542

  • 方法名:isDestroying
  • 源码定位:L542
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public int getDestroyStage() @ L546

  • 方法名:getDestroyStage
  • 源码定位:L546
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

public void handlePickItemFromBlock(BlockPos pos, boolean includeData) @ L550

  • 方法名:handlePickItemFromBlock
  • 源码定位:L550
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • includeData: boolean

说明:

TODO

public void handlePickItemFromEntity(Entity entity, boolean includeData) @ L554

  • 方法名:handlePickItemFromEntity
  • 源码定位:L554
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity
  • includeData: boolean

说明:

TODO

public void handleSlotStateChanged(int slotId, int containerId, boolean newState) @ L558

  • 方法名:handleSlotStateChanged
  • 源码定位:L558
  • 返回类型:void
  • 修饰符:public

参数:

  • slotId: int
  • containerId: int
  • newState: boolean

说明:

TODO

代码

@OnlyIn(Dist.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;
    private @Nullable GameType previousLocalPlayerMode;
    private int carriedIndex;
 
    public MultiPlayerGameMode(Minecraft minecraft, ClientPacketListener connection) {
        this.minecraft = minecraft;
        this.connection = connection;
    }
 
    public void adjustPlayer(Player player) {
        this.localPlayerMode.updatePlayerAbilities(player.getAbilities());
    }
 
    public void setLocalMode(GameType mode, @Nullable GameType previousMode) {
        this.localPlayerMode = mode;
        this.previousLocalPlayerMode = previousMode;
        this.localPlayerMode.updatePlayerAbilities(this.minecraft.player.getAbilities());
    }
 
    public void setLocalMode(GameType mode) {
        if (mode != this.localPlayerMode) {
            this.previousLocalPlayerMode = this.localPlayerMode;
        }
 
        this.localPlayerMode = mode;
        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 oldState = level.getBlockState(pos);
            if (!this.minecraft.player.getMainHandItem().canDestroyBlock(oldState, level, pos, this.minecraft.player)) {
                return false;
            } else {
                Block oldBlock = oldState.getBlock();
                if (oldBlock instanceof GameMasterBlock && !this.minecraft.player.canUseGameMasterBlocks()) {
                    return false;
                } else if (oldState.isAir()) {
                    return false;
                } else {
                    oldBlock.playerWillDestroy(level, pos, oldState, this.minecraft.player);
                    FluidState fluidState = level.getFluidState(pos);
                    boolean changed = level.setBlock(pos, fluidState.createLegacyBlock(), 11);
                    if (changed) {
                        oldBlock.destroy(level, pos, oldState);
                    }
 
                    if (SharedConstants.DEBUG_BLOCK_BREAK) {
                        LOGGER.error("client broke {} {} -> {}", pos, oldState, level.getBlockState(pos));
                    }
 
                    return changed;
                }
            }
        }
    }
 
    public boolean startDestroyBlock(BlockPos pos, Direction direction) {
        if (this.minecraft.player.blockActionRestricted(this.minecraft.level, pos, this.localPlayerMode)) {
            return false;
        } else if (!this.minecraft.level.getWorldBorder().isWithinBounds(pos)) {
            return false;
        } else {
            if (this.minecraft.player.getAbilities().instabuild) {
                BlockState state = this.minecraft.level.getBlockState(pos);
                this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, pos, state, 1.0F);
                if (SharedConstants.DEBUG_BLOCK_BREAK) {
                    LOGGER.info("Creative start {} {}", pos, state);
                }
 
                this.startPrediction(this.minecraft.level, sequence -> {
                    this.destroyBlock(pos);
                    return new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, pos, direction, sequence);
                });
                this.destroyDelay = 5;
            } else if (!this.isDestroying || !this.sameDestroyTarget(pos)) {
                if (this.isDestroying) {
                    if (SharedConstants.DEBUG_BLOCK_BREAK) {
                        LOGGER.info("Abort old break {} {}", pos, this.minecraft.level.getBlockState(pos));
                    }
 
                    this.connection
                        .send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK, this.destroyBlockPos, direction));
                }
 
                BlockState state = this.minecraft.level.getBlockState(pos);
                this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, pos, state, 0.0F);
                if (SharedConstants.DEBUG_BLOCK_BREAK) {
                    LOGGER.info("Start break {} {}", pos, state);
                }
 
                this.startPrediction(this.minecraft.level, sequence -> {
                    boolean notAir = !state.isAir();
                    if (notAir && this.destroyProgress == 0.0F) {
                        state.attack(this.minecraft.level, pos, this.minecraft.player);
                    }
 
                    if (notAir && state.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), pos) >= 1.0F) {
                        this.destroyBlock(pos);
                    } else {
                        this.isDestroying = true;
                        this.destroyBlockPos = pos;
                        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(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, pos, direction, sequence);
                });
            }
 
            return true;
        }
    }
 
    public void stopDestroyBlock() {
        if (this.isDestroying) {
            BlockState state = this.minecraft.level.getBlockState(this.destroyBlockPos);
            this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, this.destroyBlockPos, state, -1.0F);
            if (SharedConstants.DEBUG_BLOCK_BREAK) {
                LOGGER.info("Stop dest {} {}", this.destroyBlockPos, state);
            }
 
            this.connection
                .send(new ServerboundPlayerActionPacket(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 pos, Direction direction) {
        this.ensureHasSentCarriedItem();
        if (this.destroyDelay > 0) {
            this.destroyDelay--;
            return true;
        } else if (this.minecraft.player.getAbilities().instabuild && this.minecraft.level.getWorldBorder().isWithinBounds(pos)) {
            this.destroyDelay = 5;
            BlockState state = this.minecraft.level.getBlockState(pos);
            this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, pos, state, 1.0F);
            if (SharedConstants.DEBUG_BLOCK_BREAK) {
                LOGGER.info("Creative cont {} {}", pos, state);
            }
 
            this.startPrediction(this.minecraft.level, sequence -> {
                this.destroyBlock(pos);
                return new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, pos, direction, sequence);
            });
            return true;
        } else if (this.sameDestroyTarget(pos)) {
            BlockState state = this.minecraft.level.getBlockState(pos);
            if (state.isAir()) {
                this.isDestroying = false;
                return false;
            } else {
                this.destroyProgress = this.destroyProgress + state.getDestroyProgress(this.minecraft.player, this.minecraft.player.level(), pos);
                if (this.destroyTicks % 4.0F == 0.0F) {
                    SoundType soundType = state.getSoundType();
                    this.minecraft
                        .getSoundManager()
                        .play(
                            new SimpleSoundInstance(
                                soundType.getHitSound(),
                                SoundSource.BLOCKS,
                                (soundType.getVolume() + 1.0F) / 8.0F,
                                soundType.getPitch() * 0.5F,
                                SoundInstance.createUnseededRandom(),
                                pos
                            )
                        );
                }
 
                this.destroyTicks++;
                this.minecraft.getTutorial().onDestroyBlock(this.minecraft.level, pos, state, Mth.clamp(this.destroyProgress, 0.0F, 1.0F));
                if (this.destroyProgress >= 1.0F) {
                    this.isDestroying = false;
                    if (SharedConstants.DEBUG_BLOCK_BREAK) {
                        LOGGER.info("Finished breaking {} {}", pos, state);
                    }
 
                    this.startPrediction(this.minecraft.level, sequence -> {
                        this.destroyBlock(pos);
                        return new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK, pos, direction, sequence);
                    });
                    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(pos, direction);
        }
    }
 
    private void startPrediction(ClientLevel level, PredictiveAction predictiveAction) {
        try (BlockStatePredictionHandler prediction = level.getBlockStatePredictionHandler().startPredicting()) {
            int sequence = prediction.currentSequence();
            Packet<ServerGamePacketListener> packetConcludingPrediction = predictiveAction.predict(sequence);
            this.connection.send(packetConcludingPrediction);
        }
    }
 
    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 selected = this.minecraft.player.getMainHandItem();
        return pos.equals(this.destroyBlockPos) && ItemStack.isSameItemSameComponents(selected, this.destroyingItem);
    }
 
    private void ensureHasSentCarriedItem() {
        int index = this.minecraft.player.getInventory().getSelectedSlot();
        if (index != this.carriedIndex) {
            this.carriedIndex = index;
            this.connection.send(new ServerboundSetCarriedItemPacket(this.carriedIndex));
        }
    }
 
    public InteractionResult useItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult blockHit) {
        this.ensureHasSentCarriedItem();
        if (!this.minecraft.level.getWorldBorder().isWithinBounds(blockHit.getBlockPos())) {
            return InteractionResult.FAIL;
        } else {
            MutableObject<InteractionResult> result = new MutableObject<>();
            this.startPrediction(this.minecraft.level, sequence -> {
                result.setValue(this.performUseItemOn(player, hand, blockHit));
                return new ServerboundUseItemOnPacket(hand, blockHit, sequence);
            });
            return result.get();
        }
    }
 
    private InteractionResult performUseItemOn(LocalPlayer player, InteractionHand hand, BlockHitResult blockHit) {
        BlockPos pos = blockHit.getBlockPos();
        ItemStack itemStack = player.getItemInHand(hand);
        if (this.localPlayerMode == GameType.SPECTATOR) {
            return InteractionResult.CONSUME;
        } else {
            boolean haveSomethingInOurHands = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
            boolean suppressUsingBlock = player.isSecondaryUseActive() && haveSomethingInOurHands;
            if (!suppressUsingBlock) {
                BlockState blockState = this.minecraft.level.getBlockState(pos);
                if (!this.connection.isFeatureEnabled(blockState.getBlock().requiredFeatures())) {
                    return InteractionResult.FAIL;
                }
 
                InteractionResult itemUse = blockState.useItemOn(player.getItemInHand(hand), this.minecraft.level, player, hand, blockHit);
                if (itemUse.consumesAction()) {
                    return itemUse;
                }
 
                if (itemUse instanceof InteractionResult.TryEmptyHandInteraction && hand == InteractionHand.MAIN_HAND) {
                    InteractionResult use = blockState.useWithoutItem(this.minecraft.level, player, blockHit);
                    if (use.consumesAction()) {
                        return use;
                    }
                }
            }
 
            if (!itemStack.isEmpty() && !player.getCooldowns().isOnCooldown(itemStack)) {
                UseOnContext context = new UseOnContext(player, hand, blockHit);
                InteractionResult success;
                if (player.hasInfiniteMaterials()) {
                    int count = itemStack.getCount();
                    success = itemStack.useOn(context);
                    itemStack.setCount(count);
                } else {
                    success = itemStack.useOn(context);
                }
 
                return success;
            } else {
                return InteractionResult.PASS;
            }
        }
    }
 
    public InteractionResult useItem(Player player, InteractionHand hand) {
        if (this.localPlayerMode == GameType.SPECTATOR) {
            return InteractionResult.PASS;
        } else {
            this.ensureHasSentCarriedItem();
            MutableObject<InteractionResult> interactionResult = new MutableObject<>();
            this.startPrediction(this.minecraft.level, sequence -> {
                ServerboundUseItemPacket packet = new ServerboundUseItemPacket(hand, sequence, player.getYRot(), player.getXRot());
                ItemStack itemStack = player.getItemInHand(hand);
                if (player.getCooldowns().isOnCooldown(itemStack)) {
                    interactionResult.setValue(InteractionResult.PASS);
                    return packet;
                } else {
                    InteractionResult resultHolder = itemStack.use(this.minecraft.level, player, hand);
                    ItemStack result;
                    if (resultHolder instanceof InteractionResult.Success success) {
                        result = Objects.requireNonNullElseGet(success.heldItemTransformedTo(), () -> player.getItemInHand(hand));
                    } else {
                        result = player.getItemInHand(hand);
                    }
 
                    if (result != itemStack) {
                        player.setItemInHand(hand, result);
                    }
 
                    interactionResult.setValue(resultHolder);
                    return packet;
                }
            });
            return interactionResult.get();
        }
    }
 
    public LocalPlayer createPlayer(ClientLevel level, StatsCounter stats, ClientRecipeBook recipeBook) {
        return this.createPlayer(level, stats, recipeBook, Input.EMPTY, false);
    }
 
    public LocalPlayer createPlayer(ClientLevel level, StatsCounter stats, ClientRecipeBook recipeBook, Input lastSentInput, boolean wasSprinting) {
        return new LocalPlayer(this.minecraft, level, this.connection, stats, recipeBook, lastSentInput, wasSprinting, this.minecraft.computeChatAbilities());
    }
 
    public void attack(Player player, Entity entity) {
        this.ensureHasSentCarriedItem();
        this.connection.send(new ServerboundAttackPacket(entity.getId()));
        player.attack(entity);
        player.resetAttackStrengthTicker();
    }
 
    public void spectate(Entity entity) {
        this.connection.send(new ServerboundSpectateEntityPacket(entity.getId()));
    }
 
    public InteractionResult interact(Player player, Entity entity, EntityHitResult hitResult, InteractionHand hand) {
        this.ensureHasSentCarriedItem();
        Vec3 location = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
        this.connection.send(new ServerboundInteractPacket(entity.getId(), hand, location, player.isShiftKeyDown()));
        return (InteractionResult)(this.localPlayerMode == GameType.SPECTATOR ? InteractionResult.PASS : player.interactOn(entity, hand, location));
    }
 
    public void handleContainerInput(int containerId, int slotNum, int buttonNum, ContainerInput containerInput, Player player) {
        AbstractContainerMenu containerMenu = player.containerMenu;
        if (containerId != containerMenu.containerId) {
            LOGGER.warn("Ignoring click in mismatching container. Click in {}, player has {}.", containerId, containerMenu.containerId);
        } else {
            NonNullList<Slot> slots = containerMenu.slots;
            int slotCount = slots.size();
            List<ItemStack> itemsBeforeClick = Lists.newArrayListWithCapacity(slotCount);
 
            for (Slot slot : slots) {
                itemsBeforeClick.add(slot.getItem().copy());
            }
 
            containerMenu.clicked(slotNum, buttonNum, containerInput, player);
            Int2ObjectMap<HashedStack> changedSlots = new Int2ObjectOpenHashMap<>();
 
            for (int i = 0; i < slotCount; i++) {
                ItemStack before = itemsBeforeClick.get(i);
                ItemStack after = slots.get(i).getItem();
                if (!ItemStack.matches(before, after)) {
                    changedSlots.put(i, HashedStack.create(after, this.connection.decoratedHashOpsGenenerator()));
                }
            }
 
            HashedStack carriedItem = HashedStack.create(containerMenu.getCarried(), this.connection.decoratedHashOpsGenenerator());
            this.connection
                .send(
                    new ServerboundContainerClickPacket(
                        containerId,
                        containerMenu.getStateId(),
                        Shorts.checkedCast(slotNum),
                        SignedBytes.checkedCast(buttonNum),
                        containerInput,
                        changedSlots,
                        carriedItem
                    )
                );
        }
    }
 
    public void handlePlaceRecipe(int containerId, RecipeDisplayId recipe, boolean useMaxItems) {
        this.connection.send(new ServerboundPlaceRecipePacket(containerId, recipe, useMaxItems));
    }
 
    public void handleInventoryButtonClick(int containerId, int buttonId) {
        this.connection.send(new ServerboundContainerButtonClickPacket(containerId, buttonId));
    }
 
    public void handleCreativeModeItemAdd(ItemStack clicked, int slot) {
        if (this.minecraft.player.hasInfiniteMaterials() && this.connection.isFeatureEnabled(clicked.getItem().requiredFeatures())) {
            this.connection.send(new ServerboundSetCreativeModeSlotPacket(slot, clicked));
        }
    }
 
    public void handleCreativeModeItemDrop(ItemStack clicked) {
        boolean hasOtherInventoryOpen = this.minecraft.screen instanceof AbstractContainerScreen
            && !(this.minecraft.screen instanceof CreativeModeInventoryScreen);
        if (this.minecraft.player.hasInfiniteMaterials()
            && !hasOtherInventoryOpen
            && !clicked.isEmpty()
            && this.connection.isFeatureEnabled(clicked.getItem().requiredFeatures())) {
            this.connection.send(new ServerboundSetCreativeModeSlotPacket(-1, clicked));
            this.minecraft.player.getDropSpamThrottler().increment();
        }
    }
 
    public void releaseUsingItem(Player player) {
        this.ensureHasSentCarriedItem();
        this.connection.send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM, BlockPos.ZERO, Direction.DOWN));
        player.releaseUsingItem();
    }
 
    public void piercingAttack(PiercingWeapon weapon) {
        this.ensureHasSentCarriedItem();
        this.connection.send(new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.STAB, BlockPos.ZERO, Direction.DOWN));
        this.minecraft.player.onAttack();
        this.minecraft.player.postPiercingAttack();
        weapon.makeSound(this.minecraft.player);
    }
 
    public boolean hasExperience() {
        return this.localPlayerMode.isSurvival();
    }
 
    public boolean hasMissTime() {
        return !this.localPlayerMode.isCreative();
    }
 
    public boolean isServerControlledInventory() {
        return this.minecraft.player.isPassenger() && this.minecraft.player.getVehicle() instanceof HasCustomInventoryScreen;
    }
 
    public boolean isSpectator() {
        return this.localPlayerMode == GameType.SPECTATOR;
    }
 
    public @Nullable GameType getPreviousPlayerMode() {
        return this.previousLocalPlayerMode;
    }
 
    public GameType getPlayerMode() {
        return this.localPlayerMode;
    }
 
    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));
    }
}

引用的其他类