PageLaunchLeft.xaml.vb 42 KB

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