Files
MRCC/launcher/PCL-CE/Plain Craft Launcher 2/Pages/PageLaunch/PageLaunchLeft.xaml.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

1034 lines
43 KiB
C#

using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Utils;
using PCL.Network;
namespace PCL;
public partial class PageLaunchLeft
{
private double actualUsedHeight;
private double actualUsedWidth;
private int btnLaunchState;
private string _btnLaunchLanguage;
private McInstance btnLaunchVersion;
private bool isHeightAnimating;
public interface ILoginPage { void Reload(); }
private enum LaunchButtonAction
{
Loading,
Launch,
Download,
Disabled
}
private LaunchButtonAction _launchButtonAction;
private static string StageWaitWindow => Lang.Text("Minecraft.Launch.Stage.WaitWindow");
private static string StageEnd => Lang.Text("Minecraft.Launch.Stage.End");
private static string StageRoot => Lang.Text("Minecraft.Launch.Stage.Root");
// 加载当前实例
private bool isLoad;
private bool isLoadFinished;
// 尺寸改变动画
private bool isWidthAnimating;
private double showProgress;
public PageLaunchLeft()
{
InitializeComponent();
Loaded += PageLaunchLeft_Loaded;
WeakLanguageChanged.Add(this, OnLanguageChanged);
// Handles
BtnInstance.Click += BtnInstance_Click;
BtnLaunch.Click += BtnLaunch_Click;
BtnLaunch.Loaded += (_, _) => RefreshButtonsUI();
BtnCancel.Click += BtnCancel_Click;
BtnMore.Click += BtnMore_Click;
PanLaunchingInfo.SizeChanged += PanLaunchingInfo_SizeChangedW;
PanLaunchingInfo.SizeChanged += PanLaunchingInfo_SizeChangedH;
}
private static void OnLanguageChanged(PageLaunchLeft page) => ModBase.RunInUi(page.RefreshButtonsUI);
public void PageLaunchLeft_Loaded(object sender, RoutedEventArgs e)
{
if (isLoad)
RefreshPage(false);
AprilPosTrans.X = 0d;
AprilPosTrans.Y = 0d;
if (isLoad)
return;
isLoad = true;
ModAnimation.AniControlEnabled += 1;
// 开始按钮
ModInstanceList.mcInstanceListLoader.LoadingStateChanged += (_, _) => RefreshButtonsUI();
ModFolder.mcFolderListLoader.LoadingStateChanged += (_, _) => RefreshButtonsUI();
RefreshButtonsUI();
// 初始化档案
ModProfile.GetProfile();
if (!(ModProfile.profileList.Count == 0) && ModProfile.lastUsedProfile >= 0 &&
ModProfile.lastUsedProfile < ModProfile.profileList.Count)
ModProfile.selectedProfile = ModProfile.profileList[ModProfile.lastUsedProfile];
// 加载实例
ModBase.RunInNewThread(() =>
{
// 自动整合包安装:准备
string packInstallPath = null;
if (File.Exists(Path.Combine(ModBase.exePath, "modpack.zip")))
packInstallPath = Path.Combine(ModBase.exePath, "modpack.zip");
if (File.Exists(Path.Combine(ModBase.exePath, "modpack.mrpack")))
packInstallPath = Path.Combine(ModBase.exePath, "modpack.mrpack");
if (packInstallPath is not null)
{
ModBase.Log("[Launch] 需自动安装整合包:" + packInstallPath, ModBase.LogLevel.Debug);
States.Game.SelectedFolder = @"$.minecraft\";
if (!Directory.Exists(ModBase.exePath + @".minecraft\"))
{
Directory.CreateDirectory(ModBase.exePath + @".minecraft\");
Directory.CreateDirectory(ModBase.exePath + @".minecraft\versions\");
ModFolder.McFolderLauncherProfilesJsonCreate(ModBase.exePath + @".minecraft\");
}
PageSelectLeft.AddFolder(ModBase.exePath + @".minecraft\",
ModBase.GetFolderNameFromPath(ModBase.exePath), false);
ModFolder.mcFolderListLoader.WaitForExit();
}
// 确认 Minecraft 文件夹存在
ModFolder.mcFolderSelected =
States.Game.SelectedFolder.ToString().Replace("$", ModBase.exePath);
if (string.IsNullOrEmpty(ModFolder.mcFolderSelected) || !Directory.Exists(ModFolder.mcFolderSelected))
{
// 无效的文件夹
if (string.IsNullOrEmpty(ModFolder.mcFolderSelected))
ModBase.Log("[Launch] 没有已储存的 Minecraft 文件夹");
else
ModBase.Log("[Launch] Minecraft 文件夹无效,该文件夹已不存在:" + ModFolder.mcFolderSelected,
ModBase.LogLevel.Debug);
ModFolder.mcFolderListLoader.WaitForExit(isForceRestart: true);
States.Game.SelectedFolder = ModFolder.mcFolderList[0].Location.Replace(ModBase.exePath, "$");
}
ModBase.Log("[Launch] Minecraft 文件夹:" + ModFolder.mcFolderSelected);
if (Config.Debug.AddRandomDelay)
Thread.Sleep(RandomUtils.NextInt(500, 3000));
// 自动整合包安装
if (packInstallPath is not null)
try
{
var installLoader = ModModpack.ModpackInstall(packInstallPath);
ModBase.Log("[Launch] 自动安装整合包已开始:" + packInstallPath);
installLoader.WaitForExit();
if (installLoader.State == ModBase.LoadState.Finished)
{
ModBase.Log("[Launch] 自动安装整合包成功,清理安装包:" + packInstallPath);
if (File.Exists(packInstallPath))
File.Delete(packInstallPath);
}
}
catch (ModBase.CancelledException ex)
{
ModBase.Log(ex, "自动安装整合包被用户取消:" + packInstallPath);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.InstallPack", packInstallPath),
ModBase.LogLevel.Msgbox,
userSummary: Lang.Text("Select.Folder.Error.InstallPack", packInstallPath));
}
// 确认 Minecraft 版本实例
var selection = States.Game.SelectedInstance;
var instance = selection == "" ? null : new McInstance(selection);
if (instance is null || !instance.PathInstance.StartsWithF(ModFolder.mcFolderSelected) ||
!instance.Check())
{
// 无效的实例
ModBase.Log("[Launch] 当前选择的 Minecraft 实例无效:" + (instance is null ? "null" : instance.PathInstance),
instance is null ? ModBase.LogLevel.Normal : ModBase.LogLevel.Debug);
if (ModInstanceList.mcInstanceListLoader.State != ModBase.LoadState.Finished)
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\", true);
if (ModInstanceList.mcInstanceList.Count == 0 ||
ModInstanceList.mcInstanceList.First().Value[0].Logo.Contains("RedstoneBlock"))
{
instance = null;
States.Game.SelectedInstance = "";
ModBase.Log("[Launch] 无可用 Minecraft 实例");
}
else
{
instance = ModInstanceList.mcInstanceList.First().Value[0];
States.Game.SelectedInstance = instance.Name;
ModBase.Log("[Launch] 自动选择 Minecraft 实例:" + instance.PathInstance);
}
}
ModBase.RunInUi(() =>
{
ModInstanceList.McMcInstanceSelected = instance; // 绕这一圈是为了避免 McInstanceCheck 触发第二次实例改变
isLoadFinished = true;
RefreshButtonsUI();
RefreshPage(false); // 有可能选择的版本变化了,需要重新刷新
// If IsProfileVaild() = "" Then McLoginLoader.Start() '自动登录
});
}, "Instance Check", ThreadPriority.AboveNormal);
// 改变页面
RefreshPage(false);
ModAnimation.AniControlEnabled -= 1;
}
// 实例选择按钮
private void BtnInstance_Click(object sender, MouseButtonEventArgs e)
{
if (ModLaunch.mcLaunchLoader.State == ModBase.LoadState.Loading)
return;
ModMain.frmMain.PageChange(FormMain.PageType.InstanceSelect);
}
// 启动按钮
public void LaunchButtonClick()
{
if (ModLaunch.mcLaunchLoader.State == ModBase.LoadState.Loading || !BtnLaunch.IsEnabled ||
(ModMain.frmMain.pageRight is not null &&
ModMain.frmMain.pageRight.PageState != MyPageRight.PageStates.ContentStay &&
ModMain.frmMain.pageRight.PageState != MyPageRight.PageStates.ContentEnter))
return;
// 愚人节处理
if (ModMain.isAprilEnabled && !ModMain.isAprilGiveup)
{
ModMain.isAprilGiveup = true;
ModMain.frmLaunchLeft.AprilScaleTrans.ScaleX = 1d;
ModMain.frmLaunchLeft.AprilScaleTrans.ScaleY = 1d;
ModMain.frmLaunchLeft.AprilPosTrans.X = 0d;
ModMain.frmLaunchLeft.AprilPosTrans.Y = 0d;
ModMain.frmMain.BtnExtraApril.ShowRefresh();
}
// 实际的启动
switch (_launchButtonAction)
{
case LaunchButtonAction.Launch:
{
if (File.Exists(ModInstanceList.McMcInstanceSelected.PathInstance + ".pclignore"))
{
HintService.Hint(Lang.Text("Launch.Home.Instance.InstallingCannotLaunch"), HintType.Error);
return;
}
ModLaunch.McLaunchStart();
break;
}
case LaunchButtonAction.Download:
{
ModMain.frmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadInstall);
break;
}
}
}
public void RefreshButtonsUI()
{
if (!BtnLaunch.IsLoaded)
return;
// 获取当前状态
int currentState;
if (!isLoadFinished || ModInstanceList.mcInstanceListLoader.State == ModBase.LoadState.Loading ||
ModFolder.mcFolderListLoader.State == ModBase.LoadState.Loading)
{
currentState = 0;
}
else if (ModInstanceList.McMcInstanceSelected is null)
{
if (Config.Preference.Hide.PageDownload && !PageSetupUI.HiddenForceShow)
currentState = 1;
else
currentState = 2;
}
else
{
currentState = 3;
}
// 更新状态。
var currentLanguage = LocalizationService.CurrentLanguage.Code;
if (currentState == btnLaunchState &&
currentLanguage == _btnLaunchLanguage &&
((ModInstanceList.McMcInstanceSelected is null ? "" : ModInstanceList.McMcInstanceSelected.PathInstance) ?? "") ==
((btnLaunchVersion is null ? "" : btnLaunchVersion.PathInstance) ?? ""))
goto ExitRefresh;
_btnLaunchLanguage = currentLanguage;
btnLaunchVersion = ModInstanceList.McMcInstanceSelected;
btnLaunchState = currentState;
switch (currentState)
{
case 0:
{
_launchButtonAction = LaunchButtonAction.Loading;
ModBase.Log("[Minecraft] 启动按钮:正在加载 Minecraft 实例");
ModMain.frmLaunchLeft.BtnLaunch.Text = Lang.Text("Launch.Home.Button.Loading");
ModMain.frmLaunchLeft.BtnLaunch.IsEnabled = false;
ModMain.frmLaunchLeft.LabVersion.Text = Lang.Text("Launch.Home.Instance.Loading");
ModMain.frmLaunchLeft.BtnInstance.IsEnabled = false;
ModMain.frmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed;
break;
}
case 1:
{
_launchButtonAction = LaunchButtonAction.Disabled;
ModBase.Log("[Minecraft] 启动按钮:无 Minecraft 实例,下载已禁用");
ModMain.frmLaunchLeft.BtnLaunch.Text = Lang.Text("Launch.Home.Button.Launch");
ModMain.frmLaunchLeft.BtnLaunch.IsEnabled = false;
ModMain.frmLaunchLeft.LabVersion.Text = Lang.Text("Launch.Home.Instance.NotFound");
ModMain.frmLaunchLeft.BtnInstance.IsEnabled = true;
ModMain.frmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed;
break;
}
case 2:
{
_launchButtonAction = LaunchButtonAction.Download;
ModBase.Log("[Minecraft] 启动按钮:无 Minecraft 实例,要求下载");
ModMain.frmLaunchLeft.BtnLaunch.Text = Lang.Text("Launch.Home.Button.Download");
ModMain.frmLaunchLeft.BtnLaunch.IsEnabled = true;
ModMain.frmLaunchLeft.LabVersion.Text = Lang.Text("Launch.Home.Instance.NotFound");
ModMain.frmLaunchLeft.BtnInstance.IsEnabled = true;
ModMain.frmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed;
break;
}
case 3:
{
_launchButtonAction = LaunchButtonAction.Launch;
ModBase.Log("[Minecraft] 启动按钮:Minecraft 实例:" + ModInstanceList.McMcInstanceSelected.PathInstance);
ModMain.frmLaunchLeft.BtnLaunch.Text = Lang.Text("Launch.Home.Button.Launch");
ModMain.frmLaunchLeft.BtnInstance.IsEnabled = true;
if (ModProfile.selectedProfile is not null)
BtnLaunch.IsEnabled = true;
else
BtnLaunch.IsEnabled = false;
ModMain.frmLaunchLeft.LabVersion.Text = ModInstanceList.McMcInstanceSelected.Name;
break;
}
// FrmLaunchLeft.BtnMore.Visibility = Visibility.Visible '由功能隐藏设置修改
}
ExitRefresh: ;
// 功能隐藏
ModMain.frmLaunchLeft.BtnInstance.Visibility =
!PageSetupUI.HiddenForceShow && Config.Preference.Hide.FunctionSelect
? Visibility.Collapsed
: Visibility.Visible;
if (currentState == 3) ModMain.frmLaunchLeft.BtnMore.Visibility = ModMain.frmLaunchLeft.BtnInstance.Visibility;
}
// 取消按钮
private void BtnCancel_Click(object sender, MouseButtonEventArgs e)
{
if (ModLaunch.mcLaunchLoaderReal is not null)
{
ModLaunch.mcLaunchLoaderReal.Abort();
ModLaunch.McLaunchLog("已取消启动");
try
{
if (ModLaunch.mcLaunchWatcher is not null)
ModLaunch.mcLaunchWatcher.Kill();
else if (ModLaunch.mcLaunchProcess is not null)
if (!ModLaunch.mcLaunchProcess.HasExited)
ModLaunch.mcLaunchProcess.Kill();
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Minecraft.Launch.Error.CancelProcess"),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Minecraft.Launch.Error.CancelProcess"));
}
}
}
// 实例设置按钮
private void BtnMore_Click(object sender, MouseButtonEventArgs e)
{
if (ModLaunch.mcLaunchLoader.State == ModBase.LoadState.Loading)
return;
ModInstanceList.McMcInstanceSelected.Load();
PageInstanceLeft.McInstance = ModInstanceList.McMcInstanceSelected;
if (File.Exists(ModInstanceList.McMcInstanceSelected.PathInstance + ".pclignore"))
{
HintService.Hint(Lang.Text("Launch.Home.Instance.InstallingCannotSetup"), HintType.Error);
return;
}
ModMain.frmMain.PageChange(FormMain.PageType.InstanceSetup);
}
/// <summary>
/// 每 0.2s 执行一次,刷新启动的数据 UI 显示。
/// </summary>
public void LaunchingRefresh()
{
try
{
if (ModLaunch.mcLaunchLoaderReal.State == ModBase.LoadState.Aborted)
return;
// 阶段状态获取
var isLaunched = false; // 是否已经启动游戏,只是在等待窗口
do
{
try
{
var exitTry = false;
foreach (var Loader in ModLaunch.mcLaunchLoaderReal.GetLoaderList(false))
if (Loader.State == ModBase.LoadState.Loading || Loader.State == ModBase.LoadState.Waiting)
{
LabLaunchingStage.Text = Loader.name;
isLaunched = Loader.name == StageWaitWindow || Loader.name == StageEnd;
exitTry = true;
break;
}
if (exitTry) break;
LabLaunchingStage.Text = Lang.Text("Launch.Status.Completed");
}
catch (Exception ex)
{
ModBase.Log(ex, "获取是否启动完成失败,可能是由于启动状态改变导致集合已修改");
return;
}
} while (false);
if (ModAnimation.AniIsRun("Launch State Page"))
isLaunched = false; // 等待页面切换动画完成
// 计算应显示的进度
var actualProgress = ModLaunch.mcLaunchLoaderReal.Progress;
if (actualProgress >= showProgress)
showProgress += (actualProgress - showProgress) * 0.2d + 0.005d; // 向实际进度靠一点
if (actualProgress <= showProgress)
showProgress = actualProgress; // 原来或处理后变得比实际进度高,直接回退
if (isLaunched)
showProgress = 1d; // 如果已经完成了,就不卖关子了
// 文本
LabLaunchingTitle.Text = isLaunched ? Lang.Text("Launch.Status.Title.Launched") :
ModLaunch.currentLaunchOptions.SaveBatch is null ? Lang.Text("Launch.Status.Title.Launching") : Lang.Text("Launch.Status.Title.ExportingScript");
LabLaunchingProgress.Text = Lang.Number(showProgress, "P2");
var hasLaunchDownloader = false;
try
{
foreach (var Loader in ModNet.NetManager.Tasks)
if (Loader.RealParent is not null && Loader.RealParent.name == StageRoot &&
Loader.State == ModBase.LoadState.Loading)
hasLaunchDownloader = true;
}
catch (Exception ex)
{
ModBase.Log(ex, "获取 Minecraft 启动下载器失败,可能是因为启动被取消");
hasLaunchDownloader = false;
}
LabLaunchingDownload.Text = ModBase.GetString(ModNet.NetManager.Speed) + "/s";
var shouldShowHint = Config.Preference.ShowLaunchingHint;
// 进度改变动画
var animList = new List<ModAnimation.AniData>
{
ModAnimation.AaGridLengthWidth(ProgressLaunchingFinished,
showProgress - ProgressLaunchingFinished.Width.Value, 260,
ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaGridLengthWidth(ProgressLaunchingUnfinished,
1d - showProgress - ProgressLaunchingUnfinished.Width.Value, 260,
ease: new ModAnimation.AniEaseOutFluent())
};
var isDownloadStateChanged =
hasLaunchDownloader == (LabLaunchingDownload.Visibility == Visibility.Collapsed);
if (isDownloadStateChanged)
{
LabLaunchingDownload.Visibility = Visibility.Visible;
LabLaunchingDownloadLeft.Visibility = Visibility.Visible;
animList.AddRange(new[]
{
ModAnimation.AaOpacity(LabLaunchingDownload,
(hasLaunchDownloader ? 1 : 0) - LabLaunchingDownload.Opacity, 100),
ModAnimation.AaOpacity(LabLaunchingDownloadLeft,
(hasLaunchDownloader ? 0.5d : 0d) - LabLaunchingDownloadLeft.Opacity, 100),
ModAnimation.AaCode(() =>
{
if (!hasLaunchDownloader)
{
LabLaunchingDownload.Visibility = Visibility.Collapsed;
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed;
}
}, 110)
});
}
var isProgressStateChanged = !isLaunched == (LabLaunchingProgress.Visibility == Visibility.Collapsed);
if (isProgressStateChanged)
{
LabLaunchingProgress.Visibility = Visibility.Visible;
LabLaunchingProgressLeft.Visibility = Visibility.Visible;
if (isLaunched && shouldShowHint) PanLaunchingHint.Visibility = Visibility.Visible;
animList.AddRange(new[]
{
ModAnimation.AaOpacity(LabLaunchingProgress, (!isLaunched ? 1 : 0) - LabLaunchingProgress.Opacity,
100),
ModAnimation.AaOpacity(LabLaunchingProgressLeft,
(!isLaunched ? 0.5d : 0d) - LabLaunchingProgressLeft.Opacity, 100),
ModAnimation.AaOpacity(PanLaunchingHint,
(isLaunched && shouldShowHint ? 1 : 0) - PanLaunchingHint.Opacity, 100)
});
}
ModAnimation.AniStart(animList, "Launching Progress");
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Minecraft.Launch.Error.RefreshInfo"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Minecraft.Launch.Error.RefreshInfo"));
}
}
private void PanLaunchingInfo_SizeChangedW(object sender, SizeChangedEventArgs e)
{
var deltaWidth = e.NewSize.Width - e.PreviousSize.Width;
if (e.PreviousSize.Width == 0d || isWidthAnimating || Math.Abs(deltaWidth) < 1d ||
PanLaunchingInfo.ActualWidth == 0d)
return;
ModAnimation.AniStart(new[]
{
ModAnimation.AaWidth(PanLaunchingInfo, deltaWidth, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaCode(() =>
{
isWidthAnimating = false;
PanLaunchingInfo.Width = actualUsedWidth;
}, after: true)
}, "Launching Info Width");
isWidthAnimating = true;
actualUsedWidth = PanLaunchingInfo.Width;
PanLaunchingInfo.Width = e.PreviousSize.Width;
}
private void PanLaunchingInfo_SizeChangedH(object sender, SizeChangedEventArgs e)
{
var deltaHeight = e.NewSize.Height - e.PreviousSize.Height;
if (e.PreviousSize.Height == 0d || isHeightAnimating || Math.Abs(deltaHeight) < 1d ||
PanLaunchingInfo.ActualHeight == 0d)
return;
ModAnimation.AniStart(new[]
{
ModAnimation.AaHeight(PanLaunchingInfo, deltaHeight, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaCode(() =>
{
isHeightAnimating = false;
PanLaunchingInfo.Height = actualUsedHeight;
}, after: true)
}, "Launching Info Height");
isHeightAnimating = true;
actualUsedHeight = PanLaunchingInfo.Height;
PanLaunchingInfo.Height = e.PreviousSize.Height;
}
// 启动游戏按钮
private void BtnLaunch_Click(object sender, MouseButtonEventArgs e)
{
LaunchButtonClick();
}
#region 切换大页面
/// <summary>
/// 切换至启动中页面。
/// </summary>
public void PageChangeToLaunching()
{
// 修改验证方式
switch (ModProfile.selectedProfile.Type)
{
case ModLaunch.McLoginType.Legacy:
{
LabLaunchingMethod.Text = Lang.Text("Launch.Account.Type.Offline");
break;
}
case ModLaunch.McLoginType.Ms:
{
LabLaunchingMethod.Text = Lang.Text("Launch.Account.Type.Microsoft");
break;
}
case ModLaunch.McLoginType.Auth:
{
LabLaunchingMethod.Text = Lang.Text("Launch.Account.Type.ThirdParty") + (!string.IsNullOrEmpty(ModProfile.selectedProfile.ServerName)
? " / " + ModProfile.selectedProfile.ServerName
: "");
break;
}
}
// 初始化页面
LabLaunchingName.Text = ModInstanceList.McMcInstanceSelected.Name;
LabLaunchingStage.Text = Lang.Text("Common.Action.Initialize");
LabLaunchingTitle.Text = ModLaunch.currentLaunchOptions?.SaveBatch is null
? Lang.Text("Launch.Status.Title.Launching")
: Lang.Text("Launch.Status.Title.ExportingScript");
LabLaunchingProgress.Text = Lang.Number(0d, "P2");
LabLaunchingProgress.Opacity = 1d;
LabLaunchingDownload.Visibility = Visibility.Visible;
LabLaunchingProgressLeft.Opacity = 0.6d;
LabLaunchingDownload.Visibility = Visibility.Visible;
LabLaunchingDownload.Text = ModBase.GetString(0) + "/s";
LabLaunchingDownload.Opacity = 0d;
LabLaunchingDownload.Visibility = Visibility.Collapsed;
LabLaunchingDownloadLeft.Opacity = 0d;
LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed;
ProgressLaunchingFinished.Width = new GridLength(0d, GridUnitType.Star);
ProgressLaunchingUnfinished.Width = new GridLength(1d, GridUnitType.Star);
PanLaunchingHint.Opacity = 0d;
PanLaunchingHint.Visibility = Visibility.Collapsed;
PanLaunchingInfo.Width = double.NaN; // 重置宽度改变动画
ModLaunch.mcLaunchProcess = null;
ModLaunch.mcLaunchWatcher = null;
var shouldShowHint = Config.Preference.ShowLaunchingHint;
if (shouldShowHint)
LabLaunchingHint.Text = PageLaunchRight.GetRandomHint(true, true);
else
LabLaunchingHint.Text = "";
// 初始化其他页面
PanInput.IsHitTestVisible = false;
PanLaunching.IsHitTestVisible = false;
LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Run;
PanLaunching.Visibility = Visibility.Visible;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaOpacity(PanInput, 0d, 50),
ModAnimation.AaOpacity(PanInput, -PanInput.Opacity, 110, ease: new ModAnimation.AniEaseInFluent(),
after: true),
ModAnimation.AaScaleTransform(PanInput, 1.2d - ((ScaleTransform)PanInput.RenderTransform).ScaleX, 160),
ModAnimation.AaOpacity(PanLaunching, 1d - PanLaunching.Opacity, 150, 100),
ModAnimation.AaScaleTransform(PanLaunching, 1d - ((ScaleTransform)PanLaunching.RenderTransform).ScaleX,
500, 100, new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaCode(() => PanLaunching.IsHitTestVisible = true, 150)
}, "Launch State Page"); // 略作延迟,这样如果预检测失败,不会出现奇怪的弹一下的动画
}
/// <summary>
/// 切换至登录页面。
/// </summary>
public void PageChangeToLogin()
{
if (PageGet(pageCurrent) is ILoginPage loginPage) loginPage.Reload();
PanInput.IsHitTestVisible = false;
PanLaunching.IsHitTestVisible = false;
LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Stop;
PanInput.Visibility = Visibility.Visible;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaOpacity(PanLaunching, -PanLaunching.Opacity, 150),
ModAnimation.AaScaleTransform(PanLaunching,
0.8d - ((ScaleTransform)PanLaunching.RenderTransform).ScaleX, 150,
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaOpacity(PanInput, 1d - PanInput.Opacity, 250, 50),
ModAnimation.AaScaleTransform(PanInput, 1d - ((ScaleTransform)PanInput.RenderTransform).ScaleX, 300, 50,
new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaCode(() => PanInput.IsHitTestVisible = true, 200)
}, "Launch State Page", true);
}
#endregion
#region 切换登录页面
private enum PageType
{
None,
Auth,
Ms,
Profile,
ProfileSkin,
Offline
}
/// <summary>
/// 当前页面的种类。
/// </summary>
private PageType pageCurrent = PageType.None;
private object PageGet(PageType type)
{
switch (type)
{
case PageType.Auth:
{
if (ModMain.frmLoginAuth is null)
ModMain.frmLoginAuth = new PageLoginAuth();
return ModMain.frmLoginAuth;
}
case PageType.Ms:
{
if (ModMain.frmLoginMs is null)
ModMain.frmLoginMs = new PageLoginMs();
return ModMain.frmLoginMs;
}
case PageType.Profile:
{
if (ModMain.frmLoginProfile is null)
ModMain.frmLoginProfile = new PageLoginProfile();
return ModMain.frmLoginProfile;
}
case PageType.ProfileSkin:
{
if (ModMain.frmLoginProfileSkin is null)
ModMain.frmLoginProfileSkin = new PageLoginProfileSkin();
return ModMain.frmLoginProfileSkin;
}
case PageType.Offline:
{
if (ModMain.frmLoginOffline is null)
ModMain.frmLoginOffline = new PageLoginOffline();
return ModMain.frmLoginOffline;
}
default:
{
throw new ArgumentOutOfRangeException("Type", "即将切换的登录分页编号越界");
}
}
}
/// <summary>
/// 切换现有登录页面种类,返回新页面的实例。
/// </summary>
/// <param name="type">新页面的种类。</param>
/// <param name="anim">是否显示动画。</param>
private object PageChange(PageType type, bool anim)
{
object pageNew = ModMain.frmLoginMs; // 初始化一个东西,避免在执行时出现异常导致雪崩
try
{
#region 确定更改的页面实例并实例化
if (pageCurrent == type)
return pageNew;
pageNew = PageGet(type);
#endregion
#region 切换页面
ModAnimation.AniStop("FrmLogin PageChange");
// 清除页面关联性
if (pageNew is FrameworkElement element && element.Parent is not null)
{
element.SetValue(ContentPresenter.ContentProperty, null);
}
if (anim)
{
// 动画
// 执行动画
Dispatcher.Invoke(() => ModAnimation.AniStart(new[]
{
ModAnimation.AaOpacity(PanLogin, -PanLogin.Opacity, 100, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaCode(() =>
{
ModAnimation.AniControlEnabled += 1;
PanLogin.Children.Clear();
PanLogin.Children.Add((UIElement)pageNew);
ModAnimation.AniControlEnabled -= 1;
}, 100),
ModAnimation.AaOpacity(PanLogin, 1d, 100, 120, new ModAnimation.AniEaseInFluent())
}, "FrmLogin PageChange"), DispatcherPriority.Render);
}
else
{
// 无动画
ModAnimation.AniControlEnabled += 1;
PanLogin.Children.Clear();
PanLogin.Children.Add((UIElement)pageNew);
ModAnimation.AniControlEnabled -= 1;
}
#endregion
pageCurrent = type;
return pageNew;
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Launch.Account.Error.SwitchPage", ModBase.GetStringFromEnum(type)),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Launch.Account.Error.SwitchPage", ModBase.GetStringFromEnum(type)));
return pageNew;
}
}
/// <summary>
/// 确认当前显示的子页面正确,并刷新该页面。
/// </summary>
/// <param name="anim">是否显示动画</param>
/// <param name="targetLoginType">目标验证方式,若正在创建档案需填</param>
public void RefreshPage(bool anim, ModLaunch.McLoginType targetLoginType = default)
{
var type = default(PageType);
if (targetLoginType != default)
{
if (targetLoginType == ModLaunch.McLoginType.Ms)
type = PageType.Ms;
if (targetLoginType == ModLaunch.McLoginType.Auth)
type = PageType.Auth;
if (targetLoginType == ModLaunch.McLoginType.Legacy)
type = PageType.Offline;
}
else if (ModProfile.selectedProfile is not null)
{
type = PageType.ProfileSkin;
BtnLaunch.IsEnabled = true;
}
else
{
type = PageType.Profile;
if (_launchButtonAction != LaunchButtonAction.Download)
BtnLaunch.IsEnabled = false;
}
// 刷新页面
if (pageCurrent == type)
return;
PageChange(type, anim);
}
#endregion
#region 皮肤
// 正版皮肤
public static ModLoader.LoaderTask<ModBase.EqualableList<string>, string> skinMs = new("Loader Skin Ms", SkinMsLoad,
SkinMsInput, ThreadPriority.AboveNormal);
private static ModBase.EqualableList<string> SkinMsInput()
{
// 获取名称
return new ModBase.EqualableList<string>
{ ModProfile.selectedProfile.Username, ModProfile.selectedProfile.Uuid };
}
private static void SkinMsLoad(ModLoader.LoaderTask<ModBase.EqualableList<string>, string> data)
{
// 清空已有皮肤
// 如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
ModBase.RunInUi(() =>
{
if (ModMain.frmLoginProfileSkin is not null && ModMain.frmLoginProfileSkin.Skin is not null)
ModMain.frmLoginProfileSkin.Skin.Clear();
});
// 获取 Url
var userName = data.input[0];
var uuid = data.input[1];
if (ModProfile.selectedProfile is not null)
{
userName = ModProfile.selectedProfile.Username;
uuid = ModProfile.selectedProfile.Uuid;
}
if (string.IsNullOrEmpty(userName))
{
data.output = ModBase.pathImage + "Skins/" + ModSkin.McSkinSex(ModProfile.GetOfflineUuid(userName)) +
".png";
ModBase.Log("[Minecraft] 获取微软正版皮肤失败,ID 为空");
goto Finish;
}
try
{
var result = ModSkin.McSkinGetAddress(uuid, "Ms");
if (data.IsAborted)
throw new ThreadInterruptedException("当前任务已取消:" + userName);
result = ModSkin.McSkinDownload(result);
if (data.IsAborted)
throw new ThreadInterruptedException("当前任务已取消:" + userName);
data.output = result;
}
catch (Exception ex)
{
if (ex is ThreadInterruptedException)
{
data.output = "";
ModBase.Log("[Minecraft] 已取消皮肤获取:" + userName);
return;
}
if (ex.ToString().Contains("429"))
{
data.output = ModBase.pathImage + "Skins/" +
ModSkin.McSkinSex(ModProfile.GetOfflineUuid(userName)) + ".png";
ModBase.Log(
Lang.Text("Launch.Skin.Error.MsRateLimited", userName),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Launch.Skin.Error.MsRateLimited", userName));
}
else if (ex.ToString().Contains("未设置自定义皮肤"))
{
data.output = ModBase.pathImage + "Skins/" +
ModSkin.McSkinSex(ModProfile.GetOfflineUuid(userName)) + ".png";
ModBase.Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载");
}
else
{
data.output = ModBase.pathImage + "Skins/" +
ModSkin.McSkinSex(ModProfile.GetOfflineUuid(userName)) + ".png";
ModBase.Log(
ex,
Lang.Text("Launch.Skin.Error.MsGet", userName),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Launch.Skin.Error.MsGet", userName));
}
}
Finish: ;
// 刷新显示
if (ModMain.frmLoginProfileSkin is not null && ReferenceEquals(ModMain.frmLoginProfileSkin.Skin.loader, data))
ModBase.RunInUi(ModMain.frmLoginProfileSkin.Skin.Load);
else if (!data.IsAborted) // 如果已经中断,Input 也被清空,就不会再次刷新
data.input = null; // 清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
}
// 离线皮肤
public static ModLoader.LoaderTask<ModBase.EqualableList<string>, string> skinLegacy = new("Loader Skin Legacy",
SkinLegacyLoad, SkinLegacyInput, ThreadPriority.AboveNormal);
private static ModBase.EqualableList<string> SkinLegacyInput()
{
return new ModBase.EqualableList<string>
{ ModProfile.selectedProfile.Username, ModProfile.selectedProfile.Uuid };
}
private static void SkinLegacyLoad(ModLoader.LoaderTask<ModBase.EqualableList<string>, string> data)
{
// 清空已有皮肤
ModBase.RunInUi(() =>
{
if (ModMain.frmLoginProfileSkin is not null && ModMain.frmLoginProfileSkin.Skin is not null)
ModMain.frmLoginProfileSkin.Skin.Clear();
});
data.output = ModBase.pathImage + "Skins/" + ModSkin.McSkinSex(data.input[1]) + ".png";
// 刷新显示
if (ModMain.frmLoginProfileSkin is not null && ReferenceEquals(ModMain.frmLoginProfileSkin.Skin.loader, data))
ModBase.RunInUi(() => ModMain.frmLoginProfileSkin.Skin.Load());
else if (!data.IsAborted) // 如果已经中断,Input 也被清空,就不会再次刷新
data.input = null; // 清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
}
// Authlib-Injector 皮肤
public static ModLoader.LoaderTask<ModBase.EqualableList<string>, string> skinAuth = new("Loader Skin Auth",
SkinAuthLoad, SkinAuthInput, ThreadPriority.AboveNormal);
private static ModBase.EqualableList<string> SkinAuthInput()
{
// 获取名称
return new ModBase.EqualableList<string>
{ ModProfile.selectedProfile.Username, ModProfile.selectedProfile.Uuid };
}
private static void SkinAuthLoad(ModLoader.LoaderTask<ModBase.EqualableList<string>, string> data)
{
// 清空已有皮肤
// 如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
ModBase.RunInUi(() =>
{
if (ModMain.frmLoginProfileSkin is not null && ModMain.frmLoginProfileSkin.Skin is not null)
ModMain.frmLoginProfileSkin.Skin.Clear();
});
// 获取 Url
var userName = data.input[0];
var uuid = data.input[1];
if (string.IsNullOrEmpty(userName))
{
data.output = ModBase.pathImage + "Skins/Steve.png";
ModBase.Log("[Minecraft] 获取 Authlib-Injector 皮肤失败,ID 为空");
goto Finish;
}
try
{
var result = ModSkin.McSkinGetAddress(uuid, "Auth");
if (data.IsAborted)
throw new ThreadInterruptedException("当前任务已取消:" + userName);
result = ModSkin.McSkinDownload(result);
if (data.IsAborted)
throw new ThreadInterruptedException("当前任务已取消:" + userName);
data.output = result;
}
catch (Exception ex)
{
if (ex is ThreadInterruptedException)
{
data.output = "";
return;
}
if (ex.ToString().Contains("429"))
{
data.output = ModBase.pathImage + "Skins/Steve.png";
ModBase.Log(
$"[Minecraft] 获取 Authlib-Injector 皮肤失败({userName}):获取皮肤太过频繁,请 5 分钟后再试!",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Launch.Skin.Error.AuthlibRateLimited"));
}
else if (ex.ToString().Contains("未设置自定义皮肤"))
{
data.output = ModBase.pathImage + "Skins/Steve.png";
ModBase.Log("[Minecraft] 用户未设置自定义皮肤,跳过皮肤加载");
}
else
{
data.output = ModBase.pathImage + "Skins/Steve.png";
ModBase.Log(
ex,
Lang.Text("Launch.Skin.Error.AuthGet", userName),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Launch.Skin.Error.AuthGet", userName));
}
}
Finish: ;
// 刷新显示
if (ModMain.frmLoginProfileSkin is not null && ReferenceEquals(ModMain.frmLoginProfileSkin.Skin.loader, data))
ModBase.RunInUi(ModMain.frmLoginProfileSkin.Skin.Load);
else if (!data.IsAborted) // 如果已经中断,Input 也被清空,就不会再次刷新
data.input = null; // 清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
}
// 全部皮肤加载器
// 需要放在其中元素的后面,否则会因为它提前被加载而莫名其妙变成 Nothing
public static List<ModLoader.LoaderTask<ModBase.EqualableList<string>, string>> skinLoaders = new()
{ skinMs, skinLegacy, skinAuth };
#endregion
}