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;
}
}
}引用的其他类
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ChunkLevel.byStatus(), ChunkLevel.isBlockTicking(), ChunkLevel.isEntityTicking()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
LoadingChunkTracker()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
SimulationChunkTracker()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
ThrottlingChunkTaskDispatcher()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
Ticket()
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
TaskScheduler.wrapExecutor()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ChunkPos.unpack()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置: