ServerPlayerGameMode.java

net.minecraft.server.level.ServerPlayerGameMode

信息

  • 全限定名:net.minecraft.server.level.ServerPlayerGameMode
  • 类型:public class
  • 包:net.minecraft.server.level
  • 源码路径:src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
  • 起始行号:L34
  • 职责:

    TODO

字段/常量

  • FLIGHT_DISABLE_RANGE

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

      TODO

  • LOGGER

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

      TODO

  • level

    • 类型: ServerLevel
    • 修饰符: protected
    • 源码定位: L37
    • 说明:

      TODO

  • player

    • 类型: ServerPlayer
    • 修饰符: protected final
    • 源码定位: L38
    • 说明:

      TODO

  • gameModeForPlayer

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

      TODO

  • previousGameModeForPlayer

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

      TODO

  • isDestroyingBlock

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

      TODO

  • destroyProgressStart

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

      TODO

  • destroyPos

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

      TODO

  • gameTicks

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

      TODO

  • hasDelayedDestroy

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

      TODO

  • delayedDestroyPos

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

      TODO

  • delayedTickStart

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

      TODO

  • lastSentState

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

      TODO

内部类/嵌套类型

构造器

public ServerPlayerGameMode(ServerPlayer player) @ L50

  • 构造器名:ServerPlayerGameMode
  • 源码定位:L50
  • 修饰符:public

参数:

  • player: ServerPlayer

说明:

TODO

方法

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

public boolean changeGameModeForPlayer(GameType gameModeForPlayer) @ L55

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

参数:

  • gameModeForPlayer: GameType

说明:

TODO

protected void setGameModeForPlayer(GameType gameModeForPlayer, GameType previousGameModeForPlayer) @ L79

  • 方法名:setGameModeForPlayer
  • 源码定位:L79
  • 返回类型:void
  • 修饰符:protected

参数:

  • gameModeForPlayer: GameType
  • previousGameModeForPlayer: GameType

说明:

TODO

private boolean isInRangeOfGround() @ L86

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

参数:

说明:

TODO

public GameType getGameModeForPlayer() @ L91

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

参数:

说明:

TODO

public GameType getPreviousGameModeForPlayer() @ L95

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

参数:

说明:

TODO

public boolean isSurvival() @ L99

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

参数:

说明:

TODO

public boolean isCreative() @ L103

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

参数:

说明:

TODO

public void tick() @ L107

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

参数:

说明:

TODO

private float incrementDestroyProgress(BlockState blockState, BlockPos delayedDestroyPos, int destroyStartTick) @ L132

  • 方法名:incrementDestroyProgress
  • 源码定位:L132
  • 返回类型:float
  • 修饰符:private

参数:

  • blockState: BlockState
  • delayedDestroyPos: BlockPos
  • destroyStartTick: int

说明:

TODO

private void debugLogging(BlockPos pos, boolean allGood, int sequence, String message) @ L144

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

参数:

  • pos: BlockPos
  • allGood: boolean
  • sequence: int
  • message: String

说明:

TODO

public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction direction, int maxY, int sequence) @ L150

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

参数:

  • pos: BlockPos
  • action: ServerboundPlayerActionPacket.Action
  • direction: Direction
  • maxY: int
  • sequence: int

说明:

TODO

public void destroyAndAck(BlockPos pos, int sequence, String exitId) @ L251

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

参数:

  • pos: BlockPos
  • sequence: int
  • exitId: String

说明:

TODO

public boolean destroyBlock(BlockPos pos) @ L260

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

参数:

  • pos: BlockPos

说明:

TODO

public InteractionResult useItem(ServerPlayer player, Level level, ItemStack itemStack, InteractionHand hand) @ L300

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

参数:

  • player: ServerPlayer
  • level: Level
  • itemStack: ItemStack
  • hand: InteractionHand

说明:

TODO

public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack itemStack, InteractionHand hand, BlockHitResult hitResult) @ L341

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

参数:

  • player: ServerPlayer
  • level: Level
  • itemStack: ItemStack
  • hand: InteractionHand
  • hitResult: BlockHitResult

说明:

TODO

public void setLevel(ServerLevel newLevel) @ L396

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

参数:

  • newLevel: ServerLevel

说明:

TODO

代码

