minecraft-src/net/minecraft/client/main/Main.java
2025-07-04 03:45:38 +03:00

303 lines
14 KiB
Java

package net.minecraft.client.main;
import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.properties.PropertyMap.Serializer;
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 joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.client.ClientBootstrap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.User;
import net.minecraft.client.User.Type;
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.obfuscate.DontObfuscate;
import net.minecraft.server.Bootstrap;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.NativeModuleLister;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.util.datafix.DataFixers;
import net.minecraft.util.profiling.jfr.JvmProfiler;
import org.apache.commons.lang3.StringEscapeUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class Main {
@DontObfuscate
public static void main(String[] strings) {
OptionParser optionParser = new OptionParser();
optionParser.allowsUnrecognizedOptions();
optionParser.accepts("demo");
optionParser.accepts("disableMultiplayer");
optionParser.accepts("disableChat");
optionParser.accepts("fullscreen");
optionParser.accepts("checkGlErrors");
OptionSpec<Void> optionSpec = optionParser.accepts("renderDebugLabels");
OptionSpec<Void> optionSpec2 = optionParser.accepts("jfrProfile");
OptionSpec<Void> optionSpec3 = optionParser.accepts("tracy");
OptionSpec<Void> optionSpec4 = optionParser.accepts("tracyNoImages");
OptionSpec<String> optionSpec5 = optionParser.accepts("quickPlayPath").withRequiredArg();
OptionSpec<String> optionSpec6 = optionParser.accepts("quickPlaySingleplayer").withRequiredArg();
OptionSpec<String> optionSpec7 = optionParser.accepts("quickPlayMultiplayer").withRequiredArg();
OptionSpec<String> optionSpec8 = optionParser.accepts("quickPlayRealms").withRequiredArg();
OptionSpec<File> optionSpec9 = optionParser.accepts("gameDir").withRequiredArg().<File>ofType(File.class).defaultsTo(new File("."));
OptionSpec<File> optionSpec10 = optionParser.accepts("assetsDir").withRequiredArg().ofType(File.class);
OptionSpec<File> optionSpec11 = optionParser.accepts("resourcePackDir").withRequiredArg().ofType(File.class);
OptionSpec<String> optionSpec12 = optionParser.accepts("proxyHost").withRequiredArg();
OptionSpec<Integer> optionSpec13 = optionParser.accepts("proxyPort").withRequiredArg().defaultsTo("8080").ofType(Integer.class);
OptionSpec<String> optionSpec14 = optionParser.accepts("proxyUser").withRequiredArg();
OptionSpec<String> optionSpec15 = optionParser.accepts("proxyPass").withRequiredArg();
OptionSpec<String> optionSpec16 = optionParser.accepts("username").withRequiredArg().defaultsTo("Player" + System.currentTimeMillis() % 1000L);
OptionSpec<String> optionSpec17 = optionParser.accepts("uuid").withRequiredArg();
OptionSpec<String> optionSpec18 = optionParser.accepts("xuid").withOptionalArg().defaultsTo("");
OptionSpec<String> optionSpec19 = optionParser.accepts("clientId").withOptionalArg().defaultsTo("");
OptionSpec<String> optionSpec20 = optionParser.accepts("accessToken").withRequiredArg().required();
OptionSpec<String> optionSpec21 = optionParser.accepts("version").withRequiredArg().required();
OptionSpec<Integer> optionSpec22 = optionParser.accepts("width").withRequiredArg().<Integer>ofType(Integer.class).defaultsTo(854);
OptionSpec<Integer> optionSpec23 = optionParser.accepts("height").withRequiredArg().<Integer>ofType(Integer.class).defaultsTo(480);
OptionSpec<Integer> optionSpec24 = optionParser.accepts("fullscreenWidth").withRequiredArg().ofType(Integer.class);
OptionSpec<Integer> optionSpec25 = optionParser.accepts("fullscreenHeight").withRequiredArg().ofType(Integer.class);
OptionSpec<String> optionSpec26 = optionParser.accepts("userProperties").withRequiredArg().defaultsTo("{}");
OptionSpec<String> optionSpec27 = optionParser.accepts("profileProperties").withRequiredArg().defaultsTo("{}");
OptionSpec<String> optionSpec28 = optionParser.accepts("assetIndex").withRequiredArg();
OptionSpec<String> optionSpec29 = optionParser.accepts("userType").withRequiredArg().defaultsTo("legacy");
OptionSpec<String> optionSpec30 = optionParser.accepts("versionType").withRequiredArg().defaultsTo("release");
OptionSpec<String> optionSpec31 = optionParser.nonOptions();
OptionSet optionSet = optionParser.parse(strings);
File file = parseArgument(optionSet, optionSpec9);
String string = parseArgument(optionSet, optionSpec21);
String string2 = "Pre-bootstrap";
Logger logger;
GameConfig gameConfig;
try {
if (optionSet.has(optionSpec2)) {
JvmProfiler.INSTANCE.start(net.minecraft.util.profiling.jfr.Environment.CLIENT);
}
if (optionSet.has(optionSpec3)) {
TracyBootstrap.setup();
}
Stopwatch stopwatch = Stopwatch.createStarted(Ticker.systemTicker());
Stopwatch stopwatch2 = Stopwatch.createStarted(Ticker.systemTicker());
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_TOTAL_TIME_MS, stopwatch);
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_PRE_WINDOW_MS, stopwatch2);
SharedConstants.tryDetectVersion();
TracyClient.reportAppInfo("Minecraft Java Edition " + SharedConstants.getCurrentVersion().getName());
CompletableFuture<?> completableFuture = DataFixers.optimize(DataFixTypes.TYPES_FOR_LEVEL_LIST);
CrashReport.preload();
logger = LogUtils.getLogger();
string2 = "Bootstrap";
Bootstrap.bootStrap();
ClientBootstrap.bootstrap();
GameLoadTimesEvent.INSTANCE.setBootstrapTime(Bootstrap.bootstrapDuration.get());
Bootstrap.validate();
string2 = "Argument parsing";
List<String> list = optionSet.valuesOf(optionSpec31);
if (!list.isEmpty()) {
logger.info("Completely ignored arguments: {}", list);
}
String string3 = optionSpec29.value(optionSet);
Type type = Type.byName(string3);
if (type == null) {
logger.warn("Unrecognized user type: {}", string3);
}
String string4 = parseArgument(optionSet, optionSpec12);
Proxy proxy = Proxy.NO_PROXY;
if (string4 != null) {
try {
proxy = new Proxy(java.net.Proxy.Type.SOCKS, new InetSocketAddress(string4, parseArgument(optionSet, optionSpec13)));
} catch (Exception var83) {
}
}
final String string5 = parseArgument(optionSet, optionSpec14);
final String string6 = parseArgument(optionSet, optionSpec15);
if (!proxy.equals(Proxy.NO_PROXY) && stringHasValue(string5) && stringHasValue(string6)) {
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(string5, string6.toCharArray());
}
});
}
int i = parseArgument(optionSet, optionSpec22);
int j = parseArgument(optionSet, optionSpec23);
OptionalInt optionalInt = ofNullable(parseArgument(optionSet, optionSpec24));
OptionalInt optionalInt2 = ofNullable(parseArgument(optionSet, optionSpec25));
boolean bl = optionSet.has("fullscreen");
boolean bl2 = optionSet.has("demo");
boolean bl3 = optionSet.has("disableMultiplayer");
boolean bl4 = optionSet.has("disableChat");
boolean bl5 = !optionSet.has(optionSpec4);
boolean bl6 = optionSet.has(optionSpec);
Gson gson = new GsonBuilder().registerTypeAdapter(PropertyMap.class, new Serializer()).create();
PropertyMap propertyMap = GsonHelper.fromJson(gson, parseArgument(optionSet, optionSpec26), PropertyMap.class);
PropertyMap propertyMap2 = GsonHelper.fromJson(gson, parseArgument(optionSet, optionSpec27), PropertyMap.class);
String string7 = parseArgument(optionSet, optionSpec30);
File file2 = optionSet.has(optionSpec10) ? parseArgument(optionSet, optionSpec10) : new File(file, "assets/");
File file3 = optionSet.has(optionSpec11) ? parseArgument(optionSet, optionSpec11) : new File(file, "resourcepacks/");
UUID uUID = hasValidUuid(optionSpec17, optionSet, logger)
? UndashedUuid.fromStringLenient(optionSpec17.value(optionSet))
: UUIDUtil.createOfflinePlayerUUID(optionSpec16.value(optionSet));
String string8 = optionSet.has(optionSpec28) ? optionSpec28.value(optionSet) : null;
String string9 = optionSet.valueOf(optionSpec18);
String string10 = optionSet.valueOf(optionSpec19);
String string11 = parseArgument(optionSet, optionSpec5);
String string12 = unescapeJavaArgument(parseArgument(optionSet, optionSpec6));
String string13 = unescapeJavaArgument(parseArgument(optionSet, optionSpec7));
String string14 = unescapeJavaArgument(parseArgument(optionSet, optionSpec8));
User user = new User(
optionSpec16.value(optionSet), uUID, optionSpec20.value(optionSet), emptyStringToEmptyOptional(string9), emptyStringToEmptyOptional(string10), type
);
gameConfig = new GameConfig(
new GameConfig.UserData(user, propertyMap, propertyMap2, proxy),
new DisplayData(i, j, optionalInt, optionalInt2, bl),
new GameConfig.FolderData(file, file3, file2, string8),
new GameConfig.GameData(bl2, string, string7, bl3, bl4, bl5, bl6),
new GameConfig.QuickPlayData(string11, string12, string13, string14)
);
Util.startTimerHackThread();
completableFuture.join();
} catch (Throwable var84) {
CrashReport crashReport = CrashReport.forThrowable(var84, string2);
CrashReportCategory crashReportCategory = crashReport.addCategory("Initialization");
NativeModuleLister.addCrashSection(crashReportCategory);
Minecraft.fillReport(null, null, string, null, crashReport);
Minecraft.crash(null, file, crashReport);
return;
}
Thread thread = new Thread("Client Shutdown Thread") {
public void run() {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft != null) {
IntegratedServer integratedServer = minecraft.getSingleplayerServer();
if (integratedServer != null) {
integratedServer.halt(true);
}
}
}
};
thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(logger));
Runtime.getRuntime().addShutdownHook(thread);
Minecraft minecraft = null;
try {
Thread.currentThread().setName("Render thread");
RenderSystem.initRenderThread();
minecraft = new Minecraft(gameConfig);
} catch (SilentInitException var81) {
Util.shutdownExecutors();
logger.warn("Failed to create window: ", (Throwable)var81);
return;
} catch (Throwable var82) {
CrashReport crashReport2 = CrashReport.forThrowable(var82, "Initializing game");
CrashReportCategory crashReportCategory2 = crashReport2.addCategory("Initialization");
NativeModuleLister.addCrashSection(crashReportCategory2);
Minecraft.fillReport(minecraft, null, gameConfig.game.launchVersion, null, crashReport2);
Minecraft.crash(minecraft, gameConfig.location.gameDirectory, crashReport2);
return;
}
Minecraft minecraft2 = minecraft;
minecraft.run();
try {
minecraft2.stop();
} finally {
minecraft.destroy();
}
}
@Nullable
private static String unescapeJavaArgument(@Nullable String arg) {
return arg == null ? null : StringEscapeUtils.unescapeJava(arg);
}
private static Optional<String> emptyStringToEmptyOptional(String input) {
return input.isEmpty() ? Optional.empty() : Optional.of(input);
}
private static OptionalInt ofNullable(@Nullable Integer value) {
return value != null ? OptionalInt.of(value) : OptionalInt.empty();
}
/**
* Gets the value of a specified command-line parameter from an OptionSet. If it doesn't exist, it returns the default value for the parameter.
*/
@Nullable
private static <T> T parseArgument(OptionSet set, OptionSpec<T> option) {
try {
return set.valueOf(option);
} catch (Throwable var5) {
if (option instanceof ArgumentAcceptingOptionSpec<T> argumentAcceptingOptionSpec) {
List<T> list = argumentAcceptingOptionSpec.defaultValues();
if (!list.isEmpty()) {
return (T)list.get(0);
}
}
throw var5;
}
}
/**
* Returns {@code true} if the given string is neither null nor empty.
*/
private static boolean stringHasValue(@Nullable String str) {
return str != null && !str.isEmpty();
}
private static boolean hasValidUuid(OptionSpec<String> uuidOption, OptionSet options, Logger logger) {
return options.has(uuidOption) && isUuidValid(uuidOption, options, logger);
}
private static boolean isUuidValid(OptionSpec<String> uuidOption, OptionSet optionSet, Logger logger) {
try {
UndashedUuid.fromStringLenient(uuidOption.value(optionSet));
return true;
} catch (IllegalArgumentException var4) {
logger.warn("Invalid UUID: '{}", uuidOption.value(optionSet));
return false;
}
}
static {
System.setProperty("java.awt.headless", "true");
}
}