PageLaunchLeft.xaml.vb 42 KB

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