PageDownloadInstall.xaml.vb 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199
  1. Public Class PageDownloadInstall
  2. Public Sub ControlInit()
  3. DisabledPageAnimControls.Add(BtnStart)
  4. LoadOptiFine.State = DlOptiFineListLoader
  5. LoadLiteLoader.State = DlLiteLoaderListLoader
  6. LoadFabric.State = DlFabricListLoader
  7. LoadFabricApi.State = DlFabricApiLoader
  8. LoadNeoForge.State = DlNeoForgeListLoader
  9. LoadOptiFabric.State = DlOptiFabricLoader
  10. McDownloadForgeRecommendedRefresh()
  11. End Sub
  12. Public Overridable Sub LoaderInit() Handles Me.Initialized
  13. ControlInit()
  14. If RestrictMcDownload Then
  15. PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, New LoaderTask(Of String, DlClientListResult)("关闭下载", Sub() Throw New Exception("为遵守相关法律法规,PCL 暂时不再提供 MC 下载功能,非常抱歉 qwq……")), AddressOf LoadMinecraft_OnFinish)
  16. Else
  17. PageLoaderInit(LoadMinecraft, PanLoad, PanAllBack, Nothing, DlClientListLoader, AddressOf LoadMinecraft_OnFinish)
  18. End If
  19. End Sub
  20. Private IsLoad As Boolean = False
  21. Public Overridable Sub Init() Handles Me.Loaded
  22. If IsLoad Then
  23. IsSelectNameEdited = False
  24. ExitSelectPage()
  25. If DlClientListLoader.State = LoadState.Finished Then TrySelectWaitingVersion()
  26. End If
  27. IsLoad = True
  28. PanBack.ScrollToHome()
  29. DlOptiFineListLoader.Start()
  30. DlLiteLoaderListLoader.Start()
  31. DlFabricListLoader.Start()
  32. DlNeoForgeListLoader.Start()
  33. '重载预览
  34. TextSelectName.ValidateRules = New ObjectModel.Collection(Of Validate) From {New ValidateFolderName(McFolderSelected & "versions")}
  35. TextSelectName.Validate()
  36. ReloadSelected()
  37. End Sub
  38. #Region "页面切换"
  39. '页面切换动画
  40. Public IsInSelectPage As Boolean = False
  41. ''' <summary>
  42. ''' 选择页面 UI 初始化。
  43. ''' </summary>
  44. Public Sub PrepareSelectPage()
  45. IsInSelectPage = True
  46. PanInner.Margin = New Thickness(25, 10, 25, 40)
  47. PanSelect.Visibility = Visibility.Visible
  48. PanSelect.IsHitTestVisible = True
  49. PanMinecraft.IsHitTestVisible = False
  50. PanBack.IsHitTestVisible = False
  51. PanBack.ScrollToHome()
  52. DisabledPageAnimControls.Remove(BtnStart)
  53. BtnStart.Show = True
  54. For Each Card In {CardOptiFine, CardLiteLoader, CardForge, CardNeoForge, CardFabric, CardFabricApi, CardOptiFabric}
  55. Card.IsSwapped = True
  56. Next
  57. '如果在选择页面按了刷新键,选择页的东西可能会由于动画被隐藏,但不会由于加载结束而再次显示,因此这里需要手动恢复
  58. For Each Control In GetAllAnimControls(PanSelect)
  59. Control.Opacity = 1
  60. If Control.RenderTransform Is Nothing OrElse TypeOf Control.RenderTransform Is TranslateTransform Then
  61. Control.RenderTransform = New TranslateTransform
  62. End If
  63. Next
  64. End Sub
  65. Private Sub EnterSelectPage()
  66. If IsInSelectPage Then Return
  67. AutoSelectedFabricApi = False
  68. AutoSelectedOptiFabric = False
  69. PrepareSelectPage()
  70. '启动 Forge 加载
  71. If McVersion.IsStandardFormat(VanillaName) Then
  72. Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion " & VanillaName, AddressOf DlForgeVersionMain)
  73. LoadForge.State = ForgeLoader
  74. ForgeLoader.Start(VanillaName)
  75. End If
  76. '启动 Fabric API、OptiFabric 加载
  77. DlFabricApiLoader.Start()
  78. DlOptiFabricLoader.Start()
  79. AniStart({
  80. AaOpacity(PanMinecraft, -PanMinecraft.Opacity, 70, 10),
  81. AaTranslateX(PanMinecraft, -50 - CType(PanMinecraft.RenderTransform, TranslateTransform).X, 90, 10),
  82. AaCode(
  83. Sub()
  84. PanBack.ScrollToHome()
  85. ReloadComponentLists()
  86. PanMinecraft.Visibility = Visibility.Collapsed
  87. End Sub, After:=True),
  88. AaOpacity(PanSelect, 1 - PanSelect.Opacity, 70, 100),
  89. AaTranslateX(PanSelect, -CType(PanSelect.RenderTransform, TranslateTransform).X, 160, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
  90. AaCode(Sub() PanBack.IsHitTestVisible = True,, True)
  91. }, "FrmDownloadInstall SelectPageSwitch", True)
  92. End Sub
  93. Public Sub ReloadComponentLists()
  94. TextSelectName.Validate()
  95. OptiFine_Loaded()
  96. LiteLoader_Loaded()
  97. Forge_Loaded()
  98. NeoForge_Loaded()
  99. Fabric_Loaded()
  100. FabricApi_Loaded()
  101. OptiFabric_Loaded()
  102. ReloadSelected()
  103. End Sub
  104. Public Sub ExitSelectPage() Handles BtnBack.Click
  105. If Not IsInSelectPage Then Return
  106. IsInSelectPage = False
  107. PanInner.Margin = New Thickness(25, 10, 25, 25)
  108. DisabledPageAnimControls.Add(BtnStart)
  109. BtnStart.Show = False
  110. ClearSelected() '清除已选择项
  111. PanMinecraft.Visibility = Visibility.Visible
  112. PanSelect.IsHitTestVisible = False
  113. PanMinecraft.IsHitTestVisible = True
  114. PanBack.IsHitTestVisible = False
  115. PanBack.ScrollToHome()
  116. AniStart({
  117. AaOpacity(PanSelect, -PanSelect.Opacity, 70, 10),
  118. AaTranslateX(PanSelect, 50 - CType(PanSelect.RenderTransform, TranslateTransform).X, 90, 10),
  119. AaCode(Sub() PanBack.ScrollToHome(), After:=True),
  120. AaOpacity(PanMinecraft, 1 - PanMinecraft.Opacity, 70, 100),
  121. AaTranslateX(PanMinecraft, -CType(PanMinecraft.RenderTransform, TranslateTransform).X, 160, 100, Ease:=New AniEaseOutFluent(AniEasePower.ExtraStrong)),
  122. AaCode(
  123. Sub()
  124. PanSelect.Visibility = Visibility.Collapsed
  125. PanBack.IsHitTestVisible = True
  126. End Sub,, True)
  127. }, "FrmDownloadInstall SelectPageSwitch")
  128. End Sub
  129. Public Overridable Sub MinecraftSelected(sender As MyListItem, e As MouseButtonEventArgs)
  130. VanillaName = sender.Title
  131. VanillaData = sender.Tag
  132. VanillaIcon = sender.Logo
  133. LabMinecraftVersion.Text = VanillaName
  134. EnterSelectPage()
  135. End Sub
  136. #End Region
  137. #Region "选择"
  138. 'Minecraft
  139. Public VanillaName As String
  140. Public VanillaData As JObject
  141. Public VanillaIcon As String
  142. Private ReadOnly Property VanillaDrop As Integer
  143. Get
  144. Return McVersion.VersionToDrop(VanillaName)
  145. End Get
  146. End Property
  147. '附加组件
  148. Public SelectedOptiFine As DlOptiFineListEntry = Nothing
  149. Public SelectedLiteLoader As DlLiteLoaderListEntry = Nothing
  150. Public SelectedForge As DlForgeVersionEntry = Nothing
  151. Public SelectedNeoForge As DlNeoForgeListEntry = Nothing
  152. Public SelectedFabric As String = Nothing
  153. Public SelectedFabricApi As ResourceVersion = Nothing
  154. Public SelectedOptiFabric As ResourceVersion = Nothing
  155. ''' <summary>
  156. ''' 重载已选择的项目的显示。
  157. ''' </summary>
  158. Public Overridable Sub ReloadSelected() Handles CardOptiFine.Swap, LoadOptiFine.StateChanged, CardForge.Swap, LoadForge.StateChanged, CardNeoForge.Swap, LoadNeoForge.StateChanged, CardFabric.Swap, LoadFabric.StateChanged, CardFabricApi.Swap, LoadFabricApi.StateChanged, CardOptiFabric.Swap, LoadOptiFabric.StateChanged, CardLiteLoader.Swap, LoadLiteLoader.StateChanged
  159. Static Ongoing As Boolean = False '#3742 中,LoadOptiFineGetError 会初始化 LoadOptiFine,触发事件 LoadOptiFine.StateChanged,导致再次调用 SelectReload
  160. If VanillaName Is Nothing OrElse Ongoing Then Return
  161. Ongoing = True
  162. '主预览
  163. SelectNameUpdate()
  164. ImgLogo.Source = GetSelectLogo()
  165. ImgMinecraft.Source = VanillaIcon
  166. 'OptiFine
  167. Dim OptiFineError As String = LoadOptiFineGetError()
  168. CardOptiFine.MainSwap.Visibility = If(OptiFineError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  169. If OptiFineError IsNot Nothing Then CardOptiFine.IsSwapped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
  170. SetPanelVisibility(PanOptiFineInfo, CardOptiFine.IsSwapped)
  171. If SelectedOptiFine Is Nothing Then
  172. BtnOptiFineClear.Visibility = Visibility.Collapsed
  173. ImgOptiFine.Visibility = Visibility.Collapsed
  174. LabOptiFine.Text = If(OptiFineError, "可以添加")
  175. LabOptiFine.Foreground = ColorGray4
  176. Else
  177. BtnOptiFineClear.Visibility = Visibility.Visible
  178. ImgOptiFine.Visibility = Visibility.Visible
  179. LabOptiFine.Text = SelectedOptiFine.DisplayName.Replace(VanillaName & " ", "")
  180. LabOptiFine.Foreground = ColorGray1
  181. End If
  182. 'LiteLoader
  183. If VanillaDrop >= 130 Then
  184. CardLiteLoader.Visibility = Visibility.Collapsed
  185. Else
  186. CardLiteLoader.Visibility = Visibility.Visible
  187. Dim LiteLoaderError As String = LoadLiteLoaderGetError()
  188. CardLiteLoader.MainSwap.Visibility = If(LiteLoaderError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  189. If LiteLoaderError IsNot Nothing Then CardLiteLoader.IsSwapped = True '例如在同时展开卡片时选择了不兼容项则强制折叠
  190. SetPanelVisibility(PanLiteLoaderInfo, CardLiteLoader.IsSwapped)
  191. If SelectedLiteLoader Is Nothing Then
  192. BtnLiteLoaderClear.Visibility = Visibility.Collapsed
  193. ImgLiteLoader.Visibility = Visibility.Collapsed
  194. LabLiteLoader.Text = If(LiteLoaderError, "可以添加")
  195. LabLiteLoader.Foreground = ColorGray4
  196. Else
  197. BtnLiteLoaderClear.Visibility = Visibility.Visible
  198. ImgLiteLoader.Visibility = Visibility.Visible
  199. LabLiteLoader.Text = SelectedLiteLoader.Inherit
  200. LabLiteLoader.Foreground = ColorGray1
  201. End If
  202. End If
  203. 'Forge
  204. If Not McVersion.IsStandardFormat(VanillaName) Then
  205. CardForge.Visibility = Visibility.Collapsed
  206. Else
  207. CardForge.Visibility = Visibility.Visible
  208. Dim ForgeError As String = LoadForgeGetError()
  209. CardForge.MainSwap.Visibility = If(ForgeError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  210. If ForgeError IsNot Nothing Then CardForge.IsSwapped = True
  211. SetPanelVisibility(PanForgeInfo, CardForge.IsSwapped)
  212. If SelectedForge Is Nothing Then
  213. BtnForgeClear.Visibility = Visibility.Collapsed
  214. ImgForge.Visibility = Visibility.Collapsed
  215. LabForge.Text = If(ForgeError, "可以添加")
  216. LabForge.Foreground = ColorGray4
  217. Else
  218. BtnForgeClear.Visibility = Visibility.Visible
  219. ImgForge.Visibility = Visibility.Visible
  220. LabForge.Text = SelectedForge.VersionName
  221. LabForge.Foreground = ColorGray1
  222. End If
  223. End If
  224. 'NeoForge
  225. If VanillaData("releaseTime").ToObject(Of Date) < New Date(2023, 6, 11) Then '匹配 1.20.1+ 与一些愚人节版本
  226. CardNeoForge.Visibility = Visibility.Collapsed
  227. Else
  228. CardNeoForge.Visibility = Visibility.Visible
  229. Dim NeoForgeError As String = LoadNeoForgeGetError()
  230. CardNeoForge.MainSwap.Visibility = If(NeoForgeError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  231. If NeoForgeError IsNot Nothing Then CardNeoForge.IsSwapped = True
  232. SetPanelVisibility(PanNeoForgeInfo, CardNeoForge.IsSwapped)
  233. If SelectedNeoForge Is Nothing Then
  234. BtnNeoForgeClear.Visibility = Visibility.Collapsed
  235. ImgNeoForge.Visibility = Visibility.Collapsed
  236. LabNeoForge.Text = If(NeoForgeError, "可以添加")
  237. LabNeoForge.Foreground = ColorGray4
  238. Else
  239. BtnNeoForgeClear.Visibility = Visibility.Visible
  240. ImgNeoForge.Visibility = Visibility.Visible
  241. LabNeoForge.Text = SelectedNeoForge.VersionName
  242. LabNeoForge.Foreground = ColorGray1
  243. End If
  244. End If
  245. 'Fabric
  246. If VanillaDrop <= 130 Then
  247. CardFabric.Visibility = Visibility.Collapsed
  248. Else
  249. CardFabric.Visibility = Visibility.Visible
  250. Dim FabricError As String = LoadFabricGetError()
  251. CardFabric.MainSwap.Visibility = If(FabricError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  252. If FabricError IsNot Nothing Then CardFabric.IsSwapped = True
  253. SetPanelVisibility(PanFabricInfo, CardFabric.IsSwapped)
  254. If SelectedFabric Is Nothing Then
  255. BtnFabricClear.Visibility = Visibility.Collapsed
  256. ImgFabric.Visibility = Visibility.Collapsed
  257. LabFabric.Text = If(FabricError, "可以添加")
  258. LabFabric.Foreground = ColorGray4
  259. Else
  260. BtnFabricClear.Visibility = Visibility.Visible
  261. ImgFabric.Visibility = Visibility.Visible
  262. LabFabric.Text = SelectedFabric.Replace("+build", "")
  263. LabFabric.Foreground = ColorGray1
  264. End If
  265. End If
  266. 'FabricApi
  267. If SelectedFabric Is Nothing Then
  268. CardFabricApi.Visibility = Visibility.Collapsed
  269. Else
  270. CardFabricApi.Visibility = Visibility.Visible
  271. Dim FabricApiError As String = LoadFabricApiGetError()
  272. CardFabricApi.MainSwap.Visibility = If(FabricApiError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  273. If FabricApiError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardFabricApi.IsSwapped = True
  274. SetPanelVisibility(PanFabricApiInfo, CardFabricApi.IsSwapped)
  275. If SelectedFabricApi Is Nothing Then
  276. BtnFabricApiClear.Visibility = Visibility.Collapsed
  277. ImgFabricApi.Visibility = Visibility.Collapsed
  278. LabFabricApi.Text = If(FabricApiError, "可以添加")
  279. LabFabricApi.Foreground = ColorGray4
  280. Else
  281. BtnFabricApiClear.Visibility = Visibility.Visible
  282. ImgFabricApi.Visibility = Visibility.Visible
  283. LabFabricApi.Text = SelectedFabricApi.Display.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim
  284. LabFabricApi.Foreground = ColorGray1
  285. End If
  286. End If
  287. 'OptiFabric
  288. If SelectedFabric Is Nothing OrElse SelectedOptiFine Is Nothing Then
  289. CardOptiFabric.Visibility = Visibility.Collapsed
  290. Else
  291. CardOptiFabric.Visibility = Visibility.Visible
  292. Dim OptiFabricError As String = LoadOptiFabricGetError()
  293. CardOptiFabric.MainSwap.Visibility = If(OptiFabricError Is Nothing, Visibility.Visible, Visibility.Collapsed)
  294. If OptiFabricError IsNot Nothing OrElse SelectedFabric Is Nothing Then CardOptiFabric.IsSwapped = True
  295. SetPanelVisibility(PanOptiFabricInfo, CardOptiFabric.IsSwapped)
  296. If SelectedOptiFabric Is Nothing Then
  297. BtnOptiFabricClear.Visibility = Visibility.Collapsed
  298. ImgOptiFabric.Visibility = Visibility.Collapsed
  299. LabOptiFabric.Text = If(OptiFabricError, "可以添加")
  300. LabOptiFabric.Foreground = ColorGray4
  301. Else
  302. BtnOptiFabricClear.Visibility = Visibility.Visible
  303. ImgOptiFabric.Visibility = Visibility.Visible
  304. LabOptiFabric.Text = SelectedOptiFabric.Display.Lower.Replace("optifabric-", "").Replace(".jar", "").Trim.TrimStart("v")
  305. LabOptiFabric.Foreground = ColorGray1
  306. End If
  307. End If
  308. '主警告
  309. If SelectedFabric IsNot Nothing AndAlso SelectedFabricApi Is Nothing Then
  310. HintFabricAPI.Visibility = Visibility.Visible
  311. Else
  312. HintFabricAPI.Visibility = Visibility.Collapsed
  313. End If
  314. If SelectedFabric IsNot Nothing AndAlso SelectedOptiFine IsNot Nothing AndAlso SelectedOptiFabric Is Nothing Then
  315. If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then
  316. HintOptiFabric.Visibility = Visibility.Collapsed
  317. HintOptiFabricOld.Visibility = Visibility.Visible
  318. Else
  319. HintOptiFabric.Visibility = Visibility.Visible
  320. HintOptiFabricOld.Visibility = Visibility.Collapsed
  321. End If
  322. Else
  323. HintOptiFabric.Visibility = Visibility.Collapsed
  324. HintOptiFabricOld.Visibility = Visibility.Collapsed
  325. End If
  326. If VanillaDrop >= 160 AndAlso SelectedOptiFine IsNot Nothing AndAlso
  327. (SelectedForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) Then
  328. HintModOptiFine.Visibility = Visibility.Visible
  329. Else
  330. HintModOptiFine.Visibility = Visibility.Collapsed
  331. End If
  332. '结束
  333. Ongoing = False
  334. End Sub
  335. ''' <summary>
  336. ''' 清空已选择的项目。
  337. ''' </summary>
  338. Public Sub ClearSelected()
  339. VanillaName = Nothing
  340. VanillaData = Nothing
  341. VanillaIcon = Nothing
  342. SelectedOptiFine = Nothing
  343. SelectedLiteLoader = Nothing
  344. SelectedForge = Nothing
  345. SelectedNeoForge = Nothing
  346. SelectedFabric = Nothing
  347. SelectedFabricApi = Nothing
  348. SelectedOptiFabric = Nothing
  349. For Each ClearButton In {BtnOptiFineClear, BtnLiteLoaderClear, BtnForgeClear, BtnNeoForgeClear,
  350. BtnFabricClear, BtnFabricApiClear, BtnOptiFabricClear}
  351. ClearButton.Visibility = Visibility.Collapsed
  352. Next
  353. End Sub
  354. '信息栏动画
  355. Private Sub SetPanelVisibility(Panel As Grid, IsVisible As Boolean)
  356. If Panel.Tag = IsVisible.ToString Then Return
  357. Panel.Tag = IsVisible.ToString
  358. If IsVisible Then
  359. AniStart({
  360. AaTranslateY(Panel, -CType(Panel.RenderTransform, TranslateTransform).Y, 150, Ease:=New AniEaseOutFluent),
  361. AaOpacity(Panel, 1 - Panel.Opacity, 60)
  362. }, "PageDownloadInstall Visibility " & Panel.Name)
  363. Else
  364. AniStart({
  365. AaTranslateY(Panel, 6 - CType(Panel.RenderTransform, TranslateTransform).Y, 60),
  366. AaOpacity(Panel, -Panel.Opacity, 60)
  367. }, "PageDownloadInstall Visibility " & Panel.Name)
  368. End If
  369. End Sub
  370. ''' <summary>
  371. ''' 获取版本图标。
  372. ''' </summary>
  373. Public Overridable Function GetSelectLogo() As String
  374. If SelectedFabric IsNot Nothing Then
  375. Return "pack://application:,,,/images/Blocks/Fabric.png"
  376. ElseIf SelectedForge IsNot Nothing Then
  377. Return "pack://application:,,,/images/Blocks/Anvil.png"
  378. ElseIf SelectedNeoForge IsNot Nothing Then
  379. Return "pack://application:,,,/images/Blocks/NeoForge.png"
  380. ElseIf SelectedLiteLoader IsNot Nothing Then
  381. Return "pack://application:,,,/images/Blocks/Egg.png"
  382. ElseIf SelectedOptiFine IsNot Nothing Then
  383. Return "pack://application:,,,/images/Blocks/GrassPath.png"
  384. Else
  385. Return VanillaIcon
  386. End If
  387. End Function
  388. '版本名处理
  389. ''' <summary>
  390. ''' 获取默认版本名。
  391. ''' </summary>
  392. Public Function GetSelectName() As String
  393. Dim Name As String = VanillaName
  394. If SelectedFabric IsNot Nothing Then Name += "-Fabric " & SelectedFabric.BeforeFirst("+")
  395. If SelectedForge IsNot Nothing Then Name += "-Forge_" & SelectedForge.VersionName
  396. If SelectedNeoForge IsNot Nothing Then Name += "-NeoForge_" & SelectedNeoForge.VersionName.BeforeFirst("+")
  397. If SelectedLiteLoader IsNot Nothing Then Name += "-LiteLoader"
  398. If SelectedOptiFine IsNot Nothing Then Name += "-OptiFine_" & SelectedOptiFine.DisplayName.Replace(VanillaName & " ", "").Replace(" ", "_")
  399. Return Name
  400. End Function
  401. Public IsSelectNameEdited As Boolean = False
  402. Private Sub SelectNameUpdate()
  403. If IsSelectNameEdited Then Return
  404. TextSelectName.Text = GetSelectName()
  405. IsSelectNameEdited = False
  406. End Sub
  407. Private Sub TextSelectName_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TextSelectName.TextChanged
  408. IsSelectNameEdited = True
  409. End Sub
  410. Private Sub TextSelectName_ValidateChanged(sender As Object, e As EventArgs) Handles TextSelectName.ValidateChanged
  411. BtnStart.IsEnabled = TextSelectName.IsValidated
  412. HintSelectName.Text = TextSelectName.ValidateResult
  413. HintSelectName.Visibility = If(TextSelectName.IsValidated, Visibility.Collapsed, Visibility.Visible)
  414. End Sub
  415. #End Region
  416. #Region "加载器"
  417. Public MinecraftVersions As JArray
  418. Private Sub LoadMinecraft_OnFinish()
  419. MinecraftVersions = CType(DlClientListLoader.Output.Value("versions").DeepClone(), JArray)
  420. ShowMinecraftVersionList()
  421. TrySelectWaitingVersion() '自动选择版本
  422. End Sub
  423. '结果数据化
  424. Public Overridable Sub ShowMinecraftVersionList()
  425. ExitSelectPage() '返回
  426. Try
  427. Dim Dict As New Dictionary(Of String, List(Of JObject)) From {
  428. {"正式版", New List(Of JObject)}, {"预览版", New List(Of JObject)}, {"远古版", New List(Of JObject)}, {"愚人节版", New List(Of JObject)}
  429. }
  430. For Each Version As JObject In MinecraftVersions
  431. '确定分类
  432. Dim Type As String = Version("type")
  433. Select Case Type
  434. Case "release"
  435. Type = "正式版"
  436. Case "snapshot"
  437. Type = "预览版"
  438. 'Mojang 误分类
  439. If Version("id").ToString.StartsWithF("1.") AndAlso
  440. Not Version("id").ToString.Lower.Contains("combat") AndAlso
  441. Not Version("id").ToString.Lower.Contains("rc") AndAlso
  442. Not Version("id").ToString.Lower.Contains("experimental") AndAlso
  443. Not Version("id").ToString.Lower.Contains("pre") Then
  444. Type = "正式版"
  445. Version("type") = "release"
  446. End If
  447. '愚人节版本
  448. Select Case Version("id").ToString.Lower
  449. Case "20w14infinite", "20w14∞"
  450. Type = "愚人节版"
  451. Version("id") = "20w14∞"
  452. Version("type") = "special"
  453. Version.Add("lore", GetMcFoolName(Version("id")))
  454. Case "3d shareware v1.34", "1.rv-pre1", "15w14a", "2.0", "22w13oneblockatatime", "23w13a_or_b", "24w14potato", "25w14craftmine", "26w14a"
  455. Type = "愚人节版"
  456. Version("type") = "special"
  457. Version.Add("lore", GetMcFoolName(Version("id")))
  458. Case Else '4/1 自动视作愚人节版
  459. Dim ReleaseDate = Version("releaseTime").Value(Of Date).ToUniversalTime().AddHours(2)
  460. If ReleaseDate.Month = 4 AndAlso ReleaseDate.Day = 1 Then
  461. Type = "愚人节版"
  462. Version("type") = "special"
  463. End If
  464. End Select
  465. Case "special"
  466. '已被处理的愚人节版
  467. Type = "愚人节版"
  468. Case Else
  469. Type = "远古版"
  470. End Select
  471. '加入辞典
  472. Dict(Type).Add(Version)
  473. Next
  474. '排序
  475. For Each Pair In Dict.ToList
  476. Dict(Pair.Key) = Pair.Value.OrderByDescending(Function(j) j("releaseTime").Value(Of Date)).ToList
  477. Next
  478. '清空当前
  479. PanMinecraft.Children.Clear()
  480. '添加最新版本
  481. Dim CardInfo As New MyCard With {.Title = "最新版本", .Margin = New Thickness(0, 15, 0, 15)}
  482. Dim PinnedVersions As New List(Of JObject)
  483. Dim Release As JObject = Dict("正式版")(0).DeepClone()
  484. Release("lore") = "最新正式版,发布于 " & Release("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
  485. PinnedVersions.Add(Release)
  486. If Dict("正式版")(0)("releaseTime").Value(Of Date) < Dict("预览版")(0)("releaseTime").Value(Of Date) Then
  487. Dim Snapshot As JObject = Dict("预览版")(0).DeepClone()
  488. Snapshot("lore") = "最新预览版,发布于 " & Snapshot("releaseTime").Value(Of Date).ToString("yyyy'/'MM'/'dd HH':'mm")
  489. PinnedVersions.Add(Snapshot)
  490. End If
  491. Dim PanInfo As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
  492. CardInfo.Children.Add(PanInfo)
  493. CardInfo.SwapControl = PanInfo
  494. CardInfo.SetContentBuilder(PinnedVersions, '使用 Lambda 转发点击事件,避免 #535
  495. Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
  496. CardInfo.BuildContent()
  497. PanMinecraft.Children.Insert(0, CardInfo)
  498. '添加其他版本
  499. For Each Pair As KeyValuePair(Of String, List(Of JObject)) In Dict
  500. If Not Pair.Value.Any() Then Continue For
  501. '增加卡片
  502. Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15)}
  503. Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
  504. NewCard.Children.Add(NewStack)
  505. NewCard.SwapControl = NewStack
  506. NewCard.SetContentBuilder(Pair.Value,
  507. Function(Version) McDownloadListItem(Version, Sub(sender, e) MinecraftSelected(sender, e), False))
  508. NewCard.IsSwapped = True
  509. PanMinecraft.Children.Add(NewCard)
  510. Next
  511. Catch ex As Exception
  512. Logger.Error(ex, "可视化安装版本列表出错")
  513. End Try
  514. End Sub
  515. Private Sub TrySelectWaitingVersion()
  516. If VersionWaitingSelect Is Nothing Then Return
  517. For Each Version As JObject In MinecraftVersions
  518. If Version("id").ToString <> VersionWaitingSelect Then Continue For
  519. Logger.Info($"自动选择 MC 版本:{VersionWaitingSelect}")
  520. MinecraftSelected(McDownloadListItem(Version, Nothing, False).Init(), Nothing)
  521. VersionWaitingSelect = Nothing
  522. Return
  523. Next
  524. End Sub
  525. ''' <summary>
  526. ''' 当 MC 版本列表加载完时,立即自动选择的版本。用于外部调用。
  527. ''' </summary>
  528. Public Shared VersionWaitingSelect As String = Nothing
  529. #End Region
  530. #Region "OptiFine 列表"
  531. ''' <summary>
  532. ''' 获取 OptiFine 的加载异常信息。若正常则返回 Nothing。
  533. ''' </summary>
  534. Public Function LoadOptiFineGetError() As String
  535. If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
  536. '检查 Forge 1.13 - 1.14.3:全部不兼容
  537. If SelectedForge IsNot Nothing AndAlso
  538. McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
  539. Return "与 Forge 不兼容"
  540. End If
  541. '检查 Fabric 1.20.5+:全部不兼容
  542. If SelectedFabric IsNot Nothing AndAlso
  543. McUtils.CompareVersion(VanillaName, "1.20.4") > 0 Then
  544. Return "与 Fabric 不兼容"
  545. End If
  546. '检查 Loader
  547. If GetLoaderError(LoadOptiFine) IsNot Nothing Then Return GetLoaderError(LoadOptiFine)
  548. '检查 Forge 版本
  549. Dim HasAny As Boolean = False
  550. Dim HasRequiredVersion As Boolean = False
  551. For Each OptiFineVersion As DlOptiFineListEntry In DlOptiFineListLoader.Output.Value
  552. If Not OptiFineVersion.DisplayName.StartsWithF(VanillaName & " ") Then Continue For '不是同一个大版本
  553. HasAny = True
  554. If SelectedForge Is Nothing Then Return Nothing '未选择 Forge
  555. If IsOptiFineSuitForForge(OptiFineVersion, SelectedForge) Then Return Nothing '该版本可用
  556. If OptiFineVersion.RequiredForgeVersion IsNot Nothing Then HasRequiredVersion = True
  557. Next
  558. If Not HasAny Then
  559. Return "无"
  560. ElseIf HasRequiredVersion Then
  561. Return "仅兼容特定版本的 Forge"
  562. Else
  563. Return "与 Forge 不兼容"
  564. End If
  565. End Function
  566. '检查某个 OptiFine 是否与某个 Forge 兼容
  567. Public Shared Function IsOptiFineSuitForForge(OptiFine As DlOptiFineListEntry, Forge As DlForgeVersionEntry) As Boolean
  568. If Forge.Inherit <> OptiFine.Inherit Then Return False '不是同一个大版本
  569. If OptiFine.RequiredForgeVersion Is Nothing Then Return False '不兼容 Forge
  570. If String.IsNullOrWhiteSpace(OptiFine.RequiredForgeVersion) Then Return True '#4183
  571. If OptiFine.RequiredForgeVersion.Contains(".") Then 'XX.X.XXX
  572. Return McUtils.CompareVersion(Forge.Version.ToString, OptiFine.RequiredForgeVersion) = 0
  573. Else 'XXXX
  574. Return Forge.Version.Revision = OptiFine.RequiredForgeVersion
  575. End If
  576. End Function
  577. '限制展开
  578. Private Sub CardOptiFine_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardOptiFine.PreviewSwap
  579. If LoadOptiFineGetError() IsNot Nothing Then e.Handled = True
  580. End Sub
  581. ''' <summary>
  582. ''' 尝试重新可视化 OptiFine 版本列表。
  583. ''' </summary>
  584. Private Sub OptiFine_Loaded() Handles LoadOptiFine.StateChanged
  585. Try
  586. If DlOptiFineListLoader.State <> LoadState.Finished Then Return
  587. '获取版本列表
  588. Dim Versions As New List(Of DlOptiFineListEntry)
  589. For Each Version As DlOptiFineListEntry In DlOptiFineListLoader.Output.Value
  590. If SelectedForge IsNot Nothing AndAlso Not IsOptiFineSuitForForge(Version, SelectedForge) Then Continue For
  591. If Version.DisplayName.StartsWithF(VanillaName & " ") Then Versions.Add(Version)
  592. Next
  593. If Not Versions.Any() Then Return
  594. '排序
  595. Versions = Versions.SortByComparison(
  596. Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
  597. If Not Left.IsPreview AndAlso Right.IsPreview Then Return True
  598. If Left.IsPreview AndAlso Not Right.IsPreview Then Return False
  599. Return McUtils.CompareVersionGE(Left.DisplayName, Right.DisplayName)
  600. End Function)
  601. '可视化
  602. PanOptiFine.Children.Clear()
  603. For Each Version In Versions
  604. PanOptiFine.Children.Add(OptiFineDownloadListItem(Version, AddressOf OptiFine_Selected, False))
  605. Next
  606. Catch ex As Exception
  607. Logger.Error(ex, "可视化 OptiFine 安装版本列表出错")
  608. End Try
  609. End Sub
  610. '选择与清除
  611. Private Sub OptiFine_Selected(sender As MyListItem, e As EventArgs)
  612. SelectedOptiFine = sender.Tag
  613. If SelectedForge IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, SelectedForge) Then SelectedForge = Nothing
  614. OptiFabric_Loaded()
  615. Forge_Loaded()
  616. NeoForge_Loaded()
  617. CardOptiFine.IsSwapped = True
  618. ReloadSelected()
  619. End Sub
  620. Private Sub OptiFine_Clear(sender As Object, e As EventArgs) Handles BtnOptiFineClear.Click
  621. SelectedOptiFine = Nothing
  622. SelectedOptiFabric = Nothing
  623. BtnOptiFabricClear.Visibility = Visibility.Collapsed
  624. AutoSelectedOptiFabric = False
  625. CardOptiFine.IsSwapped = True
  626. Forge_Loaded()
  627. NeoForge_Loaded()
  628. ReloadSelected()
  629. End Sub
  630. #End Region
  631. #Region "LiteLoader 列表"
  632. ''' <summary>
  633. ''' 获取 LiteLoader 的加载异常信息。若正常则返回 Nothing。
  634. ''' </summary>
  635. Public Function LoadLiteLoaderGetError() As String
  636. '检查 Loader
  637. If GetLoaderError(LoadLiteLoader) IsNot Nothing Then Return GetLoaderError(LoadLiteLoader)
  638. '检查版本
  639. Return If(DlLiteLoaderListLoader.Output.Value.Any(Function(v) v.Inherit = VanillaName), Nothing, "无")
  640. End Function
  641. '限制展开
  642. Private Sub CardLiteLoader_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardLiteLoader.PreviewSwap
  643. If LoadLiteLoaderGetError() IsNot Nothing Then e.Handled = True
  644. End Sub
  645. ''' <summary>
  646. ''' 尝试重新可视化 LiteLoader 版本列表。
  647. ''' </summary>
  648. Private Sub LiteLoader_Loaded() Handles LoadLiteLoader.StateChanged
  649. Try
  650. If DlLiteLoaderListLoader.State <> LoadState.Finished Then Return
  651. '获取版本列表
  652. Dim Versions As New List(Of DlLiteLoaderListEntry)
  653. For Each Version As DlLiteLoaderListEntry In DlLiteLoaderListLoader.Output.Value
  654. If Version.Inherit = VanillaName Then Versions.Add(Version)
  655. Next
  656. If Not Versions.Any() Then Return
  657. '可视化
  658. PanLiteLoader.Children.Clear()
  659. For Each Version In Versions
  660. PanLiteLoader.Children.Add(LiteLoaderDownloadListItem(Version, AddressOf LiteLoader_Selected, False))
  661. Next
  662. Catch ex As Exception
  663. Logger.Error(ex, "可视化 LiteLoader 安装版本列表出错")
  664. End Try
  665. End Sub
  666. '选择与清除
  667. Private Sub LiteLoader_Selected(sender As MyListItem, e As EventArgs)
  668. SelectedLiteLoader = sender.Tag
  669. CardLiteLoader.IsSwapped = True
  670. ReloadSelected()
  671. End Sub
  672. Private Sub LiteLoader_Clear(sender As Object, e As EventArgs) Handles BtnLiteLoaderClear.Click
  673. SelectedLiteLoader = Nothing
  674. CardLiteLoader.IsSwapped = True
  675. ReloadSelected()
  676. End Sub
  677. #End Region
  678. #Region "Forge 列表"
  679. ''' <summary>
  680. ''' 获取 Forge 的加载异常信息。若正常则返回 Nothing。
  681. ''' </summary>
  682. Public Function LoadForgeGetError() As String
  683. If McUtils.CompareVersionGE("1.5.1", VanillaName) AndAlso McUtils.CompareVersionGE(VanillaName, "1.1") Then Return "无"
  684. '检查 Loader
  685. If GetLoaderError(LoadForge) IsNot Nothing Then Return GetLoaderError(LoadForge)
  686. Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = LoadForge.State
  687. If VanillaName <> Loader.Input Then Return "获取中……"
  688. '检查版本
  689. For Each Version In Loader.Output
  690. If Version.Category = "universal" OrElse Version.Category = "client" Then Continue For '跳过无法自动安装的版本
  691. If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
  692. If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
  693. If SelectedOptiFine IsNot Nothing AndAlso
  694. McUtils.CompareVersionGE(VanillaName, "1.13") AndAlso McUtils.CompareVersionGE("1.14.3", VanillaName) Then
  695. Return "与 OptiFine 不兼容" '1.13 ~ 1.14.3 OptiFine 检查
  696. End If
  697. If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, Version) Then Continue For
  698. Return Nothing
  699. Next
  700. Return "与 OptiFine 不兼容"
  701. End Function
  702. '限制展开
  703. Private Sub CardForge_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardForge.PreviewSwap
  704. If LoadForgeGetError() IsNot Nothing Then e.Handled = True
  705. End Sub
  706. ''' <summary>
  707. ''' 尝试重新可视化 Forge 版本列表。
  708. ''' </summary>
  709. Private Sub Forge_Loaded() Handles LoadForge.StateChanged
  710. Try
  711. If Not LoadForge.State.IsLoader Then Return
  712. Dim Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)) = LoadForge.State
  713. If VanillaName <> Loader.Input Then Return
  714. If Loader.State <> LoadState.Finished Then Return
  715. '获取要显示的版本
  716. Dim Versions = Loader.Output.ToList '复制数组,以免 Output 在实例化后变空
  717. If Not Loader.Output.Any() Then Return
  718. PanForge.Children.Clear()
  719. Versions = Versions.Where(
  720. Function(v)
  721. If v.Category = "universal" OrElse v.Category = "client" Then Return False '跳过无法自动安装的版本
  722. If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, v) Then Return False
  723. Return True
  724. End Function).OrderByDescending(Function(v) v).ToList()
  725. ForgeDownloadListItemPreload(PanForge, Versions, AddressOf Forge_Selected, False)
  726. For Each Version In Versions
  727. PanForge.Children.Add(ForgeDownloadListItem(Version, AddressOf Forge_Selected, False))
  728. Next
  729. Catch ex As Exception
  730. Logger.Error(ex, "可视化 Forge 安装版本列表出错")
  731. End Try
  732. End Sub
  733. '选择与清除
  734. Private Sub Forge_Selected(sender As MyListItem, e As EventArgs)
  735. SelectedForge = sender.Tag
  736. CardForge.IsSwapped = True
  737. If SelectedOptiFine IsNot Nothing AndAlso Not IsOptiFineSuitForForge(SelectedOptiFine, SelectedForge) Then SelectedOptiFine = Nothing
  738. OptiFine_Loaded()
  739. ReloadSelected()
  740. End Sub
  741. Private Sub Forge_Clear(sender As Object, e As EventArgs) Handles BtnForgeClear.Click
  742. SelectedForge = Nothing
  743. CardForge.IsSwapped = True
  744. OptiFine_Loaded()
  745. ReloadSelected()
  746. End Sub
  747. #End Region
  748. #Region "NeoForge 列表"
  749. ''' <summary>
  750. ''' 获取 NeoForge 的加载异常信息。若正常则返回 Nothing。
  751. ''' </summary>
  752. Public Function LoadNeoForgeGetError() As String
  753. If SelectedOptiFine IsNot Nothing Then Return "与 OptiFine 不兼容"
  754. If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
  755. If SelectedFabric IsNot Nothing Then Return "与 Fabric 不兼容"
  756. '检查 Loader
  757. If GetLoaderError(LoadNeoForge) IsNot Nothing Then Return GetLoaderError(LoadNeoForge)
  758. '检查版本
  759. Return If(DlNeoForgeListLoader.Output.Value.Any(Function(v) v.Inherit = VanillaName), Nothing, "无")
  760. End Function
  761. '限制展开
  762. Private Sub CardNeoForge_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardNeoForge.PreviewSwap
  763. If LoadNeoForgeGetError() IsNot Nothing Then e.Handled = True
  764. End Sub
  765. ''' <summary>
  766. ''' 尝试重新可视化 NeoForge 版本列表。
  767. ''' </summary>
  768. Private Sub NeoForge_Loaded() Handles LoadNeoForge.StateChanged
  769. Try
  770. '获取版本列表
  771. If DlNeoForgeListLoader.State <> LoadState.Finished Then Return
  772. Dim Versions = DlNeoForgeListLoader.Output.Value.Where(Function(v) v.Inherit = VanillaName).ToList
  773. If Not Versions.Any() Then Return
  774. '可视化
  775. PanNeoForge.Children.Clear()
  776. NeoForgeDownloadListItemPreload(PanNeoForge, Versions, AddressOf NeoForge_Selected, False)
  777. For Each Version In Versions
  778. PanNeoForge.Children.Add(NeoForgeDownloadListItem(Version, AddressOf NeoForge_Selected, False))
  779. Next
  780. Catch ex As Exception
  781. Logger.Error(ex, "可视化 NeoForge 安装版本列表出错")
  782. End Try
  783. End Sub
  784. '选择与清除
  785. Private Sub NeoForge_Selected(sender As MyListItem, e As EventArgs)
  786. SelectedNeoForge = sender.Tag
  787. CardNeoForge.IsSwapped = True
  788. OptiFine_Loaded()
  789. ReloadSelected()
  790. End Sub
  791. Private Sub NeoForge_Clear(sender As Object, e As EventArgs) Handles BtnNeoForgeClear.Click
  792. SelectedNeoForge = Nothing
  793. CardNeoForge.IsSwapped = True
  794. OptiFine_Loaded()
  795. ReloadSelected()
  796. End Sub
  797. #End Region
  798. #Region "Fabric 列表"
  799. ''' <summary>
  800. ''' 获取 Fabric 的加载异常信息。若正常则返回 Nothing。
  801. ''' </summary>
  802. Public Function LoadFabricGetError() As String
  803. '检查 OptiFine 1.20.5+:没有 OptiFabric 故全部不兼容
  804. If SelectedOptiFine IsNot Nothing AndAlso McUtils.CompareVersionGE(VanillaName, "1.20.5") Then Return "与 OptiFine 不兼容"
  805. '检查 Loader
  806. If GetLoaderError(LoadFabric) IsNot Nothing Then Return GetLoaderError(LoadFabric)
  807. '检查版本
  808. For Each Version As JObject In DlFabricListLoader.Output.Value("game")
  809. If Version("version").ToString = VanillaName.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3") Then
  810. If SelectedForge IsNot Nothing Then Return "与 Forge 不兼容"
  811. If SelectedNeoForge IsNot Nothing Then Return "与 NeoForge 不兼容"
  812. Return Nothing
  813. End If
  814. Next
  815. Return "无"
  816. End Function
  817. '限制展开
  818. Private Sub CardFabric_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardFabric.PreviewSwap
  819. If LoadFabricGetError() IsNot Nothing Then e.Handled = True
  820. End Sub
  821. ''' <summary>
  822. ''' 尝试重新可视化 Fabric 版本列表。
  823. ''' </summary>
  824. Private Sub Fabric_Loaded() Handles LoadFabric.StateChanged
  825. Try
  826. If DlFabricListLoader.State <> LoadState.Finished Then Return
  827. '获取版本列表
  828. Dim Versions As JArray = DlFabricListLoader.Output.Value("loader")
  829. If Not Versions.Any() Then Return
  830. '可视化
  831. PanFabric.Children.Clear()
  832. CardFabric.SwapControl = PanFabric
  833. CardFabric.SetContentBuilder(Versions.OfType(Of JObject),
  834. Function(Version) FabricDownloadListItem(Version, AddressOf Fabric_Selected))
  835. If Not CardFabric.IsSwapped Then CardFabric.BuildContent()
  836. Catch ex As Exception
  837. Logger.Error(ex, "可视化 Fabric 安装版本列表出错")
  838. End Try
  839. End Sub
  840. '选择与清除
  841. Public Sub Fabric_Selected(sender As MyListItem, e As EventArgs)
  842. SelectedFabric = sender.Tag("version").ToString
  843. FabricApi_Loaded()
  844. OptiFabric_Loaded()
  845. CardFabric.IsSwapped = True
  846. ReloadSelected()
  847. End Sub
  848. Private Sub Fabric_Clear(sender As Object, e As EventArgs) Handles BtnFabricClear.Click
  849. SelectedFabric = Nothing
  850. SelectedFabricApi = Nothing
  851. BtnFabricApiClear.Visibility = Visibility.Collapsed
  852. AutoSelectedFabricApi = False
  853. SelectedOptiFabric = Nothing
  854. BtnOptiFabricClear.Visibility = Visibility.Collapsed
  855. AutoSelectedOptiFabric = False
  856. CardFabric.IsSwapped = True
  857. ReloadSelected()
  858. End Sub
  859. #End Region
  860. #Region "Fabric API 列表"
  861. ''' <summary>
  862. ''' 判断某 Fabric API 是否适配指定的原版版本。
  863. ''' </summary>
  864. Public Shared Function IsFabricApiCompatible(FabricApi As ResourceVersion, MinecraftVersion As String) As Boolean
  865. Dim FabricApiName = FabricApi.Display
  866. Try
  867. If FabricApiName Is Nothing OrElse MinecraftVersion Is Nothing Then Return False
  868. FabricApiName = FabricApiName.Lower
  869. Dim TargetName = MinecraftVersion.Replace("∞", "infinite").Replace("Combat Test 7c", "1.16_combat-3").Lower
  870. If FabricApiName.StartsWithF("[" & TargetName & "]") Then Return True
  871. If Not FabricApiName.Contains("/") OrElse Not FabricApiName.Contains("]") Then Return False
  872. '直接的判断(例如 1.18.1/22w03a)
  873. For Each Part As String In FabricApiName.BeforeFirst("]").TrimStart("[").Split("/")
  874. If Part = TargetName Then Return True
  875. Next
  876. '将版本名分割语素(例如 1.16.4/5)
  877. Dim Lefts = FabricApiName.BeforeFirst("]").RegexSearch("[a-z/]+|[0-9/]+").ToList
  878. Dim Rights = TargetName.BeforeFirst("]").RegexSearch("[a-z/]+|[0-9/]+").ToList
  879. '对每段进行判断
  880. Dim i As Integer = 0
  881. While True
  882. '两边均缺失,感觉是一个东西
  883. If Lefts.Count - 1 < i AndAlso Rights.Count - 1 < i Then Return True
  884. '确定两边是否一致
  885. Dim LeftValue As String = If(Lefts.Count - 1 < i, "-1", Lefts(i))
  886. Dim RightValue As String = If(Rights.Count - 1 < i, "-1", Rights(i))
  887. If Not LeftValue.Contains("/") Then
  888. If LeftValue <> RightValue Then Return False
  889. Else
  890. '左边存在斜杠
  891. If Not LeftValue.Contains(RightValue) Then Return False
  892. End If
  893. i += 1
  894. End While
  895. Return True
  896. Catch ex As Exception
  897. Logger.Warn(ex, $"判断 Fabric API 版本适配性出错({FabricApiName}, {MinecraftVersion})")
  898. Return False
  899. End Try
  900. End Function
  901. ''' <summary>
  902. ''' 获取 FabricApi 的加载异常信息。若正常则返回 Nothing。
  903. ''' </summary>
  904. Public Function LoadFabricApiGetError() As String
  905. '检查 Loader
  906. If GetLoaderError(LoadFabricApi) IsNot Nothing Then Return GetLoaderError(LoadFabricApi)
  907. If DlFabricApiLoader.Output Is Nothing Then Return If(SelectedFabric Is Nothing, "需要安装 Fabric", "获取中……")
  908. '检查版本
  909. If DlFabricApiLoader.Output.Any(Function(f) IsFabricApiCompatible(f, VanillaName)) Then
  910. Return If(SelectedFabric Is Nothing, "需要安装 Fabric", Nothing)
  911. Else
  912. Return "无"
  913. End If
  914. End Function
  915. '限制展开
  916. Private Sub CardFabricApi_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardFabricApi.PreviewSwap
  917. If LoadFabricApiGetError() IsNot Nothing Then e.Handled = True
  918. End Sub
  919. Public AutoSelectedFabricApi As Boolean = False
  920. ''' <summary>
  921. ''' 尝试重新可视化 FabricApi 版本列表。
  922. ''' </summary>
  923. Private Sub FabricApi_Loaded() Handles LoadFabricApi.StateChanged
  924. Try
  925. If DlFabricApiLoader.State <> LoadState.Finished Then Return
  926. If VanillaName Is Nothing OrElse SelectedFabric Is Nothing Then Return
  927. '获取版本列表
  928. Dim Versions As New List(Of ResourceVersion)
  929. For Each Version In DlFabricApiLoader.Output
  930. If IsFabricApiCompatible(Version, VanillaName) Then
  931. If Not Version.Display.StartsWithF("[") Then
  932. Logger.Warn($"已特判修改 Fabric API 显示名:{Version.Display}")
  933. Version.Display = "[" & VanillaName & "] " & Version.Display
  934. End If
  935. Versions.Add(Version)
  936. End If
  937. Next
  938. If Not Versions.Any() Then Return
  939. Versions = Versions.OrderByDescending(Function(v) v.ReleaseDate).ToList
  940. '可视化
  941. PanFabricApi.Children.Clear()
  942. For Each Version In Versions
  943. PanFabricApi.Children.Add(FabricApiDownloadListItem(Version, AddressOf FabricApi_Selected))
  944. Next
  945. '自动选择 Fabric API
  946. If Not AutoSelectedFabricApi Then
  947. AutoSelectedFabricApi = True
  948. Dim Item As MyListItem = MyVirtualizingElement.TryInit(PanFabricApi.Children(0))
  949. Logger.Info($"已自动选择 Fabric API:{Item.Title}")
  950. FabricApi_Selected(Item, Nothing)
  951. End If
  952. Catch ex As Exception
  953. Logger.Error(ex, "可视化 Fabric API 安装版本列表出错")
  954. End Try
  955. End Sub
  956. '选择与清除
  957. Private Sub FabricApi_Selected(sender As MyListItem, e As EventArgs)
  958. SelectedFabricApi = sender.Tag
  959. CardFabricApi.IsSwapped = True
  960. ReloadSelected()
  961. End Sub
  962. Private Sub FabricApi_Clear(sender As Object, e As EventArgs) Handles BtnFabricApiClear.Click
  963. SelectedFabricApi = Nothing
  964. CardFabricApi.IsSwapped = True
  965. ReloadSelected()
  966. End Sub
  967. #End Region
  968. #Region "OptiFabric 列表"
  969. ''' <summary>
  970. ''' 判断某 OptiFabric 是否适配指定的原版版本。
  971. ''' </summary>
  972. Public Shared Function IsOptiFabricCompatible(ModFile As ResourceVersion, MinecraftVersion As String) As Boolean
  973. Try
  974. If MinecraftVersion Is Nothing Then Return False
  975. Return ModFile.GameVersions.Contains(MinecraftVersion)
  976. Catch ex As Exception
  977. Logger.Warn(ex, $"判断 OptiFabric 版本适配性出错({MinecraftVersion})")
  978. Return False
  979. End Try
  980. End Function
  981. Public AutoSelectedOptiFabric As Boolean = False
  982. ''' <summary>
  983. ''' 获取 OptiFabric 的加载异常信息。若正常则返回 Nothing。
  984. ''' </summary>
  985. Public Function LoadOptiFabricGetError() As String
  986. If VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return "不兼容老版本 Fabric,请手动下载 OptiFabric Origins"
  987. '检查 Loader
  988. If GetLoaderError(LoadOptiFabric) IsNot Nothing Then Return GetLoaderError(LoadOptiFabric)
  989. '检查版本
  990. If DlOptiFabricLoader.Output Is Nothing Then
  991. If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
  992. If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
  993. If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
  994. Return "获取中……"
  995. End If
  996. For Each Version In DlOptiFabricLoader.Output
  997. If Not IsOptiFabricCompatible(Version, VanillaName) Then Continue For '2135#
  998. If SelectedFabric Is Nothing AndAlso SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine 与 Fabric"
  999. If SelectedFabric Is Nothing Then Return "需要安装 Fabric"
  1000. If SelectedOptiFine Is Nothing Then Return "需要安装 OptiFine"
  1001. Return Nothing '通过检查
  1002. Next
  1003. Return "无"
  1004. End Function
  1005. '限制展开
  1006. Private Sub CardOptiFabric_PreviewSwap(sender As Object, e As RouteEventArgs) Handles CardOptiFabric.PreviewSwap
  1007. If LoadOptiFabricGetError() IsNot Nothing Then e.Handled = True
  1008. End Sub
  1009. ''' <summary>
  1010. ''' 尝试重新可视化 OptiFabric 版本列表。
  1011. ''' </summary>
  1012. Private Sub OptiFabric_Loaded() Handles LoadOptiFabric.StateChanged
  1013. Try
  1014. If DlOptiFabricLoader.State <> LoadState.Finished Then Return
  1015. If VanillaName Is Nothing OrElse SelectedFabric Is Nothing OrElse SelectedOptiFine Is Nothing Then Return
  1016. '获取版本列表
  1017. Dim Versions As New List(Of ResourceVersion)
  1018. For Each Version In DlOptiFabricLoader.Output
  1019. If IsOptiFabricCompatible(Version, VanillaName) Then Versions.Add(Version)
  1020. Next
  1021. If Not Versions.Any() Then Return
  1022. '排序
  1023. Versions = Versions.OrderByDescending(Function(v) v.ReleaseDate).ToList
  1024. '可视化
  1025. PanOptiFabric.Children.Clear()
  1026. For Each Version In Versions
  1027. PanOptiFabric.Children.Add(OptiFabricDownloadListItem(Version, AddressOf OptiFabric_Selected))
  1028. Next
  1029. '自动选择 OptiFabric
  1030. If AutoSelectedOptiFabric OrElse VanillaDrop >= 140 AndAlso VanillaDrop <= 150 Then Return '1.14~15 不自动选择
  1031. AutoSelectedOptiFabric = True
  1032. Dim Item As MyListItem = MyVirtualizingElement.TryInit(PanOptiFabric.Children(0))
  1033. Logger.Info($"已自动选择 OptiFabric:{Item.Title}")
  1034. OptiFabric_Selected(Item, Nothing)
  1035. Catch ex As Exception
  1036. Logger.Error(ex, "可视化 OptiFabric 安装版本列表出错")
  1037. End Try
  1038. End Sub
  1039. '选择与清除
  1040. Private Sub OptiFabric_Selected(sender As MyListItem, e As EventArgs)
  1041. SelectedOptiFabric = sender.Tag
  1042. CardOptiFabric.IsSwapped = True
  1043. ReloadSelected()
  1044. End Sub
  1045. Private Sub OptiFabric_Clear(sender As Object, e As EventArgs) Handles BtnOptiFabricClear.Click
  1046. SelectedOptiFabric = Nothing
  1047. CardOptiFabric.IsSwapped = True
  1048. ReloadSelected()
  1049. End Sub
  1050. #End Region
  1051. #Region "安装"
  1052. ''' <summary>
  1053. ''' 获取当前选择的组件信息。
  1054. ''' </summary>
  1055. Public Function GetSelectedComponents() As McComponents
  1056. Return New McComponents(
  1057. minecraftVersion:=VanillaName,
  1058. optiFineEntry:=If(SelectedOptiFine Is Nothing, Nothing, JObject.FromObject(SelectedOptiFine)),
  1059. forgeVersion:=SelectedForge?.VersionName,
  1060. neoForgeVersion:=SelectedNeoForge?.VersionName,
  1061. fabricVersion:=SelectedFabric,
  1062. fabricApiVersion:=If(SelectedFabricApi?.Version, If(SelectedFabricApi?.Id, SelectedFabricApi?.Display)),
  1063. optiFabricVersion:=If(SelectedOptiFabric?.Version, If(SelectedOptiFabric?.Id, SelectedOptiFabric?.Display)),
  1064. liteLoaderVersion:=SelectedLiteLoader?.JsonToken?("version").ToString())
  1065. End Function
  1066. Private Sub TextSelectName_KeyDown(sender As Object, e As KeyEventArgs) Handles TextSelectName.KeyDown
  1067. If e.Key = Key.Enter AndAlso IsInSelectPage AndAlso BtnStart.IsEnabled Then BtnStart_Click()
  1068. End Sub
  1069. Private Sub BtnStart_Click() Handles BtnStart.Click
  1070. '提交安装申请
  1071. Dim InstanceName As String = TextSelectName.Text
  1072. Dim Request As New McInstallRequest With {
  1073. .NewInstanceName = InstanceName,
  1074. .InstanceFolder = $"{McFolderSelected}versions\{InstanceName}\",
  1075. .Components = GetSelectedComponents(),
  1076. .MinecraftJson = VanillaData("url").ToString,
  1077. .ForgeEntry = SelectedForge,
  1078. .NeoForgeEntry = SelectedNeoForge,
  1079. .FabricApi = SelectedFabricApi,
  1080. .OptiFabric = SelectedOptiFabric,
  1081. .LiteLoaderEntry = SelectedLiteLoader
  1082. }
  1083. StartInstall(Request)
  1084. End Sub
  1085. Public Overridable Sub StartInstall(Request As McInstallRequest)
  1086. '确认版本隔离
  1087. If (SelectedForge IsNot Nothing OrElse SelectedNeoForge IsNot Nothing OrElse SelectedFabric IsNot Nothing) AndAlso
  1088. (Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 0 OrElse Settings.Get(Of Integer)("LaunchArgumentIndieV2") = 2) Then
  1089. If MyMsgBox("你尚未开启版本隔离,多个 MC 版本会共用同一个 Mod 文件夹。" & vbCrLf &
  1090. "因此,游戏可能会因为读取到与当前版本不符的 Mod 而崩溃。" & vbCrLf &
  1091. "推荐先在 设置 → 启动选项 → 默认版本隔离 中开启版本隔离!", "版本隔离提示", "取消下载", "继续") = 1 Then
  1092. Return
  1093. End If
  1094. End If
  1095. If Not McInstallStart(Request) Then Return
  1096. '返回,这样在再次进入安装页面时这个版本就会显示文件夹已重复
  1097. ExitSelectPage()
  1098. End Sub
  1099. #End Region
  1100. Private Function GetLoaderError(Loader As MyLoading) As String
  1101. If Loader Is Nothing Then Return "获取中……"
  1102. If Not Loader.State.IsLoader Then Return "获取中……"
  1103. Select Case Loader.State.LoadingState
  1104. Case MyLoading.MyLoadingState.Run
  1105. Return "获取中……"
  1106. Case MyLoading.MyLoadingState.Error
  1107. Dim Message As String = CType(Loader.State, LoaderBase).Error.Message
  1108. Return If(Message = "无", "无", "获取失败:" & Message)
  1109. Case MyLoading.MyLoadingState.Unloaded
  1110. Return "未知错误,状态为 Unloaded"
  1111. Case Else
  1112. Return Nothing
  1113. End Select
  1114. End Function
  1115. End Class