DistanceManager.java

net.minecraft.server.level.DistanceManager

信息

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

    TODO

字段/常量

  • LOGGER

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

      TODO

  • PLAYER_TICKET_LEVEL

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

      TODO

  • playersPerChunk

    • 类型: Long2ObjectMap<ObjectSet<ServerPlayer>>
    • 修饰符: private final
    • 源码定位: L38
    • 说明:

      TODO

  • loadingChunkTracker

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

      TODO

  • simulationChunkTracker

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

      TODO

  • ticketStorage

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

      TODO

  • naturalSpawnChunkCounter

    • 类型: DistanceManager.FixedPlayerDistanceChunkTracker
    • 修饰符: private final
    • 源码定位: L42
    • 说明:

      TODO

  • playerTicketManager

    • 类型: DistanceManager.PlayerTicketTracker
    • 修饰符: private final
    • 源码定位: L43
    • 说明:

      TODO

  • chunksToUpdateFutures

    • 类型: Set<ChunkHolder>
    • 修饰符: protected final
    • 源码定位: L44
    • 说明:

      TODO

  • ticketDispatcher

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

      TODO

  • ticketsToRelease

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

      TODO

  • mainThreadExecutor

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

      TODO

  • simulationDistance

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

      TODO

内部类/嵌套类型

  • net.minecraft.server.level.DistanceManager.FixedPlayerDistanceChunkTracker

    • 类型: class
    • 修饰符: private
    • 源码定位: L197
    • 说明:

      TODO

  • net.minecraft.server.level.DistanceManager.PlayerTicketTracker

    • 类型: class
    • 修饰符: private
    • 源码定位: L244
    • 说明:

      TODO

构造器

protected DistanceManager(TicketStorage ticketStorage, Executor executor, Executor mainThreadExecutor) @ L50

  • 构造器名:DistanceManager
  • 源码定位:L50
  • 修饰符:protected

参数:

  • ticketStorage: TicketStorage
  • executor: Executor
  • mainThreadExecutor: Executor

说明:

TODO

方法

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

protected abstract boolean isChunkToRemove(long node) @ L59

  • 方法名:isChunkToRemove
  • 源码定位:L59
  • 返回类型:boolean
  • 修饰符:protected abstract

参数:

  • node: long

说明:

TODO

protected abstract ChunkHolder getChunk(long node) @ L61

  • 方法名:getChunk
  • 源码定位:L61
  • 返回类型:ChunkHolder
  • 修饰符:protected abstract

参数:

  • node: long

说明:

TODO

protected abstract ChunkHolder updateChunkScheduling(long node, int level, ChunkHolder chunk, int oldLevel) @ L63

  • 方法名:updateChunkScheduling
  • 源码定位:L63
  • 返回类型:ChunkHolder
  • 修饰符:protected abstract

参数:

  • node: long
  • level: int
  • chunk: ChunkHolder
  • oldLevel: int

说明:

TODO

public boolean runAllUpdates(ChunkMap scheduler) @ L65

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

参数:

  • scheduler: ChunkMap

说明:

TODO

public void addPlayer(SectionPos pos, ServerPlayer player) @ L110

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

参数:

  • pos: SectionPos
  • player: ServerPlayer

说明:

TODO

public void removePlayer(SectionPos pos, ServerPlayer player) @ L119

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

参数:

  • pos: SectionPos
  • player: ServerPlayer

说明:

TODO

private int getPlayerTicketLevel() @ L132

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

参数:

说明:

TODO

public boolean inEntityTickingRange(long key) @ L136

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

参数:

  • key: long

说明:

TODO

public boolean inBlockTickingRange(long key) @ L140

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

参数:

  • key: long

说明:

TODO

public int getChunkLevel(long key, boolean simulation) @ L144

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

参数:

  • key: long
  • simulation: boolean

说明:

TODO

protected void updatePlayerTickets(int viewDistance) @ L148

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

参数:

  • viewDistance: int

说明:

TODO

public void updateSimulationDistance(int newDistance) @ L152

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

参数:

  • newDistance: int

说明:

TODO

public int getNaturalSpawnChunkCount() @ L159

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

参数:

说明:

TODO

public TriState hasPlayersNearby(long pos) @ L164

  • 方法名:hasPlayersNearby
  • 源码定位:L164
  • 返回类型:TriState
  • 修饰符:public

参数:

  • pos: long

说明:

TODO

public void forEachEntityTickingChunk(LongConsumer consumer) @ L174

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

