PlayerSpawnFinder.java
net.minecraft.server.level.PlayerSpawnFinder
信息
- 全限定名:net.minecraft.server.level.PlayerSpawnFinder
- 类型:public class
- 包:net.minecraft.server.level
- 源码路径:src/main/java/net/minecraft/server/level/PlayerSpawnFinder.java
- 起始行号:L28
- 职责:
TODO
字段/常量
-
PLAYER_DIMENSIONS- 类型:
EntityDimensions - 修饰符:
private static final - 源码定位:
L29 - 说明:
TODO
- 类型:
-
ABSOLUTE_MAX_ATTEMPTS- 类型:
int - 修饰符:
private static final - 源码定位:
L30 - 说明:
TODO
- 类型:
-
level- 类型:
ServerLevel - 修饰符:
private final - 源码定位:
L31 - 说明:
TODO
- 类型:
-
spawnSuggestion- 类型:
BlockPos - 修饰符:
private final - 源码定位:
L32 - 说明:
TODO
- 类型:
-
radius- 类型:
int - 修饰符:
private final - 源码定位:
L33 - 说明:
TODO
- 类型:
-
candidateCount- 类型:
int - 修饰符:
private final - 源码定位:
L34 - 说明:
TODO
- 类型:
-
coprime- 类型:
int - 修饰符:
private final - 源码定位:
L35 - 说明:
TODO
- 类型:
-
offset- 类型:
int - 修饰符:
private final - 源码定位:
L36 - 说明:
TODO
- 类型:
-
nextCandidateIndex- 类型:
int - 修饰符:
private - 源码定位:
L37 - 说明:
TODO
- 类型:
-
finishedFuture- 类型:
CompletableFuture<Vec3> - 修饰符:
private final - 源码定位:
L38 - 说明:
TODO
- 类型:
内部类/嵌套类型
- 无
构造器
private PlayerSpawnFinder(ServerLevel level, BlockPos spawnSuggestion, int radius) @ L40
- 构造器名:PlayerSpawnFinder
- 源码定位:L40
- 修饰符:private
参数:
- level: ServerLevel
- spawnSuggestion: BlockPos
- radius: int
说明:
TODO
方法
下面的方法块按源码顺序生成。
public static CompletableFuture<Vec3> findSpawn(ServerLevel level, BlockPos spawnSuggestion) @ L50
- 方法名:findSpawn
- 源码定位:L50
- 返回类型:CompletableFuture
- 修饰符:public static
参数:
- level: ServerLevel
- spawnSuggestion: BlockPos
说明:
TODO
private void scheduleNext() @ L70
- 方法名:scheduleNext
- 源码定位:L70
- 返回类型:void
- 修饰符:private
参数:
- 无
说明:
TODO
private static Vec3 fixupSpawnHeight(CollisionGetter level, BlockPos spawnPos) @ L89
- 方法名:fixupSpawnHeight
- 源码定位:L89
- 返回类型:Vec3
- 修饰符:private static
参数:
- level: CollisionGetter
- spawnPos: BlockPos
说明:
TODO
private static boolean noCollisionNoLiquid(CollisionGetter level, BlockPos pos) @ L106
- 方法名:noCollisionNoLiquid
- 源码定位:L106
- 返回类型:boolean
- 修饰符:private static
参数:
- level: CollisionGetter
- pos: BlockPos
说明:
TODO
private static int getCoprime(int possibleOrigins) @ L110
- 方法名:getCoprime
- 源码定位:L110
- 返回类型:int
- 修饰符:private static
参数:
- possibleOrigins: int
说明:
TODO
private void scheduleCandidate(int candidateX, int candidateZ, int candidateIndex, Supplier<Optional<Vec3>> candidateChecker) @ L114
- 方法名:scheduleCandidate
- 源码定位:L114
- 返回类型:void
- 修饰符:private
参数:
- candidateX: int
- candidateZ: int
- candidateIndex: int
- candidateChecker: Supplier<Optional
>
说明:
TODO
protected static BlockPos getOverworldRespawnPos(ServerLevel level, int x, int z) @ L148
- 方法名:getOverworldRespawnPos
- 源码定位:L148
- 返回类型:BlockPos
- 修饰符:protected static
参数:
- level: ServerLevel
- x: int
- z: int
说明:
TODO
public static BlockPos getSpawnPosInChunk(ServerLevel level, ChunkPos chunkPos) @ L178
- 方法名:getSpawnPosInChunk
- 源码定位:L178
- 返回类型:BlockPos
- 修饰符:public static
参数:
- level: ServerLevel
- chunkPos: ChunkPos
说明:
TODO
代码
public class PlayerSpawnFinder {
private static final EntityDimensions PLAYER_DIMENSIONS = EntityType.PLAYER.getDimensions();
private static final int ABSOLUTE_MAX_ATTEMPTS = 1024;
private final ServerLevel level;
private final BlockPos spawnSuggestion;
private final int radius;
private final int candidateCount;
private final int coprime;
private final int offset;
private int nextCandidateIndex;
private final CompletableFuture<Vec3> finishedFuture = new CompletableFuture<>();
private PlayerSpawnFinder(ServerLevel level, BlockPos spawnSuggestion, int radius) {
this.level = level;
this.spawnSuggestion = spawnSuggestion;
this.radius = radius;
long squareSide = radius * 2L + 1L;
this.candidateCount = (int)Math.min(1024L, squareSide * squareSide);
this.coprime = getCoprime(this.candidateCount);
this.offset = RandomSource.createThreadLocalInstance().nextInt(this.candidateCount);
}
public static CompletableFuture<Vec3> findSpawn(ServerLevel level, BlockPos spawnSuggestion) {
if (level.dimensionType().hasSkyLight() && level.getServer().getWorldData().getGameType() != GameType.ADVENTURE) {
int radius = Math.max(0, level.getGameRules().get(GameRules.RESPAWN_RADIUS));
int distToBorder = Mth.floor(level.getWorldBorder().getDistanceToBorder(spawnSuggestion.getX(), spawnSuggestion.getZ()));
if (distToBorder < radius) {
radius = distToBorder;
}
if (distToBorder <= 1) {
radius = 1;
}
PlayerSpawnFinder finder = new PlayerSpawnFinder(level, spawnSuggestion, radius);
finder.scheduleNext();
return finder.finishedFuture;
} else {
return CompletableFuture.completedFuture(fixupSpawnHeight(level, spawnSuggestion));
}
}
private void scheduleNext() {
int candidateIndex = this.nextCandidateIndex++;
if (candidateIndex < this.candidateCount) {
int value = (this.offset + this.coprime * candidateIndex) % this.candidateCount;
int deltaX = value % (this.radius * 2 + 1);
int deltaZ = value / (this.radius * 2 + 1);
int targetX = this.spawnSuggestion.getX() + deltaX - this.radius;
int targetZ = this.spawnSuggestion.getZ() + deltaZ - this.radius;
this.scheduleCandidate(targetX, targetZ, candidateIndex, () -> {
BlockPos spawnPos = getOverworldRespawnPos(this.level, targetX, targetZ);
return spawnPos != null && noCollisionNoLiquid(this.level, spawnPos) ? Optional.of(Vec3.atBottomCenterOf(spawnPos)) : Optional.empty();
});
} else {
this.scheduleCandidate(
this.spawnSuggestion.getX(), this.spawnSuggestion.getZ(), candidateIndex, () -> Optional.of(fixupSpawnHeight(this.level, this.spawnSuggestion))
);
}
}
private static Vec3 fixupSpawnHeight(CollisionGetter level, BlockPos spawnPos) {
BlockPos.MutableBlockPos mutablePos = spawnPos.mutable();
while (!noCollisionNoLiquid(level, mutablePos) && mutablePos.getY() < level.getMaxY()) {
mutablePos.move(Direction.UP);
}
mutablePos.move(Direction.DOWN);
while (noCollisionNoLiquid(level, mutablePos) && mutablePos.getY() > level.getMinY()) {
mutablePos.move(Direction.DOWN);
}
mutablePos.move(Direction.UP);
return Vec3.atBottomCenterOf(mutablePos);
}
private static boolean noCollisionNoLiquid(CollisionGetter level, BlockPos pos) {
return level.noCollision(null, PLAYER_DIMENSIONS.makeBoundingBox(pos.getBottomCenter()), true);
}
private static int getCoprime(int possibleOrigins) {
return possibleOrigins <= 16 ? possibleOrigins - 1 : 17;
}
private void scheduleCandidate(int candidateX, int candidateZ, int candidateIndex, Supplier<Optional<Vec3>> candidateChecker) {
if (!this.finishedFuture.isDone()) {
int chunkX = SectionPos.blockToSectionCoord(candidateX);
int chunkZ = SectionPos.blockToSectionCoord(candidateZ);
this.level
.getChunkSource()
.addTicketAndLoadWithRadius(TicketType.SPAWN_SEARCH, new ChunkPos(chunkX, chunkZ), 0)
.whenCompleteAsync((ignored, throwable) -> {
if (throwable == null) {
try {
Optional<Vec3> spawnPos = candidateChecker.get();
if (spawnPos.isPresent()) {
this.finishedFuture.complete(spawnPos.get());
} else {
this.scheduleNext();
}
} catch (Throwable var9) {
throwable = var9;
}
}
if (throwable != null) {
CrashReport report = CrashReport.forThrowable(throwable, "Searching for spawn");
CrashReportCategory details = report.addCategory("Spawn Lookup");
details.setDetail("Origin", this.spawnSuggestion::toString);
details.setDetail("Radius", () -> Integer.toString(this.radius));
details.setDetail("Candidate", () -> "[" + candidateX + "," + candidateZ + "]");
details.setDetail("Progress", () -> candidateIndex + " out of " + this.candidateCount);
this.finishedFuture.completeExceptionally(new ReportedException(report));
}
}, this.level.getServer());
}
}
protected static @Nullable BlockPos getOverworldRespawnPos(ServerLevel level, int x, int z) {
boolean caveWorld = level.dimensionType().hasCeiling();
LevelChunk chunk = level.getChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z));
int topY = caveWorld ? level.getChunkSource().getGenerator().getSpawnHeight(level) : chunk.getHeight(Heightmap.Types.MOTION_BLOCKING, x & 15, z & 15);
if (topY < level.getMinY()) {
return null;
} else {
int surface = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, x & 15, z & 15);
if (surface <= topY && surface > chunk.getHeight(Heightmap.Types.OCEAN_FLOOR, x & 15, z & 15)) {
return null;
} else {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
for (int y = topY + 1; y >= level.getMinY(); y--) {
pos.set(x, y, z);
BlockState blockState = level.getBlockState(pos);
if (!blockState.getFluidState().isEmpty()) {
break;
}
if (Block.isFaceFull(blockState.getCollisionShape(level, pos), Direction.UP)) {
return pos.above().immutable();
}
}
return null;
}
}
}
public static @Nullable BlockPos getSpawnPosInChunk(ServerLevel level, ChunkPos chunkPos) {
if (SharedConstants.debugVoidTerrain(chunkPos)) {
return null;
} else {
for (int x = chunkPos.getMinBlockX(); x <= chunkPos.getMaxBlockX(); x++) {
for (int z = chunkPos.getMinBlockZ(); z <= chunkPos.getMaxBlockZ(); z++) {
BlockPos validSpawnPosition = getOverworldRespawnPos(level, x, z);
if (validSpawnPosition != null) {
return validSpawnPosition;
}
}
}
return null;
}
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
CrashReport.forThrowable()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ReportedException()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SharedConstants.debugVoidTerrain()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/构造调用/返回值 - 关联成员:
BlockPos.MutableBlockPos(), MutableBlockPos()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SectionPos.blockToSectionCoord()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.floor()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
RandomSource.createThreadLocalInstance()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
参数/构造调用 - 关联成员:
ChunkPos()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Block.isFaceFull()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/返回值 - 关联成员:
Vec3.atBottomCenterOf()
- 引用位置: