327 lines
18 KiB
Java
327 lines
18 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService
|
|
* com.mojang.datafixers.DataFixer
|
|
* com.mojang.logging.LogUtils
|
|
* com.mojang.serialization.Dynamic
|
|
* com.mojang.serialization.Lifecycle
|
|
* joptsimple.AbstractOptionSpec
|
|
* joptsimple.ArgumentAcceptingOptionSpec
|
|
* joptsimple.NonOptionArgumentSpec
|
|
* joptsimple.OptionParser
|
|
* joptsimple.OptionSet
|
|
* joptsimple.OptionSpec
|
|
* joptsimple.OptionSpecBuilder
|
|
* joptsimple.ValueConverter
|
|
* joptsimple.util.PathConverter
|
|
* joptsimple.util.PathProperties
|
|
* org.jspecify.annotations.Nullable
|
|
* org.slf4j.Logger
|
|
*/
|
|
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.OutputStream;
|
|
import java.io.UncheckedIOException;
|
|
import java.net.Proxy;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.OpenOption;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.function.BooleanSupplier;
|
|
import joptsimple.AbstractOptionSpec;
|
|
import joptsimple.ArgumentAcceptingOptionSpec;
|
|
import joptsimple.NonOptionArgumentSpec;
|
|
import joptsimple.OptionParser;
|
|
import joptsimple.OptionSet;
|
|
import joptsimple.OptionSpec;
|
|
import joptsimple.OptionSpecBuilder;
|
|
import joptsimple.ValueConverter;
|
|
import joptsimple.util.PathConverter;
|
|
import joptsimple.util.PathProperties;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.DefaultUncaughtExceptionHandler;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.SuppressForbidden;
|
|
import net.minecraft.commands.Commands;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.Registry;
|
|
import net.minecraft.core.RegistryAccess;
|
|
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.server.Bootstrap;
|
|
import net.minecraft.server.Eula;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.server.Services;
|
|
import net.minecraft.server.WorldLoader;
|
|
import net.minecraft.server.WorldStem;
|
|
import net.minecraft.server.dedicated.DedicatedServer;
|
|
import net.minecraft.server.dedicated.DedicatedServerProperties;
|
|
import net.minecraft.server.dedicated.DedicatedServerSettings;
|
|
import net.minecraft.server.packs.repository.PackRepository;
|
|
import net.minecraft.server.packs.repository.ServerPacksSource;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.Util;
|
|
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.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.gamerules.GameRules;
|
|
import net.minecraft.world.level.levelgen.WorldDimensions;
|
|
import net.minecraft.world.level.levelgen.WorldOptions;
|
|
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.jspecify.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class Main {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
|
|
@SuppressForbidden(reason="System.out needed before bootstrap")
|
|
public static void main(String[] args) {
|
|
SharedConstants.tryDetectVersion();
|
|
OptionParser parser = new OptionParser();
|
|
OptionSpecBuilder nogui = parser.accepts("nogui");
|
|
OptionSpecBuilder initSettings = parser.accepts("initSettings", "Initializes 'server.properties' and 'eula.txt', then quits");
|
|
OptionSpecBuilder demo = parser.accepts("demo");
|
|
OptionSpecBuilder bonusChest = parser.accepts("bonusChest");
|
|
OptionSpecBuilder forceUpgrade = parser.accepts("forceUpgrade");
|
|
OptionSpecBuilder eraseCache = parser.accepts("eraseCache");
|
|
OptionSpecBuilder recreateRegionFiles = parser.accepts("recreateRegionFiles");
|
|
OptionSpecBuilder safeMode = parser.accepts("safeMode", "Loads level with vanilla datapack only");
|
|
AbstractOptionSpec help = parser.accepts("help").forHelp();
|
|
ArgumentAcceptingOptionSpec universe = parser.accepts("universe").withRequiredArg().defaultsTo((Object)".", (Object[])new String[0]);
|
|
ArgumentAcceptingOptionSpec worldName = parser.accepts("world").withRequiredArg();
|
|
ArgumentAcceptingOptionSpec port = parser.accepts("port").withRequiredArg().ofType(Integer.class).defaultsTo((Object)-1, (Object[])new Integer[0]);
|
|
ArgumentAcceptingOptionSpec serverId = parser.accepts("serverId").withRequiredArg();
|
|
OptionSpecBuilder jfrProfilingOption = parser.accepts("jfrProfile");
|
|
ArgumentAcceptingOptionSpec pidFile = parser.accepts("pidFile").withRequiredArg().withValuesConvertedBy((ValueConverter)new PathConverter(new PathProperties[0]));
|
|
NonOptionArgumentSpec nonOptions = parser.nonOptions();
|
|
try {
|
|
WorldStem worldStem;
|
|
Dynamic<?> loadedDataTag;
|
|
OptionSet options = parser.parse(args);
|
|
if (options.has((OptionSpec)help)) {
|
|
parser.printHelpOn((OutputStream)System.err);
|
|
return;
|
|
}
|
|
Path pidFilePath = (Path)options.valueOf((OptionSpec)pidFile);
|
|
if (pidFilePath != null) {
|
|
Main.writePidFile(pidFilePath);
|
|
}
|
|
CrashReport.preload();
|
|
if (options.has((OptionSpec)jfrProfilingOption)) {
|
|
JvmProfiler.INSTANCE.start(Environment.SERVER);
|
|
}
|
|
Bootstrap.bootStrap();
|
|
Bootstrap.validate();
|
|
Util.startTimerHackThread();
|
|
Path settingsFile = Paths.get("server.properties", new String[0]);
|
|
DedicatedServerSettings settings = new DedicatedServerSettings(settingsFile);
|
|
settings.forceSave();
|
|
RegionFileVersion.configure(settings.getProperties().regionFileComression);
|
|
Path eulaFile = Paths.get("eula.txt", new String[0]);
|
|
Eula eula = new Eula(eulaFile);
|
|
if (options.has((OptionSpec)initSettings)) {
|
|
LOGGER.info("Initialized '{}' and '{}'", (Object)settingsFile.toAbsolutePath(), (Object)eulaFile.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 universePath = new File((String)options.valueOf((OptionSpec)universe));
|
|
Services services = Services.create(new YggdrasilAuthenticationService(Proxy.NO_PROXY), universePath);
|
|
String levelName = Optional.ofNullable((String)options.valueOf((OptionSpec)worldName)).orElse(settings.getProperties().levelName);
|
|
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(universePath.toPath());
|
|
LevelStorageSource.LevelStorageAccess access = levelStorageSource.validateAndCreateAccess(levelName);
|
|
if (access.hasWorldData()) {
|
|
LevelSummary summary;
|
|
try {
|
|
loadedDataTag = access.getDataTag();
|
|
summary = access.getSummary(loadedDataTag);
|
|
}
|
|
catch (IOException | NbtException | ReportedNbtException e) {
|
|
LevelStorageSource.LevelDirectory levelDirectory = access.getLevelDirectory();
|
|
LOGGER.warn("Failed to load world data from {}", (Object)levelDirectory.dataFile(), (Object)e);
|
|
LOGGER.info("Attempting to use fallback");
|
|
try {
|
|
loadedDataTag = access.getDataTagFallback();
|
|
summary = access.getSummary(loadedDataTag);
|
|
}
|
|
catch (IOException | NbtException | ReportedNbtException ex) {
|
|
LOGGER.error("Failed to load world data from {}", (Object)levelDirectory.oldDataFile(), (Object)ex);
|
|
LOGGER.error("Failed to load world data from {} and {}. World files may be corrupted. Shutting down.", (Object)levelDirectory.dataFile(), (Object)levelDirectory.oldDataFile());
|
|
return;
|
|
}
|
|
access.restoreLevelDataFromOld();
|
|
}
|
|
if (summary.requiresManualConversion()) {
|
|
LOGGER.info("This world must be opened in an older version (like 1.6.4) to be safely converted");
|
|
return;
|
|
}
|
|
if (!summary.isCompatible()) {
|
|
LOGGER.info("This world was created by an incompatible version.");
|
|
return;
|
|
}
|
|
} else {
|
|
loadedDataTag = null;
|
|
}
|
|
Dynamic<?> levelDataTag = loadedDataTag;
|
|
boolean safeModeEnabled = options.has((OptionSpec)safeMode);
|
|
if (safeModeEnabled) {
|
|
LOGGER.warn("Safe mode active, only vanilla datapack will be loaded");
|
|
}
|
|
PackRepository packRepository = ServerPacksSource.createPackRepository(access);
|
|
try {
|
|
WorldLoader.InitConfig worldLoadConfig = Main.loadOrCreateConfig(settings.getProperties(), levelDataTag, safeModeEnabled, packRepository);
|
|
worldStem = (WorldStem)Util.blockUntilDone(arg_0 -> Main.lambda$main$1(worldLoadConfig, levelDataTag, settings, options, (OptionSpec)demo, (OptionSpec)bonusChest, arg_0)).get();
|
|
}
|
|
catch (Exception e) {
|
|
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)e);
|
|
return;
|
|
}
|
|
RegistryAccess.Frozen registryHolder = worldStem.registries().compositeAccess();
|
|
WorldData data = worldStem.worldData();
|
|
boolean recreateRegionFilesValue = options.has((OptionSpec)recreateRegionFiles);
|
|
if (options.has((OptionSpec)forceUpgrade) || recreateRegionFilesValue) {
|
|
Main.forceUpgrade(access, data, DataFixers.getDataFixer(), options.has((OptionSpec)eraseCache), () -> true, registryHolder, recreateRegionFilesValue);
|
|
}
|
|
access.saveDataTag(registryHolder, data);
|
|
final DedicatedServer dedicatedServer = MinecraftServer.spin(arg_0 -> Main.lambda$main$3(access, packRepository, worldStem, settings, services, options, (OptionSpec)port, (OptionSpec)demo, (OptionSpec)serverId, (OptionSpec)nogui, (OptionSpec)nonOptions, arg_0));
|
|
Thread shutdownThread = new Thread("Server Shutdown Thread"){
|
|
|
|
@Override
|
|
public void run() {
|
|
dedicatedServer.halt(true);
|
|
}
|
|
};
|
|
shutdownThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
|
|
Runtime.getRuntime().addShutdownHook(shutdownThread);
|
|
}
|
|
catch (Throwable t) {
|
|
LOGGER.error(LogUtils.FATAL_MARKER, "Failed to start the minecraft server", t);
|
|
}
|
|
}
|
|
|
|
private static WorldLoader.DataLoadOutput<WorldData> createNewWorldData(DedicatedServerSettings settings, WorldLoader.DataLoadContext context, Registry<LevelStem> datapackDimensions, boolean demoMode, boolean bonusChest) {
|
|
WorldDimensions dimensions;
|
|
WorldOptions worldOptions;
|
|
LevelSettings createLevelSettings;
|
|
if (demoMode) {
|
|
createLevelSettings = MinecraftServer.DEMO_SETTINGS;
|
|
worldOptions = WorldOptions.DEMO_OPTIONS;
|
|
dimensions = WorldPresets.createNormalWorldDimensions(context.datapackWorldgen());
|
|
} else {
|
|
DedicatedServerProperties properties = settings.getProperties();
|
|
createLevelSettings = new LevelSettings(properties.levelName, properties.gameMode.get(), properties.hardcore, properties.difficulty.get(), false, new GameRules(context.dataConfiguration().enabledFeatures()), context.dataConfiguration());
|
|
worldOptions = bonusChest ? properties.worldOptions.withBonusChest(true) : properties.worldOptions;
|
|
dimensions = properties.createDimensions(context.datapackWorldgen());
|
|
}
|
|
WorldDimensions.Complete finalDimensions = dimensions.bake(datapackDimensions);
|
|
Lifecycle lifecycle = finalDimensions.lifecycle().add(context.datapackWorldgen().allRegistriesLifecycle());
|
|
return new WorldLoader.DataLoadOutput<WorldData>(new PrimaryLevelData(createLevelSettings, worldOptions, finalDimensions.specialWorldProperty(), lifecycle), finalDimensions.dimensionsRegistryAccess());
|
|
}
|
|
|
|
private static void writePidFile(Path path) {
|
|
try {
|
|
long pid = ProcessHandle.current().pid();
|
|
Files.writeString(path, (CharSequence)Long.toString(pid), new OpenOption[0]);
|
|
}
|
|
catch (IOException e) {
|
|
throw new UncheckedIOException(e);
|
|
}
|
|
}
|
|
|
|
private static WorldLoader.InitConfig loadOrCreateConfig(DedicatedServerProperties properties, @Nullable Dynamic<?> levelDataTag, boolean safeModeEnabled, PackRepository packRepository) {
|
|
WorldDataConfiguration dataConfigToUse;
|
|
boolean initMode;
|
|
if (levelDataTag != null) {
|
|
WorldDataConfiguration storedConfiguration = LevelStorageSource.readDataConfig(levelDataTag);
|
|
initMode = false;
|
|
dataConfigToUse = storedConfiguration;
|
|
} else {
|
|
initMode = true;
|
|
dataConfigToUse = new WorldDataConfiguration(properties.initialDataPackConfiguration, FeatureFlags.DEFAULT_FLAGS);
|
|
}
|
|
WorldLoader.PackConfig packConfig = new WorldLoader.PackConfig(packRepository, dataConfigToUse, safeModeEnabled, initMode);
|
|
return new WorldLoader.InitConfig(packConfig, Commands.CommandSelection.DEDICATED, properties.functionPermissions);
|
|
}
|
|
|
|
private static void forceUpgrade(LevelStorageSource.LevelStorageAccess storageSource, WorldData worldData, DataFixer fixerUpper, boolean eraseCache, BooleanSupplier isRunning, RegistryAccess registryAccess, boolean recreateRegionFiles) {
|
|
LOGGER.info("Forcing world upgrade!");
|
|
try (WorldUpgrader upgrader = new WorldUpgrader(storageSource, fixerUpper, worldData, registryAccess, eraseCache, recreateRegionFiles);){
|
|
Component lastStatus = null;
|
|
while (!upgrader.isFinished()) {
|
|
int totalChunks;
|
|
Component status = upgrader.getStatus();
|
|
if (lastStatus != status) {
|
|
lastStatus = status;
|
|
LOGGER.info(upgrader.getStatus().getString());
|
|
}
|
|
if ((totalChunks = upgrader.getTotalChunks()) > 0) {
|
|
int done = upgrader.getConverted() + upgrader.getSkipped();
|
|
LOGGER.info("{}% completed ({} / {} chunks)...", new Object[]{Mth.floor((float)done / (float)totalChunks * 100.0f), done, totalChunks});
|
|
}
|
|
if (!isRunning.getAsBoolean()) {
|
|
upgrader.cancel();
|
|
continue;
|
|
}
|
|
try {
|
|
Thread.sleep(1000L);
|
|
}
|
|
catch (InterruptedException interruptedException) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static /* synthetic */ DedicatedServer lambda$main$3(LevelStorageSource.LevelStorageAccess access, PackRepository packRepository, WorldStem worldStem, DedicatedServerSettings settings, Services services, OptionSet options, OptionSpec port, OptionSpec demo, OptionSpec serverId, OptionSpec nogui, OptionSpec nonOptions, Thread thread) {
|
|
boolean gui;
|
|
DedicatedServer server = new DedicatedServer(thread, access, packRepository, worldStem, settings, DataFixers.getDataFixer(), services);
|
|
server.setPort((Integer)options.valueOf(port));
|
|
server.setDemo(options.has(demo));
|
|
server.setId((String)options.valueOf(serverId));
|
|
boolean bl = gui = !options.has(nogui) && !options.valuesOf(nonOptions).contains("nogui");
|
|
if (gui && !GraphicsEnvironment.isHeadless()) {
|
|
server.showGui();
|
|
}
|
|
return server;
|
|
}
|
|
|
|
private static /* synthetic */ CompletableFuture lambda$main$1(WorldLoader.InitConfig worldLoadConfig, Dynamic levelDataTag, DedicatedServerSettings settings, OptionSet options, OptionSpec demo, OptionSpec bonusChest, Executor executor) {
|
|
return WorldLoader.load(worldLoadConfig, context -> {
|
|
HolderLookup.RegistryLookup datapackDimensions = context.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
|
if (levelDataTag != null) {
|
|
LevelDataAndDimensions worldData = LevelStorageSource.getLevelDataAndDimensions(levelDataTag, context.dataConfiguration(), (Registry<LevelStem>)datapackDimensions, context.datapackWorldgen());
|
|
return new WorldLoader.DataLoadOutput<WorldData>(worldData.worldData(), worldData.dimensions().dimensionsRegistryAccess());
|
|
}
|
|
LOGGER.info("No existing world data, creating new world");
|
|
return Main.createNewWorldData(settings, context, (Registry<LevelStem>)datapackDimensions, options.has(demo), options.has(bonusChest));
|
|
}, WorldStem::new, Util.backgroundExecutor(), executor);
|
|
}
|
|
}
|
|
|