PageLaunchLeft.xaml.vb 42 KB

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