public class ServerPlayerGameMode {
    private static final double FLIGHT_DISABLE_RANGE = 1.0;
    private static final Logger LOGGER = LogUtils.getLogger();
    protected ServerLevel level;
    protected final ServerPlayer player;
    private GameType gameModeForPlayer = GameType.DEFAULT_MODE;
    private @Nullable 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.level();
    }
 
    public boolean changeGameModeForPlayer(GameType gameModeForPlayer) {
        if (gameModeForPlayer == this.gameModeForPlayer) {
            return false;
        } else {
            Abilities abilities = this.player.getAbilities();
            this.setGameModeForPlayer(gameModeForPlayer, this.gameModeForPlayer);
            if (abilities.flying && gameModeForPlayer != GameType.SPECTATOR && this.isInRangeOfGround()) {
                abilities.flying = false;
            }
 
            this.player.onUpdateAbilities();
            this.level
                .getServer()
                .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;
        Abilities abilities = this.player.getAbilities();
        gameModeForPlayer.updatePlayerAbilities(abilities);
    }
 
    private boolean isInRangeOfGround() {
        List<VoxelShape> clipping = Entity.collectAllColliders(this.player, this.level, this.player.getBoundingBox());
        return clipping.isEmpty() && this.player.getAvailableSpaceBelow(1.0) < 1.0;
    }
 
    public GameType getGameModeForPlayer() {
        return this.gameModeForPlayer;
    }
 
    public @Nullable GameType getPreviousGameModeForPlayer() {
        return this.previousGameModeForPlayer;
    }
 
    public boolean isSurvival() {
        return this.gameModeForPlayer.isSurvival();
    }
 
    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 destroyProgress = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart);
                if (destroyProgress >= 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 blockState, BlockPos delayedDestroyPos, int destroyStartTick) {
        int ticksSpentDestroying = this.gameTicks - destroyStartTick;
        float destroyProgress = blockState.getDestroyProgress(this.player, this.player.level(), delayedDestroyPos) * (ticksSpentDestroying + 1);
        int state = (int)(destroyProgress * 10.0F);
        if (state != this.lastSentState) {
            this.level.destroyBlockProgress(this.player.getId(), delayedDestroyPos, state);
            this.lastSentState = state;
        }
 
        return destroyProgress;
    }
 
    private void debugLogging(BlockPos pos, boolean allGood, int sequence, String message) {
        if (SharedConstants.DEBUG_BLOCK_BREAK) {
            LOGGER.debug("Server ACK {} {} {} {}", sequence, pos, allGood, message);
        }
    }
 
    public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction direction, int maxY, int sequence) {
        if (!this.player.isWithinBlockInteractionRange(pos, 1.0)) {
            this.debugLogging(pos, false, sequence, "too far");
        } else if (pos.getY() > maxY) {
            this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
            this.debugLogging(pos, false, sequence, "too high");
        } else {
            if (action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) {
                if (this.level.getServer().isUnderSpawnProtection(this.level, pos, this.player)) {
                    this.player.sendSpawnProtectionMessage(pos);
                    this.debugLogging(pos, false, sequence, "spawn protection");
                    return;
                }
 
                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 progress = 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);
                    progress = blockState.getDestroyProgress(this.player, this.player.level(), pos);
                }
 
                if (!blockState.isAir() && progress >= 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 state = (int)(progress * 10.0F);
                    this.level.destroyBlockProgress(this.player.getId(), pos, state);
                    this.debugLogging(pos, true, sequence, "actual start of destroying");
                    this.lastSentState = state;
                }
            } else if (action == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) {
                if (pos.equals(this.destroyPos)) {
                    int ticksSpentDestroying = this.gameTicks - this.destroyProgressStart;
                    BlockState state = this.level.getBlockState(pos);
                    if (!state.isAir()) {
                        float destroyProgress = state.getDestroyProgress(this.player, this.player.level(), pos) * (ticksSpentDestroying + 1);
                        if (destroyProgress >= 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 == ServerboundPlayerActionPacket.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 exitId) {
        if (this.destroyBlock(pos)) {
            this.debugLogging(pos, true, sequence, exitId);
        } else {
            this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos)));
            this.debugLogging(pos, false, sequence, exitId);
        }
    }
 
    public boolean destroyBlock(BlockPos pos) {
        BlockState state = this.level.getBlockState(pos);
        if (!this.player.getMainHandItem().canDestroyBlock(state, this.level, pos, this.player)) {
            return false;
        } else {
            BlockEntity blockEntity = this.level.getBlockEntity(pos);
            Block block = state.getBlock();
            if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) {
                this.level.sendBlockUpdated(pos, state, state, 3);
                return false;
            } else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
                return false;
            } else {
                BlockState adjustedState = block.playerWillDestroy(this.level, pos, state, this.player);
                boolean changed = this.level.removeBlock(pos, false);
                if (SharedConstants.DEBUG_BLOCK_BREAK) {
                    LOGGER.info("server broke {} {} -> {}", pos, adjustedState, this.level.getBlockState(pos));
                }
 
                if (changed) {
                    block.destroy(this.level, pos, adjustedState);
                }
 
                if (this.player.preventsBlockDrops()) {
                    return true;
                } else {
                    ItemStack itemStack = this.player.getMainHandItem();
                    ItemStack destroyedWith = itemStack.copy();
                    boolean canDestroy = this.player.hasCorrectToolForDrops(adjustedState);
                    itemStack.mineBlock(this.level, adjustedState, pos, this.player);
                    if (changed && canDestroy) {
                        block.playerDestroy(this.level, this.player, pos, adjustedState, blockEntity, destroyedWith);
                    }
 
                    return true;
                }
            }
        }
    }
 
    public InteractionResult useItem(ServerPlayer player, Level level, ItemStack itemStack, InteractionHand hand) {
        if (this.gameModeForPlayer == GameType.SPECTATOR) {
            return InteractionResult.PASS;
        } else if (player.getCooldowns().isOnCooldown(itemStack)) {
            return InteractionResult.PASS;
        } else {
            int oldCount = itemStack.getCount();
            int oldDamage = itemStack.getDamageValue();
            InteractionResult result = itemStack.use(level, player, hand);
            ItemStack resultStack;
            if (result instanceof InteractionResult.Success success) {
                resultStack = Objects.requireNonNullElse(success.heldItemTransformedTo(), player.getItemInHand(hand));
            } else {
                resultStack = player.getItemInHand(hand);
            }
 
            if (resultStack == itemStack
                && resultStack.getCount() == oldCount
                && resultStack.getUseDuration(player) <= 0
                && resultStack.getDamageValue() == oldDamage) {
                return result;
            } else if (result instanceof InteractionResult.Fail && resultStack.getUseDuration(player) > 0 && !player.isUsingItem()) {
                return result;
            } else {
                if (itemStack != resultStack) {
                    player.setItemInHand(hand, resultStack);
                }
 
                if (resultStack.isEmpty()) {
                    player.setItemInHand(hand, ItemStack.EMPTY);
                }
 
                if (!player.isUsingItem()) {
                    player.inventoryMenu.sendAllDataToRemote();
                }
 
                return result;
            }
        }
    }
 
    public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack itemStack, InteractionHand hand, BlockHitResult hitResult) {
        BlockPos pos = hitResult.getBlockPos();
        BlockState state = level.getBlockState(pos);
        if (!state.getBlock().isEnabled(level.enabledFeatures())) {
            return InteractionResult.FAIL;
        } else if (this.gameModeForPlayer == GameType.SPECTATOR) {
            MenuProvider menuProvider = state.getMenuProvider(level, pos);
            if (menuProvider != null) {
                player.openMenu(menuProvider);
                return InteractionResult.CONSUME;
            } else {
                return InteractionResult.PASS;
            }
        } else {
            boolean haveSomethingInOurHands = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty();
            boolean suppressUsingBlock = player.isSecondaryUseActive() && haveSomethingInOurHands;
            ItemStack usedItemStack = itemStack.copy();
            if (!suppressUsingBlock) {
                InteractionResult itemUse = state.useItemOn(player.getItemInHand(hand), level, player, hand, hitResult);
                if (itemUse.consumesAction()) {
                    CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, pos, usedItemStack);
                    return itemUse;
                }
 
                if (itemUse instanceof InteractionResult.TryEmptyHandInteraction && hand == InteractionHand.MAIN_HAND) {
                    InteractionResult use = state.useWithoutItem(level, player, hitResult);
                    if (use.consumesAction()) {
                        CriteriaTriggers.DEFAULT_BLOCK_USE.trigger(player, pos);
                        return use;
                    }
                }
            }
 
            if (!itemStack.isEmpty() && !player.getCooldowns().isOnCooldown(itemStack)) {
                UseOnContext context = new UseOnContext(player, hand, hitResult);
                InteractionResult success;
                if (player.hasInfiniteMaterials()) {
                    int count = itemStack.getCount();
                    success = itemStack.useOn(context);
                    itemStack.setCount(count);
                } else {
                    success = itemStack.useOn(context);
                }
 
                if (success.consumesAction()) {
                    CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, pos, usedItemStack);
                }
 
                return success;
            } else {
                return InteractionResult.PASS;
            }
        }
    }
 
    public void setLevel(ServerLevel newLevel) {
        this.level = newLevel;
    }
}

引用的其他类