206 lines
8.9 KiB
Java
206 lines
8.9 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.mojang.logging.LogUtils
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.server.network.config;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import java.lang.runtime.SwitchBootstraps;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.function.Consumer;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.network.Connection;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.server.level.ChunkLoadCounter;
|
|
import net.minecraft.server.level.PlayerSpawnFinder;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.server.level.TicketType;
|
|
import net.minecraft.server.level.progress.LevelLoadListener;
|
|
import net.minecraft.server.network.CommonListenerCookie;
|
|
import net.minecraft.server.network.ConfigurationTask;
|
|
import net.minecraft.server.players.NameAndId;
|
|
import net.minecraft.util.ProblemReporter;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.storage.LevelData;
|
|
import net.minecraft.world.level.storage.TagValueInput;
|
|
import net.minecraft.world.level.storage.ValueInput;
|
|
import net.minecraft.world.phys.Vec2;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class PrepareSpawnTask
|
|
implements ConfigurationTask {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final ConfigurationTask.Type TYPE = new ConfigurationTask.Type("prepare_spawn");
|
|
public static final int PREPARE_CHUNK_RADIUS = 3;
|
|
private final MinecraftServer server;
|
|
private final NameAndId nameAndId;
|
|
private final LevelLoadListener loadListener;
|
|
private @Nullable State state;
|
|
|
|
public PrepareSpawnTask(MinecraftServer server, NameAndId nameAndId) {
|
|
this.server = server;
|
|
this.nameAndId = nameAndId;
|
|
this.loadListener = server.getLevelLoadListener();
|
|
}
|
|
|
|
@Override
|
|
public void start(Consumer<Packet<?>> connection) {
|
|
try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(LOGGER);){
|
|
Optional<ValueInput> loadedData = this.server.getPlayerList().loadPlayerData(this.nameAndId).map(tag -> TagValueInput.create((ProblemReporter)reporter, (HolderLookup.Provider)this.server.registryAccess(), tag));
|
|
ServerPlayer.SavedPosition loadedPosition = loadedData.flatMap(tag -> tag.read(ServerPlayer.SavedPosition.MAP_CODEC)).orElse(ServerPlayer.SavedPosition.EMPTY);
|
|
LevelData.RespawnData respawnData = this.server.getWorldData().overworldData().getRespawnData();
|
|
ServerLevel spawnLevel = loadedPosition.dimension().map(this.server::getLevel).orElseGet(() -> {
|
|
ServerLevel spawnDataLevel = this.server.getLevel(respawnData.dimension());
|
|
return spawnDataLevel != null ? spawnDataLevel : this.server.overworld();
|
|
});
|
|
CompletableFuture spawnPosition = loadedPosition.position().map(CompletableFuture::completedFuture).orElseGet(() -> PlayerSpawnFinder.findSpawn(spawnLevel, respawnData.pos()));
|
|
Vec2 spawnAngle = loadedPosition.rotation().orElse(new Vec2(respawnData.yaw(), respawnData.pitch()));
|
|
this.state = new Preparing(spawnLevel, spawnPosition, spawnAngle);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean tick() {
|
|
State state = this.state;
|
|
int n = 0;
|
|
return switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{Preparing.class, Ready.class}, (Object)state, n)) {
|
|
default -> throw new MatchException(null, null);
|
|
case 0 -> {
|
|
Preparing preparing = (Preparing)state;
|
|
Ready ready = preparing.tick();
|
|
if (ready != null) {
|
|
this.state = ready;
|
|
yield true;
|
|
}
|
|
yield false;
|
|
}
|
|
case 1 -> {
|
|
Ready ignored = (Ready)state;
|
|
yield true;
|
|
}
|
|
case -1 -> false;
|
|
};
|
|
}
|
|
|
|
public ServerPlayer spawnPlayer(Connection connection, CommonListenerCookie cookie) {
|
|
State state = this.state;
|
|
if (state instanceof Ready) {
|
|
Ready ready = (Ready)state;
|
|
return ready.spawn(connection, cookie);
|
|
}
|
|
throw new IllegalStateException("Player spawn was not ready");
|
|
}
|
|
|
|
public void keepAlive() {
|
|
State state = this.state;
|
|
if (state instanceof Ready) {
|
|
Ready ready = (Ready)state;
|
|
ready.keepAlive();
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
State state = this.state;
|
|
if (state instanceof Preparing) {
|
|
Preparing preparing = (Preparing)state;
|
|
preparing.cancel();
|
|
}
|
|
this.state = null;
|
|
}
|
|
|
|
@Override
|
|
public ConfigurationTask.Type type() {
|
|
return TYPE;
|
|
}
|
|
|
|
private final class Preparing
|
|
implements State {
|
|
private final ServerLevel spawnLevel;
|
|
private final CompletableFuture<Vec3> spawnPosition;
|
|
private final Vec2 spawnAngle;
|
|
private @Nullable CompletableFuture<?> chunkLoadFuture;
|
|
private final ChunkLoadCounter chunkLoadCounter = new ChunkLoadCounter();
|
|
|
|
private Preparing(ServerLevel spawnLevel, CompletableFuture<Vec3> spawnPosition, Vec2 spawnAngle) {
|
|
this.spawnLevel = spawnLevel;
|
|
this.spawnPosition = spawnPosition;
|
|
this.spawnAngle = spawnAngle;
|
|
}
|
|
|
|
public void cancel() {
|
|
this.spawnPosition.cancel(false);
|
|
}
|
|
|
|
public @Nullable Ready tick() {
|
|
if (!this.spawnPosition.isDone()) {
|
|
return null;
|
|
}
|
|
Vec3 spawnPosition = this.spawnPosition.join();
|
|
if (this.chunkLoadFuture == null) {
|
|
ChunkPos spawnChunk = new ChunkPos(BlockPos.containing(spawnPosition));
|
|
this.chunkLoadCounter.track(this.spawnLevel, () -> {
|
|
this.chunkLoadFuture = this.spawnLevel.getChunkSource().addTicketAndLoadWithRadius(TicketType.PLAYER_SPAWN, spawnChunk, 3);
|
|
});
|
|
PrepareSpawnTask.this.loadListener.start(LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS, this.chunkLoadCounter.totalChunks());
|
|
PrepareSpawnTask.this.loadListener.updateFocus(this.spawnLevel.dimension(), spawnChunk);
|
|
}
|
|
PrepareSpawnTask.this.loadListener.update(LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS, this.chunkLoadCounter.readyChunks(), this.chunkLoadCounter.totalChunks());
|
|
if (!this.chunkLoadFuture.isDone()) {
|
|
return null;
|
|
}
|
|
PrepareSpawnTask.this.loadListener.finish(LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS);
|
|
return new Ready(this.spawnLevel, spawnPosition, this.spawnAngle);
|
|
}
|
|
}
|
|
|
|
private static sealed interface State
|
|
permits Preparing, Ready {
|
|
}
|
|
|
|
private final class Ready
|
|
implements State {
|
|
private final ServerLevel spawnLevel;
|
|
private final Vec3 spawnPosition;
|
|
private final Vec2 spawnAngle;
|
|
|
|
private Ready(ServerLevel spawnLevel, Vec3 spawnPosition, Vec2 spawnAngle) {
|
|
this.spawnLevel = spawnLevel;
|
|
this.spawnPosition = spawnPosition;
|
|
this.spawnAngle = spawnAngle;
|
|
}
|
|
|
|
public void keepAlive() {
|
|
this.spawnLevel.getChunkSource().addTicketWithRadius(TicketType.PLAYER_SPAWN, new ChunkPos(BlockPos.containing(this.spawnPosition)), 3);
|
|
}
|
|
|
|
public ServerPlayer spawn(Connection connection, CommonListenerCookie cookie) {
|
|
ChunkPos spawnChunk = new ChunkPos(BlockPos.containing(this.spawnPosition));
|
|
this.spawnLevel.waitForEntities(spawnChunk, 3);
|
|
ServerPlayer player = new ServerPlayer(PrepareSpawnTask.this.server, this.spawnLevel, cookie.gameProfile(), cookie.clientInformation());
|
|
try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER);){
|
|
Optional<ValueInput> input = PrepareSpawnTask.this.server.getPlayerList().loadPlayerData(PrepareSpawnTask.this.nameAndId).map(tag -> TagValueInput.create((ProblemReporter)reporter, (HolderLookup.Provider)PrepareSpawnTask.this.server.registryAccess(), tag));
|
|
input.ifPresent(player::load);
|
|
player.snapTo(this.spawnPosition, this.spawnAngle.x, this.spawnAngle.y);
|
|
PrepareSpawnTask.this.server.getPlayerList().placeNewPlayer(connection, player, cookie);
|
|
input.ifPresent(tag -> {
|
|
player.loadAndSpawnEnderPearls((ValueInput)tag);
|
|
player.loadAndSpawnParentVehicle((ValueInput)tag);
|
|
});
|
|
ServerPlayer serverPlayer = player;
|
|
return serverPlayer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|