PageLaunchLeft.xaml.vb 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. Public Class PageLaunchLeft
  2. '加载当前版本
  3. Private IsLoad As Boolean = False
  4. Private IsLoadFinished As Boolean = False
  5. Public Sub PageLaunchLeft_Loaded() Handles Me.Loaded
  6. If IsLoad Then RefreshPage(True, False)
  7. AprilPosTrans.X = 0
  8. AprilPosTrans.Y = 0
  9. If IsLoad Then Return
  10. IsLoad = True
  11. AniControlEnabled += 1
  12. '开始按钮
  13. AddHandler McInstanceListLoader.LoadingStateChanged, AddressOf RefreshButtonsUI
  14. AddHandler McFolderListLoader.LoadingStateChanged, AddressOf RefreshButtonsUI
  15. RefreshButtonsUI()
  16. '加载版本
  17. RunInNewThread(
  18. Sub()
  19. '自动整合包安装:准备
  20. Dim PackInstallPath As String = Nothing
  21. If FileUtils.Exists(PathExeFolder & "modpack.zip") Then PackInstallPath = PathExeFolder & "modpack.zip"
  22. If FileUtils.Exists(PathExeFolder & "modpack.mrpack") Then PackInstallPath = PathExeFolder & "modpack.mrpack"
  23. If PackInstallPath IsNot Nothing Then
  24. Logger.Warn($"需自动安装整合包:{PackInstallPath}")
  25. PageSelectLeft.CreateMcFolderInCurrentPath()
  26. McFolderListLoader.WaitForExit()
  27. End If
  28. '确认 Minecraft 文件夹存在
  29. If McFolderSelected = "" OrElse Not DirectoryUtils.Exists(McFolderSelected) Then
  30. '无效的文件夹
  31. If McFolderSelected = "" Then
  32. Logger.Info("没有已储存的 Minecraft 文件夹")
  33. Else
  34. Logger.Warn($"Minecraft 文件夹无效,该文件夹已不存在:{McFolderSelected}")
  35. End If
  36. McFolderListLoader.WaitForExit(IsForceRestart:=True)
  37. McFolderSelected = McFolderList.First.Location
  38. End If
  39. If Settings.Get(Of Boolean)("SystemDebugDelay") Then Thread.Sleep(RandomInteger(500, 3000))
  40. '自动整合包安装
  41. If PackInstallPath IsNot Nothing Then
  42. Try
  43. Dim InstallLoader = ModpackInstall(PackInstallPath)
  44. Logger.Info($"自动安装整合包已开始:{PackInstallPath}")
  45. InstallLoader.WaitForExit()
  46. If InstallLoader.State = LoadState.Finished Then
  47. Logger.Info($"自动安装整合包成功,清理安装包:{PackInstallPath}")
  48. FileUtils.Delete(PackInstallPath)
  49. End If
  50. Catch ex As CancelledException
  51. Logger.Warn(ex, $"自动安装整合包被用户取消:{PackInstallPath}")
  52. Catch ex As Exception
  53. Logger.Error(ex, $"自动安装整合包失败:{PackInstallPath}", LogBehavior.Alert)
  54. End Try
  55. End If
  56. '确认 Minecraft 版本存在
  57. Dim Selection As String = ReadIni(McFolderSelected & "PCL.ini", "Version")
  58. Dim Instance As McInstance = If(Selection = "", Nothing, New McInstance(Selection))
  59. If Instance Is Nothing OrElse Not Instance.PathVersion.StartsWithF(McFolderSelected) OrElse Not Instance.Check() Then
  60. '无效的版本
  61. Logger.Info($"当前选择的 Minecraft 版本无效:{If(Instance Is Nothing, "null", Instance.PathVersion)}", If(IsNothing(Instance), LogBehavior.None, LogBehavior.ToastIfDebug))
  62. If Not McInstanceListLoader.State = LoadState.Finished Then LoaderFolderRun(McInstanceListLoader, McFolderSelected, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
  63. If Not McInstanceList.Any() OrElse McInstanceList.First.Value(0).Logo.Contains("RedstoneBlock") Then
  64. Instance = Nothing
  65. Logger.Info("无可用 Minecraft 版本")
  66. Else
  67. Instance = McInstanceList.First.Value(0)
  68. Logger.Info($"自动选择 Minecraft 版本:{Instance.PathVersion}")
  69. End If
  70. End If
  71. RunInUi(
  72. Sub()
  73. McInstanceSelected = Instance '绕这一圈是为了避免 McVersionCheck 触发第二次版本改变
  74. IsLoadFinished = True
  75. RefreshButtonsUI()
  76. RefreshPage(False, False) '有可能选择的版本变化了,需要重新刷新
  77. If McLoginAble() = "" Then McLoginLoader.Start() '自动登录
  78. If Environment.CommandLine.Contains("--test") Then FormMain.EndProgramForce(123) '用于更新器测试生成的程序是否可以正常运行
  79. End Sub)
  80. End Sub, "Version Check", ThreadPriority.AboveNormal)
  81. '改变页面
  82. Dim LoginType = Settings.Get(Of McLoginType)("LoginType")
  83. If LoginType = McLoginType.Legacy OrElse LoginType = McLoginType.Ms Then CType(FindName("RadioLoginType" & LoginType), MyRadioButton).Checked = True
  84. RefreshPage(False, False)
  85. AniControlEnabled -= 1
  86. End Sub
  87. #Region "切换大页面"
  88. ''' <summary>
  89. ''' 切换至启动中页面。
  90. ''' </summary>
  91. Public Sub PageChangeToLaunching()
  92. '修改登陆方式
  93. Select Case Settings.Get(Of McLoginType)("LoginType")
  94. Case McLoginType.Legacy
  95. LabLaunchingMethod.Text = "离线登录"
  96. Case McLoginType.Ms
  97. LabLaunchingMethod.Text = "正版登录"
  98. Case McLoginType.Nide
  99. LabLaunchingMethod.Text = "统一通行证"
  100. Case McLoginType.Auth
  101. LabLaunchingMethod.Text = "Authlib-Injector"
  102. End Select
  103. '初始化页面
  104. LabLaunchingName.Text = McInstanceSelected.Name
  105. LabLaunchingStage.Text = "初始化"
  106. LabLaunchingTitle.Text = If(CurrentLaunchOptions?.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本")
  107. LabLaunchingProgress.Text = "0.00 %"
  108. LabLaunchingProgress.Opacity = 1
  109. LabLaunchingDownload.Visibility = Visibility.Visible
  110. LabLaunchingProgressLeft.Opacity = 0.6
  111. LabLaunchingDownload.Visibility = Visibility.Visible
  112. LabLaunchingDownload.Text = "0 B/s"
  113. LabLaunchingDownload.Opacity = 0
  114. LabLaunchingDownload.Visibility = Visibility.Collapsed
  115. LabLaunchingDownloadLeft.Opacity = 0
  116. LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
  117. ProgressLaunchingFinished.Width = New GridLength(0, GridUnitType.Star)
  118. ProgressLaunchingUnfinished.Width = New GridLength(1, GridUnitType.Star)
  119. PanLaunchingHint.Opacity = 0
  120. PanLaunchingHint.Visibility = Visibility.Collapsed
  121. PanLaunchingInfo.Width = Double.NaN '重置宽度改变动画
  122. McLaunchProcess = Nothing
  123. McLaunchWatcher = Nothing
  124. '获取 “你知道吗” 提示
  125. LabLaunchingHint.Text = PageOtherTest.GetRandomHint()
  126. '初始化其他页面
  127. PanInput.IsHitTestVisible = False
  128. PanLaunching.IsHitTestVisible = False
  129. LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Run
  130. PanLaunching.Visibility = Visibility.Visible
  131. AniStart({
  132. AaOpacity(PanInput, 0, 50), '略作延迟,这样如果预检测失败,不会出现奇怪的弹一下的动画
  133. AaOpacity(PanInput, -PanInput.Opacity, 110, , New AniEaseInFluent, True),
  134. AaScaleTransform(PanInput, 1.2 - CType(PanInput.RenderTransform, ScaleTransform).ScaleX, 160),
  135. AaOpacity(PanLaunching, 1 - PanLaunching.Opacity, 150, 100),
  136. AaScaleTransform(PanLaunching, 1 - CType(PanLaunching.RenderTransform, ScaleTransform).ScaleX, 500, 100, New AniEaseOutBack(AniEasePower.Weak)),
  137. AaCode(Sub() PanLaunching.IsHitTestVisible = True, 150)
  138. }, "Launch State Page")
  139. End Sub
  140. ''' <summary>
  141. ''' 切换至登录页面。
  142. ''' </summary>
  143. Public Sub PageChangeToLogin()
  144. PageGet(PageCurrent).Reload(KeepInput:=False)
  145. PanInput.IsHitTestVisible = False
  146. PanLaunching.IsHitTestVisible = False
  147. LoadLaunching.State.LoadingState = MyLoading.MyLoadingState.Stop
  148. PanInput.Visibility = Visibility.Visible
  149. AniStart({
  150. AaOpacity(PanLaunching, -PanLaunching.Opacity, 150),
  151. AaScaleTransform(PanLaunching, 0.8 - CType(PanLaunching.RenderTransform, ScaleTransform).ScaleX, 150,, New AniEaseOutFluent(AniEasePower.Weak)),
  152. AaOpacity(PanInput, 1 - PanInput.Opacity, 250, 50),
  153. AaScaleTransform(PanInput, 1 - CType(PanInput.RenderTransform, ScaleTransform).ScaleX, 300, 50, New AniEaseOutBack(AniEasePower.Weak)),
  154. AaCode(Sub() PanInput.IsHitTestVisible = True, 200)
  155. }, "Launch State Page", True)
  156. End Sub
  157. #End Region
  158. #Region "切换登录页面"
  159. Private Enum PageType
  160. None
  161. Legacy
  162. Nide
  163. NideSkin
  164. Auth
  165. AuthSkin
  166. Ms
  167. MsSkin
  168. End Enum
  169. ''' <summary>
  170. ''' 当前页面的种类。
  171. ''' </summary>
  172. Private PageCurrent As PageType = PageType.None
  173. Private Function PageGet(Type As PageType)
  174. Select Case Type
  175. Case PageType.Legacy
  176. If IsNothing(FrmLoginLegacy) Then FrmLoginLegacy = New PageLoginLegacy
  177. Return FrmLoginLegacy
  178. Case PageType.Nide
  179. If IsNothing(FrmLoginNide) Then FrmLoginNide = New PageLoginNide
  180. Return FrmLoginNide
  181. Case PageType.NideSkin
  182. If IsNothing(FrmLoginNideSkin) Then FrmLoginNideSkin = New PageLoginNideSkin
  183. Return FrmLoginNideSkin
  184. Case PageType.Auth
  185. If IsNothing(FrmLoginAuth) Then FrmLoginAuth = New PageLoginAuth
  186. Return FrmLoginAuth
  187. Case PageType.AuthSkin
  188. If IsNothing(FrmLoginAuthSkin) Then FrmLoginAuthSkin = New PageLoginAuthSkin
  189. Return FrmLoginAuthSkin
  190. Case PageType.Ms
  191. If IsNothing(FrmLoginMs) Then FrmLoginMs = New PageLoginMs
  192. Return FrmLoginMs
  193. Case PageType.MsSkin
  194. If IsNothing(FrmLoginMsSkin) Then FrmLoginMsSkin = New PageLoginMsSkin
  195. Return FrmLoginMsSkin
  196. Case Else
  197. Throw New ArgumentOutOfRangeException("Type", "即将切换的登录分页编号越界")
  198. End Select
  199. End Function
  200. ''' <summary>
  201. ''' 切换现有登录页面种类,返回新页面的实例。
  202. ''' </summary>
  203. ''' <param name="Type">新页面的种类。</param>
  204. ''' <param name="Anim">是否显示动画。</param>
  205. Private Function PageChange(Type As PageType, Anim As Boolean)
  206. Dim PageNew As Object = FrmLoginMs '初始化一个东西,避免在执行时出现异常导致雪崩
  207. Try
  208. #Region "确定更改的页面实例并实例化"
  209. If PageCurrent = Type Then Return PageNew
  210. PageNew = PageGet(Type)
  211. #End Region
  212. #Region "切换页面"
  213. AniStop("FrmLogin PageChange")
  214. '清除页面关联性
  215. If Not IsNothing(PageNew) AndAlso Not IsNothing(PageNew.Parent) Then PageNew.SetValue(ContentPresenter.ContentProperty, Nothing)
  216. If Anim Then
  217. '动画
  218. Dispatcher.Invoke(
  219. Sub()
  220. '执行动画
  221. AniStart({
  222. AaOpacity(PanLogin, -PanLogin.Opacity, 100,, New AniEaseOutFluent),
  223. AaCode(
  224. Sub()
  225. AniControlEnabled += 1
  226. PanLogin.Children.Clear()
  227. PanLogin.Children.Add(PageNew)
  228. AniControlEnabled -= 1
  229. End Sub, 100),
  230. AaOpacity(PanLogin, 1, 100, 120, New AniEaseInFluent)
  231. }, "FrmLogin PageChange")
  232. End Sub, Threading.DispatcherPriority.Render)
  233. Else
  234. '无动画
  235. AniControlEnabled += 1
  236. PanLogin.Children.Clear()
  237. PanLogin.Children.Add(PageNew)
  238. AniControlEnabled -= 1
  239. End If
  240. #End Region
  241. PageCurrent = Type
  242. Return PageNew
  243. Catch ex As Exception
  244. Logger.Error(ex, $"切换登录分页失败({Type})")
  245. Return PageNew
  246. End Try
  247. End Function
  248. ''' <summary>
  249. ''' 确认当前显示的子页面正确,并刷新该页面。
  250. ''' </summary>
  251. Public Sub RefreshPage(KeepInput As Boolean, Anim As Boolean)
  252. '获取页面的可用种类并回写缓存
  253. Dim Type As PageType
  254. Dim LoginPageType As Integer
  255. If McInstanceSelected IsNot Nothing Then
  256. LoginPageType = Settings.Get(Of Integer)("VersionServerLogin", Instance:=McInstanceSelected)
  257. '缓存当前版本的页面种类,下一次打开 McInstanceSelected 为空时才能加载出正确的页面
  258. Settings.Set("LoginPageType", LoginPageType)
  259. Else
  260. LoginPageType = Settings.Get(Of Integer)("LoginPageType")
  261. End If
  262. Select Case LoginPageType
  263. Case 0 '正版或离线
  264. UnknownType:
  265. If RadioLoginType5.Checked Then
  266. If Settings.Get(Of String)("CacheMsV2Access") = "" Then
  267. Type = PageType.Ms
  268. Else
  269. Type = PageType.MsSkin
  270. End If
  271. Settings.Set("LoginType", McLoginType.Ms)
  272. Else
  273. Type = PageType.Legacy
  274. Settings.Set("LoginType", McLoginType.Legacy)
  275. End If
  276. PanType.Visibility = Visibility.Visible
  277. PanTypeOne.Visibility = Visibility.Collapsed
  278. RadioLoginType5.Visibility = Visibility.Visible
  279. RadioLoginType0.Visibility = Visibility.Visible
  280. Case 1 '仅正版
  281. If Settings.Get(Of String)("CacheMsV2Access") = "" Then
  282. Type = PageType.Ms
  283. Else
  284. Type = PageType.MsSkin
  285. End If
  286. Settings.Set("LoginType", McLoginType.Ms)
  287. PanType.Visibility = Visibility.Collapsed
  288. PanTypeOne.Visibility = Visibility.Visible
  289. PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonShield)
  290. LabTypeOne.Text = "正版登录"
  291. RadioLoginType5.Visibility = Visibility.Visible
  292. RadioLoginType0.Visibility = Visibility.Collapsed
  293. Case 2 '仅离线
  294. Type = PageType.Legacy
  295. Settings.Set("LoginType", McLoginType.Legacy)
  296. PanType.Visibility = Visibility.Collapsed
  297. PanTypeOne.Visibility = Visibility.Visible
  298. PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonOffline)
  299. LabTypeOne.Text = "离线登录"
  300. Case 3 '统一通行证
  301. If Settings.Get(Of String)("CacheNideAccess") = "" Then
  302. Type = PageType.Nide
  303. Else
  304. Type = PageType.NideSkin
  305. End If
  306. Settings.Set("LoginType", McLoginType.Nide)
  307. PanType.Visibility = Visibility.Collapsed
  308. PanTypeOne.Visibility = Visibility.Visible
  309. PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonCard)
  310. LabTypeOne.Text = "统一通行证登录"
  311. Case 4 'Authlib-Injector
  312. If Settings.Get(Of String)("CacheAuthAccess") = "" Then
  313. Type = PageType.Auth
  314. Else
  315. Type = PageType.AuthSkin
  316. End If
  317. Settings.Set("LoginType", McLoginType.Auth)
  318. PanType.Visibility = Visibility.Collapsed
  319. PanTypeOne.Visibility = Visibility.Visible
  320. PathTypeOne.Data = (New GeometryConverter).ConvertFromString(Logo.IconButtonCard)
  321. LabTypeOne.Text = If(McInstanceSelected Is Nothing, Settings.Get(Of String)("CacheAuthServerName"), Settings.Get(Of String)("VersionServerAuthName", Instance:=McInstanceSelected))
  322. If LabTypeOne.Text = "" Then LabTypeOne.Text = "第三方登录"
  323. Case Else
  324. Logger.Error($"未知的登录页面:{LoginPageType}", LogBehavior.Toast)
  325. GoTo UnknownType
  326. End Select
  327. '刷新页面
  328. If PageCurrent = Type Then Return
  329. PageChange(Type, Anim).Reload(KeepInput)
  330. Dim Control As MyRadioButton = FindName("RadioLoginType" & Settings.Get(Of McLoginType)("LoginType"))
  331. If Control IsNot Nothing Then Control.Checked = True
  332. End Sub
  333. Private Sub RadioLoginType_Change(sender As Object, raiseByMouse As Boolean) Handles RadioLoginType0.Check, RadioLoginType5.Check
  334. If raiseByMouse Then RefreshPage(True, True)
  335. End Sub
  336. #End Region
  337. #Region "皮肤"
  338. '微软正版皮肤
  339. Public Shared SkinMs As New LoaderTask(Of (String, String), String)("Loader Skin Ms", AddressOf SkinMsLoad, AddressOf SkinMsInput, ThreadPriority.AboveNormal)
  340. Private Shared Function SkinMsInput() As (String, String)
  341. '获取名称
  342. Return (Settings.Get(Of String)("CacheMsV2Name"), Settings.Get(Of String)("CacheMsV2Uuid"))
  343. End Function
  344. Private Shared Sub SkinMsLoad(Data As LoaderTask(Of (String, String), String))
  345. '清空已有皮肤
  346. '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
  347. RunInUi(Sub() If FrmLoginMsSkin IsNot Nothing AndAlso FrmLoginMsSkin.Skin IsNot Nothing Then FrmLoginMsSkin.Skin.Clear())
  348. '获取 Url
  349. Dim UserName As String = Data.Input.Item1
  350. Dim Uuid As String = Data.Input.Item2
  351. If UserName = "" Then
  352. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
  353. Logger.Info("获取微软正版皮肤失败,ID 为空")
  354. GoTo Finish
  355. End If
  356. Try
  357. Dim Result As String = McSkinGetAddress(Uuid, "Ms")
  358. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  359. Result = McSkinDownload(Result)
  360. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  361. Data.Output = Result
  362. Catch ex As ThreadInterruptedException
  363. Data.Output = ""
  364. Return
  365. Catch ex As Exception
  366. If ex.GetDisplay(False).Contains("(429)") Then
  367. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
  368. Logger.Error($"获取正版皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast)
  369. ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then
  370. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
  371. Logger.Info("用户未设置自定义皮肤,跳过皮肤加载")
  372. Else
  373. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
  374. Logger.Error(ex, $"获取微软正版皮肤失败({UserName})", LogBehavior.Toast)
  375. End If
  376. End Try
  377. Finish:
  378. '刷新显示
  379. If FrmLoginMsSkin IsNot Nothing Then
  380. RunInUi(AddressOf FrmLoginMsSkin.Skin.Load)
  381. ElseIf Not Data.IsInterrupted Then '如果已经中断,Input 也被清空,就不会再次刷新
  382. Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
  383. End If
  384. End Sub
  385. '离线皮肤
  386. Public Shared SkinLegacy As New LoaderTask(Of (String, String), String)("Loader Skin Legacy", AddressOf SkinLegacyLoad, AddressOf SkinLegacyInput, ThreadPriority.AboveNormal)
  387. Private Shared Function SkinLegacyInput() As (String, String)
  388. '根据类型判断输入
  389. Dim Type As Integer = Settings.Get(Of Integer)("LaunchSkinType")
  390. Select Case Type
  391. Case 0
  392. If FrmLoginLegacy?.IsReloaded Then
  393. Return ("0", If(FrmLoginLegacy.ComboName.Text.Trim, ""))
  394. ElseIf Settings.Get(Of String)("LoginLegacyName") = "" Then
  395. Return ("0", "")
  396. Else
  397. Return ("0", If(Settings.Get(Of String)("LoginLegacyName").ToString.BeforeFirst("¨"), ""))
  398. End If
  399. Case 3
  400. Return ("3", Settings.Get(Of String)("LaunchSkinID"))
  401. Case Else
  402. Return (Type.ToString, "")
  403. End Select
  404. End Function
  405. Private Shared Sub SkinLegacyLoad(Data As LoaderTask(Of (String, String), String))
  406. '清空已有皮肤
  407. RunInUi(Sub() If FrmLoginLegacy IsNot Nothing AndAlso FrmLoginLegacy.Skin IsNot Nothing Then FrmLoginLegacy.Skin.Clear())
  408. '获取 Url
  409. Select Case CInt(Data.Input.Item1)
  410. Case 0 '默认
  411. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(Data.Input.Item2)) & ".png"
  412. Case 1 'Steve
  413. UseDefault:
  414. Data.Output = PathImage & "Skins/Steve.png"
  415. Case 2 'Alex
  416. Data.Output = PathImage & "Skins/Alex.png"
  417. Case 3 '正版
  418. Dim ID As String = Data.Input.Item2
  419. Try
  420. If ID.Count < 2 Then
  421. Data.Output = PathImage & "Skins/Steve.png"
  422. Else
  423. Dim Result As String = McLoginMojangUuid(ID, True)
  424. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & ID)
  425. Result = McSkinGetAddress(Result, "Mojang")
  426. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & ID)
  427. Result = McSkinDownload(Result)
  428. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & ID)
  429. Data.Output = Result
  430. End If
  431. Catch ex As ThreadInterruptedException
  432. Data.Output = ""
  433. Return
  434. Catch ex As Exception
  435. If ex.GetDisplay(False).Contains("(429)") Then
  436. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(ID)) & ".png"
  437. Logger.Info($"获取离线登录使用的正版皮肤失败({ID}):获取皮肤太过频繁,请 5 分钟后再试!")
  438. Else
  439. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(ID)) & ".png"
  440. Logger.Warn(ex, $"获取离线登录使用的正版皮肤失败({ID})")
  441. End If
  442. End Try
  443. Case 4 '自定义
  444. If Not FileUtils.Exists(PathAppdata & "CustomSkin.png") Then
  445. Hint("未找到离线皮肤自定义文件,可能它已被删除。PCL 将使用默认的 Steve 皮肤!")
  446. Settings.Set("LaunchSkinType", 1)
  447. GoTo UseDefault
  448. End If
  449. Data.Output = PathAppdata & "CustomSkin.png"
  450. End Select
  451. '刷新显示
  452. If FrmLoginLegacy IsNot Nothing Then
  453. RunInUi(AddressOf FrmLoginLegacy.Skin.Load)
  454. ElseIf Not Data.IsInterrupted Then '如果已经中断,Input 也被清空,就不会再次刷新
  455. Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
  456. End If
  457. End Sub
  458. '统一通行证皮肤
  459. Public Shared SkinNide As New LoaderTask(Of (String, String), String)("Loader Skin Nide", AddressOf SkinNideLoad, AddressOf SkinNideInput, ThreadPriority.AboveNormal)
  460. Private Shared Function SkinNideInput() As (String, String)
  461. '获取名称
  462. Return (Settings.Get(Of String)("CacheNideName"), Settings.Get(Of String)("CacheNideUuid"))
  463. End Function
  464. Private Shared Sub SkinNideLoad(Data As LoaderTask(Of (String, String), String))
  465. '清空已有皮肤
  466. '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
  467. RunInUi(Sub() If FrmLoginNideSkin IsNot Nothing AndAlso FrmLoginNideSkin.Skin IsNot Nothing Then FrmLoginNideSkin.Skin.Clear())
  468. '获取 Url
  469. Dim UserName As String = Data.Input.Item1
  470. Dim Uuid As String = Data.Input.Item2
  471. If UserName = "" Then
  472. Data.Output = PathImage & "Skins/" & McSkinSex(McLoginLegacyUuid(UserName)) & ".png"
  473. Logger.Info("获取统一通行证皮肤失败,ID 为空")
  474. GoTo Finish
  475. End If
  476. Try
  477. Dim Result As String = McSkinGetAddress(Uuid, "Nide")
  478. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  479. Result = McSkinDownload(Result)
  480. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  481. Data.Output = Result
  482. Catch ex As ThreadInterruptedException
  483. Data.Output = ""
  484. Return
  485. Catch ex As Exception
  486. If ex.GetDisplay(False).Contains("(429)") Then
  487. Data.Output = PathImage & "Skins/Steve.png"
  488. Logger.Error($"获取统一通行证皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast)
  489. ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then
  490. Data.Output = PathImage & "Skins/Steve.png"
  491. Logger.Info("用户未设置自定义皮肤,跳过皮肤加载")
  492. Else
  493. Data.Output = PathImage & "Skins/Steve.png"
  494. Logger.Error(ex, $"获取统一通行证皮肤失败({UserName})", LogBehavior.Toast)
  495. End If
  496. End Try
  497. Finish:
  498. '刷新显示
  499. If FrmLoginNideSkin IsNot Nothing Then
  500. RunInUi(AddressOf FrmLoginNideSkin.Skin.Load)
  501. ElseIf Not Data.IsInterrupted Then '如果已经中断,Input 也被清空,就不会再次刷新
  502. Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
  503. End If
  504. End Sub
  505. 'Authlib-Injector 皮肤
  506. Public Shared SkinAuth As New LoaderTask(Of (String, String), String)("Loader Skin Auth", AddressOf SkinAuthLoad, AddressOf SkinAuthInput, ThreadPriority.AboveNormal)
  507. Private Shared Function SkinAuthInput() As (String, String)
  508. '获取名称
  509. Return (Settings.Get(Of String)("CacheAuthName"), Settings.Get(Of String)("CacheAuthUuid"))
  510. End Function
  511. Private Shared Sub SkinAuthLoad(Data As LoaderTask(Of (String, String), String))
  512. '清空已有皮肤
  513. '如果在输入时清空皮肤,若输入内容一样则不会执行 Load 方法,导致皮肤不被加载
  514. RunInUi(Sub() If FrmLoginAuthSkin IsNot Nothing AndAlso FrmLoginAuthSkin.Skin IsNot Nothing Then FrmLoginAuthSkin.Skin.Clear())
  515. '获取 Url
  516. Dim UserName As String = Data.Input.Item1
  517. Dim UUID As String = Data.Input.Item2
  518. If UserName = "" Then
  519. Data.Output = PathImage & "Skins/Steve.png"
  520. Logger.Info("获取 Authlib-Injector 皮肤失败,ID 为空")
  521. GoTo Finish
  522. End If
  523. Try
  524. Dim Result As String = McSkinGetAddress(UUID, "Auth")
  525. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  526. Result = McSkinDownload(Result)
  527. If Data.IsInterrupted Then Throw New ThreadInterruptedException("当前任务已取消:" & UserName)
  528. Data.Output = Result
  529. Catch ex As ThreadInterruptedException
  530. Data.Output = ""
  531. Return
  532. Catch ex As Exception
  533. If ex.GetDisplay(False).Contains("(429)") Then
  534. Data.Output = PathImage & "Skins/Steve.png"
  535. Logger.Error($"获取 Authlib-Injector 皮肤失败({UserName}):获取皮肤太过频繁,请 5 分钟后再试!", LogBehavior.Toast)
  536. ElseIf ex.GetDisplay(False).Contains("未设置自定义皮肤") Then
  537. Data.Output = PathImage & "Skins/Steve.png"
  538. Logger.Info("用户未设置自定义皮肤,跳过皮肤加载")
  539. Else
  540. Data.Output = PathImage & "Skins/Steve.png"
  541. Logger.Error(ex, $"获取 Authlib-Injector 皮肤失败({UserName})", LogBehavior.Toast)
  542. End If
  543. End Try
  544. Finish:
  545. '刷新显示
  546. If FrmLoginAuthSkin IsNot Nothing Then
  547. RunInUi(AddressOf FrmLoginAuthSkin.Skin.Load)
  548. ElseIf Not Data.IsInterrupted Then '如果已经中断,Input 也被清空,就不会再次刷新
  549. Data.Input = Nothing '清空输入,因为皮肤实际上没有被渲染,如果不清空切换到页面的 Start 会由于输入相同而不渲染
  550. End If
  551. End Sub
  552. '全部皮肤加载器
  553. '需要放在其中元素的后面,否则会因为它提前被加载而莫名其妙变成 Nothing
  554. Public Shared SkinLoaders As New List(Of LoaderTask(Of (String, String), String)) From {SkinMs, SkinLegacy, SkinNide, SkinAuth}
  555. #End Region
  556. '版本选择按钮
  557. Private Sub BtnVersion_Click(sender As Object, e As EventArgs) Handles BtnVersion.Click
  558. If McLaunchLoader.State = LoadState.Loading Then Return
  559. FrmMain.PageChange(FormMain.PageType.InstanceSelect)
  560. End Sub
  561. '启动按钮
  562. Public Sub LaunchButtonClick() Handles BtnLaunch.Click
  563. If McLaunchLoader.State = LoadState.Loading OrElse Not BtnLaunch.IsEnabled OrElse
  564. (FrmMain.PageRight IsNot Nothing AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentStay AndAlso FrmMain.PageRight.PageState <> MyPageRight.PageStates.ContentEnter) Then Return
  565. '愚人节处理
  566. If IsAprilEnabled AndAlso Not IsAprilGiveup Then
  567. ThemeUnlock(12, False, "隐藏主题 滑稽彩 已解锁!")
  568. IsAprilGiveup = True
  569. Settings.Set("AprilDoneYear", Date.Now.Year)
  570. FrmLaunchLeft.AprilScaleTrans.ScaleX = 1
  571. FrmLaunchLeft.AprilScaleTrans.ScaleY = 1
  572. FrmLaunchLeft.AprilPosTrans.X = 0
  573. FrmLaunchLeft.AprilPosTrans.Y = 0
  574. FrmMain.BtnExtraApril.ShowRefresh()
  575. End If
  576. '实际的启动
  577. If BtnLaunch.Text = "启动游戏" Then
  578. McLaunchStart()
  579. ElseIf BtnLaunch.Text = "下载游戏" Then
  580. FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadInstall)
  581. End If
  582. End Sub
  583. Private BtnLaunchState As Integer = 0
  584. Private BtnLaunchInstance As McInstance = Nothing
  585. Public Sub RefreshButtonsUI() Handles BtnLaunch.Loaded
  586. If Not BtnLaunch.IsLoaded Then Return
  587. '获取当前状态
  588. Dim CurrentState As Integer
  589. If (Not IsLoadFinished) OrElse McInstanceListLoader.State = LoadState.Loading OrElse McFolderListLoader.State = LoadState.Loading Then
  590. CurrentState = 0
  591. Else
  592. If McInstanceSelected Is Nothing Then
  593. If Settings.Get(Of Boolean)("UiHiddenPageDownload") AndAlso Not PageSetupUI.HiddenForceShow Then
  594. CurrentState = 1
  595. Else
  596. CurrentState = 2
  597. End If
  598. Else
  599. CurrentState = 3
  600. End If
  601. End If
  602. '更新状态
  603. If CurrentState = BtnLaunchState AndAlso
  604. If(McInstanceSelected Is Nothing, "", McInstanceSelected.PathVersion) = If(BtnLaunchInstance Is Nothing, "", BtnLaunchInstance.PathVersion) Then GoTo ExitRefresh
  605. BtnLaunchInstance = McInstanceSelected
  606. BtnLaunchState = CurrentState
  607. Select Case CurrentState
  608. Case 0
  609. Logger.Info("启动按钮:正在加载 Minecraft 版本")
  610. FrmLaunchLeft.BtnLaunch.Text = "正在加载"
  611. FrmLaunchLeft.BtnLaunch.IsEnabled = False
  612. FrmLaunchLeft.LabVersion.Text = "正在加载中,请稍候"
  613. FrmLaunchLeft.BtnVersion.IsEnabled = False
  614. FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed
  615. Case 1
  616. Logger.Info("启动按钮:无 Minecraft 版本,下载已禁用")
  617. FrmLaunchLeft.BtnLaunch.Text = "启动游戏"
  618. FrmLaunchLeft.BtnLaunch.IsEnabled = False
  619. FrmLaunchLeft.LabVersion.Text = "未找到可用的游戏版本"
  620. FrmLaunchLeft.BtnVersion.IsEnabled = True
  621. FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed
  622. Case 2
  623. Logger.Info("启动按钮:无 Minecraft 版本,要求下载")
  624. FrmLaunchLeft.BtnLaunch.Text = "下载游戏"
  625. FrmLaunchLeft.BtnLaunch.IsEnabled = True
  626. FrmLaunchLeft.LabVersion.Text = "未找到可用的游戏版本"
  627. FrmLaunchLeft.BtnVersion.IsEnabled = True
  628. FrmLaunchLeft.BtnMore.Visibility = Visibility.Collapsed
  629. Case 3
  630. Logger.Info($"启动按钮:Minecraft 版本:{McInstanceSelected.PathVersion}")
  631. FrmLaunchLeft.BtnLaunch.Text = "启动游戏"
  632. FrmLaunchLeft.BtnVersion.IsEnabled = True
  633. FrmLaunchLeft.BtnLaunch.IsEnabled = True
  634. FrmLaunchLeft.LabVersion.Text = McInstanceSelected.Name
  635. 'FrmLaunchLeft.BtnMore.Visibility = Visibility.Visible '由功能隐藏设置修改
  636. End Select
  637. ExitRefresh:
  638. '功能隐藏
  639. FrmLaunchLeft.BtnVersion.Visibility = If(Not PageSetupUI.HiddenForceShow AndAlso Settings.Get(Of Boolean)("UiHiddenFunctionSelect"), Visibility.Collapsed, Visibility.Visible)
  640. If CurrentState = 3 Then
  641. FrmLaunchLeft.BtnMore.Visibility = FrmLaunchLeft.BtnVersion.Visibility
  642. End If
  643. End Sub
  644. '取消按钮
  645. Private Sub BtnCancel_Click() Handles BtnCancel.Click
  646. If McLaunchLoaderReal IsNot Nothing Then
  647. McLaunchLoaderReal.Interrupt()
  648. McLaunchLog("已取消启动")
  649. Try
  650. If McLaunchWatcher IsNot Nothing Then
  651. McLaunchWatcher.Kill()
  652. ElseIf McLaunchProcess IsNot Nothing Then
  653. If Not McLaunchProcess.HasExited Then McLaunchProcess.Kill()
  654. End If
  655. Catch ex As Exception
  656. Logger.Error(ex, "取消启动结束进程失败", LogBehavior.Toast)
  657. End Try
  658. End If
  659. End Sub
  660. '版本设置按钮
  661. Private Sub BtnMore_Click(sender As Object, e As EventArgs) Handles BtnMore.Click
  662. If McLaunchLoader.State = LoadState.Loading Then Return
  663. McInstanceSelected.Load()
  664. PageInstanceLeft.Instance = McInstanceSelected
  665. FrmMain.PageChange(FormMain.PageType.InstanceSetup, 0)
  666. End Sub
  667. ''' <summary>
  668. ''' 每 0.1s 执行一次,刷新启动的数据 UI 显示。
  669. ''' </summary>
  670. Public Sub LaunchingRefresh()
  671. Try
  672. If McLaunchLoaderReal.State = LoadState.Interrupted Then Return
  673. '阶段状态获取
  674. Dim IsLaunched As Boolean = False '是否已经启动游戏,只是在等待窗口
  675. Try
  676. For Each Loader In McLaunchLoaderReal.GetLoaderList(False)
  677. If Loader.State = LoadState.Loading OrElse Loader.State = LoadState.Waiting Then
  678. LabLaunchingStage.Text = Loader.Name
  679. IsLaunched = Loader.Name = "等待游戏窗口出现" OrElse Loader.Name = "结束处理"
  680. Exit Try
  681. End If
  682. Next
  683. LabLaunchingStage.Text = "已完成"
  684. Catch ex As Exception
  685. Logger.Warn(ex, "获取是否启动完成失败,可能是由于启动状态改变导致集合已修改")
  686. Return
  687. End Try
  688. LabLaunchingTitle.Text = If(IsLaunched OrElse McLaunchLoaderReal.State = LoadState.Finished,
  689. "已启动游戏",
  690. If(CurrentLaunchOptions.SaveBatch Is Nothing, "正在启动游戏", "正在导出启动脚本"))
  691. If AniIsRun("Launch State Page") Then IsLaunched = False '等待页面切换动画完成
  692. '更新进度
  693. Dim ActualProgress = McLaunchLoaderReal.Progress
  694. If ActualProgress >= ShowProgress Then ShowProgress += (ActualProgress - ShowProgress) * 0.1 + 0.0025 '向实际进度靠一点
  695. If ActualProgress <= ShowProgress Then ShowProgress = ActualProgress '原来或处理后变得比实际进度高,直接回退
  696. If IsLaunched Then ShowProgress = 1 '如果已经完成了,就不卖关子了
  697. LabLaunchingProgress.Text = (ShowProgress * 100).ToString("0.00") & " %"
  698. '更新下载速度
  699. Dim HasLaunchDownloader As Boolean = False
  700. Try
  701. If NetManager.Speed = 0 AndAlso LabLaunchingDownload.Visibility = Visibility.Collapsed Then
  702. '可能只是在检查文件,没有实际下载
  703. HasLaunchDownloader = False
  704. Else
  705. For Each Loader In NetManager.Tasks
  706. If Loader.RealParent IsNot Nothing AndAlso Loader.RealParent.Name = "Minecraft 启动" AndAlso Loader.State = LoadState.Loading Then HasLaunchDownloader = True
  707. Next
  708. End If
  709. Catch ex As Exception
  710. Logger.Warn(ex, "获取 Minecraft 启动下载器失败,可能是因为启动被取消")
  711. HasLaunchDownloader = False
  712. End Try
  713. LabLaunchingDownload.Text = FormatFileSize(NetManager.Speed) & "/s"
  714. '进度改变动画
  715. Dim AnimList As New List(Of AniData) From {
  716. AaGridLengthWidth(ProgressLaunchingFinished, ShowProgress - ProgressLaunchingFinished.Width.Value, 130,, New AniEaseOutFluent),
  717. AaGridLengthWidth(ProgressLaunchingUnfinished, 1 - ShowProgress - ProgressLaunchingUnfinished.Width.Value, 130,, New AniEaseOutFluent)
  718. }
  719. If HasLaunchDownloader = (LabLaunchingDownload.Visibility = Visibility.Collapsed) Then 'IsDownloadStateChanged
  720. LabLaunchingDownload.Visibility = Visibility.Visible
  721. LabLaunchingDownloadLeft.Visibility = Visibility.Visible
  722. AnimList.AddRange({
  723. AaOpacity(LabLaunchingDownload, If(HasLaunchDownloader, 1, 0) - LabLaunchingDownload.Opacity, 90),
  724. AaOpacity(LabLaunchingDownloadLeft, If(HasLaunchDownloader, 0.5, 0) - LabLaunchingDownloadLeft.Opacity, 90),
  725. AaCode(
  726. Sub()
  727. If Not HasLaunchDownloader Then
  728. LabLaunchingDownload.Visibility = Visibility.Collapsed
  729. LabLaunchingDownloadLeft.Visibility = Visibility.Collapsed
  730. End If
  731. End Sub, 110)
  732. })
  733. End If
  734. If (Not IsLaunched) = (LabLaunchingProgress.Visibility = Visibility.Collapsed) Then 'IsProgressStateChanged
  735. LabLaunchingProgress.Visibility = Visibility.Visible
  736. LabLaunchingProgressLeft.Visibility = Visibility.Visible
  737. If IsLaunched Then PanLaunchingHint.Visibility = Visibility.Visible
  738. AnimList.AddRange({
  739. AaOpacity(LabLaunchingProgress, If(Not IsLaunched, 1, 0) - LabLaunchingProgress.Opacity, 90),
  740. AaOpacity(LabLaunchingProgressLeft, If(Not IsLaunched, 0.5, 0) - LabLaunchingProgressLeft.Opacity, 90),
  741. AaOpacity(PanLaunchingHint, If(IsLaunched, 1, 0) - PanLaunchingHint.Opacity, 90)
  742. })
  743. End If
  744. AniStart(AnimList, "Launching Progress")
  745. Catch ex As Exception
  746. Logger.Error(ex, "刷新启动信息失败")
  747. End Try
  748. End Sub
  749. Private ShowProgress As Double = 0
  750. '尺寸改变动画
  751. Private IsWidthAnimating As Boolean = False
  752. Private ActualUsedWidth As Double
  753. Private Sub PanLaunchingInfo_SizeChangedW(sender As Object, e As SizeChangedEventArgs) Handles PanLaunchingInfo.SizeChanged
  754. Dim DeltaWidth As Double = e.NewSize.Width - e.PreviousSize.Width
  755. If e.PreviousSize.Width = 0 OrElse IsWidthAnimating OrElse Math.Abs(DeltaWidth) < 1 OrElse PanLaunchingInfo.ActualWidth = 0 Then Return
  756. AniStart({
  757. AaWidth(PanLaunchingInfo, DeltaWidth, 180,, New AniEaseOutFluent),
  758. AaCode(Sub()
  759. IsWidthAnimating = False
  760. PanLaunchingInfo.Width = ActualUsedWidth
  761. End Sub,, True)
  762. }, "Launching Info Width")
  763. IsWidthAnimating = True
  764. ActualUsedWidth = PanLaunchingInfo.Width
  765. PanLaunchingInfo.Width = e.PreviousSize.Width
  766. End Sub
  767. Private IsHeightAnimating As Boolean = False
  768. Private ActualUsedHeight As Double
  769. Private Sub PanLaunchingInfo_SizeChangedH(sender As Object, e As SizeChangedEventArgs) Handles PanLaunchingInfo.SizeChanged
  770. Dim DeltaHeight As Double = e.NewSize.Height - e.PreviousSize.Height
  771. If e.PreviousSize.Height = 0 OrElse IsHeightAnimating OrElse Math.Abs(DeltaHeight) < 1 OrElse PanLaunchingInfo.ActualHeight = 0 Then Return
  772. AniStart({
  773. AaHeight(PanLaunchingInfo, DeltaHeight, 180,, New AniEaseOutFluent),
  774. AaCode(Sub()
  775. IsHeightAnimating = False
  776. PanLaunchingInfo.Height = ActualUsedHeight
  777. End Sub,, True)
  778. }, "Launching Info Height")
  779. IsHeightAnimating = True
  780. ActualUsedHeight = PanLaunchingInfo.Height
  781. PanLaunchingInfo.Height = e.PreviousSize.Height
  782. End Sub
  783. End Class