import com.mojang.blaze3d.platform.DisplayData; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.authlib.properties.PropertyMap; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.User; import net.minecraft.client.main.GameConfig; import net.minecraft.server.Bootstrap; import java.io.File; import java.net.Proxy; import java.util.OptionalInt; /** * mcfold scaffold —— 只起 MC 的**渲染栈**,不进游戏主循环。 * * 依据(反编译 net.minecraft.client.main.Main 的实测序列): * Bootstrap.bootStrap() * RenderSystem.initRenderThread(); beginInitialization(); * new Minecraft(gameConfig) ← 构造即完成 GL/资源/渲染器初始化 * RenderSystem.finishInitialization() * 之后 Main 才起 Game thread 跑主循环 —— 我们**不**走那步。 */ public class Scaffold { public static void main(String[] args) throws Exception { String gameDir = arg(args, "--gameDir", "/home/newqqagent/mcview/instances/default"); String assets = arg(args, "--assets", "/home/newqqagent/mcview/store/vanilla-1.20.1/assets"); // ★ 索引名必须是 assets/indexes/.json 中真实存在的那个。 // version.json 的 assetIndex.id 是 "5",但 MCLC 落盘用 version.number(1.20.1) // —— 传 "5" 会导致索引加载失败,所有资源/icons 全部找不到(实测踩过)。 String assetIdx = arg(args, "--assetIndex", "1.20.1"); int width = Integer.parseInt(arg(args, "--width", "1280")); int height = Integer.parseInt(arg(args, "--height", "720")); System.out.println("[scaffold] bootstrap..."); // ★ 官方 Main 的实测序列:tryDetectVersion 必须在 bootStrap 之前, // 否则 DataFixers. 抛 "Game version not set" SharedConstants.tryDetectVersion(); SharedConstants.enableDataFixerOptimizations(); Bootstrap.bootStrap(); System.out.println("[scaffold] bootstrap ok, mcVersion=" + SharedConstants.getCurrentVersion().getName()); File runDir = new File(gameDir); File assetsDir = new File(assets); User user = new User("mcfold_cam", "0".repeat(32), "mcfold_cam", java.util.Optional.empty(), java.util.Optional.empty(), User.Type.LEGACY); PropertyMap props = new PropertyMap(); GameConfig cfg = new GameConfig( new GameConfig.UserData(user, props, props, Proxy.NO_PROXY), new DisplayData(width, height, OptionalInt.empty(), OptionalInt.empty(), false), new GameConfig.FolderData( // ★ 顺序是 (gameDirectory, resourcePackDirectory, assetDirectory, assetIndex) // 反着写会把 assets 当游戏目录,导致资源全找不到(实测踩过:icons/icon_16x16.png 404) runDir, new File(runDir, "resourcepacks"), assetsDir, assetIdx), new GameConfig.GameData( false, // demo "mcfold", // version "release", // versionType false, // disableMultiplayer false), // disableChat new GameConfig.QuickPlayData(null, null, null, null) ); System.out.println("[scaffold] RenderSystem init..."); RenderSystem.initRenderThread(); RenderSystem.beginInitialization(); System.out.println("[scaffold] new Minecraft(cfg) ..."); Minecraft mc = new Minecraft(cfg); RenderSystem.finishInitialization(); System.out.println("[scaffold] OK Minecraft constructed"); // 检查渲染栈关键对象是否就绪(不启主循环) System.out.println("[scaffold] window=" + mc.getWindow().getWidth() + "x" + mc.getWindow().getHeight()); System.out.println("[scaffold] levelRenderer=" + (mc.levelRenderer != null ? "present" : "null")); System.out.println("[scaffold] gameRenderer=" + (mc.gameRenderer != null ? "present" : "null")); System.out.println("[scaffold] level=" + (mc.level != null ? "present" : "null (не connected)")); System.out.println("[scaffold] DONE (no main loop)"); } private static String arg(String[] a, String key, String def) { for (int i = 0; i < a.length - 1; i++) if (a[i].equals(key)) return a[i + 1]; return def; } }