using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Windows;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Minecraft;
using PCL.Core.Minecraft.Launch.Utils;
using PCL.Core.Utils;
using PCL.Core.Utils.OS;
using PCL.Core.Utils.Secret;
using PCL.Network;
using PCL.Core.IO.Net.Http;
using PCL.Core.Minecraft.IdentityModel.Yggdrasil;
using System.Globalization;
namespace PCL;
public static class ModLaunch
{
public const string mesaLoaderWindowsVersion = "26.0.4";
#region 预检测
private static void McLaunchPrecheck()
{
if (Config.Debug.AddRandomDelay)
Thread.Sleep(RandomUtils.NextInt(100, 2000));
// 检查路径
if (ModInstanceList.McMcInstanceSelected.PathIndie.Contains("!") ||
ModInstanceList.McMcInstanceSelected.PathIndie.Contains(";"))
throw new Exception(Lang.Text("Minecraft.Launch.Precheck.InvalidPathChars", ModInstanceList.McMcInstanceSelected.PathIndie));
if (ModInstanceList.McMcInstanceSelected.PathInstance.Contains("!") ||
ModInstanceList.McMcInstanceSelected.PathInstance.Contains(";"))
throw new Exception(Lang.Text("Minecraft.Launch.Precheck.InvalidPathChars", ModInstanceList.McMcInstanceSelected.PathInstance));
if (ModBase.IsUtf8CodePage() && !States.Hint.NonAsciiGamePath &&
!ModInstanceList.McMcInstanceSelected.PathInstance.IsASCII())
{
var userChoice = ModMain.MyMsgBox(
Lang.Text("Minecraft.Launch.Precheck.NonAsciiPath.Message", ModInstanceList.McMcInstanceSelected.Name),
Lang.Text("Minecraft.Launch.Precheck.NonAsciiPath.Title"), Lang.Text("Minecraft.Launch.Precheck.NonAsciiPath.Continue"), Lang.Text("Minecraft.Launch.Precheck.NonAsciiPath.Back"), Lang.Text("Common.Hint.DoNotShowAgain"));
if (userChoice == 2) throw new Exception("$$");
if (userChoice == 3) States.Hint.NonAsciiGamePath = true;
}
// 检查实例
if (ModInstanceList.McMcInstanceSelected is null)
throw new Exception(Lang.Text("Minecraft.Launch.Precheck.NoInstance"));
ModInstanceList.McMcInstanceSelected.Load();
if (ModInstanceList.McMcInstanceSelected.state == McInstanceState.Error)
throw new Exception(Lang.Text("Minecraft.Launch.Precheck.InstanceError", ModInstanceList.McMcInstanceSelected.Desc));
// 检查输入信息
var checkResult = "";
ModBase.RunInUiWait(() => checkResult = ModProfile.IsProfileValid());
if (ModProfile.selectedProfile is null) // 没选档案
{
checkResult = Lang.Text("Minecraft.Launch.Precheck.NoProfile");
}
else if (ModInstanceList.McMcInstanceSelected.Info.HasLabyMod ||
Config.InstanceAuth.LoginRequirementSolution[ModInstanceList.McMcInstanceSelected?.PathInstance] == 1) // 要求正版验证
{
if (ModProfile.selectedProfile.Type != McLoginType.Ms) checkResult = Lang.Text("Minecraft.Launch.Precheck.RequireMicrosoft");
}
else if (Config.InstanceAuth.LoginRequirementSolution[ModInstanceList.McMcInstanceSelected?.PathInstance] == 2) // 要求第三方验证
{
if (ModProfile.selectedProfile.Type != McLoginType.Auth)
checkResult = Lang.Text("Minecraft.Launch.Precheck.RequireThirdParty");
else if (ModProfile.selectedProfile.Server.BeforeLast("/authserver") !=
Config.InstanceAuth.AuthServerAddress[ModInstanceList.McMcInstanceSelected?.PathInstance])
checkResult = Lang.Text("Minecraft.Launch.Precheck.AuthServerMismatch");
}
else if (Config.InstanceAuth.LoginRequirementSolution[ModInstanceList.McMcInstanceSelected?.PathInstance] == 3) // 要求正版验证或第三方验证
{
if (ModProfile.selectedProfile.Type == McLoginType.Legacy)
checkResult = Lang.Text("Minecraft.Launch.Precheck.RequireMicrosoftOrThirdParty");
else if (ModProfile.selectedProfile.Type == McLoginType.Auth &&
ModProfile.selectedProfile.Server.BeforeLast("/authserver") !=
Config.InstanceAuth.AuthServerAddress[ModInstanceList.McMcInstanceSelected?.PathInstance])
checkResult = Lang.Text("Minecraft.Launch.Precheck.AuthServerMismatch");
}
if (!string.IsNullOrEmpty(checkResult))
throw new ArgumentException(checkResult);
#if BETA
if (currentLaunchOptions?.SaveBatch is null) // 保存脚本时不提示
{
ModBase.RunInNewThread(() =>
{
switch (States.System.LaunchCount)
{
case 10:
case 20:
case 40:
case 60:
case 80:
case 100:
case 120:
case 150:
case 200:
case 250:
case 300:
case 350:
case 400:
case 500:
case 600:
case 700:
case 800:
case 900:
case 1000:
case 1200:
case 1400:
case 1600:
case 1800:
case 2000:
if (ModMain.MyMsgBox(
Lang.Text("Minecraft.Launch.Donate.Message", States.System.LaunchCount),
Lang.Text("Minecraft.Launch.Donate.Title", States.System.LaunchCount),
Lang.Text("Minecraft.Launch.Donate.Support"),
Lang.Text("Minecraft.Launch.Donate.Decline")) == 1)
{
ModBase.OpenWebsite("https://afdian.com/a/LTCat");
}
break;
}
}, "Donate");
}
#endif
#if DEBUG || DEBUGCI
return;
#endif
// 正版购买提示
if (!ModProfile.profileList.Any(x => x.Type == McLoginType.Ms))
{
if (Lang.IsFeaturesUnrestricted)
{
if (ModMain.MyMsgBox(
Lang.Text("Minecraft.Launch.PurchaseHint.Message"),
Lang.Text("Minecraft.Launch.PurchaseHint.Title"), Lang.Text("Minecraft.Launch.PurchaseHint.Purchase"), Lang.Text("Minecraft.Launch.PurchaseHint.Later")) ==
1)
ModBase.OpenWebsite(
"https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj");
}
else
{
switch (ModMain.MyMsgBox(Lang.Text("Minecraft.Launch.AccountVerification.Message"),
Lang.Text("Minecraft.Launch.AccountVerification.Title"),
Lang.Text("Minecraft.Launch.AccountVerification.Purchase"),
Lang.Text("Minecraft.Launch.AccountVerification.Demo"),
Lang.Text("Minecraft.Launch.AccountVerification.Back"),
button1Action: () =>
ModBase.OpenWebsite(
"https://www.xbox.com/zh-cn/games/store/minecraft-java-bedrock-edition-for-pc/9nxp44l49shj")))
{
case 2:
{
HintService.Hint(Lang.Text("Minecraft.Launch.DemoMode"), HintType.Error);
currentLaunchOptions.ExtraArgs.Add("--demo");
break;
}
case 3:
{
throw new Exception("$$");
}
}
}
}
}
#endregion
#region 开始
public static bool isLaunching;
public static McLaunchOptions currentLaunchOptions;
public class McLaunchOptions
{
///
/// 额外的启动参数。
///
public List ExtraArgs = new();
///
/// 强行指定启动的 MC 实例。
/// 默认值:Nothing。使用 McInstanceCurrent。
///
public McInstance instance = null;
///
/// 是否为 “测试游戏” 按钮启动的游戏。
/// 如果是,则显示游戏实时日志。
///
public bool IsTest = false;
///
/// 将启动脚本保存到该地址,然后取消启动。这同时会改变启动时的提示等。
/// 默认值:Nothing。不保存。
///
public string SaveBatch = null;
///
/// 强制指定在启动后进入的服务器 IP。
/// 默认值:Nothing。使用实例设置的值。
///
public string ServerIp = null;
///
/// 指定在启动之后进入的存档名称。
/// 默认值:Nothing。使用实例设置的值。
///
public string WorldName = null;
}
///
/// 尝试启动 Minecraft。必须在 UI 线程调用。
/// 返回是否实际开始了启动(如果没有,则一定弹出了错误提示)。
///
public static bool McLaunchStart(McLaunchOptions options = null)
{
isLaunching = true;
currentLaunchOptions = options ?? new McLaunchOptions();
// 预检查
if (!ModBase.RunInUi())
throw new Exception("McLaunchStart 必须在 UI 线程调用!");
if (mcLaunchLoader.State == ModBase.LoadState.Loading)
{
HintService.Hint(Lang.Text("Minecraft.Launch.Error.AlreadyLaunching"), HintType.Error);
isLaunching = false;
return false;
}
// 强制切换需要启动的实例
if (currentLaunchOptions.instance is not null &&
ModInstanceList.McMcInstanceSelected != currentLaunchOptions.instance)
{
McLaunchLog("在启动前切换到实例 " + currentLaunchOptions.instance.Name);
// 检查实例
currentLaunchOptions.instance.Load();
if (currentLaunchOptions.instance.state == McInstanceState.Error)
{
HintService.Hint(Lang.Text("Minecraft.Launch.Error.CannotLaunch", currentLaunchOptions.instance.Desc), HintType.Error);
isLaunching = false;
return false;
}
// 切换实例
ModInstanceList.McMcInstanceSelected = currentLaunchOptions.instance;
States.Game.SelectedInstance = ModInstanceList.McMcInstanceSelected.Name;
ModMain.frmLaunchLeft.RefreshButtonsUI();
ModMain.frmLaunchLeft.RefreshPage(false);
}
ModMain.frmMain.AprilGiveup();
// 禁止进入实例选择页面(否则就可以在启动中切换 McInstanceCurrent 了)
ModMain.frmMain.pageStack =
ModMain.frmMain.pageStack.Where(p => p.page != FormMain.PageType.InstanceSelect).ToList();
// 实际启动加载器
mcLaunchLoader.Start(options, true);
return true;
}
///
/// 记录启动日志。
///
public static void McLaunchLog(string text)
{
text = McLogFilter.FilterUserName(McLogFilter.FilterAccessToken(text, '*'), '*');
ModBase.RunInUi(() =>
ModMain.frmLaunchRight.LabLog.Text += "\r\n" + "[" + TimeUtils.GetTimeNow() + "] " + text);
ModBase.Log("[Launch] " + text);
}
// 启动状态切换
public static ModLoader.LoaderTask mcLaunchLoader = new("Loader Launch", McLaunchStart)
{ OnStateChanged = a => McLaunchState((dynamic)a) };
public static ModLoader.LoaderCombo