参数:

  • consumer: LongConsumer

说明:

TODO

public LongIterator getSpawnCandidateChunks() @ L184

  • 方法名:getSpawnCandidateChunks
  • 源码定位:L184
  • 返回类型:LongIterator
  • 修饰符:public

参数:

说明:

TODO

public String getDebugStatus() @ L189

  • 方法名:getDebugStatus
  • 源码定位:L189
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

public boolean hasTickets() @ L193

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

参数:

说明:

TODO

代码

public abstract class DistanceManager {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final int PLAYER_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING);
    private final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap<>();
    private final LoadingChunkTracker loadingChunkTracker;
    private final SimulationChunkTracker simulationChunkTracker;
    private final TicketStorage ticketStorage;
    private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
    private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(32);
    protected final Set<ChunkHolder> chunksToUpdateFutures = new ReferenceOpenHashSet<>();
    private final ThrottlingChunkTaskDispatcher ticketDispatcher;
    private final LongSet ticketsToRelease = new LongOpenHashSet();
    private final Executor mainThreadExecutor;
    private int simulationDistance = 10;
 
    protected DistanceManager(TicketStorage ticketStorage, Executor executor, Executor mainThreadExecutor) {
        this.ticketStorage = ticketStorage;
        this.loadingChunkTracker = new LoadingChunkTracker(this, ticketStorage);
        this.simulationChunkTracker = new SimulationChunkTracker(ticketStorage);
        TaskScheduler<Runnable> mainThreadTaskScheduler = TaskScheduler.wrapExecutor("player ticket throttler", mainThreadExecutor);
        this.ticketDispatcher = new ThrottlingChunkTaskDispatcher(mainThreadTaskScheduler, executor, 4);
        this.mainThreadExecutor = mainThreadExecutor;
    }
 
    protected abstract boolean isChunkToRemove(final long node);
 
    protected abstract @Nullable ChunkHolder getChunk(final long node);
 
    protected abstract @Nullable ChunkHolder updateChunkScheduling(final long node, final int level, final @Nullable ChunkHolder chunk, final int oldLevel);
 
    public boolean runAllUpdates(ChunkMap scheduler) {
        this.naturalSpawnChunkCounter.runAllUpdates();
        this.simulationChunkTracker.runAllUpdates();
        this.playerTicketManager.runAllUpdates();
        int updates = Integer.MAX_VALUE - this.loadingChunkTracker.runDistanceUpdates(Integer.MAX_VALUE);
        boolean updated = updates != 0;
        if (updated && SharedConstants.DEBUG_VERBOSE_SERVER_EVENTS) {
            LOGGER.debug("DMU {}", updates);
        }
 
        if (!this.chunksToUpdateFutures.isEmpty()) {
            for (ChunkHolder chunksToUpdateFuture : this.chunksToUpdateFutures) {
                chunksToUpdateFuture.updateHighestAllowedStatus(scheduler);
            }
 
            for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) {
                chunkHolder.updateFutures(scheduler, this.mainThreadExecutor);
            }
 
            this.chunksToUpdateFutures.clear();
            return true;
        } else {
            if (!this.ticketsToRelease.isEmpty()) {
                LongIterator iterator = this.ticketsToRelease.iterator();
 
                while (iterator.hasNext()) {
                    long pos = iterator.nextLong();
                    if (this.ticketStorage.getTickets(pos).stream().anyMatch(t -> t.getType() == TicketType.PLAYER_LOADING)) {
                        ChunkHolder chunk = scheduler.getUpdatingChunkIfPresent(pos);
                        if (chunk == null) {
                            throw new IllegalStateException();
                        }
 
                        CompletableFuture<ChunkResult<LevelChunk>> future = chunk.getEntityTickingChunkFuture();
                        future.thenAccept(c -> this.mainThreadExecutor.execute(() -> this.ticketDispatcher.release(pos, () -> {}, false)));
                    }
                }
 
                this.ticketsToRelease.clear();
            }
 
            return updated;
        }
    }
 
    public void addPlayer(SectionPos pos, ServerPlayer player) {
        ChunkPos chunk = pos.chunk();
        long chunkPos = chunk.pack();
        this.playersPerChunk.computeIfAbsent(chunkPos, k -> new ObjectOpenHashSet<>()).add(player);
        this.naturalSpawnChunkCounter.update(chunkPos, 0, true);
        this.playerTicketManager.update(chunkPos, 0, true);
        this.ticketStorage.addTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunk);
    }
 
    public void removePlayer(SectionPos pos, ServerPlayer player) {
        ChunkPos chunk = pos.chunk();
        long chunkPos = chunk.pack();
        ObjectSet<ServerPlayer> chunkPlayers = this.playersPerChunk.get(chunkPos);
        chunkPlayers.remove(player);
        if (chunkPlayers.isEmpty()) {
            this.playersPerChunk.remove(chunkPos);
            this.naturalSpawnChunkCounter.update(chunkPos, Integer.MAX_VALUE, false);
            this.playerTicketManager.update(chunkPos, Integer.MAX_VALUE, false);
            this.ticketStorage.removeTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunk);
        }
    }
 
    private int getPlayerTicketLevel() {
        return Math.max(0, ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING) - this.simulationDistance);
    }
 
    public boolean inEntityTickingRange(long key) {
        return ChunkLevel.isEntityTicking(this.simulationChunkTracker.getLevel(key));
    }
 
    public boolean inBlockTickingRange(long key) {
        return ChunkLevel.isBlockTicking(this.simulationChunkTracker.getLevel(key));
    }
 
    public int getChunkLevel(long key, boolean simulation) {
        return simulation ? this.simulationChunkTracker.getLevel(key) : this.loadingChunkTracker.getLevel(key);
    }
 
    protected void updatePlayerTickets(int viewDistance) {
        this.playerTicketManager.updateViewDistance(viewDistance);
    }
 
    public void updateSimulationDistance(int newDistance) {
        if (newDistance != this.simulationDistance) {
            this.simulationDistance = newDistance;
            this.ticketStorage.replaceTicketLevelOfType(this.getPlayerTicketLevel(), TicketType.PLAYER_SIMULATION);
        }
    }
 
    public int getNaturalSpawnChunkCount() {
        this.naturalSpawnChunkCounter.runAllUpdates();
        return this.naturalSpawnChunkCounter.chunks.size();
    }
 
    public TriState hasPlayersNearby(long pos) {
        this.naturalSpawnChunkCounter.runAllUpdates();
        int distance = this.naturalSpawnChunkCounter.getLevel(pos);
        if (distance <= NaturalSpawner.INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK) {
            return TriState.TRUE;
        } else {
            return distance > 8 ? TriState.FALSE : TriState.DEFAULT;
        }
    }
 
    public void forEachEntityTickingChunk(LongConsumer consumer) {
        for (Entry entry : Long2ByteMaps.fastIterable(this.simulationChunkTracker.chunks)) {
            byte level = entry.getByteValue();
            long key = entry.getLongKey();
            if (ChunkLevel.isEntityTicking(level)) {
                consumer.accept(key);
            }
        }
    }
 
    public LongIterator getSpawnCandidateChunks() {
        this.naturalSpawnChunkCounter.runAllUpdates();
        return this.naturalSpawnChunkCounter.chunks.keySet().iterator();
    }
 
    public String getDebugStatus() {
        return this.ticketDispatcher.getDebugStatus();
    }
 
    public boolean hasTickets() {
        return this.ticketStorage.hasTickets();
    }
 
    private class FixedPlayerDistanceChunkTracker extends ChunkTracker {
        protected final Long2ByteMap chunks;
        protected final int maxDistance;
 
        protected FixedPlayerDistanceChunkTracker(int maxDistance) {
            Objects.requireNonNull(DistanceManager.this);
            super(maxDistance + 2, 16, 256);
            this.chunks = new Long2ByteOpenHashMap();
            this.maxDistance = maxDistance;
            this.chunks.defaultReturnValue((byte)(maxDistance + 2));
        }
 
        @Override
        protected int getLevel(long node) {
            return this.chunks.get(node);
        }
 
        @Override
        protected void setLevel(long node, int level) {
            byte oldLevel;
            if (level > this.maxDistance) {
                oldLevel = this.chunks.remove(node);
            } else {
                oldLevel = this.chunks.put(node, (byte)level);
            }
 
            this.onLevelChange(node, oldLevel, level);
        }
 
        protected void onLevelChange(long node, int oldLevel, int level) {
        }
 
        @Override
        protected int getLevelFromSource(long to) {
            return this.havePlayer(to) ? 0 : Integer.MAX_VALUE;
        }
 
        private boolean havePlayer(long chunkPos) {
            ObjectSet<ServerPlayer> players = DistanceManager.this.playersPerChunk.get(chunkPos);
            return players != null && !players.isEmpty();
        }
 
        public void runAllUpdates() {
            this.runUpdates(Integer.MAX_VALUE);
        }
    }
 
    private class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker {
        private int viewDistance;
        private final Long2IntMap queueLevels;
        private final LongSet toUpdate;
 
        protected PlayerTicketTracker(int maxDistance) {
            Objects.requireNonNull(DistanceManager.this);
            super(maxDistance);
            this.queueLevels = Long2IntMaps.synchronize(new Long2IntOpenHashMap());
            this.toUpdate = new LongOpenHashSet();
            this.viewDistance = 0;
            this.queueLevels.defaultReturnValue(maxDistance + 2);
        }
 
        @Override
        protected void onLevelChange(long node, int oldLevel, int level) {
            this.toUpdate.add(node);
        }
 
        public void updateViewDistance(int viewDistance) {
            for (Entry entry : this.chunks.long2ByteEntrySet()) {
                byte level = entry.getByteValue();
                long key = entry.getLongKey();
                this.onLevelChange(key, level, this.haveTicketFor(level), level <= viewDistance);
            }
 
            this.viewDistance = viewDistance;
        }
 
        private void onLevelChange(long key, int level, boolean saw, boolean sees) {
            if (saw != sees) {
                Ticket ticket = new Ticket(TicketType.PLAYER_LOADING, DistanceManager.PLAYER_TICKET_LEVEL);
                if (sees) {
                    DistanceManager.this.ticketDispatcher.submit(() -> DistanceManager.this.mainThreadExecutor.execute(() -> {
                        if (this.haveTicketFor(this.getLevel(key))) {
                            DistanceManager.this.ticketStorage.addTicket(key, ticket);
                            DistanceManager.this.ticketsToRelease.add(key);
                        } else {
                            DistanceManager.this.ticketDispatcher.release(key, () -> {}, false);
                        }
                    }), key, () -> level);
                } else {
                    DistanceManager.this.ticketDispatcher
                        .release(
                            key,
                            () -> DistanceManager.this.mainThreadExecutor.execute(() -> DistanceManager.this.ticketStorage.removeTicket(key, ticket)),
                            true
                        );
                }
            }
        }
 
        @Override
        public void runAllUpdates() {
            super.runAllUpdates();
            if (!this.toUpdate.isEmpty()) {
                LongIterator iterator = this.toUpdate.iterator();
 
                while (iterator.hasNext()) {
                    long node = iterator.nextLong();
                    int oldLevel = this.queueLevels.get(node);
                    int level = this.getLevel(node);
                    if (oldLevel != level) {
                        DistanceManager.this.ticketDispatcher.onLevelChange(ChunkPos.unpack(node), () -> this.queueLevels.get(node), level, l -> {
                            if (l >= this.queueLevels.defaultReturnValue()) {
                                this.queueLevels.remove(node);
                            } else {
                                this.queueLevels.put(node, l);
                            }
                        });
                        this.onLevelChange(node, level, this.haveTicketFor(oldLevel), this.haveTicketFor(level));
                    }
                }
 
                this.toUpdate.clear();
            }
        }
 
        private boolean haveTicketFor(int level) {
            return level <= this.viewDistance;
        }
    }
}

