PageLaunchLeft.xaml.vb 42 KB

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