PageDownloadInstall.xaml.vb 55 KB

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