引用的其他类

  • SectionPos

    • 引用位置: 参数
  • ChunkHolder

    • 引用位置: 参数/字段/返回值
  • ChunkLevel

    • 引用位置: 方法调用
    • 关联成员: ChunkLevel.byStatus(), ChunkLevel.isBlockTicking(), ChunkLevel.isEntityTicking()
  • ChunkMap

    • 引用位置: 参数
  • LoadingChunkTracker

    • 引用位置: 字段/构造调用
    • 关联成员: LoadingChunkTracker()
  • ServerPlayer

    • 引用位置: 参数/字段
  • SimulationChunkTracker

    • 引用位置: 字段/构造调用
    • 关联成员: SimulationChunkTracker()
  • ThrottlingChunkTaskDispatcher

    • 引用位置: 字段/构造调用
    • 关联成员: ThrottlingChunkTaskDispatcher()
  • Ticket

    • 引用位置: 构造调用
    • 关联成员: Ticket()
  • TriState

    • 引用位置: 返回值
  • TaskScheduler

    • 引用位置: 方法调用
    • 关联成员: TaskScheduler.wrapExecutor()
  • ChunkPos

    • 引用位置: 方法调用
    • 关联成员: ChunkPos.unpack()
  • TicketStorage

    • 引用位置: 参数/字段