Imports System.Windows.Threading Public Class PageLaunchLeft '加载当前版本 Private IsLoad As Boolean = False Private IsLoadFinished As Boolean = False Public Sub PageLaunchLeft_Loaded() Handles Me.Loaded If IsLoad Then RefreshPage(True, False) AprilPosTrans.X = 0 AprilPosTrans.Y = 0 If IsLoad Then Return IsLoad = True AniControlEnabled += 1 '开始按钮 AddHandler McInstanceListLoader.LoadingStateChanged, AddressOf RefreshButtonsUI AddHandler McFolderListLoader.LoadingStateChanged, AddressOf RefreshButtonsUI RefreshButtonsUI() '加载版本 RunInNewThread( Sub() '自动整合包安装:准备 Dim PackInstallPath As String = Nothing If FileUtils.Exists(Paths.Base & "modpack.zip") Then PackInstallPath = Paths.Base & "modpack.zip" If FileUtils.Exists(Paths.Base & "modpack.mrpack") Then PackInstallPath = Paths.Base & "modpack.mrpack" If PackInstallPath IsNot Nothing Then Logger.Warn($"需自动安装整合包:{PackInstallPath}") PageSelectLeft.CreateMcFolderInCurrentPath() McFolderListLoader.WaitForExit() End If '确认 Minecraft 文件夹存在 If McFolderSelected = "" OrElse Not DirectoryUtils.Exists(McFolderSelected) Then '无效的文件夹 If McFolderSelected = "" Then Logger.Info("没有已储存的 Minecraft 文件夹") Else Logger.Warn($"Minecraft 文件夹无效,该文件夹已不存在:{McFolderSelected}") End If McFolderListLoader.WaitForExit(IsForceRestart:=True) McFolderSelected = McFolderList.First.Location End If If Settings.Get(Of Boolean)("SystemDebugDelay") Then Thread.Sleep(RandomInteger(500, 3000)) '自动整合包安装 If PackInstallPath IsNot Nothing Then Try Dim InstallLoader = ModpackInstall(PackInstallPath) Logger.Info($"自动安装整合包已开始:{PackInstallPath}") InstallLoader.WaitForExit() If InstallLoader.State = LoadState.Finished Then Logger.Info($"自动安装整合包成功,清理安装包:{PackInstallPath}") FileUtils.Delete(PackInstallPath) End If Catch ex As Exception If ex.IsCanceled Then Logger.Info($"自动安装整合包已取消:{PackInstallPath}") Else Logger.Error(ex, $"自动安装整合包失败:{PackInstallPath}", LogBehavior.Alert) End If End Try End If '确认 Minecraft 版本存在 Dim Selection As String = ReadIni(McFolderSelected & "PCL.ini", "Version") Dim Instance As McInstance = If(Selection = "", Nothing, New McInstance(Selection)) If Instance Is Nothing OrElse Not PathUtils.IsParentOf(McFolderSelected, Instance.PathVersion) OrElse Not Instance.Check() Then '无效的版本 Logger.Info($"当前选择的 Minecraft 版本无效:{If(Instance Is Nothing, "null", Instance.PathVersion)}", If(IsNothing(Instance), LogBehavior.None, LogBehavior.ToastIfDebug)) If Not McInstanceListLoader.State = LoadState.Finished Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True) If Not McInstanceList.Any() OrElse McInstanceList.First.Value(0).Logo.Contains("RedstoneBlock") Then Instance = Nothing Logger.Info("无可用 Minecraft 版本") Else Instance = McInstanceList.First.Value(0) Logger.Info($"自动选择 Minecraft 版本:{Instance.PathVersion}") End If End If RunInUi( Sub() McInstanceSelected = Instance '绕这一圈是为了避免 McVersionCheck 触发第二次版本改变 IsLoadFinished = True RefreshButtonsUI() RefreshPage(False, False) '有可能选择的版本变化了,需要重新刷新 If McLoginAble() = "" Then McLoginLoader.Start() '自动登录 '用于自动化测试生成的程序是否可以正常运行 If Environment.CommandLine.Contains("--test") Then RunInThread( Sub() Thread.Sleep(500) RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.Download)) Thread.Sleep(500) RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)) Thread.Sleep(500) RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.Setup)) Thread.Sleep(500) RunInUi(Sub() FrmMain.PageChange(FormMain.PageType.Other)) Thread.Sleep(500) RunInUi(Sub() BtnVersion_Click()) Thread.Sleep(500) RunInUi(Sub() BtnMore_Click()) Thread.Sleep(500) FormMain.EndProgramForce(123) End Sub) End If End Sub) End Sub, "Version Check", ThreadPriority.AboveNormal) '改变页面 Dim LoginType = Settings.Get(Of McLoginType)("LoginType") If LoginType = McLoginType.Legacy OrElse LoginType = McLoginType.Ms Then CType(FindName("RadioLoginType" & LoginType), MyRadioButton).Checked = True RefreshPage(False, False) AniControlEnabled -= 1 End Sub #Region "切换大页面" ''' ''' 切换至启动中页面。 ''' Public Sub PageChangeToLaunching() '修改登陆方式 Select Case Settings.Get(Of McLoginType)("LoginType") Case McLoginType.Legacy LabLaunchingMethod.Text = "离线登录" Case McLoginType.Ms LabLaunchingMethod.Text = "正版登录" Case McLoginType.Nide LabLaunchingMethod.Text = "统一通行证" Case McLoginType.Auth LabLaunchingMethod.Text = "Authlib-Injector" End Select '初始化页面 LabLaunchingName.Text = McInstanceSelected.Name LabLaunchingStage.Text = "初始化" LabLaunchingTitle.Text = If(CurrentLaunchOptions?.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本") LabLaunchingProgress.Text = "0.00 %" LabLaunchingProgress.Opacity = 1 LabLaunchingDownload.Visibility = Visibility.Visible LabLaunchingProgressLeft.Opacity = 0.6 LabLaunchingDownload.Visibility = Visibility.Visible LabLaunchingDownload.Text = "0 B/s" LabLaunchingDownload.Opacity = 0 LabLaunchingDownload.Visibility = Visibility.Collapsed LabLaunchingDownloadLeft.Opacity = 0 LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star) ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star) PanLaunchingHint.Opacity = 0 PanLaunchingHint.Visibility = Visibility.Collapsed PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画 McLaunchProcess = Nothing McLaunchWatcher = Nothing '获取 “你知道吗” 提示 LabLaunchingHint.Text = PageOtherTest.GetRandomHint() '初始化其他页面 PanInput.IsHitTestVisible = False PanLaunching.IsHitTestVisible = False LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Run PanLaunching.Visibility = Visibility.Visible AniStart({ AaOpacity(PanInput, 0, 50), '略作延迟,这样如果预检测失败,不会出现奇怪的弹一下的动画 AaOpacity(PanInput, -PanInput.Opacity, 110, , New AniEaseInFluent, True), AaScaleTransform(PanInput, 1.2 - CType(PanInput.RenderTransform, ScaleTransform).ScaleX, 160), AaOpacity(PanLaunching, 1 - PanLaunching.Opacity, 150, 100), AaScaleTransform(PanLaunching, 1 - CType(PanLaunching.RenderTransform, ScaleTransform).ScaleX, 500, 100, New AniEaseOutBack(AniEasePower.Weak)), AaCode(Sub() PanLaunching.IsHitTestVisible = True, 150) }, "Launch State Page") End Sub ''' ''' 切换至登录页面。 ''' Public Sub PageChangeToLogin() PageGet(PageCurrent).Reload(KeepInput:=False) PanInput.IsHitTestVisible = False PanLaunching.IsHitTestVisible = False LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Stop PanInput.Visibility = Visibility.Visible AniStart({ AaOpacity(PanLaunching, -PanLaunching.Opacity, 150), AaScaleTransform(PanLaunching, 0.8 - CType(PanLaunching.RenderTransform, ScaleTransform).ScaleX, 150,, New AniEaseOutFluent(AniEasePower.Weak)), AaOpacity(PanInput, 1 - PanInput.Opacity, 250, 50), AaScaleTransform(PanInput, 1 - CType(PanInput.RenderTransform, ScaleTransform).ScaleX, 300, 50, New AniEaseOutBack(AniEasePower.Weak)), AaCode(Sub() PanInput.IsHitTestVisible = True, 200) }, "Launch State Page", True) End Sub #End Region #Region "切换登录页面" Private Enum PageType None Legacy Nide NideSkin Auth AuthSkin Ms MsSkin End Enum ''' ''' 当前页面的种类。 ''' Private PageCurrent As PageType = PageType.None Private Function PageGet(Type As PageType) Select Case Type Case PageType.Legacy If IsNothing(FrmLoginLegacy) Then FrmLoginLegacy = New PageLoginLegacy Return FrmLoginLegacy Case PageType.Nide If IsNothing(FrmLoginNide) Then FrmLoginNide = New PageLoginNide Return FrmLoginNide Case PageType.NideSkin If IsNothing(FrmLoginNideSkin) Then FrmLoginNideSkin = New PageLoginNideSkin Return FrmLoginNideSkin Case PageType.Auth If IsNothing(FrmLoginAuth) Then FrmLoginAuth = New PageLoginAuth Return FrmLoginAuth Case PageType.AuthSkin If IsNothing(FrmLoginAuthSkin) Then FrmLoginAuthSkin = New PageLoginAuthSkin Return FrmLoginAuthSkin Case PageType.Ms If IsNothing(FrmLoginMs) Then FrmLoginMs = New PageLoginMs Return FrmLoginMs Case PageType.MsSkin If IsNothing(FrmLoginMsSkin) Then FrmLoginMsSkin = New PageLoginMsSkin Return FrmLoginMsSkin Case Else Throw New ArgumentOutOfRangeException("Type", "即将切换的登录分页编号越界") End Select End Function ''' ''' 切换现有登录页面种类,返回新页面的实例。 ''' ''' 新页面的种类。 ''' 是否显示动画。 Private Function PageChange(Type As PageType, Anim As Boolean) Dim PageNew As Object = FrmLoginMs '初始化一个东西,避免在执行时出现异常导致雪崩 Try #Region "确定更改的页面实例并实例化" If PageCurrent = Type Then Return PageNew PageNew = PageGet(Type) #End Region #Region "切换页面" AniStop("FrmLogin PageChange") '清除页面关联性 If Not IsNothing(PageNew) AndAlso Not IsNothing(PageNew.Parent) Then PageNew.SetValue(ContentPresenter.ContentProperty, Nothing) If Anim Then '动画 Dispatcher.Invoke( Sub() '执行动画 AniStart({ AaOpacity(PanLogin, -PanLogin.Opacity, 100,, New AniEaseOutFluent), AaCode( Sub() AniControlEnabled += 1 PanLogin.Children.Clear() PanLogin.Children.Add(PageNew) AniControlEnabled -= 1 End Sub, 100), AaOpacity(PanLogin, 1, 100, 120, New AniEaseInFluent) }, "FrmLogin PageChange") End Sub, DispatcherPriority.Render) Else '无动画 AniControlEnabled += 1 PanLogin.Children.Clear() PanLogin.Children.Add(PageNew) AniControlEnabled -= 1 End If #End Region PageCurrent = Type Return PageNew Catch ex As Exception Logger.Error(ex, $"切换登录分页失败({Type})") Return PageNew End Try End Function ''' ''' 确认当前显示的子页面正确,并刷新该页面。 ''' Public Sub RefreshPage(KeepInput As Boolean, Anim As Boolean) '获取页面的可用种类并回写缓存 Dim Type As PageType Dim LoginPageType As Integer If McInstanceSelected IsNot Nothing Then LoginPageType = Settings.Get(Of Integer)("VersionServerLogin", Instance:=McInstanceSelected) '缓存当前版本的页面种类,下一次打开 McInstanceSelected 为空时才能加载出正确的页面 Settings.Set("LoginPageType", LoginPageType) Else LoginPageType = Settings.Get(Of Integer)("LoginPageType") End If Select Case LoginPageType Case 0 '正版或离线 UnknownType: If RadioLoginType5.Checked Then If Settings.Get(Of String)("CacheMsV2Access") = "" Then Type = PageType.Ms Else Type = PageType.MsSkin End If Settings.Set("LoginType", McLoginType.Ms) Else Type = PageType.Legacy Settings.Set("LoginType", McLoginType.Legacy) End If PanType.Visibility = Visibility.Visible PanTypeOne.Visibility = Visibility.Collapsed RadioLoginType5.Visibility = Visibility.Visible RadioLoginType0.Visibility = Visibility.Visible Case 1 '仅正版 If Settings.Get(Of String)("CacheMsV2Access") = "" Then Type = PageType.Ms Else Type = PageType.MsSkin End If Settings.Set("LoginType", McLoginType.Ms) PanType.Visibility = Visibility.Collapsed PanTypeOne.Visibility = Visibility.Visible PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield) LabTypeOne.Text = "正版登录" RadioLoginType5.Visibility = Visibility.Visible RadioLoginType0.Visibility = Visibility.Collapsed Case 2 '仅离线 Type = PageType.Legacy Settings.Set("LoginType", McLoginType.Legacy) PanType.Visibility = Visibility.Collapsed PanTypeOne.Visibility = Visibility.Visible PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline) LabTypeOne.Text = "离线登录" Case 3 '统一通行证 If Settings.Get(Of String)("CacheNideAccess") = "" Then Type = PageType.Nide Else Type = PageType.NideSkin End If Settings.Set("LoginType", McLoginType.Nide) PanType.Visibility = Visibility.Collapsed PanTypeOne.Visibility = Visibility.Visible PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonCard) LabTypeOne.Text = "统一通行证登录" Case 4 'Authlib-Injector If Settings.Get(Of String)("CacheAuthAccess") = "" Then Type = PageType.Auth Else Type = PageType.AuthSkin End If Settings.Set("LoginType", McLoginType.Auth) PanType.Visibility = Visibility.Collapsed PanTypeOne.Visibility = Visibility.Visible PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonCard) LabTypeOne.Text = If(McInstanceSelected Is Nothing, Settings.Get(Of String)("CacheAuthServerName"), Settings.Get(Of String)("VersionServerAuthName", Instance:=McInstanceSelected)) If LabTypeOne.Text = "" Then LabTypeOne.Text = "第三方登录" Case Else Logger.Error($"未知的登录页面:{LoginPageType}", LogBehavior.Toast) GoTo UnknownType End Select '刷新页面 If PageCurrent = Type Then Return PageChange(Type, Anim).Reload(KeepInput) Dim Control As MyRadioButton = FindName("RadioLoginType" & Settings.Get(Of McLoginType)("LoginType")) If Control IsNot Nothing Then Control.Checked = True End Sub Private Sub RadioLoginType_Change(sender As Object, raiseByMouse As Boolean) Handles RadioLoginType0.Check, RadioLoginType5.Check If raiseByMouse Then RefreshPage(True, True) End Sub #End Region #Region "皮肤" '微软正版皮肤 Public Shared SkinMs As New LoaderTask(Of (String, String), String)("Loader Skin Ms", AddressOf SkinMsLoad, AddressOf SkinMsInput, ThreadPriority.AboveNormal) Private Shared Function SkinMsInput() As (String, String) '获取名称 Return (Settings.Get(Of String)("CacheMsV2Name"), Settings.Get(Of String)("CacheMsV2Uuid")) End Function Private Shared Sub SkinMsLoad(Data As LoaderTask(Of (String, String), String)) '清空已有皮肤 '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载 RunInUi(Sub() If FrmLoginMsSkin IsNot Nothing AndAlso FrmLoginMsSkin.Skin IsNot Nothing Then FrmLoginMsSkin.Skin.Clear()) '获取 Url Dim UserName As String = Data.Input.Item1 Dim Uuid As String = Data.Input.Item2 If UserName = "" Then Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png" Logger.Info("获取微软正版皮肤失败,ID 为空") GoTo Finish End If Try Dim Result As String = McSkinGetAddress(Uuid, "Ms") If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Result = McSkinDownload(Result) If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Data.Output = Result Catch ex As Exception If ex.IsCanceled Then Data.Output = "" Return ElseIf ex.GetDisplay(False).Contains("(429)") Then Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png" Logger.Error($"获取正版皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast) ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png" Logger.Info("用户未设置自定义皮肤,跳过皮肤加载") Else Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png" Logger.Error(ex, $"获取微软正版皮肤失败({UserName})", LogBehavior.Toast) End If End Try Finish: '刷新显示 If FrmLoginMsSkin IsNot Nothing Then RunInUi(AddressOf FrmLoginMsSkin.Skin.Load) ElseIf Not Data.IsCanceled Then '如果已经中断,Input 也被清空,就不会再次刷新 Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染 End If End Sub '离线皮肤 Public Shared SkinLegacy As New LoaderTask(Of (String, String), String)("Loader Skin Legacy", AddressOf SkinLegacyLoad, AddressOf SkinLegacyInput, ThreadPriority.AboveNormal) Private Shared Function SkinLegacyInput() As (String, String) '根据类型判断输入 Dim Type As Integer = Settings.Get(Of Integer)("LaunchSkinType") Select Case Type Case 0 If FrmLoginLegacy?.IsReloaded Then Return ("0", If(FrmLoginLegacy.ComboName.Text.Trim, "")) ElseIf Settings.Get(Of String)("LoginLegacyName") = "" Then Return ("0", "") Else Return ("0", If(Settings.Get(Of String)("LoginLegacyName").ToString.BeforeFirst("¨"), "")) End If Case 3 Return ("3", Settings.Get(Of String)("LaunchSkinID")) Case Else Return (Type.ToString, "") End Select End Function Private Shared Sub SkinLegacyLoad(Data As LoaderTask(Of (String, String), String)) '清空已有皮肤 RunInUi(Sub() If FrmLoginLegacy IsNot Nothing AndAlso FrmLoginLegacy.Skin IsNot Nothing Then FrmLoginLegacy.Skin.Clear()) '获取 Url Select Case CInt(Data.Input.Item1) Case 0 '默认 Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(Data.Input.Item2)) & ".png" Case 1 'Steve UseDefault: Data.Output = PathImage & "Skins/Steve.png" Case 2 'Alex Data.Output = PathImage & "Skins/Alex.png" Case 3 '正版 Dim ID As String = Data.Input.Item2 Try If ID.Count < 2 Then Data.Output = PathImage & "Skins/Steve.png" Else Dim Result As String = McLoginMojangUuid(ID, True) If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & ID) Result = McSkinGetAddress(Result, "Mojang") If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & ID) Result = McSkinDownload(Result) If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & ID) Data.Output = Result End If Catch ex As Exception If ex.IsCanceled Then Data.Output = "" Return ElseIf ex.GetDisplay(False).Contains("(429)") Then Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(ID)) & ".png" Logger.Info($"获取离线登录使用的正版皮肤失败({ID}):获取皮肤太过频繁,请 5 分钟后再试!") Else Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(ID)) & ".png" Logger.Warn(ex, $"获取离线登录使用的正版皮肤失败({ID})") End If End Try Case 4 '自定义 If Not FileUtils.Exists(Paths.AppDataThenName & "CustomSkin.png") Then Hint("未找到离线皮肤自定义文件,可能它已被删除。PCL 将使用默认的 Steve 皮肤!") Settings.Set("LaunchSkinType", 1) GoTo UseDefault End If Data.Output = Paths.AppDataThenName & "CustomSkin.png" End Select '刷新显示 If FrmLoginLegacy IsNot Nothing Then RunInUi(AddressOf FrmLoginLegacy.Skin.Load) ElseIf Not Data.IsCanceled Then '如果已经中断,Input 也被清空,就不会再次刷新 Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染 End If End Sub '统一通行证皮肤 Public Shared SkinNide As New LoaderTask(Of (String, String), String)("Loader Skin Nide", AddressOf SkinNideLoad, AddressOf SkinNideInput, ThreadPriority.AboveNormal) Private Shared Function SkinNideInput() As (String, String) '获取名称 Return (Settings.Get(Of String)("CacheNideName"), Settings.Get(Of String)("CacheNideUuid")) End Function Private Shared Sub SkinNideLoad(Data As LoaderTask(Of (String, String), String)) '清空已有皮肤 '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载 RunInUi(Sub() If FrmLoginNideSkin IsNot Nothing AndAlso FrmLoginNideSkin.Skin IsNot Nothing Then FrmLoginNideSkin.Skin.Clear()) '获取 Url Dim UserName As String = Data.Input.Item1 Dim Uuid As String = Data.Input.Item2 If UserName = "" Then Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png" Logger.Info("获取统一通行证皮肤失败,ID 为空") GoTo Finish End If Try Dim Result As String = McSkinGetAddress(Uuid, "Nide") If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Result = McSkinDownload(Result) If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Data.Output = Result Catch ex As Exception If ex.IsCanceled Then Data.Output = "" Return ElseIf ex.GetDisplay(False).Contains("(429)") Then Data.Output = PathImage & "Skins/Steve.png" Logger.Error($"获取统一通行证皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast) ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then Data.Output = PathImage & "Skins/Steve.png" Logger.Info("用户未设置自定义皮肤,跳过皮肤加载") Else Data.Output = PathImage & "Skins/Steve.png" Logger.Error(ex, $"获取统一通行证皮肤失败({UserName})", LogBehavior.Toast) End If End Try Finish: '刷新显示 If FrmLoginNideSkin IsNot Nothing Then RunInUi(AddressOf FrmLoginNideSkin.Skin.Load) ElseIf Not Data.IsCanceled Then '如果已经中断,Input 也被清空,就不会再次刷新 Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染 End If End Sub 'Authlib-Injector 皮肤 Public Shared SkinAuth As New LoaderTask(Of (String, String), String)("Loader Skin Auth", AddressOf SkinAuthLoad, AddressOf SkinAuthInput, ThreadPriority.AboveNormal) Private Shared Function SkinAuthInput() As (String, String) '获取名称 Return (Settings.Get(Of String)("CacheAuthName"), Settings.Get(Of String)("CacheAuthUuid")) End Function Private Shared Sub SkinAuthLoad(Data As LoaderTask(Of (String, String), String)) '清空已有皮肤 '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载 RunInUi(Sub() If FrmLoginAuthSkin IsNot Nothing AndAlso FrmLoginAuthSkin.Skin IsNot Nothing Then FrmLoginAuthSkin.Skin.Clear()) '获取 Url Dim UserName As String = Data.Input.Item1 Dim UUID As String = Data.Input.Item2 If UserName = "" Then Data.Output = PathImage & "Skins/Steve.png" Logger.Info("获取 Authlib-Injector 皮肤失败,ID 为空") GoTo Finish End If Try Dim Result As String = McSkinGetAddress(UUID, "Auth") If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Result = McSkinDownload(Result) If Data.IsCanceled Then Throw New OperationCanceledException("当前任务已取消:" & UserName) Data.Output = Result Catch ex As Exception If ex.IsCanceled Then Data.Output = "" Return ElseIf ex.GetDisplay(False).Contains("(429)") Then Data.Output = PathImage & "Skins/Steve.png" Logger.Error($"获取 Authlib-Injector 皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast) ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then Data.Output = PathImage & "Skins/Steve.png" Logger.Info("用户未设置自定义皮肤,跳过皮肤加载") Else Data.Output = PathImage & "Skins/Steve.png" Logger.Error(ex, $"获取 Authlib-Injector 皮肤失败({UserName})", LogBehavior.Toast) End If End Try Finish: '刷新显示 If FrmLoginAuthSkin IsNot Nothing Then RunInUi(AddressOf FrmLoginAuthSkin.Skin.Load) ElseIf Not Data.IsCanceled Then '如果已经中断,Input 也被清空,就不会再次刷新 Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染 End If End Sub '全部皮肤加载器 '需要放在其中元素的后面,否则会因为它提前被加载而莫名其妙变成 Nothing Public Shared SkinLoaders As New List(Of LoaderTask(Of (String, String), String)) From {SkinMs, SkinLegacy, SkinNide, SkinAuth} #End Region '版本选择按钮 Private Sub BtnVersion_Click() Handles BtnVersion.Click If McLaunchLoader.State = LoadState.Loading Then Return FrmMain.PageChange(FormMain.PageType.InstanceSelect) End Sub '启动按钮 Public Sub LaunchButtonClick() Handles BtnLaunch.Click If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled OrElse (FrmMain.PageRight IsNot Nothing AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentStay AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentEnter) Then Return '愚人节处理 If IsAprilEnabled AndAlso Not IsAprilGiveup Then ThemeUnlock(12, False, "隐藏主题 滑稽彩 已解锁!") IsAprilGiveup = True Settings.Set("AprilYear", Date.Now.Year) FrmLaunchLeft.AprilScaleTrans.ScaleX = 1 FrmLaunchLeft.AprilScaleTrans.ScaleY = 1 FrmLaunchLeft.AprilPosTrans.X = 0 FrmLaunchLeft.AprilPosTrans.Y = 0 FrmMain.BtnExtraApril.ShowRefresh() End If '实际的启动 If BtnLaunch.Text = "启动游戏" Then McLaunchStart() ElseIf BtnLaunch.Text = "下载游戏" Then FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadInstall) End If End Sub Private BtnLaunchState As Integer = 0 Private BtnLaunchInstance As McInstance = Nothing Public Sub RefreshButtonsUI() Handles BtnLaunch.Loaded If Not BtnLaunch.IsLoaded Then Return '获取当前状态 Dim CurrentState As Integer If (Not IsLoadFinished) OrElse McInstanceListLoader.State = LoadState.Loading OrElse McFolderListLoader.State = LoadState.Loading Then CurrentState = 0 Else If McInstanceSelected Is Nothing Then If Settings.Get(Of Boolean)("UiHiddenPageDownload") AndAlso Not PageSetupUI.HiddenForceShow Then CurrentState = 1 Else CurrentState = 2 End If Else CurrentState = 3 End If End If '更新状态 If CurrentState = BtnLaunchState AndAlso If(McInstanceSelected Is Nothing, "", McInstanceSelected.PathVersion) = If(BtnLaunchInstance Is Nothing, "", BtnLaunchInstance.PathVersion) Then GoTo ExitRefresh BtnLaunchInstance = McInstanceSelected BtnLaunchState = CurrentState Select Case CurrentState Case 0 Logger.Info("启动按钮:正在加载 Minecraft 版本") FrmLaunchLeft.BtnLaunch.Text = "正在加载" FrmLaunchLeft.BtnLaunch.IsEnabled = False FrmLaunchLeft.LabVersion.Text = "正在加载中,请稍候" FrmLaunchLeft.BtnVersion.IsEnabled = False FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed Case 1 Logger.Info("启动按钮:无 Minecraft 版本,下载已禁用") FrmLaunchLeft.BtnLaunch.Text = "启动游戏" FrmLaunchLeft.BtnLaunch.IsEnabled = False FrmLaunchLeft.LabVersion.Text = "未找到可用的游戏版本" FrmLaunchLeft.BtnVersion.IsEnabled = True FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed Case 2 Logger.Info("启动按钮:无 Minecraft 版本,要求下载") FrmLaunchLeft.BtnLaunch.Text = "下载游戏" FrmLaunchLeft.BtnLaunch.IsEnabled = True FrmLaunchLeft.LabVersion.Text = "未找到可用的游戏版本" FrmLaunchLeft.BtnVersion.IsEnabled = True FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed Case 3 Logger.Info($"启动按钮:Minecraft 版本:{McInstanceSelected.PathVersion}") FrmLaunchLeft.BtnLaunch.Text = "启动游戏" FrmLaunchLeft.BtnVersion.IsEnabled = True FrmLaunchLeft.BtnLaunch.IsEnabled = True FrmLaunchLeft.LabVersion.Text = McInstanceSelected.Name 'FrmLaunchLeft.BtnMore.Visibility = Visibility.Visible '由功能隐藏设置修改 End Select ExitRefresh: '功能隐藏 FrmLaunchLeft.BtnVersion.Visibility = If(Not PageSetupUI.HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionSelect"), Visibility.Collapsed, Visibility.Visible) If CurrentState = 3 Then FrmLaunchLeft.BtnMore.Visibility = FrmLaunchLeft.BtnVersion.Visibility End If End Sub '取消按钮 Public Sub BtnCancel_Click() Handles BtnCancel.Click If McLaunchLoaderReal IsNot Nothing Then McLaunchLoaderReal.Cancel() McLaunchLog("已取消启动") Try If McLaunchWatcher IsNot Nothing Then McLaunchWatcher.Kill() ElseIf McLaunchProcess IsNot Nothing Then If Not McLaunchProcess.HasExited Then McLaunchProcess.Kill() End If Catch ex As Exception Logger.Error(ex, "取消启动结束进程失败", LogBehavior.Toast) End Try End If End Sub '版本设置按钮 Private Sub BtnMore_Click() Handles BtnMore.Click If McLaunchLoader.State = LoadState.Loading Then Return If McInstanceLock.IsLocked(McInstanceSelected) Then Hint("该版本正在被修改,暂时无法进入版本设置!", HintType.Red) Return End If McInstanceSelected.Load() PageInstanceLeft.Instance = McInstanceSelected FrmMain.PageChange(FormMain.PageType.InstanceSetup, 0) End Sub ''' ''' 每 0.1s 执行一次,刷新启动的数据 UI 显示。 ''' Public Sub LaunchingRefresh() Try If McLaunchLoaderReal.State = LoadState.Canceled Then Return '阶段状态获取 Dim IsLaunched As Boolean = False '是否已经启动游戏,只是在等待窗口 Try For Each Loader In McLaunchLoaderReal.GetLoaderList(False) If Loader.State = LoadState.Loading OrElse Loader.State = LoadState.Waiting Then LabLaunchingStage.Text = Loader.Name IsLaunched = Loader.Name = "等待游戏窗口出现" OrElse Loader.Name = "结束处理" Exit Try End If Next LabLaunchingStage.Text = "已完成" Catch ex As Exception Logger.Warn(ex, "获取是否启动完成失败,可能是由于启动状态改变导致集合已修改") Return End Try LabLaunchingTitle.Text = If(IsLaunched OrElse McLaunchLoaderReal.State = LoadState.Finished, "已启动游戏", If(CurrentLaunchOptions.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")) If AniIsRun("Launch State Page") Then IsLaunched = False '等待页面切换动画完成 '更新进度 Dim ActualProgress = McLaunchLoaderReal.Progress If ActualProgress >= ShowProgress Then ShowProgress += (ActualProgress - ShowProgress) * 0.1 + 0.0025 '向实际进度靠一点 If ActualProgress <= ShowProgress Then ShowProgress = ActualProgress '原来或处理后变得比实际进度高,直接回退 If IsLaunched Then ShowProgress = 1 '如果已经完成了,就不卖关子了 LabLaunchingProgress.Text = (ShowProgress * 100).ToString("0.00") & " %" '更新下载速度 Dim HasLaunchDownloader As Boolean = False Try If NetManager.Speed = 0 AndAlso LabLaunchingDownload.Visibility = Visibility.Collapsed Then '可能只是在检查文件,没有实际下载 HasLaunchDownloader = False Else For Each Loader In NetManager.Tasks If Loader.RealParent IsNot Nothing AndAlso Loader.RealParent.Name = "Minecraft 启动" AndAlso Loader.State = LoadState.Loading Then HasLaunchDownloader = True Next End If Catch ex As Exception Logger.Warn(ex, "获取 Minecraft 启动下载器失败,可能是因为启动被取消") HasLaunchDownloader = False End Try LabLaunchingDownload.Text = StringUtils.FormatByteSize(NetManager.Speed) & "/s" '进度改变动画 Dim AnimList As New List(Of AniData) From { AaGridLengthWidth(ProgressLaunchingFinished, ShowProgress - ProgressLaunchingFinished.Width.Value, 130,, New AniEaseOutFluent), AaGridLengthWidth(ProgressLaunchingUnfinished, 1 - ShowProgress - ProgressLaunchingUnfinished.Width.Value, 130,, New AniEaseOutFluent) } If HasLaunchDownloader = (LabLaunchingDownload.Visibility = Visibility.Collapsed) Then 'IsDownloadStateChanged LabLaunchingDownload.Visibility = Visibility.Visible LabLaunchingDownloadLeft.Visibility = Visibility.Visible AnimList.AddRange({ AaOpacity(LabLaunchingDownload, If(HasLaunchDownloader, 1, 0) - LabLaunchingDownload.Opacity, 90), AaOpacity(LabLaunchingDownloadLeft, If(HasLaunchDownloader, 0.5, 0) - LabLaunchingDownloadLeft.Opacity, 90), AaCode( Sub() If Not HasLaunchDownloader Then LabLaunchingDownload.Visibility = Visibility.Collapsed LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed End If End Sub, 110) }) End If If (Not IsLaunched) = (LabLaunchingProgress.Visibility = Visibility.Collapsed) Then 'IsProgressStateChanged LabLaunchingProgress.Visibility = Visibility.Visible LabLaunchingProgressLeft.Visibility = Visibility.Visible If IsLaunched Then PanLaunchingHint.Visibility = Visibility.Visible AnimList.AddRange({ AaOpacity(LabLaunchingProgress, If(Not IsLaunched, 1, 0) - LabLaunchingProgress.Opacity, 90), AaOpacity(LabLaunchingProgressLeft, If(Not IsLaunched, 0.5, 0) - LabLaunchingProgressLeft.Opacity, 90), AaOpacity(PanLaunchingHint, If(IsLaunched, 1, 0) - PanLaunchingHint.Opacity, 90) }) End If AniStart(AnimList, "Launching Progress") Catch ex As Exception Logger.Error(ex, "刷新启动信息失败") End Try End Sub Private ShowProgress As Double = 0 '尺寸改变动画 Private IsWidthAnimating As Boolean = False Private ActualUsedWidth As Double Private Sub PanLaunchingInfo_SizeChangedW(sender As Object, e As SizeChangedEventArgs) Handles PanLaunchingInfo.SizeChanged Dim DeltaWidth As Double = e.NewSize.Width - e.PreviousSize.Width If e.PreviousSize.Width = 0 OrElse IsWidthAnimating OrElse Math.Abs(DeltaWidth) < 1 OrElse PanLaunchingInfo.ActualWidth = 0 Then Return AniStart({ AaWidth(PanLaunchingInfo, DeltaWidth, 180,, New AniEaseOutFluent), AaCode(Sub() IsWidthAnimating = False PanLaunchingInfo.Width = ActualUsedWidth End Sub,, True) }, "Launching Info Width") IsWidthAnimating = True ActualUsedWidth = PanLaunchingInfo.Width PanLaunchingInfo.Width = e.PreviousSize.Width End Sub Private IsHeightAnimating As Boolean = False Private ActualUsedHeight As Double Private Sub PanLaunchingInfo_SizeChangedH(sender As Object, e As SizeChangedEventArgs) Handles PanLaunchingInfo.SizeChanged Dim DeltaHeight As Double = e.NewSize.Height - e.PreviousSize.Height If e.PreviousSize.Height = 0 OrElse IsHeightAnimating OrElse Math.Abs(DeltaHeight) < 1 OrElse PanLaunchingInfo.ActualHeight = 0 Then Return AniStart({ AaHeight(PanLaunchingInfo, DeltaHeight, 180,, New AniEaseOutFluent), AaCode(Sub() IsHeightAnimating = False PanLaunchingInfo.Height = ActualUsedHeight End Sub,, True) }, "Launching Info Height") IsHeightAnimating = True ActualUsedHeight = PanLaunchingInfo.Height PanLaunchingInfo.Height = e.PreviousSize.Height End Sub End Class