2025-11-24 22:52:51 +03:00

319 lines
18 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.base.Stopwatch
* com.google.common.base.Ticker
* com.mojang.jtracy.TracyClient
* com.mojang.logging.LogUtils
* com.mojang.util.UndashedUuid
* joptsimple.ArgumentAcceptingOptionSpec
* joptsimple.NonOptionArgumentSpec
* joptsimple.OptionParser
* joptsimple.OptionSet
* joptsimple.OptionSpec
* joptsimple.OptionSpecBuilder
* org.apache.commons.lang3.StringEscapeUtils
* org.jspecify.annotations.Nullable
* org.slf4j.Logger
*/
package net.minecraft.client.main;
import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import com.mojang.blaze3d.TracyBootstrap;
import com.mojang.blaze3d.platform.DisplayData;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.jtracy.TracyClient;
import com.mojang.logging.LogUtils;
import com.mojang.util.UndashedUuid;
import java.io.File;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.NonOptionArgumentSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.OptionSpecBuilder;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.Optionull;
import net.minecraft.SharedConstants;
import net.minecraft.client.ClientBootstrap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.User;
import net.minecraft.client.main.GameConfig;
import net.minecraft.client.main.SilentInitException;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.client.telemetry.TelemetryProperty;
import net.minecraft.client.telemetry.events.GameLoadTimesEvent;
import net.minecraft.core.UUIDUtil;
import net.minecraft.server.Bootstrap;
import net.minecraft.util.NativeModuleLister;
import net.minecraft.util.Util;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.util.datafix.DataFixers;
import net.minecraft.util.profiling.jfr.Environment;
import net.minecraft.util.profiling.jfr.JvmProfiler;
import org.apache.commons.lang3.StringEscapeUtils;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
public class Main {
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public static void main(String[] args) {
GameConfig gameConfig;
Logger logger;
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
parser.accepts("demo");
parser.accepts("disableMultiplayer");
parser.accepts("disableChat");
parser.accepts("fullscreen");
parser.accepts("checkGlErrors");
OptionSpecBuilder renderDebugLabelsOption = parser.accepts("renderDebugLabels");
OptionSpecBuilder jfrProfilingOption = parser.accepts("jfrProfile");
OptionSpecBuilder tracyProfilingOption = parser.accepts("tracy");
OptionSpecBuilder tracyNoImageOption = parser.accepts("tracyNoImages");
ArgumentAcceptingOptionSpec quickPlayPathOption = parser.accepts("quickPlayPath").withRequiredArg();
ArgumentAcceptingOptionSpec quickPlaySingleplayerOption = parser.accepts("quickPlaySingleplayer").withOptionalArg();
ArgumentAcceptingOptionSpec quickPlayMultiplayerOption = parser.accepts("quickPlayMultiplayer").withRequiredArg();
ArgumentAcceptingOptionSpec quickPlayRealmsOption = parser.accepts("quickPlayRealms").withRequiredArg();
ArgumentAcceptingOptionSpec gameDirOption = parser.accepts("gameDir").withRequiredArg().ofType(File.class).defaultsTo((Object)new File("."), (Object[])new File[0]);
ArgumentAcceptingOptionSpec assetsDirOption = parser.accepts("assetsDir").withRequiredArg().ofType(File.class);
ArgumentAcceptingOptionSpec resourcePackDirOption = parser.accepts("resourcePackDir").withRequiredArg().ofType(File.class);
ArgumentAcceptingOptionSpec proxyHostOption = parser.accepts("proxyHost").withRequiredArg();
ArgumentAcceptingOptionSpec proxyPortOption = parser.accepts("proxyPort").withRequiredArg().defaultsTo((Object)"8080", (Object[])new String[0]).ofType(Integer.class);
ArgumentAcceptingOptionSpec proxyUserOption = parser.accepts("proxyUser").withRequiredArg();
ArgumentAcceptingOptionSpec proxyPassOption = parser.accepts("proxyPass").withRequiredArg();
ArgumentAcceptingOptionSpec usernameOption = parser.accepts("username").withRequiredArg().defaultsTo((Object)("Player" + System.currentTimeMillis() % 1000L), (Object[])new String[0]);
OptionSpecBuilder offlineDeveloperMode = parser.accepts("offlineDeveloperMode");
ArgumentAcceptingOptionSpec uuidOption = parser.accepts("uuid").withRequiredArg();
ArgumentAcceptingOptionSpec xuidOption = parser.accepts("xuid").withOptionalArg().defaultsTo((Object)"", (Object[])new String[0]);
ArgumentAcceptingOptionSpec clientIdOption = parser.accepts("clientId").withOptionalArg().defaultsTo((Object)"", (Object[])new String[0]);
ArgumentAcceptingOptionSpec accessTokenOption = parser.accepts("accessToken").withRequiredArg().required();
ArgumentAcceptingOptionSpec versionOption = parser.accepts("version").withRequiredArg().required();
ArgumentAcceptingOptionSpec widthOption = parser.accepts("width").withRequiredArg().ofType(Integer.class).defaultsTo((Object)854, (Object[])new Integer[0]);
ArgumentAcceptingOptionSpec heightOption = parser.accepts("height").withRequiredArg().ofType(Integer.class).defaultsTo((Object)480, (Object[])new Integer[0]);
ArgumentAcceptingOptionSpec fullscreenWidthOption = parser.accepts("fullscreenWidth").withRequiredArg().ofType(Integer.class);
ArgumentAcceptingOptionSpec fullscreenHeightOption = parser.accepts("fullscreenHeight").withRequiredArg().ofType(Integer.class);
ArgumentAcceptingOptionSpec assetIndexOption = parser.accepts("assetIndex").withRequiredArg();
ArgumentAcceptingOptionSpec versionTypeString = parser.accepts("versionType").withRequiredArg().defaultsTo((Object)"release", (Object[])new String[0]);
NonOptionArgumentSpec nonOption = parser.nonOptions();
OptionSet optionSet = parser.parse(args);
File gameDir = (File)Main.parseArgument(optionSet, gameDirOption);
String launchedVersion = (String)Main.parseArgument(optionSet, versionOption);
String stage = "Pre-bootstrap";
try {
if (optionSet.has((OptionSpec)jfrProfilingOption)) {
JvmProfiler.INSTANCE.start(Environment.CLIENT);
}
if (optionSet.has((OptionSpec)tracyProfilingOption)) {
TracyBootstrap.setup();
}
Stopwatch totalTimePreClassLoadTimer = Stopwatch.createStarted((Ticker)Ticker.systemTicker());
Stopwatch preWindowPreClassLoadTimer = Stopwatch.createStarted((Ticker)Ticker.systemTicker());
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_TOTAL_TIME_MS, totalTimePreClassLoadTimer);
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_PRE_WINDOW_MS, preWindowPreClassLoadTimer);
SharedConstants.tryDetectVersion();
TracyClient.reportAppInfo((String)("Minecraft Java Edition " + SharedConstants.getCurrentVersion().name()));
CompletableFuture<?> dataFixerOptimization = DataFixers.optimize(DataFixTypes.TYPES_FOR_LEVEL_LIST);
CrashReport.preload();
logger = LogUtils.getLogger();
stage = "Bootstrap";
Bootstrap.bootStrap();
ClientBootstrap.bootstrap();
GameLoadTimesEvent.INSTANCE.setBootstrapTime(Bootstrap.bootstrapDuration.get());
Bootstrap.validate();
stage = "Argument parsing";
List leftoverArgs = optionSet.valuesOf((OptionSpec)nonOption);
if (!leftoverArgs.isEmpty()) {
logger.info("Completely ignored arguments: {}", (Object)leftoverArgs);
}
String hostName = (String)Main.parseArgument(optionSet, proxyHostOption);
Proxy proxy = Proxy.NO_PROXY;
if (hostName != null) {
try {
proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(hostName, (int)((Integer)Main.parseArgument(optionSet, proxyPortOption))));
}
catch (Exception exception) {
// empty catch block
}
}
final String proxyUser = (String)Main.parseArgument(optionSet, proxyUserOption);
final String proxyPass = (String)Main.parseArgument(optionSet, proxyPassOption);
if (!proxy.equals(Proxy.NO_PROXY) && Main.stringHasValue(proxyUser) && Main.stringHasValue(proxyPass)) {
Authenticator.setDefault(new Authenticator(){
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(proxyUser, proxyPass.toCharArray());
}
});
}
int width = (Integer)Main.parseArgument(optionSet, widthOption);
int height = (Integer)Main.parseArgument(optionSet, heightOption);
OptionalInt fullscreenWidth = Main.ofNullable((Integer)Main.parseArgument(optionSet, fullscreenWidthOption));
OptionalInt fullscreenHeight = Main.ofNullable((Integer)Main.parseArgument(optionSet, fullscreenHeightOption));
boolean isFullscreen = optionSet.has("fullscreen");
boolean isDemo = optionSet.has("demo");
boolean disableMultiplayer = optionSet.has("disableMultiplayer");
boolean disableChat = optionSet.has("disableChat");
boolean captureTracyImages = !optionSet.has((OptionSpec)tracyNoImageOption);
boolean renderDebugLabels = optionSet.has((OptionSpec)renderDebugLabelsOption);
String versionType = (String)Main.parseArgument(optionSet, versionTypeString);
File assetsDir = optionSet.has((OptionSpec)assetsDirOption) ? (File)Main.parseArgument(optionSet, assetsDirOption) : new File(gameDir, "assets/");
File resourcePackDir = optionSet.has((OptionSpec)resourcePackDirOption) ? (File)Main.parseArgument(optionSet, resourcePackDirOption) : new File(gameDir, "resourcepacks/");
UUID uuid = Main.hasValidUuid((OptionSpec<String>)uuidOption, optionSet, logger) ? UndashedUuid.fromStringLenient((String)((String)uuidOption.value(optionSet))) : UUIDUtil.createOfflinePlayerUUID((String)usernameOption.value(optionSet));
String assetIndex = optionSet.has((OptionSpec)assetIndexOption) ? (String)assetIndexOption.value(optionSet) : null;
String xuid = (String)optionSet.valueOf((OptionSpec)xuidOption);
String clientId = (String)optionSet.valueOf((OptionSpec)clientIdOption);
String quickPlayLogPath = (String)Main.parseArgument(optionSet, quickPlayPathOption);
GameConfig.QuickPlayVariant quickPlayVariant = Main.getQuickPlayVariant(optionSet, (OptionSpec<String>)quickPlaySingleplayerOption, (OptionSpec<String>)quickPlayMultiplayerOption, (OptionSpec<String>)quickPlayRealmsOption);
User user = new User((String)usernameOption.value(optionSet), uuid, (String)accessTokenOption.value(optionSet), Main.emptyStringToEmptyOptional(xuid), Main.emptyStringToEmptyOptional(clientId));
gameConfig = new GameConfig(new GameConfig.UserData(user, proxy), new DisplayData(width, height, fullscreenWidth, fullscreenHeight, isFullscreen), new GameConfig.FolderData(gameDir, resourcePackDir, assetsDir, assetIndex), new GameConfig.GameData(isDemo, launchedVersion, versionType, disableMultiplayer, disableChat, captureTracyImages, renderDebugLabels, optionSet.has((OptionSpec)offlineDeveloperMode)), new GameConfig.QuickPlayData(quickPlayLogPath, quickPlayVariant));
Util.startTimerHackThread();
dataFixerOptimization.join();
}
catch (Throwable t) {
CrashReport report = CrashReport.forThrowable(t, stage);
CrashReportCategory initialization = report.addCategory("Initialization");
NativeModuleLister.addCrashSection(initialization);
Minecraft.fillReport(null, null, launchedVersion, null, report);
Minecraft.crash(null, gameDir, report);
return;
}
Thread shutdownThread = new Thread("Client Shutdown Thread"){
@Override
public void run() {
Minecraft instance = Minecraft.getInstance();
if (instance == null) {
return;
}
IntegratedServer server = instance.getSingleplayerServer();
if (server != null) {
server.halt(true);
}
}
};
shutdownThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(logger));
Runtime.getRuntime().addShutdownHook(shutdownThread);
Minecraft newMinecraft = null;
try {
Thread.currentThread().setName("Render thread");
RenderSystem.initRenderThread();
newMinecraft = new Minecraft(gameConfig);
}
catch (SilentInitException e) {
Util.shutdownExecutors();
logger.warn("Failed to create window: ", (Throwable)e);
return;
}
catch (Throwable t) {
CrashReport report = CrashReport.forThrowable(t, "Initializing game");
CrashReportCategory initialization = report.addCategory("Initialization");
NativeModuleLister.addCrashSection(initialization);
Minecraft.fillReport(newMinecraft, null, gameConfig.game.launchVersion, null, report);
Minecraft.crash(newMinecraft, gameConfig.location.gameDirectory, report);
return;
}
Minecraft minecraft = newMinecraft;
minecraft.run();
try {
minecraft.stop();
}
finally {
minecraft.destroy();
}
}
private static GameConfig.QuickPlayVariant getQuickPlayVariant(OptionSet optionSet, OptionSpec<String> quickPlaySingleplayerOption, OptionSpec<String> quickPlayMultiplayerOption, OptionSpec<String> quickPlayRealmsOption) {
long enabledOptions = Stream.of(quickPlaySingleplayerOption, quickPlayMultiplayerOption, quickPlayRealmsOption).filter(arg_0 -> ((OptionSet)optionSet).has(arg_0)).count();
if (enabledOptions == 0L) {
return GameConfig.QuickPlayVariant.DISABLED;
}
if (enabledOptions > 1L) {
throw new IllegalArgumentException("Only one quick play option can be specified");
}
if (optionSet.has(quickPlaySingleplayerOption)) {
String worldId = Main.unescapeJavaArgument(Main.parseArgument(optionSet, quickPlaySingleplayerOption));
return new GameConfig.QuickPlaySinglePlayerData(worldId);
}
if (optionSet.has(quickPlayMultiplayerOption)) {
String serverAddress = Main.unescapeJavaArgument(Main.parseArgument(optionSet, quickPlayMultiplayerOption));
return Optionull.mapOrDefault(serverAddress, GameConfig.QuickPlayMultiplayerData::new, GameConfig.QuickPlayVariant.DISABLED);
}
if (optionSet.has(quickPlayRealmsOption)) {
String realmId = Main.unescapeJavaArgument(Main.parseArgument(optionSet, quickPlayRealmsOption));
return Optionull.mapOrDefault(realmId, GameConfig.QuickPlayRealmsData::new, GameConfig.QuickPlayVariant.DISABLED);
}
return GameConfig.QuickPlayVariant.DISABLED;
}
private static @Nullable String unescapeJavaArgument(@Nullable String arg) {
if (arg == null) {
return null;
}
return StringEscapeUtils.unescapeJava((String)arg);
}
private static Optional<String> emptyStringToEmptyOptional(String xuid) {
return xuid.isEmpty() ? Optional.empty() : Optional.of(xuid);
}
private static OptionalInt ofNullable(@Nullable Integer value) {
return value != null ? OptionalInt.of(value) : OptionalInt.empty();
}
private static <T> @Nullable T parseArgument(OptionSet optionSet, OptionSpec<T> optionSpec) {
try {
return (T)optionSet.valueOf(optionSpec);
}
catch (Throwable t) {
ArgumentAcceptingOptionSpec options;
List defaultValues;
if (optionSpec instanceof ArgumentAcceptingOptionSpec && !(defaultValues = (options = (ArgumentAcceptingOptionSpec)optionSpec).defaultValues()).isEmpty()) {
return (T)defaultValues.get(0);
}
throw t;
}
}
private static boolean stringHasValue(@Nullable String string) {
return string != null && !string.isEmpty();
}
private static boolean hasValidUuid(OptionSpec<String> uuidOption, OptionSet optionSet, Logger logger) {
return optionSet.has(uuidOption) && Main.isUuidValid(uuidOption, optionSet, logger);
}
private static boolean isUuidValid(OptionSpec<String> uuidOption, OptionSet optionSet, Logger logger) {
try {
UndashedUuid.fromStringLenient((String)((String)uuidOption.value(optionSet)));
}
catch (IllegalArgumentException e) {
logger.warn("Invalid UUID: '{}", uuidOption.value(optionSet));
return false;
}
return true;
}
static {
System.setProperty("java.awt.headless", "true");
}
}