package net.minecraft.server; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import com.mojang.datafixers.DataFixer; import com.mojang.logging.LogUtils; import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import java.awt.GraphicsEnvironment; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.Proxy; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import java.util.function.BooleanSupplier; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import joptsimple.util.PathConverter; import net.minecraft.CrashReport; import net.minecraft.DefaultUncaughtExceptionHandler; import net.minecraft.SharedConstants; import net.minecraft.SuppressForbidden; import net.minecraft.Util; import net.minecraft.commands.Commands; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess.Frozen; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.NbtException; import net.minecraft.nbt.ReportedNbtException; import net.minecraft.network.chat.Component; import net.minecraft.obfuscate.DontObfuscate; import net.minecraft.server.WorldLoader.DataLoadOutput; import net.minecraft.server.WorldLoader.InitConfig; import net.minecraft.server.WorldLoader.PackConfig; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerProperties; import net.minecraft.server.dedicated.DedicatedServerSettings; import net.minecraft.server.level.progress.LoggerChunkProgressListener; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.packs.repository.ServerPacksSource; import net.minecraft.util.Mth; import net.minecraft.util.datafix.DataFixers; import net.minecraft.util.profiling.jfr.Environment; import net.minecraft.util.profiling.jfr.JvmProfiler; import net.minecraft.util.worldupdate.WorldUpgrader; import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.WorldDataConfiguration; import net.minecraft.world.level.chunk.storage.RegionFileVersion; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldDimensions; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.WorldDimensions.Complete; import net.minecraft.world.level.levelgen.presets.WorldPresets; import net.minecraft.world.level.storage.LevelDataAndDimensions; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.LevelSummary; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.WorldData; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public class Main { private static final Logger LOGGER = LogUtils.getLogger(); @SuppressForbidden( reason = "System.out needed before bootstrap" ) @DontObfuscate public static void main(String[] strings) { SharedConstants.tryDetectVersion(); OptionParser optionParser = new OptionParser(); OptionSpec optionSpec = optionParser.accepts("nogui"); OptionSpec optionSpec2 = optionParser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits"); OptionSpec optionSpec3 = optionParser.accepts("demo"); OptionSpec optionSpec4 = optionParser.accepts("bonusChest"); OptionSpec optionSpec5 = optionParser.accepts("forceUpgrade"); OptionSpec optionSpec6 = optionParser.accepts("eraseCache"); OptionSpec optionSpec7 = optionParser.accepts("recreateRegionFiles"); OptionSpec optionSpec8 = optionParser.accepts("safeMode", "Loads level with vanilla datapack only"); OptionSpec optionSpec9 = optionParser.accepts("help").forHelp(); OptionSpec optionSpec10 = optionParser.accepts("universe").withRequiredArg().defaultsTo("."); OptionSpec optionSpec11 = optionParser.accepts("world").withRequiredArg(); OptionSpec optionSpec12 = optionParser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo(-1); OptionSpec optionSpec13 = optionParser.accepts("serverId").withRequiredArg(); OptionSpec optionSpec14 = optionParser.accepts("jfrProfile"); OptionSpec optionSpec15 = optionParser.accepts("pidFile").withRequiredArg().withValuesConvertedBy(new PathConverter()); OptionSpec optionSpec16 = optionParser.nonOptions(); try { OptionSet optionSet = optionParser.parse(strings); if (optionSet.has(optionSpec9)) { optionParser.printHelpOn(System.err); return; } Path path = optionSet.valueOf(optionSpec15); if (path != null) { writePidFile(path); } CrashReport.preload(); if (optionSet.has(optionSpec14)) { JvmProfiler.INSTANCE.start(Environment.SERVER); } Bootstrap.bootStrap(); Bootstrap.validate(); Util.startTimerHackThread(); Path path2 = Paths.get("server.properties"); DedicatedServerSettings dedicatedServerSettings = new DedicatedServerSettings(path2); dedicatedServerSettings.forceSave(); RegionFileVersion.configure(dedicatedServerSettings.getProperties().regionFileComression); Path path3 = Paths.get("eula.txt"); Eula eula = new Eula(path3); if (optionSet.has(optionSpec2)) { LOGGER.info("Initialized '{}' and '{}'", path2.toAbsolutePath(), path3.toAbsolutePath()); return; } if (!eula.hasAgreedToEULA()) { LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info."); return; } File file = new File(optionSet.valueOf(optionSpec10)); Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), file); String string = (String)Optional.ofNullable(optionSet.valueOf(optionSpec11)).orElse(dedicatedServerSettings.getProperties().levelName); LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(file.toPath()); LevelStorageSource.LevelStorageAccess levelStorageAccess = levelStorageSource.validateAndCreateAccess(string); Dynamic dynamic; if (levelStorageAccess.hasWorldData()) { LevelSummary levelSummary; try { dynamic = levelStorageAccess.getDataTag(); levelSummary = levelStorageAccess.getSummary(dynamic); } catch (NbtException | ReportedNbtException | IOException var41) { LevelStorageSource.LevelDirectory levelDirectory = levelStorageAccess.getLevelDirectory(); LOGGER.warn("Failed to load world data from {}", levelDirectory.dataFile(), var41); LOGGER.info("Attempting to use fallback"); try { dynamic = levelStorageAccess.getDataTagFallback(); levelSummary = levelStorageAccess.getSummary(dynamic); } catch (NbtException | ReportedNbtException | IOException var40) { LOGGER.error("Failed to load world data from {}", levelDirectory.oldDataFile(), var40); LOGGER.error( "Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", levelDirectory.dataFile(), levelDirectory.oldDataFile() ); return; } levelStorageAccess.restoreLevelDataFromOld(); } if (levelSummary.requiresManualConversion()) { LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted"); return; } if (!levelSummary.isCompatible()) { LOGGER.info("This world was created by an incompatible version."); return; } } else { dynamic = null; } Dynamic dynamic2 = dynamic; boolean bl = optionSet.has(optionSpec8); if (bl) { LOGGER.warn("Safe mode active, only vanilla datapack will be loaded"); } PackRepository packRepository = ServerPacksSource.createPackRepository(levelStorageAccess); WorldStem worldStem; try { InitConfig initConfig = loadOrCreateConfig(dedicatedServerSettings.getProperties(), dynamic2, bl, packRepository); worldStem = (WorldStem)Util.blockUntilDone( executor -> WorldLoader.load( initConfig, dataLoadContext -> { Registry registry = dataLoadContext.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM); if (dynamic2 != null) { LevelDataAndDimensions levelDataAndDimensions = LevelStorageSource.getLevelDataAndDimensions( dynamic2, dataLoadContext.dataConfiguration(), registry, dataLoadContext.datapackWorldgen() ); return new DataLoadOutput<>(levelDataAndDimensions.worldData(), levelDataAndDimensions.dimensions().dimensionsRegistryAccess()); } else { LOGGER.info("No existing world data, creating new world"); LevelSettings levelSettings; WorldOptions worldOptions; WorldDimensions worldDimensions; if (optionSet.has(optionSpec3)) { levelSettings = MinecraftServer.DEMO_SETTINGS; worldOptions = WorldOptions.DEMO_OPTIONS; worldDimensions = WorldPresets.createNormalWorldDimensions(dataLoadContext.datapackWorldgen()); } else { DedicatedServerProperties dedicatedServerProperties = dedicatedServerSettings.getProperties(); levelSettings = new LevelSettings( dedicatedServerProperties.levelName, dedicatedServerProperties.gamemode, dedicatedServerProperties.hardcore, dedicatedServerProperties.difficulty, false, new GameRules(dataLoadContext.dataConfiguration().enabledFeatures()), dataLoadContext.dataConfiguration() ); worldOptions = optionSet.has(optionSpec4) ? dedicatedServerProperties.worldOptions.withBonusChest(true) : dedicatedServerProperties.worldOptions; worldDimensions = dedicatedServerProperties.createDimensions(dataLoadContext.datapackWorldgen()); } Complete complete = worldDimensions.bake(registry); Lifecycle lifecycle = complete.lifecycle().add(dataLoadContext.datapackWorldgen().allRegistriesLifecycle()); return new DataLoadOutput<>( new PrimaryLevelData(levelSettings, worldOptions, complete.specialWorldProperty(), lifecycle), complete.dimensionsRegistryAccess() ); } }, WorldStem::new, Util.backgroundExecutor(), executor ) ) .get(); } catch (Exception var39) { LOGGER.warn( "Failed to load datapacks, can't proceed with server load. You can either fix your datapacks or reset to vanilla with --safeMode", (Throwable)var39 ); return; } Frozen frozen = worldStem.registries().compositeAccess(); WorldData worldData = worldStem.worldData(); boolean bl2 = optionSet.has(optionSpec7); if (optionSet.has(optionSpec5) || bl2) { forceUpgrade(levelStorageAccess, worldData, DataFixers.getDataFixer(), optionSet.has(optionSpec6), () -> true, frozen, bl2); } levelStorageAccess.saveDataTag(frozen, worldData); final DedicatedServer dedicatedServer = MinecraftServer.spin( threadx -> { DedicatedServer dedicatedServerx = new DedicatedServer( threadx, levelStorageAccess, packRepository, worldStem, dedicatedServerSettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::createFromGameruleRadius ); dedicatedServerx.setPort(optionSet.valueOf(optionSpec12)); dedicatedServerx.setDemo(optionSet.has(optionSpec3)); dedicatedServerx.setId(optionSet.valueOf(optionSpec13)); boolean blx = !optionSet.has(optionSpec) && !optionSet.valuesOf(optionSpec16).contains("nogui"); if (blx && !GraphicsEnvironment.isHeadless()) { dedicatedServerx.showGui(); } return dedicatedServerx; } ); Thread thread = new Thread("Server Shutdown Thread") { public void run() { dedicatedServer.halt(true); } }; thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)); Runtime.getRuntime().addShutdownHook(thread); } catch (Exception var42) { LOGGER.error(LogUtils.FATAL_MARKER, "Failed to start the minecraft server", (Throwable)var42); } } private static void writePidFile(Path path) { try { long l = ProcessHandle.current().pid(); Files.writeString(path, Long.toString(l)); } catch (IOException var3) { throw new UncheckedIOException(var3); } } private static InitConfig loadOrCreateConfig( DedicatedServerProperties dedicatedServerProperties, @Nullable Dynamic dynamic, boolean safeMode, PackRepository packRepository ) { boolean bl; WorldDataConfiguration worldDataConfiguration2; if (dynamic != null) { WorldDataConfiguration worldDataConfiguration = LevelStorageSource.readDataConfig(dynamic); bl = false; worldDataConfiguration2 = worldDataConfiguration; } else { bl = true; worldDataConfiguration2 = new WorldDataConfiguration(dedicatedServerProperties.initialDataPackConfiguration, FeatureFlags.DEFAULT_FLAGS); } PackConfig packConfig = new PackConfig(packRepository, worldDataConfiguration2, safeMode, bl); return new InitConfig(packConfig, Commands.CommandSelection.DEDICATED, dedicatedServerProperties.functionPermissionLevel); } private static void forceUpgrade( LevelStorageSource.LevelStorageAccess levelStorage, WorldData worldData, DataFixer dataFixer, boolean eraseCache, BooleanSupplier running, RegistryAccess registryAccess, boolean recreateRegionFiles ) { LOGGER.info("Forcing world upgrade!"); try (WorldUpgrader worldUpgrader = new WorldUpgrader(levelStorage, dataFixer, worldData, registryAccess, eraseCache, recreateRegionFiles)) { Component component = null; while (!worldUpgrader.isFinished()) { Component component2 = worldUpgrader.getStatus(); if (component != component2) { component = component2; LOGGER.info(worldUpgrader.getStatus().getString()); } int i = worldUpgrader.getTotalChunks(); if (i > 0) { int j = worldUpgrader.getConverted() + worldUpgrader.getSkipped(); LOGGER.info("{}% completed ({} / {} chunks)...", Mth.floor((float)j / i * 100.0F), j, i); } if (!running.getAsBoolean()) { worldUpgrader.cancel(); } else { try { Thread.sleep(1000L); } catch (InterruptedException var13) { } } } } } }