aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch2472
1 files changed, 2472 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch
new file mode 100644
index 0000000000..8b50495875
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/server/MinecraftServer.java.patch
@@ -0,0 +1,2472 @@
+--- a/net/minecraft/server/MinecraftServer.java
++++ b/net/minecraft/server/MinecraftServer.java
+@@ -14,29 +14,32 @@
+ import it.unimi.dsi.fastutil.longs.LongIterator;
+ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+ import java.awt.image.BufferedImage;
++import java.io.BufferedWriter;
+ import java.io.ByteArrayOutputStream;
+ import java.io.File;
+ import java.io.IOException;
+-import java.io.Writer;
+ import java.lang.management.ManagementFactory;
+ import java.lang.management.ThreadInfo;
+ import java.lang.management.ThreadMXBean;
+ import java.net.Proxy;
+ import java.nio.file.Files;
++import java.nio.file.LinkOption;
+ import java.nio.file.Path;
+ import java.security.KeyPair;
++import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.Comparator;
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.Locale;
+ import java.util.Map;
++import java.util.Map.Entry;
+ import java.util.Objects;
+ import java.util.Optional;
+ import java.util.Set;
+ import java.util.UUID;
+-import java.util.Map.Entry;
+ import java.util.concurrent.CompletableFuture;
+ import java.util.concurrent.Executor;
+ import java.util.concurrent.RejectedExecutionException;
+@@ -45,6 +48,7 @@
+ import java.util.function.Consumer;
+ import java.util.function.Function;
+ import java.util.stream.Collectors;
++import java.util.stream.Stream;
+ import javax.annotation.Nullable;
+ import javax.imageio.ImageIO;
+ import net.minecraft.CrashReport;
+@@ -56,7 +60,6 @@
+ import net.minecraft.commands.CommandSourceStack;
+ import net.minecraft.commands.Commands;
+ import net.minecraft.core.BlockPos;
+-import net.minecraft.core.Holder;
+ import net.minecraft.core.HolderGetter;
+ import net.minecraft.core.LayeredRegistryAccess;
+ import net.minecraft.core.Registry;
+@@ -73,7 +76,6 @@
+ import net.minecraft.obfuscate.DontObfuscate;
+ import net.minecraft.resources.ResourceKey;
+ import net.minecraft.resources.ResourceLocation;
+-import net.minecraft.server.bossevents.CustomBossEvents;
+ import net.minecraft.server.level.DemoMode;
+ import net.minecraft.server.level.PlayerRespawnLogic;
+ import net.minecraft.server.level.ServerChunkCache;
+@@ -88,7 +90,7 @@
+ import net.minecraft.server.packs.PackType;
+ import net.minecraft.server.packs.repository.Pack;
+ import net.minecraft.server.packs.repository.PackRepository;
+-import net.minecraft.server.packs.resources.CloseableResourceManager;
++import net.minecraft.server.packs.resources.IReloadableResourceManager;
+ import net.minecraft.server.packs.resources.MultiPackResourceManager;
+ import net.minecraft.server.packs.resources.ResourceManager;
+ import net.minecraft.server.players.GameProfileCache;
+@@ -100,10 +102,12 @@
+ import net.minecraft.util.ModCheck;
+ import net.minecraft.util.Mth;
+ import net.minecraft.util.NativeModuleLister;
++import net.minecraft.util.ProgressListener;
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.util.SignatureValidator;
+ import net.minecraft.util.TimeUtil;
+ import net.minecraft.util.Unit;
++import net.minecraft.util.datafix.DataFixers;
+ import net.minecraft.util.profiling.EmptyProfileResults;
+ import net.minecraft.util.profiling.ProfileResults;
+ import net.minecraft.util.profiling.ProfilerFiller;
+@@ -119,6 +123,7 @@
+ import net.minecraft.util.thread.ReentrantBlockableEventLoop;
+ import net.minecraft.world.Difficulty;
+ import net.minecraft.world.RandomSequences;
++import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.ai.village.VillageSiege;
+ import net.minecraft.world.entity.npc.CatSpawner;
+ import net.minecraft.world.entity.npc.WanderingTraderSpawner;
+@@ -137,34 +142,57 @@
+ import net.minecraft.world.level.WorldDataConfiguration;
+ import net.minecraft.world.level.biome.BiomeManager;
+ import net.minecraft.world.level.block.Block;
+-import net.minecraft.world.level.border.BorderChangeListener;
+ import net.minecraft.world.level.border.WorldBorder;
+ import net.minecraft.world.level.dimension.LevelStem;
+ import net.minecraft.world.level.levelgen.Heightmap;
++import net.minecraft.world.level.levelgen.MobSpawnerPhantom;
+ import net.minecraft.world.level.levelgen.PatrolSpawner;
+-import net.minecraft.world.level.levelgen.PhantomSpawner;
+ import net.minecraft.world.level.levelgen.WorldOptions;
++import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
+ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
++import net.minecraft.world.level.storage.WorldData;
++import net.minecraft.world.level.storage.loot.LootDataManager;
++import org.slf4j.Logger;
++
++// CraftBukkit start
++import com.mojang.serialization.Dynamic;
++import com.mojang.serialization.Lifecycle;
++import java.util.Random;
++import jline.console.ConsoleReader;
++import joptsimple.OptionSet;
++import net.minecraft.nbt.NbtException;
++import net.minecraft.nbt.ReportedNbtException;
++import net.minecraft.server.bossevents.CustomBossEvents;
++import net.minecraft.server.dedicated.DedicatedServer;
++import net.minecraft.server.dedicated.DedicatedServerProperties;
++import net.minecraft.world.level.levelgen.WorldDimensions;
++import net.minecraft.world.level.levelgen.presets.WorldPresets;
+ import net.minecraft.world.level.storage.CommandStorage;
+-import net.minecraft.world.level.storage.DerivedLevelData;
+ import net.minecraft.world.level.storage.DimensionDataStorage;
+ import net.minecraft.world.level.storage.LevelData;
++import net.minecraft.world.level.storage.LevelDataAndDimensions;
+ import net.minecraft.world.level.storage.LevelResource;
+ import net.minecraft.world.level.storage.LevelStorageSource;
++import net.minecraft.world.level.storage.LevelSummary;
+ import net.minecraft.world.level.storage.PlayerDataStorage;
++import net.minecraft.world.level.storage.PrimaryLevelData;
+ import net.minecraft.world.level.storage.ServerLevelData;
+-import net.minecraft.world.level.storage.WorldData;
+-import net.minecraft.world.level.storage.loot.LootDataManager;
++import net.minecraft.world.level.validation.ContentValidationException;
+ import net.minecraft.world.phys.Vec2;
+ import net.minecraft.world.phys.Vec3;
+-import org.slf4j.Logger;
++import org.bukkit.Bukkit;
++import org.bukkit.craftbukkit.CraftServer;
++import org.bukkit.craftbukkit.Main;
++import org.bukkit.event.server.ServerLoadEvent;
++// CraftBukkit end
+
+ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTask> implements ServerInfo, CommandSource, AutoCloseable {
+- private static final Logger LOGGER = LogUtils.getLogger();
++
++ public static final Logger LOGGER = LogUtils.getLogger();
+ public static final String VANILLA_BRAND = "vanilla";
+ private static final float AVERAGE_TICK_TIME_SMOOTHING = 0.8F;
+ private static final int TICK_STATS_SPAN = 100;
+- private static final long OVERLOADED_THRESHOLD_NANOS = 20L * TimeUtil.NANOSECONDS_PER_SECOND / 20L;
++ private static final long OVERLOADED_THRESHOLD_NANOS = 30L * TimeUtil.NANOSECONDS_PER_SECOND / 20L; // CraftBukkit
+ private static final int OVERLOADED_TICKS_THRESHOLD = 20;
+ private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeUtil.NANOSECONDS_PER_SECOND;
+ private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100;
+@@ -177,39 +205,36 @@
+ private static final int MIMINUM_AUTOSAVE_TICKS = 100;
+ private static final int MAX_TICK_LATENCY = 3;
+ public static final int ABSOLUTE_MAX_WORLD_SIZE = 29999984;
+- public static final LevelSettings DEMO_SETTINGS = new LevelSettings(
+- "Demo World", GameType.SURVIVAL, false, Difficulty.NORMAL, false, new GameRules(), WorldDataConfiguration.DEFAULT
+- );
++ public static final LevelSettings DEMO_SETTINGS = new LevelSettings("Demo World", GameType.SURVIVAL, false, Difficulty.NORMAL, false, new GameRules(), WorldDataConfiguration.DEFAULT);
+ public static final GameProfile ANONYMOUS_PLAYER_PROFILE = new GameProfile(Util.NIL_UUID, "Anonymous Player");
+- protected final LevelStorageSource.LevelStorageAccess storageSource;
+- protected final PlayerDataStorage playerDataStorage;
++ public LevelStorageSource.LevelStorageAccess storageSource;
++ public final PlayerDataStorage playerDataStorage;
+ private final List<Runnable> tickables = Lists.newArrayList();
+- private MetricsRecorder metricsRecorder = InactiveMetricsRecorder.INSTANCE;
+- private ProfilerFiller profiler = this.metricsRecorder.getProfiler();
+- private Consumer<ProfileResults> onMetricsRecordingStopped = profileResults -> this.stopRecordingMetrics();
+- private Consumer<Path> onMetricsRecordingFinished = path -> {
+- };
++ private MetricsRecorder metricsRecorder;
++ private ProfilerFiller profiler;
++ private Consumer<ProfileResults> onMetricsRecordingStopped;
++ private Consumer<Path> onMetricsRecordingFinished;
+ private boolean willStartRecordingMetrics;
+ @Nullable
+ private MinecraftServer.TimeProfiler debugCommandProfiler;
+ private boolean debugCommandProfilerDelayStart;
+- private final ServerConnectionListener connection;
+- private final ChunkProgressListenerFactory progressListenerFactory;
++ private ServerConnectionListener connection;
++ public final ChunkProgressListenerFactory progressListenerFactory;
+ @Nullable
+ private ServerStatus status;
+ @Nullable
+- private ServerStatus.Favicon statusIcon;
+- private final RandomSource random = RandomSource.create();
+- private final DataFixer fixerUpper;
++ private ServerStatus.a statusIcon;
++ private final RandomSource random;
++ public final DataFixer fixerUpper;
+ private String localIp;
+- private int port = -1;
++ private int port;
+ private final LayeredRegistryAccess<RegistryLayer> registries;
+- private final Map<ResourceKey<Level>, ServerLevel> levels = Maps.newLinkedHashMap();
++ private Map<ResourceKey<Level>, ServerLevel> levels;
+ private PlayerList playerList;
+- private volatile boolean running = true;
++ private volatile boolean running;
+ private boolean stopped;
+ private int tickCount;
+- private int ticksUntilAutosave = 6000;
++ private int ticksUntilAutosave;
+ protected final Proxy proxy;
+ private boolean onlineMode;
+ private boolean preventProxyConnections;
+@@ -218,8 +243,8 @@
+ @Nullable
+ private String motd;
+ private int playerIdleTimeout;
+- private final long[] tickTimesNanos = new long[100];
+- private long aggregatedTickTimesNanos = 0L;
++ private final long[] tickTimesNanos;
++ private long aggregatedTickTimesNanos;
+ @Nullable
+ private KeyPair keyPair;
+ @Nullable
+@@ -229,60 +254,87 @@
+ private long lastOverloadWarningNanos;
+ protected final Services services;
+ private long lastServerStatus;
+- private final Thread serverThread;
+- private long nextTickTimeNanos = Util.getNanos();
++ public final Thread serverThread;
++ private long nextTickTimeNanos;
+ private long delayedTasksMaxNextTickTimeNanos;
+ private boolean mayHaveDelayedTasks;
+ private final PackRepository packRepository;
+- private final ServerScoreboard scoreboard = new ServerScoreboard(this);
++ private final ServerScoreboard scoreboard;
+ @Nullable
+ private CommandStorage commandStorage;
+- private final CustomBossEvents customBossEvents = new CustomBossEvents();
++ private final CustomBossEvents customBossEvents;
+ private final ServerFunctionManager functionManager;
+ private boolean enforceWhitelist;
+ private float smoothedTickTimeMillis;
+- private final Executor executor;
++ public final Executor executor;
+ @Nullable
+ private String serverId;
+- private MinecraftServer.ReloadableResources resources;
++ public MinecraftServer.ReloadableResources resources;
+ private final StructureTemplateManager structureTemplateManager;
+ private final ServerTickRateManager tickRateManager;
+- protected final WorldData worldData;
++ protected WorldData worldData;
+ private volatile boolean isSaving;
+
++ // CraftBukkit start
++ public final WorldLoader.a worldLoader;
++ public org.bukkit.craftbukkit.CraftServer server;
++ public OptionSet options;
++ public org.bukkit.command.ConsoleCommandSender console;
++ public ConsoleReader reader;
++ public static int currentTick = (int) (System.currentTimeMillis() / 50);
++ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
++ public int autosavePeriod;
++ public Commands vanillaCommandDispatcher;
++ private boolean forceTicks;
++ // CraftBukkit end
++
+ public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
+- AtomicReference<S> atomicReference = new AtomicReference<>();
+- Thread thread = new Thread(() -> atomicReference.get().runServer(), "Server thread");
+- thread.setUncaughtExceptionHandler((thread1, throwable) -> LOGGER.error("Uncaught exception in server thread", throwable));
++ AtomicReference<S> atomicreference = new AtomicReference();
++ Thread thread = new Thread(() -> {
++ ((MinecraftServer) atomicreference.get()).runServer();
++ }, "Server thread");
++
++ thread.setUncaughtExceptionHandler((thread1, throwable) -> {
++ MinecraftServer.LOGGER.error("Uncaught exception in server thread", throwable);
++ });
+ if (Runtime.getRuntime().availableProcessors() > 4) {
+ thread.setPriority(8);
+ }
+
+- S minecraftServer = (S)threadFunction.apply(thread);
+- atomicReference.set(minecraftServer);
++ S s0 = threadFunction.apply(thread); // CraftBukkit - decompile error
++
++ atomicreference.set(s0);
+ thread.start();
+- return minecraftServer;
++ return s0;
+ }
+
+- public MinecraftServer(
+- Thread serverThread,
+- LevelStorageSource.LevelStorageAccess storageSource,
+- PackRepository packRepository,
+- WorldStem worldStem,
+- Proxy proxy,
+- DataFixer fixerUpper,
+- Services services,
+- ChunkProgressListenerFactory progressListenerFactory
+- ) {
++ public MinecraftServer(OptionSet options, WorldLoader.a worldLoader, Thread thread, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PackRepository resourcepackrepository, WorldStem worldstem, Proxy proxy, DataFixer datafixer, Services services, ChunkProgressListenerFactory worldloadlistenerfactory) {
+ super("Server");
+- this.registries = worldStem.registries();
+- this.worldData = worldStem.worldData();
+- if (!this.registries.compositeAccess().registryOrThrow(Registries.LEVEL_STEM).containsKey(LevelStem.OVERWORLD)) {
++ this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
++ this.profiler = this.metricsRecorder.getProfiler();
++ this.onMetricsRecordingStopped = (methodprofilerresults) -> {
++ this.stopRecordingMetrics();
++ };
++ this.onMetricsRecordingFinished = (path) -> {
++ };
++ this.random = RandomSource.create();
++ this.port = -1;
++ this.levels = Maps.newLinkedHashMap();
++ this.running = true;
++ this.ticksUntilAutosave = 6000;
++ this.tickTimesNanos = new long[100];
++ this.aggregatedTickTimesNanos = 0L;
++ this.nextTickTimeNanos = Util.getNanos();
++ this.scoreboard = new ServerScoreboard(this);
++ this.customBossEvents = new CustomBossEvents();
++ this.registries = worldstem.registries();
++ this.worldData = worldstem.worldData();
++ if (false && !this.registries.compositeAccess().registryOrThrow(Registries.LEVEL_STEM).containsKey(LevelStem.OVERWORLD)) { // CraftBukkit - initialised later
+ throw new IllegalStateException("Missing Overworld dimension data");
+ } else {
+ this.proxy = proxy;
+- this.packRepository = packRepository;
+- this.resources = new MinecraftServer.ReloadableResources(worldStem.resourceManager(), worldStem.dataPackResources());
++ this.packRepository = resourcepackrepository;
++ this.resources = new MinecraftServer.ReloadableResources(worldstem.resourceManager(), worldstem.dataPackResources());
+ this.services = services;
+ if (services.profileCache() != null) {
+ services.profileCache().setExecutor(this);
+@@ -290,20 +342,44 @@
+
+ this.connection = new ServerConnectionListener(this);
+ this.tickRateManager = new ServerTickRateManager(this);
+- this.progressListenerFactory = progressListenerFactory;
+- this.storageSource = storageSource;
+- this.playerDataStorage = storageSource.createPlayerStorage();
+- this.fixerUpper = fixerUpper;
++ this.progressListenerFactory = worldloadlistenerfactory;
++ this.storageSource = convertable_conversionsession;
++ this.playerDataStorage = convertable_conversionsession.createPlayerStorage();
++ this.fixerUpper = datafixer;
+ this.functionManager = new ServerFunctionManager(this, this.resources.managers.getFunctionLibrary());
+- HolderGetter<Block> holderGetter = this.registries
+- .compositeAccess()
+- .registryOrThrow(Registries.BLOCK)
+- .asLookup()
+- .filterFeatures(this.worldData.enabledFeatures());
+- this.structureTemplateManager = new StructureTemplateManager(worldStem.resourceManager(), storageSource, fixerUpper, holderGetter);
+- this.serverThread = serverThread;
++ HolderGetter<Block> holdergetter = this.registries.compositeAccess().registryOrThrow(Registries.BLOCK).asLookup().filterFeatures(this.worldData.enabledFeatures());
++
++ this.structureTemplateManager = new StructureTemplateManager(worldstem.resourceManager(), convertable_conversionsession, datafixer, holdergetter);
++ this.serverThread = thread;
+ this.executor = Util.backgroundExecutor();
+ }
++ // CraftBukkit start
++ this.options = options;
++ this.worldLoader = worldLoader;
++ this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
++ // Try to see if we're actually running in a terminal, disable jline if not
++ if (System.console() == null && System.getProperty("jline.terminal") == null) {
++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
++ Main.useJline = false;
++ }
++
++ try {
++ reader = new ConsoleReader(System.in, System.out);
++ reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators
++ } catch (Throwable e) {
++ try {
++ // Try again with jline disabled for Windows users without C++ 2008 Redistributable
++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
++ System.setProperty("user.language", "en");
++ Main.useJline = false;
++ reader = new ConsoleReader(System.in, System.out);
++ reader.setExpandEvents(false);
++ } catch (IOException ex) {
++ LOGGER.warn((String) null, ex);
++ }
++ }
++ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
++ // CraftBukkit end
+ }
+
+ private void readScoreboard(DimensionDataStorage dataStorage) {
+@@ -312,207 +388,403 @@
+
+ protected abstract boolean initServer() throws IOException;
+
+- protected void loadLevel() {
++ protected void loadLevel(String s) { // CraftBukkit
+ if (!JvmProfiler.INSTANCE.isRunning()) {
++ ;
+ }
+
+ boolean flag = false;
+- ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onWorldLoadedStarted();
+- this.worldData.setModdedInfo(this.getServerModName(), this.getModdedStatus().shouldReportAsModified());
+- ChunkProgressListener chunkProgressListener = this.progressListenerFactory.create(11);
+- this.createLevels(chunkProgressListener);
+- this.forceDifficulty();
+- this.prepareLevels(chunkProgressListener);
+- if (profiledDuration != null) {
+- profiledDuration.finish();
++ ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted();
++
++ loadWorld0(s); // CraftBukkit
++
++ if (profiledduration != null) {
++ profiledduration.finish();
+ }
+
+ if (flag) {
+ try {
+ JvmProfiler.INSTANCE.stop();
+- } catch (Throwable var5) {
+- LOGGER.warn("Failed to stop JFR profiling", var5);
++ } catch (Throwable throwable) {
++ MinecraftServer.LOGGER.warn("Failed to stop JFR profiling", throwable);
+ }
+ }
+- }
+
+- protected void forceDifficulty() {
+ }
+
+- protected void createLevels(ChunkProgressListener listener) {
+- ServerLevelData serverLevelData = this.worldData.overworldData();
+- boolean isDebugWorld = this.worldData.isDebugWorld();
+- Registry<LevelStem> registry = this.registries.compositeAccess().registryOrThrow(Registries.LEVEL_STEM);
+- WorldOptions worldOptions = this.worldData.worldGenOptions();
+- long l = worldOptions.seed();
+- long l1 = BiomeManager.obfuscateSeed(l);
+- List<CustomSpawner> list = ImmutableList.of(
+- new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(serverLevelData)
+- );
+- LevelStem levelStem = registry.get(LevelStem.OVERWORLD);
+- ServerLevel serverLevel = new ServerLevel(
+- this, this.executor, this.storageSource, serverLevelData, Level.OVERWORLD, levelStem, listener, isDebugWorld, l1, list, true, null
+- );
+- this.levels.put(Level.OVERWORLD, serverLevel);
+- DimensionDataStorage dataStorage = serverLevel.getDataStorage();
+- this.readScoreboard(dataStorage);
+- this.commandStorage = new CommandStorage(dataStorage);
+- WorldBorder worldBorder = serverLevel.getWorldBorder();
+- if (!serverLevelData.isInitialized()) {
+- try {
+- setInitialSpawn(serverLevel, serverLevelData, worldOptions.generateBonusChest(), isDebugWorld);
+- serverLevelData.setInitialized(true);
+- if (isDebugWorld) {
+- this.setupDebugLevel(this.worldData);
++ protected void forceDifficulty() {}
++
++ // CraftBukkit start
++ private void loadWorld0(String s) {
++ LevelStorageSource.LevelStorageAccess worldSession = this.storageSource;
++
++ Registry<LevelStem> dimensions = this.registries.compositeAccess().registryOrThrow(Registries.LEVEL_STEM);
++ for (LevelStem worldDimension : dimensions) {
++ ResourceKey<LevelStem> dimensionKey = dimensions.getResourceKey(worldDimension).get();
++
++ ServerLevel world;
++ int dimension = 0;
++
++ if (dimensionKey == LevelStem.NETHER) {
++ if (isNetherEnabled()) {
++ dimension = -1;
++ } else {
++ continue;
+ }
+- } catch (Throwable var23) {
+- CrashReport crashReport = CrashReport.forThrowable(var23, "Exception initializing level");
++ } else if (dimensionKey == LevelStem.END) {
++ if (server.getAllowEnd()) {
++ dimension = 1;
++ } else {
++ continue;
++ }
++ } else if (dimensionKey != LevelStem.OVERWORLD) {
++ dimension = -999;
++ }
+
++ String worldType = (dimension == -999) ? dimensionKey.location().getNamespace() + "_" + dimensionKey.location().getPath() : org.bukkit.World.Environment.getEnvironment(dimension).toString().toLowerCase();
++ String name = (dimensionKey == LevelStem.OVERWORLD) ? s : s + "_" + worldType;
++ if (dimension != 0) {
++ File newWorld = LevelStorageSource.getStorageFolder(new File(name).toPath(), dimensionKey).toFile();
++ File oldWorld = LevelStorageSource.getStorageFolder(new File(s).toPath(), dimensionKey).toFile();
++ File oldLevelDat = new File(new File(s), "level.dat"); // The data folders exist on first run as they are created in the PersistentCollection constructor above, but the level.dat won't
++
++ if (!newWorld.isDirectory() && oldWorld.isDirectory() && oldLevelDat.isFile()) {
++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder required ----");
++ MinecraftServer.LOGGER.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly.");
++ MinecraftServer.LOGGER.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future.");
++ MinecraftServer.LOGGER.info("Attempting to move " + oldWorld + " to " + newWorld + "...");
++
++ if (newWorld.exists()) {
++ MinecraftServer.LOGGER.warn("A file or folder already exists at " + newWorld + "!");
++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
++ } else if (newWorld.getParentFile().mkdirs()) {
++ if (oldWorld.renameTo(newWorld)) {
++ MinecraftServer.LOGGER.info("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld);
++ // Migrate world data too.
++ try {
++ com.google.common.io.Files.copy(oldLevelDat, new File(new File(name), "level.dat"));
++ org.apache.commons.io.FileUtils.copyDirectory(new File(new File(s), "data"), new File(new File(name), "data"));
++ } catch (IOException exception) {
++ MinecraftServer.LOGGER.warn("Unable to migrate world data.");
++ }
++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder complete ----");
++ } else {
++ MinecraftServer.LOGGER.warn("Could not move folder " + oldWorld + " to " + newWorld + "!");
++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
++ }
++ } else {
++ MinecraftServer.LOGGER.warn("Could not create path for " + newWorld + "!");
++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----");
++ }
++ }
++
+ try {
+- serverLevel.fillReportDetails(crashReport);
+- } catch (Throwable var22) {
++ worldSession = LevelStorageSource.createDefault(server.getWorldContainer().toPath()).validateAndCreateAccess(name, dimensionKey);
++ } catch (IOException | ContentValidationException ex) {
++ throw new RuntimeException(ex);
+ }
++ }
+
+- throw new ReportedException(crashReport);
++ Dynamic<?> dynamic;
++ if (worldSession.hasWorldData()) {
++ LevelSummary worldinfo;
++
++ try {
++ dynamic = worldSession.getDataTag();
++ worldinfo = worldSession.getSummary(dynamic);
++ } catch (NbtException | ReportedNbtException | IOException ioexception) {
++ LevelStorageSource.LevelDirectory convertable_b = worldSession.getLevelDirectory();
++
++ MinecraftServer.LOGGER.warn("Failed to load world data from {}", convertable_b.dataFile(), ioexception);
++ MinecraftServer.LOGGER.info("Attempting to use fallback");
++
++ try {
++ dynamic = worldSession.getDataTagFallback();
++ worldinfo = worldSession.getSummary(dynamic);
++ } catch (NbtException | ReportedNbtException | IOException ioexception1) {
++ MinecraftServer.LOGGER.error("Failed to load world data from {}", convertable_b.oldDataFile(), ioexception1);
++ MinecraftServer.LOGGER.error("Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", convertable_b.dataFile(), convertable_b.oldDataFile());
++ return;
++ }
++
++ worldSession.restoreLevelDataFromOld();
++ }
++
++ if (worldinfo.requiresManualConversion()) {
++ MinecraftServer.LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted");
++ return;
++ }
++
++ if (!worldinfo.isCompatible()) {
++ MinecraftServer.LOGGER.info("This world was created by an incompatible version.");
++ return;
++ }
++ } else {
++ dynamic = null;
+ }
+
+- serverLevelData.setInitialized(true);
++ org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name);
++ org.bukkit.generator.BiomeProvider biomeProvider = this.server.getBiomeProvider(name);
++
++ PrimaryLevelData worlddata;
++ WorldLoader.a worldloader_a = this.worldLoader;
++ Registry<LevelStem> iregistry = worldloader_a.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
++ if (dynamic != null) {
++ LevelDataAndDimensions leveldataanddimensions = LevelStorageSource.getLevelDataAndDimensions(dynamic, worldloader_a.dataConfiguration(), iregistry, worldloader_a.datapackWorldgen());
++
++ worlddata = (PrimaryLevelData) leveldataanddimensions.worldData();
++ } else {
++ LevelSettings worldsettings;
++ WorldOptions worldoptions;
++ WorldDimensions worlddimensions;
++
++ if (this.isDemo()) {
++ worldsettings = MinecraftServer.DEMO_SETTINGS;
++ worldoptions = WorldOptions.DEMO_OPTIONS;
++ worlddimensions = WorldPresets.createNormalWorldDimensions(worldloader_a.datapackWorldgen());
++ } else {
++ DedicatedServerProperties dedicatedserverproperties = ((DedicatedServer) this).getProperties();
++
++ worldsettings = new LevelSettings(dedicatedserverproperties.levelName, dedicatedserverproperties.gamemode, dedicatedserverproperties.hardcore, dedicatedserverproperties.difficulty, false, new GameRules(), worldloader_a.dataConfiguration());
++ worldoptions = options.has("bonusChest") ? dedicatedserverproperties.worldOptions.withBonusChest(true) : dedicatedserverproperties.worldOptions;
++ worlddimensions = dedicatedserverproperties.createDimensions(worldloader_a.datapackWorldgen());
++ }
++
++ WorldDimensions.b worlddimensions_b = worlddimensions.bake(iregistry);
++ Lifecycle lifecycle = worlddimensions_b.lifecycle().add(worldloader_a.datapackWorldgen().allRegistriesLifecycle());
++
++ worlddata = new PrimaryLevelData(worldsettings, worldoptions, worlddimensions_b.specialWorldProperty(), lifecycle);
++ }
++ worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
++ if (options.has("forceUpgrade")) {
++ net.minecraft.server.Main.forceUpgrade(worldSession, DataFixers.getDataFixer(), options.has("eraseCache"), () -> {
++ return true;
++ }, dimensions);
++ }
++
++ PrimaryLevelData iworlddataserver = worlddata;
++ boolean flag = worlddata.isDebugWorld();
++ WorldOptions worldoptions = worlddata.worldGenOptions();
++ long i = worldoptions.seed();
++ long j = BiomeManager.obfuscateSeed(i);
++ List<CustomSpawner> list = ImmutableList.of(new MobSpawnerPhantom(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(iworlddataserver));
++ LevelStem worlddimension = (LevelStem) dimensions.get(dimensionKey);
++
++ org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), worlddimension.type().value());
++ if (biomeProvider == null && gen != null) {
++ biomeProvider = gen.getDefaultBiomeProvider(worldInfo);
++ }
++
++ ResourceKey<Level> worldKey = ResourceKey.create(Registries.DIMENSION, dimensionKey.location());
++
++ if (dimensionKey == LevelStem.OVERWORLD) {
++ this.worldData = worlddata;
++ this.worldData.setGameType(((DedicatedServer) this).getProperties().gamemode); // From DedicatedServer.init
++
++ ChunkProgressListener worldloadlistener = this.progressListenerFactory.create(11);
++
++ world = new ServerLevel(this, this.executor, worldSession, iworlddataserver, worldKey, worlddimension, worldloadlistener, flag, j, list, true, (RandomSequences) null, org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider);
++ DimensionDataStorage worldpersistentdata = world.getDataStorage();
++ this.readScoreboard(worldpersistentdata);
++ this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard());
++ this.commandStorage = new CommandStorage(worldpersistentdata);
++ } else {
++ ChunkProgressListener worldloadlistener = this.progressListenerFactory.create(11);
++ world = new ServerLevel(this, this.executor, worldSession, iworlddataserver, worldKey, worlddimension, worldloadlistener, flag, j, ImmutableList.of(), true, this.overworld().getRandomSequences(), org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider);
++ }
++
++ worlddata.setModdedInfo(this.getServerModName(), this.getModdedStatus().shouldReportAsModified());
++ this.initWorld(world, worlddata, worldData, worldoptions);
++
++ this.addLevel(world);
++ this.getPlayerList().addWorldborderListener(world);
++
++ if (worlddata.getCustomBossEvents() != null) {
++ this.getCustomBossEvents().load(worlddata.getCustomBossEvents());
++ }
+ }
++ this.forceDifficulty();
++ for (ServerLevel worldserver : this.getAllLevels()) {
++ this.prepareLevels(worldserver.getChunkSource().chunkMap.progressListener, worldserver);
++ worldserver.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API
++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(worldserver.getWorld()));
++ }
+
+- this.getPlayerList().addWorldborderListener(serverLevel);
+- if (this.worldData.getCustomBossEvents() != null) {
+- this.getCustomBossEvents().load(this.worldData.getCustomBossEvents());
++ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
++ this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
++ this.connection.acceptConnections();
++ }
++
++ public void initWorld(ServerLevel worldserver, ServerLevelData iworlddataserver, WorldData saveData, WorldOptions worldoptions) {
++ boolean flag = saveData.isDebugWorld();
++ // CraftBukkit start
++ if (worldserver.generator != null) {
++ worldserver.getWorld().getPopulators().addAll(worldserver.generator.getDefaultPopulators(worldserver.getWorld()));
+ }
++ WorldBorder worldborder = worldserver.getWorldBorder();
++ worldborder.applySettings(iworlddataserver.getWorldBorder()); // CraftBukkit - move up so that WorldBorder is set during WorldInitEvent
++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(worldserver.getWorld())); // CraftBukkit - SPIGOT-5569: Call WorldInitEvent before any chunks are generated
+
+- RandomSequences randomSequences = serverLevel.getRandomSequences();
++ if (!iworlddataserver.isInitialized()) {
++ try {
++ setInitialSpawn(worldserver, iworlddataserver, worldoptions.generateBonusChest(), flag);
++ iworlddataserver.setInitialized(true);
++ if (flag) {
++ this.setupDebugLevel(this.worldData);
++ }
++ } catch (Throwable throwable) {
++ CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception initializing level");
+
+- for (Entry<ResourceKey<LevelStem>, LevelStem> entry : registry.entrySet()) {
+- ResourceKey<LevelStem> resourceKey = entry.getKey();
+- if (resourceKey != LevelStem.OVERWORLD) {
+- ResourceKey<Level> resourceKey1 = ResourceKey.create(Registries.DIMENSION, resourceKey.location());
+- DerivedLevelData derivedLevelData = new DerivedLevelData(this.worldData, serverLevelData);
+- ServerLevel serverLevel1 = new ServerLevel(
+- this,
+- this.executor,
+- this.storageSource,
+- derivedLevelData,
+- resourceKey1,
+- entry.getValue(),
+- listener,
+- isDebugWorld,
+- l1,
+- ImmutableList.of(),
+- false,
+- randomSequences
+- );
+- worldBorder.addListener(new BorderChangeListener.DelegateBorderChangeListener(serverLevel1.getWorldBorder()));
+- this.levels.put(resourceKey1, serverLevel1);
++ try {
++ worldserver.fillReportDetails(crashreport);
++ } catch (Throwable throwable1) {
++ ;
++ }
++
++ throw new ReportedException(crashreport);
+ }
++
++ iworlddataserver.setInitialized(true);
+ }
+
+- worldBorder.applySettings(serverLevelData.getWorldBorder());
+ }
++ // CraftBukkit end
+
+ private static void setInitialSpawn(ServerLevel level, ServerLevelData levelData, boolean generateBonusChest, boolean debug) {
+ if (debug) {
+ levelData.setSpawn(BlockPos.ZERO.above(80), 0.0F);
+ } else {
+- ServerChunkCache chunkSource = level.getChunkSource();
+- ChunkPos chunkPos = new ChunkPos(chunkSource.randomState().sampler().findSpawnPosition());
+- int spawnHeight = chunkSource.getGenerator().getSpawnHeight(level);
+- if (spawnHeight < level.getMinBuildHeight()) {
+- BlockPos worldPosition = chunkPos.getWorldPosition();
+- spawnHeight = level.getHeight(Heightmap.Types.WORLD_SURFACE, worldPosition.getX() + 8, worldPosition.getZ() + 8);
++ ServerChunkCache chunkproviderserver = level.getChunkSource();
++ ChunkPos chunkcoordintpair = new ChunkPos(chunkproviderserver.randomState().sampler().findSpawnPosition());
++ // CraftBukkit start
++ if (level.generator != null) {
++ Random rand = new Random(level.getSeed());
++ org.bukkit.Location spawn = level.generator.getFixedSpawnLocation(level.getWorld(), rand);
++
++ if (spawn != null) {
++ if (spawn.getWorld() != level.getWorld()) {
++ throw new IllegalStateException("Cannot set spawn point for " + levelData.getLevelName() + " to be in another world (" + spawn.getWorld().getName() + ")");
++ } else {
++ levelData.setSpawn(new BlockPos(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()), spawn.getYaw());
++ return;
++ }
++ }
+ }
++ // CraftBukkit end
++ int i = chunkproviderserver.getGenerator().getSpawnHeight(level);
+
+- levelData.setSpawn(chunkPos.getWorldPosition().offset(8, spawnHeight, 8), 0.0F);
+- int i = 0;
+- int i1 = 0;
+- int i2 = 0;
+- int i3 = -1;
+- int i4 = 5;
++ if (i < level.getMinBuildHeight()) {
++ BlockPos blockposition = chunkcoordintpair.getWorldPosition();
+
+- for (int i5 = 0; i5 < Mth.square(11); i5++) {
+- if (i >= -5 && i <= 5 && i1 >= -5 && i1 <= 5) {
+- BlockPos spawnPosInChunk = PlayerRespawnLogic.getSpawnPosInChunk(level, new ChunkPos(chunkPos.x + i, chunkPos.z + i1));
+- if (spawnPosInChunk != null) {
+- levelData.setSpawn(spawnPosInChunk, 0.0F);
++ i = level.getHeight(Heightmap.Types.WORLD_SURFACE, blockposition.getX() + 8, blockposition.getZ() + 8);
++ }
++
++ levelData.setSpawn(chunkcoordintpair.getWorldPosition().offset(8, i, 8), 0.0F);
++ int j = 0;
++ int k = 0;
++ int l = 0;
++ int i1 = -1;
++ boolean flag2 = true;
++
++ for (int j1 = 0; j1 < Mth.square(11); ++j1) {
++ if (j >= -5 && j <= 5 && k >= -5 && k <= 5) {
++ BlockPos blockposition1 = PlayerRespawnLogic.getSpawnPosInChunk(level, new ChunkPos(chunkcoordintpair.x + j, chunkcoordintpair.z + k));
++
++ if (blockposition1 != null) {
++ levelData.setSpawn(blockposition1, 0.0F);
+ break;
+ }
+ }
+
+- if (i == i1 || i < 0 && i == -i1 || i > 0 && i == 1 - i1) {
+- int i6 = i2;
+- i2 = -i3;
+- i3 = i6;
++ if (j == k || j < 0 && j == -k || j > 0 && j == 1 - k) {
++ int k1 = l;
++
++ l = -i1;
++ i1 = k1;
+ }
+
+- i += i2;
+- i1 += i3;
++ j += l;
++ k += i1;
+ }
+
+ if (generateBonusChest) {
+- level.registryAccess()
+- .registry(Registries.CONFIGURED_FEATURE)
+- .flatMap(registry -> registry.getHolder(MiscOverworldFeatures.BONUS_CHEST))
+- .ifPresent(
+- reference -> reference.value()
+- .place(
+- level,
+- chunkSource.getGenerator(),
+- level.random,
+- new BlockPos(levelData.getXSpawn(), levelData.getYSpawn(), levelData.getZSpawn())
+- )
+- );
++ level.registryAccess().registry(Registries.CONFIGURED_FEATURE).flatMap((iregistry) -> {
++ return iregistry.getHolder(MiscOverworldFeatures.BONUS_CHEST);
++ }).ifPresent((holder_c) -> {
++ ((ConfiguredFeature) holder_c.value()).place(level, chunkproviderserver.getGenerator(), level.random, new BlockPos(levelData.getXSpawn(), levelData.getYSpawn(), levelData.getZSpawn()));
++ });
+ }
++
+ }
+ }
+
+ private void setupDebugLevel(WorldData worldData) {
+ worldData.setDifficulty(Difficulty.PEACEFUL);
+ worldData.setDifficultyLocked(true);
+- ServerLevelData serverLevelData = worldData.overworldData();
+- serverLevelData.setRaining(false);
+- serverLevelData.setThundering(false);
+- serverLevelData.setClearWeatherTime(1000000000);
+- serverLevelData.setDayTime(6000L);
+- serverLevelData.setGameType(GameType.SPECTATOR);
++ ServerLevelData iworlddataserver = worldData.overworldData();
++
++ iworlddataserver.setRaining(false);
++ iworlddataserver.setThundering(false);
++ iworlddataserver.setClearWeatherTime(1000000000);
++ iworlddataserver.setDayTime(6000L);
++ iworlddataserver.setGameType(GameType.SPECTATOR);
+ }
+
+- private void prepareLevels(ChunkProgressListener listener) {
+- ServerLevel serverLevel = this.overworld();
+- LOGGER.info("Preparing start region for dimension {}", serverLevel.dimension().location());
+- BlockPos sharedSpawnPos = serverLevel.getSharedSpawnPos();
+- listener.updateSpawnPos(new ChunkPos(sharedSpawnPos));
+- ServerChunkCache chunkSource = serverLevel.getChunkSource();
++ // CraftBukkit start
++ public void prepareLevels(ChunkProgressListener worldloadlistener, ServerLevel worldserver) {
++ // WorldServer worldserver = this.overworld();
++ this.forceTicks = true;
++ // CraftBukkit end
++
++ MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location());
++ BlockPos blockposition = worldserver.getSharedSpawnPos();
++
++ worldloadlistener.updateSpawnPos(new ChunkPos(blockposition));
++ ServerChunkCache chunkproviderserver = worldserver.getChunkSource();
++
+ this.nextTickTimeNanos = Util.getNanos();
+- chunkSource.addRegionTicket(TicketType.START, new ChunkPos(sharedSpawnPos), 11, Unit.INSTANCE);
++ // CraftBukkit start
++ if (worldserver.getWorld().getKeepSpawnInMemory()) {
++ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(blockposition), 11, Unit.INSTANCE);
+
+- while (chunkSource.getTickingGenerated() != 441) {
+- this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
+- this.waitUntilNextTick();
++ while (chunkproviderserver.getTickingGenerated() != 441) {
++ // this.nextTickTimeNanos = SystemUtils.getNanos() + MinecraftServer.PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
++ this.executeModerately();
++ }
+ }
+
+- this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
+- this.waitUntilNextTick();
++ // this.nextTickTimeNanos = SystemUtils.getNanos() + MinecraftServer.PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
++ this.executeModerately();
++ // Iterator iterator = this.levels.values().iterator();
+
+- for (ServerLevel serverLevel1 : this.levels.values()) {
+- ForcedChunksSavedData forcedChunksSavedData = serverLevel1.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
+- if (forcedChunksSavedData != null) {
+- LongIterator longIterator = forcedChunksSavedData.getChunks().iterator();
++ if (true) {
++ ServerLevel worldserver1 = worldserver;
++ // CraftBukkit end
++ ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) worldserver1.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
+
+- while (longIterator.hasNext()) {
+- long l = longIterator.nextLong();
+- ChunkPos chunkPos = new ChunkPos(l);
+- serverLevel1.getChunkSource().updateChunkForced(chunkPos, true);
++ if (forcedchunk != null) {
++ LongIterator longiterator = forcedchunk.getChunks().iterator();
++
++ while (longiterator.hasNext()) {
++ long i = longiterator.nextLong();
++ ChunkPos chunkcoordintpair = new ChunkPos(i);
++
++ worldserver1.getChunkSource().updateChunkForced(chunkcoordintpair, true);
+ }
+ }
+ }
+
+- this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
+- this.waitUntilNextTick();
+- listener.stop();
+- this.updateMobSpawningFlags();
++ // CraftBukkit start
++ // this.nextTickTimeNanos = SystemUtils.getNanos() + MinecraftServer.PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
++ this.executeModerately();
++ // CraftBukkit end
++ worldloadlistener.stop();
++ // CraftBukkit start
++ // this.updateMobSpawningFlags();
++ worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
++
++ this.forceTicks = false;
++ // CraftBukkit end
+ }
+
+ public GameType getDefaultGameType() {
+@@ -530,44 +802,55 @@
+ public abstract boolean shouldRconBroadcast();
+
+ public boolean saveAllChunks(boolean suppressLog, boolean flush, boolean forced) {
+- boolean flag = false;
++ boolean flag3 = false;
+
+- for (ServerLevel serverLevel : this.getAllLevels()) {
++ for (Iterator iterator = this.getAllLevels().iterator(); iterator.hasNext(); flag3 = true) {
++ ServerLevel worldserver = (ServerLevel) iterator.next();
++
+ if (!suppressLog) {
+- LOGGER.info("Saving chunks for level '{}'/{}", serverLevel, serverLevel.dimension().location());
++ MinecraftServer.LOGGER.info("Saving chunks for level '{}'/{}", worldserver, worldserver.dimension().location());
+ }
+
+- serverLevel.save(null, flush, serverLevel.noSave && !forced);
+- flag = true;
++ worldserver.save((ProgressListener) null, flush, worldserver.noSave && !forced);
+ }
+
+- ServerLevel serverLevel1 = this.overworld();
+- ServerLevelData serverLevelData = this.worldData.overworldData();
+- serverLevelData.setWorldBorder(serverLevel1.getWorldBorder().createSettings());
++ // CraftBukkit start - moved to WorldServer.save
++ /*
++ WorldServer worldserver1 = this.overworld();
++ IWorldDataServer iworlddataserver = this.worldData.overworldData();
++
++ iworlddataserver.setWorldBorder(worldserver1.getWorldBorder().createSettings());
+ this.worldData.setCustomBossEvents(this.getCustomBossEvents().save());
+ this.storageSource.saveDataTag(this.registryAccess(), this.worldData, this.getPlayerList().getSingleplayerData());
++ */
++ // CraftBukkit end
+ if (flush) {
+- for (ServerLevel serverLevel2 : this.getAllLevels()) {
+- LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", serverLevel2.getChunkSource().chunkMap.getStorageName());
++ Iterator iterator1 = this.getAllLevels().iterator();
++
++ while (iterator1.hasNext()) {
++ ServerLevel worldserver2 = (ServerLevel) iterator1.next();
++
++ MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName());
+ }
+
+- LOGGER.info("ThreadedAnvilChunkStorage: All dimensions are saved");
++ MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage: All dimensions are saved");
+ }
+
+- return flag;
++ return flag3;
+ }
+
+ public boolean saveEverything(boolean suppressLog, boolean flush, boolean forced) {
+- boolean var4;
++ boolean flag3;
++
+ try {
+ this.isSaving = true;
+ this.getPlayerList().saveAll();
+- var4 = this.saveAllChunks(suppressLog, flush, forced);
++ flag3 = this.saveAllChunks(suppressLog, flush, forced);
+ } finally {
+ this.isSaving = false;
+ }
+
+- return var4;
++ return flag3;
+ }
+
+ @Override
+@@ -575,47 +858,81 @@
+ this.stopServer();
+ }
+
++ // CraftBukkit start
++ private boolean hasStopped = false;
++ private final Object stopLock = new Object();
++ public final boolean hasStopped() {
++ synchronized (stopLock) {
++ return hasStopped;
++ }
++ }
++ // CraftBukkit end
++
+ public void stopServer() {
++ // CraftBukkit start - prevent double stopping on multiple threads
++ synchronized(stopLock) {
++ if (hasStopped) return;
++ hasStopped = true;
++ }
++ // CraftBukkit end
+ if (this.metricsRecorder.isRecording()) {
+ this.cancelRecordingMetrics();
+ }
+
+- LOGGER.info("Stopping server");
++ MinecraftServer.LOGGER.info("Stopping server");
++ // CraftBukkit start
++ if (this.server != null) {
++ this.server.disablePlugins();
++ }
++ // CraftBukkit end
+ this.getConnection().stop();
+ this.isSaving = true;
+ if (this.playerList != null) {
+- LOGGER.info("Saving players");
++ MinecraftServer.LOGGER.info("Saving players");
+ this.playerList.saveAll();
+ this.playerList.removeAll();
++ try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
+ }
+
+- LOGGER.info("Saving worlds");
++ MinecraftServer.LOGGER.info("Saving worlds");
++ Iterator iterator = this.getAllLevels().iterator();
+
+- for (ServerLevel serverLevel : this.getAllLevels()) {
+- if (serverLevel != null) {
+- serverLevel.noSave = false;
++ ServerLevel worldserver;
++
++ while (iterator.hasNext()) {
++ worldserver = (ServerLevel) iterator.next();
++ if (worldserver != null) {
++ worldserver.noSave = false;
+ }
+ }
+
+- while (this.levels.values().stream().anyMatch(serverLevel1 -> serverLevel1.getChunkSource().chunkMap.hasWork())) {
++ while (this.levels.values().stream().anyMatch((worldserver1) -> {
++ return worldserver1.getChunkSource().chunkMap.hasWork();
++ })) {
+ this.nextTickTimeNanos = Util.getNanos() + TimeUtil.NANOSECONDS_PER_MILLISECOND;
++ iterator = this.getAllLevels().iterator();
+
+- for (ServerLevel serverLevelx : this.getAllLevels()) {
+- serverLevelx.getChunkSource().removeTicketsOnClosing();
+- serverLevelx.getChunkSource().tick(() -> true, false);
++ while (iterator.hasNext()) {
++ worldserver = (ServerLevel) iterator.next();
++ worldserver.getChunkSource().removeTicketsOnClosing();
++ worldserver.getChunkSource().tick(() -> {
++ return true;
++ }, false);
+ }
+
+ this.waitUntilNextTick();
+ }
+
+ this.saveAllChunks(false, true, false);
++ iterator = this.getAllLevels().iterator();
+
+- for (ServerLevel serverLevelx : this.getAllLevels()) {
+- if (serverLevelx != null) {
++ while (iterator.hasNext()) {
++ worldserver = (ServerLevel) iterator.next();
++ if (worldserver != null) {
+ try {
+- serverLevelx.close();
+- } catch (IOException var5) {
+- LOGGER.error("Exception closing the level", (Throwable)var5);
++ worldserver.close();
++ } catch (IOException ioexception) {
++ MinecraftServer.LOGGER.error("Exception closing the level", ioexception);
+ }
+ }
+ }
+@@ -625,9 +942,10 @@
+
+ try {
+ this.storageSource.close();
+- } catch (IOException var4) {
+- LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), var4);
++ } catch (IOException ioexception1) {
++ MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1);
+ }
++
+ }
+
+ public String getLocalIp() {
+@@ -647,10 +965,11 @@
+ if (waitForServer) {
+ try {
+ this.serverThread.join();
+- } catch (InterruptedException var3) {
+- LOGGER.error("Error while shutting down", (Throwable)var3);
++ } catch (InterruptedException interruptedexception) {
++ MinecraftServer.LOGGER.error("Error while shutting down", interruptedexception);
+ }
+ }
++
+ }
+
+ protected void runServer() {
+@@ -660,40 +979,47 @@
+ }
+
+ this.nextTickTimeNanos = Util.getNanos();
+- this.statusIcon = this.loadStatusIcon().orElse(null);
++ this.statusIcon = (ServerStatus.a) this.loadStatusIcon().orElse(null); // CraftBukkit - decompile error
+ this.status = this.buildServerStatus();
+
+ while (this.running) {
+- long l;
++ long i;
++
+ if (!this.isPaused() && this.tickRateManager.isSprinting() && this.tickRateManager.checkShouldSprintThisTick()) {
+- l = 0L;
++ i = 0L;
+ this.nextTickTimeNanos = Util.getNanos();
+ this.lastOverloadWarningNanos = this.nextTickTimeNanos;
+ } else {
+- l = this.tickRateManager.nanosecondsPerTick();
+- long l1 = Util.getNanos() - this.nextTickTimeNanos;
+- if (l1 > OVERLOADED_THRESHOLD_NANOS + 20L * l
+- && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= OVERLOADED_WARNING_INTERVAL_NANOS + 100L * l) {
+- long l2 = l1 / l;
+- LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", l1 / TimeUtil.NANOSECONDS_PER_MILLISECOND, l2);
+- this.nextTickTimeNanos += l2 * l;
++ i = this.tickRateManager.nanosecondsPerTick();
++ long j = Util.getNanos() - this.nextTickTimeNanos;
++
++ if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
++ long k = j / i;
++
++ if (server.getWarnOnOverload()) // CraftBukkit
++ MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", j / TimeUtil.NANOSECONDS_PER_MILLISECOND, k);
++ this.nextTickTimeNanos += k * i;
+ this.lastOverloadWarningNanos = this.nextTickTimeNanos;
+ }
+ }
+
+- boolean flag = l == 0L;
++ boolean flag = i == 0L;
++
+ if (this.debugCommandProfilerDelayStart) {
+ this.debugCommandProfilerDelayStart = false;
+ this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
+ }
+
+- this.nextTickTimeNanos += l;
++ MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit
++ this.nextTickTimeNanos += i;
+ this.startMetricsRecordingTick();
+ this.profiler.push("tick");
+- this.tickServer(flag ? () -> false : this::haveTime);
++ this.tickServer(flag ? () -> {
++ return false;
++ } : this::haveTime);
+ this.profiler.popPush("nextTickWait");
+ this.mayHaveDelayedTasks = true;
+- this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + l, this.nextTickTimeNanos);
++ this.delayedTasksMaxNextTickTimeNanos = Math.max(Util.getNanos() + i, this.nextTickTimeNanos);
+ this.waitUntilNextTick();
+ if (flag) {
+ this.tickRateManager.endTickWork();
+@@ -704,78 +1030,100 @@
+ this.isReady = true;
+ JvmProfiler.INSTANCE.onServerTick(this.smoothedTickTimeMillis);
+ }
+- } catch (Throwable var46) {
+- LOGGER.error("Encountered an unexpected exception", var46);
+- CrashReport crashReport = constructOrExtractCrashReport(var46);
+- this.fillSystemReport(crashReport.getSystemReport());
++ } catch (Throwable throwable) {
++ MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
++ CrashReport crashreport = constructOrExtractCrashReport(throwable);
++
++ this.fillSystemReport(crashreport.getSystemReport());
+ File file = new File(new File(this.getServerDirectory(), "crash-reports"), "crash-" + Util.getFilenameFormattedDateTime() + "-server.txt");
+- if (crashReport.saveToFile(file)) {
+- LOGGER.error("This crash report has been saved to: {}", file.getAbsolutePath());
++
++ if (crashreport.saveToFile(file)) {
++ MinecraftServer.LOGGER.error("This crash report has been saved to: {}", file.getAbsolutePath());
+ } else {
+- LOGGER.error("We were unable to save this crash report to disk.");
++ MinecraftServer.LOGGER.error("We were unable to save this crash report to disk.");
+ }
+
+- this.onServerCrash(crashReport);
++ this.onServerCrash(crashreport);
+ } finally {
+ try {
+ this.stopped = true;
+ this.stopServer();
+- } catch (Throwable var44) {
+- LOGGER.error("Exception stopping the server", var44);
++ } catch (Throwable throwable1) {
++ MinecraftServer.LOGGER.error("Exception stopping the server", throwable1);
+ } finally {
+ if (this.services.profileCache() != null) {
+ this.services.profileCache().clearExecutor();
+ }
+
++ // CraftBukkit start - Restore terminal to original settings
++ try {
++ reader.getTerminal().restore();
++ } catch (Exception ignored) {
++ }
++ // CraftBukkit end
+ this.onServerExit();
+ }
++
+ }
++
+ }
+
+ private static CrashReport constructOrExtractCrashReport(Throwable cause) {
+- ReportedException reportedException = null;
++ ReportedException reportedexception = null;
+
+- for (Throwable throwable = cause; throwable != null; throwable = throwable.getCause()) {
+- if (throwable instanceof ReportedException reportedException1) {
+- reportedException = reportedException1;
++ for (Throwable throwable1 = cause; throwable1 != null; throwable1 = throwable1.getCause()) {
++ if (throwable1 instanceof ReportedException) {
++ ReportedException reportedexception1 = (ReportedException) throwable1;
++
++ reportedexception = reportedexception1;
+ }
+ }
+
+- CrashReport report;
+- if (reportedException != null) {
+- report = reportedException.getReport();
+- if (reportedException != cause) {
+- report.addCategory("Wrapped in").setDetailError("Wrapping exception", cause);
++ CrashReport crashreport;
++
++ if (reportedexception != null) {
++ crashreport = reportedexception.getReport();
++ if (reportedexception != cause) {
++ crashreport.addCategory("Wrapped in").setDetailError("Wrapping exception", cause);
+ }
+ } else {
+- report = new CrashReport("Exception in server tick loop", cause);
++ crashreport = new CrashReport("Exception in server tick loop", cause);
+ }
+
+- return report;
++ return crashreport;
+ }
+
+ private boolean haveTime() {
+- return this.runningTask() || Util.getNanos() < (this.mayHaveDelayedTasks ? this.delayedTasksMaxNextTickTimeNanos : this.nextTickTimeNanos);
++ // CraftBukkit start
++ return this.forceTicks || this.runningTask() || Util.getNanos() < (this.mayHaveDelayedTasks ? this.delayedTasksMaxNextTickTimeNanos : this.nextTickTimeNanos);
+ }
+
++ private void executeModerately() {
++ this.runAllTasks();
++ java.util.concurrent.locks.LockSupport.parkNanos("executing tasks", 1000L);
++ // CraftBukkit end
++ }
++
+ protected void waitUntilNextTick() {
+ this.runAllTasks();
+- this.managedBlock(() -> !this.haveTime());
++ this.managedBlock(() -> {
++ return !this.haveTime();
++ });
+ }
+
+ @Override
+- protected TickTask wrapRunnable(Runnable runnable) {
++ public TickTask wrapRunnable(Runnable runnable) {
+ return new TickTask(this.tickCount, runnable);
+ }
+
+- @Override
+- protected boolean shouldRun(TickTask runnable) {
+- return runnable.getTick() + 3 < this.tickCount || this.haveTime();
++ protected boolean shouldRun(TickTask ticktask) {
++ return ticktask.getTick() + 3 < this.tickCount || this.haveTime();
+ }
+
+ @Override
+ public boolean pollTask() {
+ boolean flag = this.pollTaskInternal();
++
+ this.mayHaveDelayedTasks = flag;
+ return flag;
+ }
+@@ -785,8 +1133,12 @@
+ return true;
+ } else {
+ if (this.tickRateManager.isSprinting() || this.haveTime()) {
+- for (ServerLevel serverLevel : this.getAllLevels()) {
+- if (serverLevel.getChunkSource().pollTask()) {
++ Iterator iterator = this.getAllLevels().iterator();
++
++ while (iterator.hasNext()) {
++ ServerLevel worldserver = (ServerLevel) iterator.next();
++
++ if (worldserver.getChunkSource().pollTask()) {
+ return true;
+ }
+ }
+@@ -796,26 +1148,32 @@
+ }
+ }
+
+- @Override
+- public void doRunTask(TickTask task) {
++ public void doRunTask(TickTask ticktask) { // CraftBukkit - decompile error
+ this.getProfiler().incrementCounter("runTask");
+- super.doRunTask(task);
++ super.doRunTask(ticktask);
+ }
+
+- private Optional<ServerStatus.Favicon> loadStatusIcon() {
+- Optional<Path> optional = Optional.of(this.getFile("server-icon.png").toPath())
+- .filter(path -> Files.isRegularFile(path))
+- .or(() -> this.storageSource.getIconFile().filter(path -> Files.isRegularFile(path)));
+- return optional.flatMap(path -> {
++ private Optional<ServerStatus.a> loadStatusIcon() {
++ Optional<Path> optional = Optional.of(this.getFile("server-icon.png").toPath()).filter((path) -> {
++ return Files.isRegularFile(path, new LinkOption[0]);
++ }).or(() -> {
++ return this.storageSource.getIconFile().filter((path) -> {
++ return Files.isRegularFile(path, new LinkOption[0]);
++ });
++ });
++
++ return optional.flatMap((path) -> {
+ try {
+- BufferedImage bufferedImage = ImageIO.read(path.toFile());
+- Preconditions.checkState(bufferedImage.getWidth() == 64, "Must be 64 pixels wide");
+- Preconditions.checkState(bufferedImage.getHeight() == 64, "Must be 64 pixels high");
+- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+- ImageIO.write(bufferedImage, "PNG", byteArrayOutputStream);
+- return Optional.of(new ServerStatus.Favicon(byteArrayOutputStream.toByteArray()));
+- } catch (Exception var3) {
+- LOGGER.error("Couldn't load server icon", (Throwable)var3);
++ BufferedImage bufferedimage = ImageIO.read(path.toFile());
++
++ Preconditions.checkState(bufferedimage.getWidth() == 64, "Must be 64 pixels wide");
++ Preconditions.checkState(bufferedimage.getHeight() == 64, "Must be 64 pixels high");
++ ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
++
++ ImageIO.write(bufferedimage, "PNG", bytearrayoutputstream);
++ return Optional.of(new ServerStatus.a(bytearrayoutputstream.toByteArray()));
++ } catch (Exception exception) {
++ MinecraftServer.LOGGER.error("Couldn't load server icon", exception);
+ return Optional.empty();
+ }
+ });
+@@ -829,124 +1187,153 @@
+ return new File(".");
+ }
+
+- public void onServerCrash(CrashReport report) {
+- }
++ public void onServerCrash(CrashReport report) {}
+
+- public void onServerExit() {
+- }
++ public void onServerExit() {}
+
+ public boolean isPaused() {
+ return false;
+ }
+
+ public void tickServer(BooleanSupplier hasTimeLeft) {
+- long nanos = Util.getNanos();
+- this.tickCount++;
++ long i = Util.getNanos();
++
++ ++this.tickCount;
+ this.tickRateManager.tick();
+ this.tickChildren(hasTimeLeft);
+- if (nanos - this.lastServerStatus >= STATUS_EXPIRE_TIME_NANOS) {
+- this.lastServerStatus = nanos;
++ if (i - this.lastServerStatus >= MinecraftServer.STATUS_EXPIRE_TIME_NANOS) {
++ this.lastServerStatus = i;
+ this.status = this.buildServerStatus();
+ }
+
+- this.ticksUntilAutosave--;
+- if (this.ticksUntilAutosave <= 0) {
+- this.ticksUntilAutosave = this.computeNextAutosaveInterval();
+- LOGGER.debug("Autosave started");
++ --this.ticksUntilAutosave;
++ // CraftBukkit start
++ if (this.autosavePeriod > 0 && this.ticksUntilAutosave <= 0) {
++ this.ticksUntilAutosave = this.autosavePeriod;
++ // CraftBukkit end
++ MinecraftServer.LOGGER.debug("Autosave started");
+ this.profiler.push("save");
+ this.saveEverything(true, false, false);
+ this.profiler.pop();
+- LOGGER.debug("Autosave finished");
++ MinecraftServer.LOGGER.debug("Autosave finished");
+ }
+
+ this.profiler.push("tallying");
+- long l = Util.getNanos() - nanos;
+- int i = this.tickCount % 100;
+- this.aggregatedTickTimesNanos = this.aggregatedTickTimesNanos - this.tickTimesNanos[i];
+- this.aggregatedTickTimesNanos += l;
+- this.tickTimesNanos[i] = l;
+- this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float)l / (float)TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
+- long nanos1 = Util.getNanos();
+- this.logTickTime(nanos1 - nanos);
++ long j = Util.getNanos() - i;
++ int k = this.tickCount % 100;
++
++ this.aggregatedTickTimesNanos -= this.tickTimesNanos[k];
++ this.aggregatedTickTimesNanos += j;
++ this.tickTimesNanos[k] = j;
++ this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float) j / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
++ long l = Util.getNanos();
++
++ this.logTickTime(l - i);
+ this.profiler.pop();
+ }
+
+ private int computeNextAutosaveInterval() {
+ float f;
++
+ if (this.tickRateManager.isSprinting()) {
+- long l = this.getAverageTickTimeNanos() + 1L;
+- f = (float)TimeUtil.NANOSECONDS_PER_SECOND / (float)l;
++ long i = this.getAverageTickTimeNanos() + 1L;
++
++ f = (float) TimeUtil.NANOSECONDS_PER_SECOND / (float) i;
+ } else {
+ f = this.tickRateManager.tickrate();
+ }
+
+- int i = 300;
+- return Math.max(100, (int)(f * 300.0F));
++ boolean flag = true;
++
++ return Math.max(100, (int) (f * 300.0F));
+ }
+
+ public void onTickRateChanged() {
+ int i = this.computeNextAutosaveInterval();
++
+ if (i < this.ticksUntilAutosave) {
+ this.ticksUntilAutosave = i;
+ }
+- }
+
+- protected void logTickTime(long l) {
+ }
+
++ protected void logTickTime(long i) {}
++
+ private ServerStatus buildServerStatus() {
+- ServerStatus.Players players = this.buildPlayerStatus();
+- return new ServerStatus(
+- Component.nullToEmpty(this.motd),
+- Optional.of(players),
+- Optional.of(ServerStatus.Version.current()),
+- Optional.ofNullable(this.statusIcon),
+- this.enforceSecureProfile()
+- );
++ ServerStatus.ServerPingPlayerSample serverping_serverpingplayersample = this.buildPlayerStatus();
++
++ return new ServerStatus(Component.nullToEmpty(this.motd), Optional.of(serverping_serverpingplayersample), Optional.of(ServerStatus.Version.current()), Optional.ofNullable(this.statusIcon), this.enforceSecureProfile());
+ }
+
+- private ServerStatus.Players buildPlayerStatus() {
+- List<ServerPlayer> players = this.playerList.getPlayers();
+- int maxPlayers = this.getMaxPlayers();
++ private ServerStatus.ServerPingPlayerSample buildPlayerStatus() {
++ List<ServerPlayer> list = this.playerList.getPlayers();
++ int i = this.getMaxPlayers();
++
+ if (this.hidesOnlinePlayers()) {
+- return new ServerStatus.Players(maxPlayers, players.size(), List.of());
++ return new ServerStatus.ServerPingPlayerSample(i, list.size(), List.of());
+ } else {
+- int min = Math.min(players.size(), 12);
+- ObjectArrayList<GameProfile> list = new ObjectArrayList<>(min);
+- int randomInt = Mth.nextInt(this.random, 0, players.size() - min);
++ int j = Math.min(list.size(), 12);
++ ObjectArrayList<GameProfile> objectarraylist = new ObjectArrayList(j);
++ int k = Mth.nextInt(this.random, 0, list.size() - j);
+
+- for (int i = 0; i < min; i++) {
+- ServerPlayer serverPlayer = players.get(randomInt + i);
+- list.add(serverPlayer.allowsListing() ? serverPlayer.getGameProfile() : ANONYMOUS_PLAYER_PROFILE);
++ for (int l = 0; l < j; ++l) {
++ ServerPlayer entityplayer = (ServerPlayer) list.get(k + l);
++
++ objectarraylist.add(entityplayer.allowsListing() ? entityplayer.getGameProfile() : MinecraftServer.ANONYMOUS_PLAYER_PROFILE);
+ }
+
+- Util.shuffle(list, this.random);
+- return new ServerStatus.Players(maxPlayers, players.size(), list);
++ Util.shuffle(objectarraylist, this.random);
++ return new ServerStatus.ServerPingPlayerSample(i, list.size(), objectarraylist);
+ }
+ }
+
+ public void tickChildren(BooleanSupplier hasTimeLeft) {
+- this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
++ this.getPlayerList().getPlayers().forEach((entityplayer) -> {
++ entityplayer.connection.suspendFlushing();
++ });
++ this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // CraftBukkit
+ this.profiler.push("commandFunctions");
+ this.getFunctions().tick();
+ this.profiler.popPush("levels");
++ Iterator iterator = this.getAllLevels().iterator();
+
+- for (ServerLevel serverLevel : this.getAllLevels()) {
+- this.profiler.push(() -> serverLevel + " " + serverLevel.dimension().location());
++ // CraftBukkit start
++ // Run tasks that are waiting on processing
++ while (!processQueue.isEmpty()) {
++ processQueue.remove().run();
++ }
++
++ // Send time updates to everyone, it will get the right time from the world the player is in.
++ if (this.tickCount % 20 == 0) {
++ for (int i = 0; i < this.getPlayerList().players.size(); ++i) {
++ ServerPlayer entityplayer = (ServerPlayer) this.getPlayerList().players.get(i);
++ entityplayer.connection.send(new ClientboundSetTimePacket(entityplayer.level().getGameTime(), entityplayer.getPlayerTime(), entityplayer.level().getGameRules().getBoolean(GameRules.RULE_DAYLIGHT))); // Add support for per player time
++ }
++ }
++
++ while (iterator.hasNext()) {
++ ServerLevel worldserver = (ServerLevel) iterator.next();
++
++ this.profiler.push(() -> {
++ return worldserver + " " + worldserver.dimension().location();
++ });
++ /* Drop global time updates
+ if (this.tickCount % 20 == 0) {
+ this.profiler.push("timeSync");
+- this.synchronizeTime(serverLevel);
++ this.synchronizeTime(worldserver);
+ this.profiler.pop();
+ }
++ // CraftBukkit end */
+
+ this.profiler.push("tick");
+
+ try {
+- serverLevel.tick(hasTimeLeft);
+- } catch (Throwable var6) {
+- CrashReport crashReport = CrashReport.forThrowable(var6, "Exception ticking world");
+- serverLevel.fillReportDetails(crashReport);
+- throw new ReportedException(crashReport);
++ worldserver.tick(hasTimeLeft);
++ } catch (Throwable throwable) {
++ CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
++
++ worldserver.fillReportDetails(crashreport);
++ throw new ReportedException(crashreport);
+ }
+
+ this.profiler.pop();
+@@ -963,33 +1350,35 @@
+
+ this.profiler.popPush("server gui refresh");
+
+- for (int i = 0; i < this.tickables.size(); i++) {
+- this.tickables.get(i).run();
++ for (int i = 0; i < this.tickables.size(); ++i) {
++ ((Runnable) this.tickables.get(i)).run();
+ }
+
+ this.profiler.popPush("send chunks");
++ iterator = this.playerList.getPlayers().iterator();
+
+- for (ServerPlayer serverPlayer : this.playerList.getPlayers()) {
+- serverPlayer.connection.chunkSender.sendNextChunks(serverPlayer);
+- serverPlayer.connection.resumeFlushing();
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ entityplayer.connection.chunkSender.sendNextChunks(entityplayer);
++ entityplayer.connection.resumeFlushing();
+ }
+
+ this.profiler.pop();
+ }
+
+ private void synchronizeTime(ServerLevel level) {
+- this.playerList
+- .broadcastAll(
+- new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)),
+- level.dimension()
+- );
++ this.playerList.broadcastAll(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)), level.dimension());
+ }
+
+ public void forceTimeSynchronization() {
+ this.profiler.push("timeSync");
++ Iterator iterator = this.getAllLevels().iterator();
+
+- for (ServerLevel serverLevel : this.getAllLevels()) {
+- this.synchronizeTime(serverLevel);
++ while (iterator.hasNext()) {
++ ServerLevel worldserver = (ServerLevel) iterator.next();
++
++ this.synchronizeTime(worldserver);
+ }
+
+ this.profiler.pop();
+@@ -1016,14 +1405,30 @@
+ }
+
+ public final ServerLevel overworld() {
+- return this.levels.get(Level.OVERWORLD);
++ return (ServerLevel) this.levels.get(Level.OVERWORLD);
+ }
+
+ @Nullable
+ public ServerLevel getLevel(ResourceKey<Level> dimension) {
+- return this.levels.get(dimension);
++ return (ServerLevel) this.levels.get(dimension);
+ }
+
++ // CraftBukkit start
++ public void addLevel(ServerLevel level) {
++ Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
++ Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
++ newLevels.put(level.dimension(), level);
++ this.levels = Collections.unmodifiableMap(newLevels);
++ }
++
++ public void removeLevel(ServerLevel level) {
++ Map<ResourceKey<Level>, ServerLevel> oldLevels = this.levels;
++ Map<ResourceKey<Level>, ServerLevel> newLevels = Maps.newLinkedHashMap(oldLevels);
++ newLevels.remove(level.dimension());
++ this.levels = Collections.unmodifiableMap(newLevels);
++ }
++ // CraftBukkit end
++
+ public Set<ResourceKey<Level>> levelKeys() {
+ return this.levels.keySet();
+ }
+@@ -1053,32 +1458,38 @@
+
+ @DontObfuscate
+ public String getServerModName() {
+- return "vanilla";
++ return server.getName(); // CraftBukkit - cb > vanilla!
+ }
+
+ public SystemReport fillSystemReport(SystemReport systemReport) {
+- systemReport.setDetail("Server Running", () -> Boolean.toString(this.running));
++ systemReport.setDetail("Server Running", () -> {
++ return Boolean.toString(this.running);
++ });
+ if (this.playerList != null) {
+- systemReport.setDetail(
+- "Player Count", () -> this.playerList.getPlayerCount() + " / " + this.playerList.getMaxPlayers() + "; " + this.playerList.getPlayers()
+- );
++ systemReport.setDetail("Player Count", () -> {
++ int i = this.playerList.getPlayerCount();
++
++ return i + " / " + this.playerList.getMaxPlayers() + "; " + this.playerList.getPlayers();
++ });
+ }
+
+- systemReport.setDetail(
+- "Data Packs",
+- () -> this.packRepository
+- .getSelectedPacks()
+- .stream()
+- .map(pack -> pack.getId() + (pack.getCompatibility().isCompatible() ? "" : " (incompatible)"))
+- .collect(Collectors.joining(", "))
+- );
+- systemReport.setDetail(
+- "Enabled Feature Flags",
+- () -> FeatureFlags.REGISTRY.toNames(this.worldData.enabledFeatures()).stream().map(ResourceLocation::toString).collect(Collectors.joining(", "))
+- );
+- systemReport.setDetail("World Generation", () -> this.worldData.worldGenSettingsLifecycle().toString());
++ systemReport.setDetail("Data Packs", () -> {
++ return (String) this.packRepository.getSelectedPacks().stream().map((resourcepackloader) -> {
++ String s = resourcepackloader.getId();
++
++ return s + (resourcepackloader.getCompatibility().isCompatible() ? "" : " (incompatible)");
++ }).collect(Collectors.joining(", "));
++ });
++ systemReport.setDetail("Enabled Feature Flags", () -> {
++ return (String) FeatureFlags.REGISTRY.toNames(this.worldData.enabledFeatures()).stream().map(ResourceLocation::toString).collect(Collectors.joining(", "));
++ });
++ systemReport.setDetail("World Generation", () -> {
++ return this.worldData.worldGenSettingsLifecycle().toString();
++ });
+ if (this.serverId != null) {
+- systemReport.setDetail("Server Id", () -> this.serverId);
++ systemReport.setDetail("Server Id", () -> {
++ return this.serverId;
++ });
+ }
+
+ return this.fillServerSystemReport(systemReport);
+@@ -1092,7 +1503,7 @@
+
+ @Override
+ public void sendSystemMessage(Component component) {
+- LOGGER.info(component.getString());
++ MinecraftServer.LOGGER.info(component.getString());
+ }
+
+ public KeyPair getKeyPair() {
+@@ -1121,12 +1532,12 @@
+ }
+
+ protected void initializeKeyPair() {
+- LOGGER.info("Generating keypair");
++ MinecraftServer.LOGGER.info("Generating keypair");
+
+ try {
+ this.keyPair = Crypt.generateKeyPair();
+- } catch (CryptException var2) {
+- throw new IllegalStateException("Failed to generate key pair", var2);
++ } catch (CryptException cryptographyexception) {
++ throw new IllegalStateException("Failed to generate key pair", cryptographyexception);
+ }
+ }
+
+@@ -1143,9 +1554,14 @@
+ }
+
+ private void updateMobSpawningFlags() {
+- for (ServerLevel serverLevel : this.getAllLevels()) {
+- serverLevel.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
++ Iterator iterator = this.getAllLevels().iterator();
++
++ while (iterator.hasNext()) {
++ ServerLevel worldserver = (ServerLevel) iterator.next();
++
++ worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
+ }
++
+ }
+
+ public void setDifficultyLocked(boolean locked) {
+@@ -1154,8 +1570,9 @@
+ }
+
+ private void sendDifficultyUpdate(ServerPlayer player) {
+- LevelData levelData = player.level().getLevelData();
+- player.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked()));
++ LevelData worlddata = player.level().getLevelData();
++
++ player.connection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
+ }
+
+ public boolean isSpawningMonsters() {
+@@ -1380,131 +1797,130 @@
+ }
+
+ public CompletableFuture<Void> reloadResources(Collection<String> selectedIds) {
+- RegistryAccess.Frozen accessForLoading = this.registries.getAccessForLoading(RegistryLayer.RELOADABLE);
+- CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
+- () -> selectedIds.stream().map(this.packRepository::getPack).filter(Objects::nonNull).map(Pack::open).collect(ImmutableList.toImmutableList()),
+- this
+- )
+- .thenCompose(
+- list -> {
+- CloseableResourceManager closeableResourceManager = new MultiPackResourceManager(PackType.SERVER_DATA, list);
+- return ReloadableServerResources.loadResources(
+- closeableResourceManager,
+- accessForLoading,
+- this.worldData.enabledFeatures(),
+- this.isDedicatedServer() ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED,
+- this.getFunctionCompilationLevel(),
+- this.executor,
+- this
+- )
+- .whenComplete((reloadableServerResources, throwable) -> {
+- if (throwable != null) {
+- closeableResourceManager.close();
+- }
+- })
+- .thenApply(reloadableServerResources -> new MinecraftServer.ReloadableResources(closeableResourceManager, reloadableServerResources));
++ RegistryAccess.Dimension iregistrycustom_dimension = this.registries.getAccessForLoading(RegistryLayer.RELOADABLE);
++ CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
++ Stream<String> stream = selectedIds.stream(); // CraftBukkit - decompile error
++ PackRepository resourcepackrepository = this.packRepository;
++
++ Objects.requireNonNull(this.packRepository);
++ return stream.map(resourcepackrepository::getPack).filter(Objects::nonNull).map(Pack::open).collect(ImmutableList.toImmutableList()); // CraftBukkit - decompile error
++ }, this).thenCompose((immutablelist) -> {
++ MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
++
++ return ReloadableServerResources.loadResources(resourcemanager, iregistrycustom_dimension, this.worldData.enabledFeatures(), this.isDedicatedServer() ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED, this.getFunctionCompilationLevel(), this.executor, this).whenComplete((datapackresources, throwable) -> {
++ if (throwable != null) {
++ resourcemanager.close();
+ }
+- )
+- .thenAcceptAsync(
+- reloadableResources -> {
+- this.resources.close();
+- this.resources = reloadableResources;
+- this.packRepository.setSelected(selectedIds);
+- WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
+- getSelectedPacks(this.packRepository), this.worldData.enabledFeatures()
+- );
+- this.worldData.setDataConfiguration(worldDataConfiguration);
+- this.resources.managers.updateRegistryTags(this.registryAccess());
+- this.getPlayerList().saveAll();
+- this.getPlayerList().reloadResources();
+- this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
+- this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
+- },
+- this
+- );
++
++ }).thenApply((datapackresources) -> {
++ return new MinecraftServer.ReloadableResources(resourcemanager, datapackresources);
++ });
++ }).thenAcceptAsync((minecraftserver_reloadableresources) -> {
++ this.resources.close();
++ this.resources = minecraftserver_reloadableresources;
++ this.server.syncCommands(); // SPIGOT-5884: Lost on reload
++ this.packRepository.setSelected(selectedIds);
++ WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(getSelectedPacks(this.packRepository), this.worldData.enabledFeatures());
++
++ this.worldData.setDataConfiguration(worlddataconfiguration);
++ this.resources.managers.updateRegistryTags(this.registryAccess());
++ this.getPlayerList().saveAll();
++ this.getPlayerList().reloadResources();
++ this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
++ this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
++ }, this);
++
+ if (this.isSameThread()) {
+- this.managedBlock(completableFuture::isDone);
++ Objects.requireNonNull(completablefuture);
++ this.managedBlock(completablefuture::isDone);
+ }
+
+- return completableFuture;
++ return completablefuture;
+ }
+
+- public static WorldDataConfiguration configurePackRepository(
+- PackRepository packRepository, DataPackConfig dataPackConfig, boolean safeMode, FeatureFlagSet enabledFeatures
+- ) {
++ public static WorldDataConfiguration configurePackRepository(PackRepository packRepository, DataPackConfig dataPackConfig, boolean safeMode, FeatureFlagSet enabledFeatures) {
+ packRepository.reload();
+ if (safeMode) {
+ packRepository.setSelected(Collections.singleton("vanilla"));
+ return WorldDataConfiguration.DEFAULT;
+ } else {
+ Set<String> set = Sets.newLinkedHashSet();
++ Iterator iterator = dataPackConfig.getEnabled().iterator();
+
+- for (String string : dataPackConfig.getEnabled()) {
+- if (packRepository.isAvailable(string)) {
+- set.add(string);
++ while (iterator.hasNext()) {
++ String s = (String) iterator.next();
++
++ if (packRepository.isAvailable(s)) {
++ set.add(s);
+ } else {
+- LOGGER.warn("Missing data pack {}", string);
++ MinecraftServer.LOGGER.warn("Missing data pack {}", s);
+ }
+ }
+
+- for (Pack pack : packRepository.getAvailablePacks()) {
+- String id = pack.getId();
+- if (!dataPackConfig.getDisabled().contains(id)) {
+- FeatureFlagSet requestedFeatures = pack.getRequestedFeatures();
+- boolean flag = set.contains(id);
+- if (!flag && pack.getPackSource().shouldAddAutomatically()) {
+- if (requestedFeatures.isSubsetOf(enabledFeatures)) {
+- LOGGER.info("Found new data pack {}, loading it automatically", id);
+- set.add(id);
++ iterator = packRepository.getAvailablePacks().iterator();
++
++ while (iterator.hasNext()) {
++ Pack resourcepackloader = (Pack) iterator.next();
++ String s1 = resourcepackloader.getId();
++
++ if (!dataPackConfig.getDisabled().contains(s1)) {
++ FeatureFlagSet featureflagset1 = resourcepackloader.getRequestedFeatures();
++ boolean flag1 = set.contains(s1);
++
++ if (!flag1 && resourcepackloader.getPackSource().shouldAddAutomatically()) {
++ if (featureflagset1.isSubsetOf(enabledFeatures)) {
++ MinecraftServer.LOGGER.info("Found new data pack {}, loading it automatically", s1);
++ set.add(s1);
+ } else {
+- LOGGER.info(
+- "Found new data pack {}, but can't load it due to missing features {}",
+- id,
+- FeatureFlags.printMissingFlags(enabledFeatures, requestedFeatures)
+- );
++ MinecraftServer.LOGGER.info("Found new data pack {}, but can't load it due to missing features {}", s1, FeatureFlags.printMissingFlags(enabledFeatures, featureflagset1));
+ }
+ }
+
+- if (flag && !requestedFeatures.isSubsetOf(enabledFeatures)) {
+- LOGGER.warn(
+- "Pack {} requires features {} that are not enabled for this world, disabling pack.",
+- id,
+- FeatureFlags.printMissingFlags(enabledFeatures, requestedFeatures)
+- );
+- set.remove(id);
++ if (flag1 && !featureflagset1.isSubsetOf(enabledFeatures)) {
++ MinecraftServer.LOGGER.warn("Pack {} requires features {} that are not enabled for this world, disabling pack.", s1, FeatureFlags.printMissingFlags(enabledFeatures, featureflagset1));
++ set.remove(s1);
+ }
+ }
+ }
+
+ if (set.isEmpty()) {
+- LOGGER.info("No datapacks selected, forcing vanilla");
++ MinecraftServer.LOGGER.info("No datapacks selected, forcing vanilla");
+ set.add("vanilla");
+ }
+
+ packRepository.setSelected(set);
+- DataPackConfig selectedPacks = getSelectedPacks(packRepository);
+- FeatureFlagSet requestedFeatureFlags = packRepository.getRequestedFeatureFlags();
+- return new WorldDataConfiguration(selectedPacks, requestedFeatureFlags);
++ DataPackConfig datapackconfiguration1 = getSelectedPacks(packRepository);
++ FeatureFlagSet featureflagset2 = packRepository.getRequestedFeatureFlags();
++
++ return new WorldDataConfiguration(datapackconfiguration1, featureflagset2);
+ }
+ }
+
+ private static DataPackConfig getSelectedPacks(PackRepository packRepository) {
+- Collection<String> selectedIds = packRepository.getSelectedIds();
+- List<String> list = ImmutableList.copyOf(selectedIds);
+- List<String> list1 = packRepository.getAvailableIds().stream().filter(string -> !selectedIds.contains(string)).collect(ImmutableList.toImmutableList());
++ Collection<String> collection = packRepository.getSelectedIds();
++ List<String> list = ImmutableList.copyOf(collection);
++ List<String> list1 = (List) packRepository.getAvailableIds().stream().filter((s) -> {
++ return !collection.contains(s);
++ }).collect(ImmutableList.toImmutableList());
++
+ return new DataPackConfig(list, list1);
+ }
+
+ public void kickUnlistedPlayers(CommandSourceStack commandSource) {
+ if (this.isEnforceWhitelist()) {
+- PlayerList playerList = commandSource.getServer().getPlayerList();
+- UserWhiteList whiteList = playerList.getWhiteList();
++ PlayerList playerlist = commandSource.getServer().getPlayerList();
++ UserWhiteList whitelist = playerlist.getWhiteList();
++ List<ServerPlayer> list = Lists.newArrayList(playerlist.getPlayers());
++ Iterator iterator = list.iterator();
+
+- for (ServerPlayer serverPlayer : Lists.newArrayList(playerList.getPlayers())) {
+- if (!whiteList.isWhiteListed(serverPlayer.getGameProfile())) {
+- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.not_whitelisted"));
++ while (iterator.hasNext()) {
++ ServerPlayer entityplayer = (ServerPlayer) iterator.next();
++
++ if (!whitelist.isWhiteListed(entityplayer.getGameProfile())) {
++ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.not_whitelisted"));
+ }
+ }
++
+ }
+ }
+
+@@ -1517,18 +1933,9 @@
+ }
+
+ public CommandSourceStack createCommandSourceStack() {
+- ServerLevel serverLevel = this.overworld();
+- return new CommandSourceStack(
+- this,
+- serverLevel == null ? Vec3.ZERO : Vec3.atLowerCornerOf(serverLevel.getSharedSpawnPos()),
+- Vec2.ZERO,
+- serverLevel,
+- 4,
+- "Server",
+- Component.literal("Server"),
+- this,
+- null
+- );
++ ServerLevel worldserver = this.overworld();
++
++ return new CommandSourceStack(this, worldserver == null ? Vec3.ZERO : Vec3.atLowerCornerOf(worldserver.getSharedSpawnPos()), Vec2.ZERO, worldserver, 4, "Server", Component.literal("Server"), this, (Entity) null);
+ }
+
+ @Override
+@@ -1589,7 +1996,7 @@
+ }
+
+ public long getAverageTickTimeNanos() {
+- return this.aggregatedTickTimesNanos / (long)Math.min(100, Math.max(this.tickCount, 1));
++ return this.aggregatedTickTimesNanos / (long) Math.min(100, Math.max(this.tickCount, 1));
+ }
+
+ public long[] getTickTimesNanos() {
+@@ -1598,16 +2005,9 @@
+
+ public int getProfilePermissions(GameProfile profile) {
+ if (this.getPlayerList().isOp(profile)) {
+- ServerOpListEntry serverOpListEntry = this.getPlayerList().getOps().get(profile);
+- if (serverOpListEntry != null) {
+- return serverOpListEntry.getLevel();
+- } else if (this.isSingleplayerOwner(profile)) {
+- return 4;
+- } else if (this.isSingleplayer()) {
+- return this.getPlayerList().isAllowCheatsForAllPlayers() ? 4 : 0;
+- } else {
+- return this.getOperatorUserPermissionLevel();
+- }
++ ServerOpListEntry oplistentry = (ServerOpListEntry) this.getPlayerList().getOps().get(profile);
++
++ return oplistentry != null ? oplistentry.getLevel() : (this.isSingleplayerOwner(profile) ? 4 : (this.isSingleplayer() ? (this.getPlayerList().isAllowCheatsForAllPlayers() ? 4 : 0) : this.getOperatorUserPermissionLevel()));
+ } else {
+ return 0;
+ }
+@@ -1619,18 +2019,21 @@
+
+ public abstract boolean isSingleplayerOwner(GameProfile profile);
+
+- public void dumpServerProperties(Path path) throws IOException {
+- }
++ public void dumpServerProperties(Path path) throws IOException {}
+
+ private void saveDebugReport(Path path) {
+ Path path1 = path.resolve("levels");
+
+ try {
+- for (Entry<ResourceKey<Level>, ServerLevel> entry : this.levels.entrySet()) {
+- ResourceLocation resourceLocation = entry.getKey().location();
+- Path path2 = path1.resolve(resourceLocation.getNamespace()).resolve(resourceLocation.getPath());
++ Iterator iterator = this.levels.entrySet().iterator();
++
++ while (iterator.hasNext()) {
++ Entry<ResourceKey<Level>, ServerLevel> entry = (Entry) iterator.next();
++ ResourceLocation minecraftkey = ((ResourceKey) entry.getKey()).location();
++ Path path2 = path1.resolve(minecraftkey.getNamespace()).resolve(minecraftkey.getPath());
++
+ Files.createDirectories(path2);
+- entry.getValue().saveDebugReport(path2);
++ ((ServerLevel) entry.getValue()).saveDebugReport(path2);
+ }
+
+ this.dumpGameRules(path.resolve("gamerules.txt"));
+@@ -1639,94 +2042,225 @@
+ this.dumpThreads(path.resolve("threads.txt"));
+ this.dumpServerProperties(path.resolve("server.properties.txt"));
+ this.dumpNativeModules(path.resolve("modules.txt"));
+- } catch (IOException var7) {
+- LOGGER.warn("Failed to save debug report", (Throwable)var7);
++ } catch (IOException ioexception) {
++ MinecraftServer.LOGGER.warn("Failed to save debug report", ioexception);
+ }
++
+ }
+
+ private void dumpMiscStats(Path path) throws IOException {
+- try (Writer bufferedWriter = Files.newBufferedWriter(path)) {
+- bufferedWriter.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getPendingTasksCount()));
+- bufferedWriter.write(String.format(Locale.ROOT, "average_tick_time: %f\n", this.getCurrentSmoothedTickTime()));
+- bufferedWriter.write(String.format(Locale.ROOT, "tick_times: %s\n", Arrays.toString(this.tickTimesNanos)));
+- bufferedWriter.write(String.format(Locale.ROOT, "queue: %s\n", Util.backgroundExecutor()));
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path);
++
++ try {
++ bufferedwriter.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getPendingTasksCount()));
++ bufferedwriter.write(String.format(Locale.ROOT, "average_tick_time: %f\n", this.getCurrentSmoothedTickTime()));
++ bufferedwriter.write(String.format(Locale.ROOT, "tick_times: %s\n", Arrays.toString(this.tickTimesNanos)));
++ bufferedwriter.write(String.format(Locale.ROOT, "queue: %s\n", Util.backgroundExecutor()));
++ } catch (Throwable throwable) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable1) {
++ throwable.addSuppressed(throwable1);
++ }
++ }
++
++ throw throwable;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
+ }
+
+ private void dumpGameRules(Path path) throws IOException {
+- try (Writer bufferedWriter = Files.newBufferedWriter(path)) {
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path);
++
++ try {
+ final List<String> list = Lists.newArrayList();
+- final GameRules gameRules = this.getGameRules();
++ final GameRules gamerules = this.getGameRules();
++
+ GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
+ @Override
+ public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
+- list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gameRules.getRule(key)));
++ list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key)));
+ }
+ });
++ Iterator iterator = list.iterator();
+
+- for (String string : list) {
+- bufferedWriter.write(string);
++ while (iterator.hasNext()) {
++ String s = (String) iterator.next();
++
++ bufferedwriter.write(s);
+ }
++ } catch (Throwable throwable) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable1) {
++ throwable.addSuppressed(throwable1);
++ }
++ }
++
++ throw throwable;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
+ }
+
+ private void dumpClasspath(Path path) throws IOException {
+- try (Writer bufferedWriter = Files.newBufferedWriter(path)) {
+- String property = System.getProperty("java.class.path");
+- String property1 = System.getProperty("path.separator");
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path);
+
+- for (String string : Splitter.on(property1).split(property)) {
+- bufferedWriter.write(string);
+- bufferedWriter.write("\n");
++ try {
++ String s = System.getProperty("java.class.path");
++ String s1 = System.getProperty("path.separator");
++ Iterator iterator = Splitter.on(s1).split(s).iterator();
++
++ while (iterator.hasNext()) {
++ String s2 = (String) iterator.next();
++
++ bufferedwriter.write(s2);
++ bufferedwriter.write("\n");
+ }
++ } catch (Throwable throwable) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable1) {
++ throwable.addSuppressed(throwable1);
++ }
++ }
++
++ throw throwable;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
+ }
+
+ private void dumpThreads(Path path) throws IOException {
+- ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+- ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
+- Arrays.sort(threadInfos, Comparator.comparing(ThreadInfo::getThreadName));
++ ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean();
++ ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true);
+
+- try (Writer bufferedWriter = Files.newBufferedWriter(path)) {
+- for (ThreadInfo threadInfo : threadInfos) {
+- bufferedWriter.write(threadInfo.toString());
+- bufferedWriter.write(10);
++ Arrays.sort(athreadinfo, Comparator.comparing(ThreadInfo::getThreadName));
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path);
++
++ try {
++ ThreadInfo[] athreadinfo1 = athreadinfo;
++ int i = athreadinfo.length;
++
++ for (int j = 0; j < i; ++j) {
++ ThreadInfo threadinfo = athreadinfo1[j];
++
++ bufferedwriter.write(threadinfo.toString());
++ bufferedwriter.write(10);
+ }
++ } catch (Throwable throwable) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable1) {
++ throwable.addSuppressed(throwable1);
++ }
++ }
++
++ throw throwable;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
+ }
+
+ private void dumpNativeModules(Path path) throws IOException {
+- try (Writer bufferedWriter = Files.newBufferedWriter(path)) {
+- List<NativeModuleLister.NativeModuleInfo> list;
++ BufferedWriter bufferedwriter = Files.newBufferedWriter(path);
++
++ label50:
++ {
+ try {
+- list = Lists.newArrayList(NativeModuleLister.listModules());
+- } catch (Throwable var7) {
+- LOGGER.warn("Failed to list native modules", var7);
+- return;
+- }
++ label51:
++ {
++ ArrayList<NativeModuleLister.NativeModuleInfo> arraylist; // CraftBukkit - decompile error
+
+- list.sort(Comparator.comparing(nativeModuleInfo1 -> nativeModuleInfo1.name));
++ try {
++ arraylist = Lists.newArrayList(NativeModuleLister.listModules());
++ } catch (Throwable throwable) {
++ MinecraftServer.LOGGER.warn("Failed to list native modules", throwable);
++ break label51;
++ }
+
+- for (NativeModuleLister.NativeModuleInfo nativeModuleInfo : list) {
+- bufferedWriter.write(nativeModuleInfo.toString());
+- bufferedWriter.write(10);
++ arraylist.sort(Comparator.comparing((nativemodulelister_a) -> {
++ return nativemodulelister_a.name;
++ }));
++ Iterator iterator = arraylist.iterator();
++
++ while (true) {
++ if (!iterator.hasNext()) {
++ break label50;
++ }
++
++ NativeModuleLister.NativeModuleInfo nativemodulelister_a = (NativeModuleLister.NativeModuleInfo) iterator.next();
++
++ bufferedwriter.write(nativemodulelister_a.toString());
++ bufferedwriter.write(10);
++ }
++ }
++ } catch (Throwable throwable1) {
++ if (bufferedwriter != null) {
++ try {
++ bufferedwriter.close();
++ } catch (Throwable throwable2) {
++ throwable1.addSuppressed(throwable2);
++ }
++ }
++
++ throw throwable1;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
++ return;
+ }
++
++ if (bufferedwriter != null) {
++ bufferedwriter.close();
++ }
++
+ }
+
++ // CraftBukkit start
++ @Override
++ public boolean isSameThread() {
++ return super.isSameThread() || this.isStopped(); // CraftBukkit - MC-142590
++ }
++
++ public boolean isDebugging() {
++ return false;
++ }
++
++ @Deprecated
++ public static MinecraftServer getServer() {
++ return (Bukkit.getServer() instanceof CraftServer) ? ((CraftServer) Bukkit.getServer()).getServer() : null;
++ }
++ // CraftBukkit end
++
+ private void startMetricsRecordingTick() {
+ if (this.willStartRecordingMetrics) {
+- this.metricsRecorder = ActiveMetricsRecorder.createStarted(
+- new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()),
+- Util.timeSource,
+- Util.ioPool(),
+- new MetricsPersister("server"),
+- this.onMetricsRecordingStopped,
+- path -> {
+- this.executeBlocking(() -> this.saveDebugReport(path.resolve("server")));
+- this.onMetricsRecordingFinished.accept(path);
+- }
+- );
++ this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
++ this.executeBlocking(() -> {
++ this.saveDebugReport(path.resolve("server"));
++ });
++ this.onMetricsRecordingFinished.accept(path);
++ });
+ this.willStartRecordingMetrics = false;
+ }
+
+@@ -1745,9 +2279,9 @@
+ }
+
+ public void startRecordingMetrics(Consumer<ProfileResults> output, Consumer<Path> onMetricsRecordingFinished) {
+- this.onMetricsRecordingStopped = profileResults -> {
++ this.onMetricsRecordingStopped = (methodprofilerresults) -> {
+ this.stopRecordingMetrics();
+- output.accept(profileResults);
++ output.accept(methodprofilerresults);
+ };
+ this.onMetricsRecordingFinished = onMetricsRecordingFinished;
+ this.willStartRecordingMetrics = true;
+@@ -1782,7 +2316,7 @@
+ return this.worldData;
+ }
+
+- public RegistryAccess.Frozen registryAccess() {
++ public RegistryAccess.Dimension registryAccess() {
+ return this.registries.compositeAccess();
+ }
+
+@@ -1795,7 +2329,7 @@
+ }
+
+ public ServerPlayerGameMode createGameModeForPlayer(ServerPlayer player) {
+- return (ServerPlayerGameMode)(this.isDemo() ? new DemoMode(player) : new ServerPlayerGameMode(player));
++ return (ServerPlayerGameMode) (this.isDemo() ? new DemoMode(player) : new ServerPlayerGameMode(player));
+ }
+
+ @Nullable
+@@ -1823,9 +2357,10 @@
+ if (this.debugCommandProfiler == null) {
+ return EmptyProfileResults.EMPTY;
+ } else {
+- ProfileResults profileResults = this.debugCommandProfiler.stop(Util.getNanos(), this.tickCount);
++ ProfileResults methodprofilerresults = this.debugCommandProfiler.stop(Util.getNanos(), this.tickCount);
++
+ this.debugCommandProfiler = null;
+- return profileResults;
++ return methodprofilerresults;
+ }
+ }
+
+@@ -1834,14 +2369,21 @@
+ }
+
+ public void logChatMessage(Component content, ChatType.Bound boundChatType, @Nullable String header) {
+- String string = boundChatType.decorate(content).getString();
++ String s1 = boundChatType.decorate(content).getString();
++
+ if (header != null) {
+- LOGGER.info("[{}] {}", header, string);
++ MinecraftServer.LOGGER.info("[{}] {}", header, s1);
+ } else {
+- LOGGER.info("{}", string);
++ MinecraftServer.LOGGER.info("{}", s1);
+ }
++
+ }
+
++ // CraftBukkit start
++ public final java.util.concurrent.ExecutorService chatExecutor = java.util.concurrent.Executors.newCachedThreadPool(
++ new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Chat Thread - #%d").build());
++ // CraftBukkit end
++
+ public ChatDecorator getChatDecorator() {
+ return ChatDecorator.PLAIN;
+ }
+@@ -1850,26 +2392,24 @@
+ return true;
+ }
+
+- static record ReloadableResources(CloseableResourceManager resourceManager, ReloadableServerResources managers) implements AutoCloseable {
+- @Override
++ public static record ReloadableResources(IReloadableResourceManager resourceManager, ReloadableServerResources managers) implements AutoCloseable {
++
+ public void close() {
+ this.resourceManager.close();
+ }
+ }
+
+- public static record ServerResourcePackInfo(UUID id, String url, String hash, boolean isRequired, @Nullable Component prompt) {
+- }
++ private static class TimeProfiler {
+
+- static class TimeProfiler {
+ final long startNanos;
+ final int startTick;
+
+- TimeProfiler(long startNanos, int startTick) {
++ TimeProfiler(long startNanos, int j) {
+ this.startNanos = startNanos;
+- this.startTick = startTick;
++ this.startTick = j;
+ }
+
+- ProfileResults stop(final long endTimeNano, final int endTimeTicks) {
++ ProfileResults stop(final long endTimeNano, final int j) {
+ return new ProfileResults() {
+ @Override
+ public List<ResultField> getTimes(String sectionPath) {
+@@ -1898,7 +2438,7 @@
+
+ @Override
+ public int getEndTimeTicks() {
+- return endTimeTicks;
++ return j;
+ }
+
+ @Override
+@@ -1908,4 +2448,8 @@
+ };
+ }
+ }
++
++ public static record ServerResourcePackInfo(UUID id, String url, String hash, boolean isRequired, @Nullable Component prompt) {
++
++ }
+ }