PageLaunchLeft.xaml.vb 42 KB

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