ModJava.vb 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. Public Module ModJava
  2. ''' <summary>
  3. ''' 初始化 Java 列表。
  4. ''' 若没有任何可用的 Java,则触发搜索。
  5. ''' </summary>
  6. Public Sub JavaInit()
  7. Try
  8. '从老版本迁移
  9. If Configs.JavaConfigVersion.Get() = 0 Then
  10. Try
  11. 'Java 列表
  12. Logger.Warn("正在从老版本迁移 Java 列表")
  13. Dim OldJavaList = Settings.Get(Of String)("LaunchArgumentJavaAll").DeserializeJson(Of JArray).
  14. Select(Function(JavaObject) New Java(JavaObject("Path").ToString)).ToList()
  15. OldJavaList = JavaUtils.SortAsync(OldJavaList).Run()
  16. Logger.Info($"已从老版本设置中发现 {OldJavaList.Count} 个 Java")
  17. '强制选择的 Java
  18. Dim SelectedJavaSetting = Settings.Get(Of String)("LaunchArgumentJavaSelect")
  19. Dim SelectedJava = If(SelectedJavaSetting.StartsWithF("{"), SelectedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString, "")
  20. Dim Match = OldJavaList.FirstOrDefault(Function(j) j.Folder.Equals(SelectedJava, StringComparison.OrdinalIgnoreCase))
  21. If Match IsNot Nothing Then
  22. OldJavaList.Remove(Match)
  23. OldJavaList.Insert(0, Match)
  24. Logger.Info($"已将老版本设置中强制选择的 Java 置顶:{Match}")
  25. End If
  26. Configs.JavaList.Set(New ConcurrentList(Of Java)(OldJavaList))
  27. '完成迁移
  28. Configs.JavaConfigVersion.Set(Versions.JavaConfigVersion)
  29. Catch ex As Exception
  30. Logger.Error(ex, "从老版本 PCL 迁移 Java 列表失败")
  31. End Try
  32. End If
  33. '读取缓存
  34. Dim List = Configs.JavaList.Get()
  35. If List.Any Then
  36. Logger.Info($"共有 {List.Count} 个可用的 Java:{vbCrLf}- {List.Select(Function(j) j.ToString).Join(vbCrLf + "- ")}")
  37. Return
  38. End If
  39. '如果没有 Java,则触发搜索
  40. Logger.Warn("初始化未找到可用的 Java,将自动触发搜索")
  41. JavaListRefreshWorker.Start()
  42. Catch ex As Exception
  43. Logger.Error(ex, "初始化 Java 列表失败")
  44. Configs.JavaList.Reset()
  45. End Try
  46. End Sub
  47. ''' <summary>
  48. ''' 搜索并获取所有可用的 Java,检查所有 Java 的可用性,写入设置,并在结束后更新设置页面显示。
  49. ''' </summary>
  50. Public WithEvents JavaListRefreshWorker As New RedoableWorker(Sub(c, p) JavaUtils.RefreshListAsync(c, p).Run())
  51. #Region "UI 事件"
  52. ''' <summary>
  53. ''' 更新设置页面的 Java 列表显示。
  54. ''' </summary>
  55. Public Sub UpdateJavaLists() Handles JavaListRefreshWorker.Started, JavaListRefreshWorker.Stopped
  56. RunInUi(
  57. Sub()
  58. FrmSetupLaunch?.UpdateJavaList()
  59. FrmInstanceSetup?.UpdateJavaList()
  60. End Sub)
  61. End Sub
  62. ''' <summary>
  63. ''' 手动触发导入 Java。
  64. ''' </summary>
  65. Public Sub ManuallyImportJava()
  66. '选择
  67. Dim NewJavaPath As String = Dialogs.SelectFile("选择 java.exe(可能在 bin 文件夹中)", False, filter:="java.exe|java.exe").FirstOrDefault()
  68. If String.IsNullOrEmpty(NewJavaPath) Then Return
  69. '验证 Java 可用
  70. Dim NewJava As New Java(PathUtils.RemoveLastPart(NewJavaPath))
  71. If Not NewJava.CheckAsync().Run() Then
  72. Hint("该 Java 存在异常,无法使用", HintType.Red)
  73. Return
  74. End If
  75. '不再移除它
  76. Configs.JavaRemovedList.Edit(Sub(ByRef List) List.RemoveIf(Function(f) f.Equals(NewJava.Folder, StringComparison.OrdinalIgnoreCase)))
  77. '加入列表顶部
  78. Configs.JavaList.Edit(
  79. Sub(ByRef List)
  80. List.RemoveIf(Function(j) j.Folder.Equals(NewJava.Folder, StringComparison.OrdinalIgnoreCase))
  81. List.Insert(0, NewJava)
  82. End Sub)
  83. Hint($"已将 Java 加入列表:{NewJavaPath}", HintType.Green)
  84. UpdateJavaLists()
  85. End Sub
  86. ''' <summary>
  87. ''' 手动从 Java 列表中移除指定的 Java,并记录到已移除列表中。
  88. ''' </summary>
  89. Public Sub ManuallyRemoveJava(JavaEntry As Java)
  90. Configs.JavaList.Edit(Sub(ByRef List) List.Remove(JavaEntry)) '从 Java 列表中移除
  91. Configs.JavaRemovedList.Edit(Function(List) List.Append(JavaEntry.Folder).DistinctBy(Function(s) s.Lower()).ToList()) '记录已移除
  92. Hint($"已将 Java 移除出列表:{JavaEntry}", HintType.Green)
  93. UpdateJavaLists()
  94. PageInstanceLeft.ReloadCurrentJava() '如果强制指定了该 Java,这会把它自动重置掉
  95. End Sub
  96. ''' <summary>
  97. ''' 手动触发搜索 Java。
  98. ''' </summary>
  99. Public Sub ManuallySearchJava(Optional CancellationToken As CancellationToken = Nothing)
  100. RunInThread(
  101. Sub()
  102. JavaListRefreshWorker.Start(cancellationToken:=CancellationToken)
  103. JavaListRefreshWorker.WaitIfRunningAsync(cancellationToken:=CancellationToken).Run() 'worker 开始/结束时会自动重载 UI
  104. If Configs.JavaList.Get().Any Then
  105. Hint("已找到 " & Configs.JavaList.Get().Count & " 个 Java!", HintType.Green)
  106. Else
  107. Hint("未找到可用的 Java!", HintType.Red)
  108. End If
  109. End Sub)
  110. End Sub
  111. #End Region
  112. #Region "选取"
  113. ''' <summary>
  114. ''' 分析指定 Minecraft 版本的基础 Java 版本要求。不受设置影响。
  115. ''' 如果由于 Java 版本要求冲突而导致没有可用的 Java,则返回 null。
  116. ''' </summary>
  117. Public Function GetJavaRequirement(Instance As McInstance) As (Range As ValueRange(Of Version), RecommendedComponent As String)
  118. '版本设置已经强制指定了 Java 版本要求
  119. If Settings.Get(Of Integer)("VersionArgumentJavaV2", Instance) = 1 Then
  120. Try
  121. Dim Range = ValueRange(Of Version).FromString(Settings.Get(Of String)("VersionArgumentJavaRange", Instance), Function(s) StringUtils.ParseVersionWithDefaults(s))
  122. Return (Range, Nothing)
  123. Catch ex As Exception
  124. Logger.Warn($"版本设置中指定的 Java 版本范围有误({Settings.Get(Of String)("VersionArgumentJavaRange", Instance)}),这可能是因为首次修改此设置,尚未初始化")
  125. End Try
  126. End If
  127. '----------------------------------- 从 Minecraft 版本信息获取需求 -----------------------------------
  128. Dim JavaRange = ValueRange(Of Version).All()
  129. Dim AddConstraint =
  130. Sub(Constraint As ValueRange(Of Version))
  131. JavaRange = JavaRange?.Intersect(Constraint)
  132. If JavaRange.IsEmpty Then Logger.Warn($"Java 版本要求冲突(当前追加的要求为 {Constraint},版本为 {Instance.VersionDisplayName})")
  133. End Sub
  134. '原版 26-:通过版本号判断
  135. If (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2024, 4, 2)) OrElse
  136. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla >= New Version(20, 0, 5)) Then
  137. '1.20.5+(24w14a+):至少 Java 21
  138. AddConstraint(ValueRange(Of Version).AtLeast(New Version(21, 0)))
  139. ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 11, 16)) OrElse
  140. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 18) Then
  141. '1.18 pre2+:至少 Java 17
  142. AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
  143. ElseIf (Not Instance.Version.Vaild AndAlso Instance.ReleaseTime >= New Date(2021, 5, 11)) OrElse
  144. (Instance.Version.Vaild AndAlso Instance.Version.Vanilla.Major >= 17) Then
  145. '1.17+ (21w19a+):至少 Java 16
  146. AddConstraint(ValueRange(Of Version).AtLeast(New Version(16, 0)))
  147. ElseIf Instance.ReleaseTime.Year >= 2017 Then 'Minecraft 1.12 与 1.11 的分界线正好是 2017 年,太棒了
  148. '1.12+:至少 Java 8
  149. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
  150. ElseIf Instance.ReleaseTime <= New Date(2013, 5, 1) AndAlso Instance.ReleaseTime.Year >= 2001 Then '避免某些版本写个 1960 年
  151. '1.5.2-:最高 Java 8
  152. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  153. End If
  154. '原版 26+:获取 Mojang 要求的 Java 版本
  155. Dim RecommendedComponent As String = Nothing
  156. Dim RecommendedCode As Integer =
  157. If(Instance.JsonObject?("javaVersion")?("majorVersion")?.ToObject(Of Integer),
  158. If(Instance.JsonVersion?("java_version")?.ToObject(Of Integer), 0))
  159. If RecommendedCode >= 22 Then
  160. Logger.Info("Mojang 要求至少使用 Java " & RecommendedCode)
  161. AddConstraint(ValueRange(Of Version).AtLeast(New Version(RecommendedCode, 0)))
  162. RecommendedComponent =
  163. If(Instance.JsonObject?("javaVersion")?("component")?.ToString,
  164. Instance.JsonVersion?("java_component")?.ToString)
  165. If RecommendedComponent = "" Then RecommendedComponent = Nothing
  166. End If
  167. 'OptiFine 检测
  168. If Instance.Version.HasOptiFine AndAlso Instance.Version.Vaild Then '不管非标准版本
  169. If Instance.Version.Vanilla.Major < 7 Then
  170. '<1.7:至多 Java 8
  171. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  172. ElseIf Instance.Version.Vanilla.Major >= 8 AndAlso Instance.Version.Vanilla.Major < 12 Then
  173. '1.8 - 1.11:必须恰好 Java 8
  174. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(9, 0)))
  175. ElseIf Instance.Version.Vanilla.Major = 12 Then
  176. '1.12:最高 Java 8
  177. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  178. End If
  179. End If
  180. 'Forge 检测(部分测试结果见 #8432)
  181. If Instance.Version.HasForge Then
  182. If Instance.Version.Vanilla >= New Version(6, 0, 1) AndAlso Instance.Version.Vanilla <= New Version(7, 0, 2) Then
  183. '1.6.1 - 1.7.2:必须 Java 7
  184. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(7, 0), New Version(8, 0)))
  185. ElseIf Instance.Version.Vanilla.Major <= 12 OrElse Not Instance.Version.Vaild Then '非标准版本
  186. '<=1.12:Java 8
  187. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  188. ElseIf Instance.Version.Vanilla.Major <= 14 Then
  189. '1.13 - 1.14:Java 8 - 10
  190. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(11, 0)))
  191. ElseIf Instance.Version.Vanilla.Major = 15 Then
  192. '1.15:Java 8 - 15
  193. AddConstraint(ValueRange(Of Version).ClosedOpen(New Version(8, 0), New Version(16, 0)))
  194. ElseIf CompareVersionGE(Instance.Version.Forge, "34.0.0") AndAlso CompareVersionGE("36.2.25", Instance.Version.Forge) Then
  195. '1.16.3~5,Forge 34.0.0 ~ 36.2.25:最高 Java 8u320
  196. AddConstraint(ValueRange(Of Version).AtMost(New Version(8, 0, 320)))
  197. ElseIf CompareVersionGE(Instance.Version.Forge, "36.2.26") AndAlso CompareVersionGE("36.999999.999999", Instance.Version.Forge) Then
  198. '1.16.5,Forge 36.2.26+,<37.0.0:最高 Java 23
  199. AddConstraint(ValueRange(Of Version).LessThan(New Version(24, 0)))
  200. ElseIf CompareVersionGE(Instance.Version.Forge, "37.0.0") AndAlso CompareVersionGE("37.0.79", Instance.Version.Forge) Then
  201. '1.17.1,Forge 37.0.0 ~ 37.0.79:最高 Java 16
  202. AddConstraint(ValueRange(Of Version).LessThan(New Version(17, 0)))
  203. ElseIf Instance.Version.Vanilla.Major = 18 AndAlso Instance.Version.HasOptiFine Then '#305
  204. '1.18:若安装了 OptiFine,最高 Java 18
  205. AddConstraint(ValueRange(Of Version).LessThan(New Version(19, 0)))
  206. ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.21") AndAlso CompareVersionGE("45.0.65", Instance.Version.Forge) Then
  207. '1.19.4,Forge 45.0.21 ~ 45.0.65:最高 Java 19
  208. AddConstraint(ValueRange(Of Version).LessThan(New Version(20, 0)))
  209. ElseIf CompareVersionGE(Instance.Version.Forge, "45.0.66") AndAlso CompareVersionGE("47.4.8", Instance.Version.Forge) Then
  210. '1.19.4~1.20.1,Forge 45.0.66 ~ 47.4.8:最高 Java 21
  211. AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
  212. End If
  213. End If
  214. 'NeoForge 检测
  215. If Instance.Version.HasNeoForge Then
  216. If Instance.Version.Vanilla = New Version(20, 0, 1) OrElse
  217. (CompareVersionGE("20.2.62-beta", Instance.Version.NeoForge) AndAlso Not Instance.Version.NeoForge.Contains("25w14craftmine")) Then
  218. '1.20.1,以及 1.20.2 的 20.2.62-beta 之前:最高 Java 21
  219. AddConstraint(ValueRange(Of Version).LessThan(New Version(22, 0)))
  220. End If
  221. End If
  222. 'Fabric 检测
  223. If Instance.Version.HasFabric AndAlso Instance.Version.Vaild Then '不管非标准版本
  224. If Instance.Version.Vanilla.Major >= 15 AndAlso Instance.Version.Vanilla.Major <= 16 Then
  225. '1.15 - 1.16:Java 8+
  226. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0)))
  227. ElseIf Instance.Version.Vanilla.Major >= 18 Then
  228. '1.18+:Java 17+
  229. AddConstraint(ValueRange(Of Version).AtLeast(New Version(17, 0)))
  230. End If
  231. End If
  232. 'LiteLoader 检测
  233. If Instance.Version.HasLiteLoader AndAlso Instance.Version.Vaild Then '不管非标准版本
  234. '最高 Java 8
  235. AddConstraint(ValueRange(Of Version).LessThan(New Version(9, 0)))
  236. End If
  237. '统一通行证检测
  238. If Settings.Get(Of McLoginType)("LoginType") = McLoginType.Nide Then
  239. '至少 Java 8u101
  240. AddConstraint(ValueRange(Of Version).AtLeast(New Version(8, 0, 141)))
  241. End If
  242. Return (JavaRange, RecommendedComponent)
  243. End Function
  244. ''' <summary>
  245. ''' 根据版本或 Java 范围要求寻找适合的 Java。
  246. ''' 若指定了 <paramref name="TryFixOrHint"/>,则会在找不到时会尝试下载 Java 或提示用户下载,否则只会静默运行。
  247. ''' </summary>
  248. ''' <returns>最适合的 Java。若找不到或失败则返回 null,除非取消否则不会抛出异常。</returns>
  249. Public Function SelectOrDownloadJava(Target As OneOf(Of ValueRange(Of Version), McInstance), TryFixOrHint As Boolean, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing) As Java
  250. Try
  251. If Target.Is(Of McInstance) Then MigrateInstanceJavaSettings(Target.As(Of McInstance), c, p?.SplitTo(0.2)) '迁移
  252. Dim Instance As McInstance = If(Target.Is(Of McInstance), Target.As(Of McInstance), Nothing)
  253. Select Case If(Instance Is Nothing, 0, Settings.Get(Of Integer)("VersionArgumentJavaV2", Instance))
  254. Case 2 '============================= 使用版本文件夹中的 Java =============================
  255. '查找版本文件夹下的 Java
  256. Logger.Info($"版本设置要求使用版本文件夹中的 Java:{Instance.PathVersion}")
  257. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run().FirstOrDefault()
  258. If JavaFound IsNot Nothing Then
  259. Logger.Info($"已发现版本文件夹中的 Java:{JavaFound}")
  260. Return JavaFound
  261. End If
  262. '未能找到
  263. If TryFixOrHint Then
  264. SwitchToInstanceSetup(Instance)
  265. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用版本文件夹中的 Java{vbRQ},但版本文件夹里没能找到 Java。{vbCrLf}请先修改启动选项中的 Java 设置,或在版本文件夹中放一个 Java,然后再试。",
  266. "未找到 Java", IsWarn:=True, ForceWait:=True)
  267. End If
  268. Return Nothing
  269. Case 3 '=============================== 强制指定特定 Java ===============================
  270. Dim ChosenJava = Configs.InstanceForcedJava.Get(Instance.Config)
  271. Logger.Info($"版本设置中强制指定的 Java:{Instance.PathVersion} → {ChosenJava}")
  272. If ChosenJava Is Nothing Then
  273. If TryFixOrHint Then
  274. SwitchToInstanceSetup(Instance)
  275. MyMsgBox($"该版本的版本设置选择了 {vbLQ}使用指定的 Java{vbRQ},但还没有指定任何 Java。{vbCrLf}请先选择你想使用的 Java,然后再试。",
  276. "未选择 Java", IsWarn:=True, ForceWait:=True)
  277. End If
  278. p?.Skip() : Return Nothing '<==== 设置中未指定 Java
  279. End If
  280. If Not Configs.JavaList.Get().Contains(ChosenJava) Then
  281. Configs.InstanceForcedJava.Reset(Instance.Config)
  282. UpdateJavaLists()
  283. If TryFixOrHint Then
  284. SwitchToInstanceSetup(Instance)
  285. Logger.Error("版本设置中指定的 Java 不在 Java 列表中,请重新选择一个 Java!", LogBehavior.Alert)
  286. End If
  287. p?.Skip() : Return Nothing '<==== 设置中指定的 Java 不在列表中
  288. End If
  289. If Not ChosenJava.CheckAsync(c).Run() Then
  290. Configs.InstanceForcedJava.Reset(Instance.Config)
  291. UpdateJavaLists()
  292. If TryFixOrHint Then
  293. SwitchToInstanceSetup(Instance)
  294. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java,然后再试。", LogBehavior.Alert)
  295. Else
  296. Logger.Error("版本设置中指定的 Java 已无法使用,请重新选择一个 Java!", LogBehavior.Toast)
  297. End If
  298. p?.Finish() : Return Nothing '<==== 指定的 Java 存在异常
  299. End If
  300. p?.Finish() : Return ChosenJava '<==== 成功找到并检查通过
  301. Case Else '======================== 根据版本范围自动选择 JavaList 中的 Java ========================
  302. '获取范围
  303. Dim Range As ValueRange(Of Version) =
  304. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).Range, Target.As(Of ValueRange(Of Version)))
  305. Dim RecommendedComponent As String =
  306. If(Target.Is(Of McInstance), GetJavaRequirement(Instance).RecommendedComponent, Nothing)
  307. Logger.Info($"自动选择以下范围的 Java:{Range} {RecommendedComponent}")
  308. If Range.IsEmpty Then Throw New Exception("Java 版本需求存在冲突,导致没有任何可能适配的 Java,请查看 PCL 的日志了解详细信息!")
  309. '搜索 Java
  310. Dim SelectJavaFromJavaList =
  311. Function() As Java
  312. JavaListRefreshWorker.WaitIfRunning() '等待进行中的搜索结束
  313. For Each ChosenJava In Configs.JavaList.Get()
  314. If Not ChosenJava.CheckAsync(c).Run() Then '检查
  315. Logger.Warn($"Java 检查失败,将尝试列表中的下一个 Java:{ChosenJava}")
  316. Continue For
  317. End If
  318. If ChosenJava.Version Is Nothing OrElse Not Range.Contains(ChosenJava.Version) Then Continue For '版本范围
  319. Logger.Info($"已选定 Java:{ChosenJava}")
  320. Return ChosenJava
  321. Next
  322. Return Nothing
  323. End Function
  324. p?.Set(0.25)
  325. Dim Result = SelectJavaFromJavaList()
  326. If Result IsNot Nothing Then p?.Skip() : Return Result
  327. SyncLock JavaLock '#3797
  328. '既有列表中没有合适的 Java,重新触发搜索
  329. Logger.Warn($"既有列表中没有合适的 Java,重新触发搜索")
  330. JavaListRefreshWorker.Start(c, p?.SplitTo(0.3))
  331. Result = SelectJavaFromJavaList()
  332. If Result IsNot Nothing Then p?.Skip() : Return Result
  333. If Not TryFixOrHint Then Return Nothing
  334. '电脑中没有合适的 Java,获取 Mojang 提供的 Java 下载列表
  335. Logger.Warn($"电脑中没有合适的 Java,开始获取 Mojang 提供的 Java 下载列表")
  336. Dim Components = DownloadJavaList()
  337. c.ThrowIfCancellationRequested()
  338. p?.Set(0.4)
  339. '确定要下载的目标 Java
  340. Dim JavaToDownload As JavaDownloadInfo = If(
  341. Components.FirstOrDefault(Function(Comp) Comp.Java("version")("name").ToString = RecommendedComponent), '优先选择指定的 Component
  342. Components.FirstOrDefault(Function(Comp) Range.Contains(Comp.JavaVersion)))
  343. If JavaToDownload Is Nothing Then
  344. Logger.Warn($"Mojang 提供的 Java 列表中没有合适的 Java,无法自动下载")
  345. '提示需要手动下载 Java
  346. Dim Description As String
  347. Dim HasPureLower As Boolean = Range.HasLower AndAlso Range.IsLowerInclusive AndAlso
  348. Range.Lower.Minor <= 0 AndAlso Range.Lower.Build <= 0 AndAlso Range.Lower.Revision <= 0
  349. Dim HasPureUpper As Boolean = Range.HasUpper AndAlso Not Range.IsUpperInclusive AndAlso
  350. Range.Upper.Minor <= 0 AndAlso Range.Upper.Build <= 0 AndAlso Range.Upper.Revision <= 0
  351. If Range.HasLower AndAlso Range.HasUpper Then '有上下界
  352. If HasPureLower AndAlso HasPureUpper Then
  353. If Range.Lower.Major = Range.Upper.Major - 1 Then
  354. Description = $" Java {Range.Lower.Major}"
  355. Else
  356. Description = $" Java {Range.Lower.Major} ~ {Range.Upper.Major - 1}"
  357. End If
  358. Else
  359. Description = $" Java {Range}" '摆烂
  360. End If
  361. ElseIf Range.HasLower Then '只有下界
  362. If HasPureLower Then
  363. Description = $" Java {Range.Lower.Major} 或更高版本的 Java"
  364. ElseIf Range.IsLowerInclusive Then
  365. Description = $" Java {Range.Lower} 或更高版本的 Java"
  366. Else
  367. Description = $"版本高于 {Range.Lower} 的 Java"
  368. End If
  369. Else '只有上界
  370. If HasPureUpper Then
  371. Description = $" Java {Range.Upper.Major - 1} 或更低版本的 Java"
  372. ElseIf Range.IsUpperInclusive Then
  373. Description = $" Java {Range.Upper} 或更低版本的 Java"
  374. Else
  375. Description = $"版本低于 {Range.Upper} 的 Java"
  376. End If
  377. End If
  378. MyMsgBox($"PCL 没能找到合适的 Java。{vbCrLf}请在网上搜索并手动安装{Description}, 然后在【设置 → 启动 → 高级选项 → Java 列表】中导入它。", "需要手动安装 Java")
  379. Return Nothing
  380. End If
  381. '自动下载 Java
  382. Dim JavaLoader = CreateJavaDownloadLoader(JavaToDownload)
  383. Try
  384. JavaLoader.WaitForExit(IsForceRestart:=True, c:=c, p:=p?.SplitTo(0.95))
  385. Finally
  386. JavaLoader.Cancel() '确保取消时中止 Java 下载
  387. End Try
  388. Result = SelectJavaFromJavaList()
  389. If Result IsNot Nothing Then p?.Finish() : Return Result
  390. Throw New Exception($"下载 Java {JavaToDownload.ComponentName} 后仍然无法找到合适的 Java")
  391. End SyncLock
  392. End Select
  393. Catch ex As Exception
  394. ex.ThrowIfCanceled()
  395. Logger.Error(ex, "选择 Java 时出现异常")
  396. p?.Skip() : Return Nothing
  397. End Try
  398. End Function
  399. Private Sub SwitchToInstanceSetup(Instance As McInstance)
  400. RunInUi(
  401. Sub()
  402. PageInstanceLeft.Instance = Instance
  403. FrmMain.PageChange(FormMain.PageType.InstanceSetup, FormMain.PageSubType.InstanceSetup)
  404. End Sub)
  405. End Sub
  406. Private ReadOnly JavaLock As New Object
  407. ''' <summary>
  408. ''' 从老版本 PCL 迁移版本独立的 Java 设置。
  409. ''' </summary>
  410. Private Sub MigrateInstanceJavaSettings(Instance As McInstance, Optional c As CancellationToken = Nothing, Optional p As ProgressProvider = Nothing)
  411. If Configs.InstanceMigratedJava.Get(Instance.Config) Then p?.Skip() : Return
  412. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(Sub() FrmInstanceSetup.ComboArgumentJava.IsEnabled = False)
  413. Try
  414. Logger.Info($"正在尝试迁移版本独立的 Java 设置({Instance.Name})")
  415. '强制使用指定 Java
  416. Dim ForcedJavaSetting = Settings.Get(Of String)("VersionArgumentJavaSelect", Instance)
  417. If Not ForcedJavaSetting.StartsWithF("{") Then
  418. p?.Set(0.4, True) : GoTo NoForcedJava
  419. End If
  420. Dim ForcedJava = New Java(ForcedJavaSetting.DeserializeJson(Of JObject)()("Path").ToString)
  421. If Not ForcedJava.CheckAsync(c).Run() Then
  422. Hint("该版本的设置中强制指定的 Java 存在异常,该设置已重置", HintType.Red)
  423. p?.Set(0.4) : GoTo NoForcedJava
  424. End If
  425. Logger.Info($"将版本独立设置改为老版本中强制指定的 Java:{ForcedJava}")
  426. Dim Match = Configs.JavaList.Get().FirstOrDefault(Function(j) j.Folder.Equals(ForcedJava.Folder, StringComparison.OrdinalIgnoreCase))
  427. If Match Is Nothing Then
  428. Configs.JavaList.Edit(Sub(ByRef List) List.Add(ForcedJava)) '加入列表底部
  429. Match = ForcedJava
  430. Logger.Info($"已将强制指定的 Java 加入 Java 列表底部:{ForcedJava}")
  431. End If
  432. Configs.InstanceForcedJava.Set(Match, Instance.Config)
  433. Settings.Set("VersionArgumentJavaV2", 3, Instance)
  434. UpdateJavaLists()
  435. Configs.InstanceMigratedJava.Set(True, Instance.Config) '完成
  436. p?.Finish() : Return
  437. NoForcedJava:
  438. Dim JavaFound = JavaUtils.SearchFoldersAsync(True, {Instance.PathVersion}, c, p?.SplitTo(1)).Run()
  439. If JavaFound.Any() Then
  440. '从版本文件夹中发现了 Java
  441. Logger.Info($"在版本文件夹中发现了 Java,将版本独立设置改为优先使用版本文件夹的 Java:{JavaFound}")
  442. Settings.Set("VersionArgumentJavaV2", 2, Instance)
  443. Else
  444. '自动选择
  445. Logger.Info("没有需要迁移的内容,自动选择 Java")
  446. Settings.Set("VersionArgumentJavaV2", 0, Instance)
  447. End If
  448. Configs.InstanceMigratedJava.Set(True, Instance.Config) '完成
  449. Finally
  450. If FrmInstanceSetup IsNot Nothing Then RunInUiWait(
  451. Sub()
  452. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJava)
  453. SettingService.RefreshSettings(FrmInstanceSetup.ComboArgumentJavaSelect)
  454. FrmInstanceSetup.ComboArgumentJava.IsEnabled = True
  455. End Sub)
  456. End Try
  457. End Sub
  458. #End Region
  459. #Region "下载(均为内部工具方法,无外部调用)"
  460. Private Class JavaDownloadInfo
  461. Public Property ComponentName As String
  462. Public Property Java As JObject
  463. Public Property JavaVersion As Version
  464. End Class
  465. ''' <summary>
  466. ''' 下载 Mojang 提供的 Java 列表。
  467. ''' <para/> 若本次打开程序已经下载过,则会直接返回缓存的结果。
  468. ''' </summary>
  469. Private Function DownloadJavaList() As List(Of JavaDownloadInfo)
  470. 'TODO: 写一个更简单的 Lazy,允许重置缓存
  471. Static Components As New Lazy(Of List(Of JavaDownloadInfo))(
  472. Function()
  473. '下载 Mojang 提供的 Java 列表
  474. Dim IndexFileStr As String = NetRequestByLoader(DlVersionListOrder(
  475. {"https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"},
  476. {"https://bmclapi2.bangbang93.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json"}
  477. ), IsJson:=True)
  478. '解析 Java 列表
  479. Dim Results = New List(Of JavaDownloadInfo)
  480. For Each Prop As JProperty In IndexFileStr.DeserializeJson(Of JObject)()("windows-x64")
  481. If Prop.Name = "minecraft-java-exe" Then Continue For '不是完整 Java
  482. Try
  483. Dim Entry = Prop.Value.FirstOrDefault
  484. If Entry Is Nothing Then Continue For
  485. Dim JavaName = Entry("version")("name").ToString '可能的名称格式:"12"、"8u51-cacert462b08"、"17.0.8.1"、"17.0.1.12.1"、"21.0.7"
  486. If JavaName.Contains(".") Then
  487. Dim Parts As String() = JavaName.Split(".")
  488. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(Parts(0)), Val(Parts(1)), If(Parts.Length >= 3, Val(Parts(2)), 0), If(Parts.Length >= 4, Val(Parts(3)), 0))})
  489. ElseIf JavaName.StartsWithF("8u51-") Then
  490. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(8, 0, 51, 0)})
  491. Else
  492. Results.Add(New JavaDownloadInfo With {.ComponentName = Prop.Name, .Java = Entry, .JavaVersion = New Version(Val(JavaName.BeforeFirstOfAny({"u", "."})), 0, 0, 0)})
  493. End If
  494. Catch ex As Exception
  495. Logger.Warn(ex, $"解析 Mojang 提供的 Java 列表项失败,已跳过({Prop.Name})")
  496. End Try
  497. Next
  498. Return Results
  499. End Function)
  500. Return Components.Value
  501. End Function
  502. ''' <summary>
  503. ''' 构建下载指定 Java 的加载器。
  504. ''' </summary>
  505. Private Function CreateJavaDownloadLoader(Target As JavaDownloadInfo) As LoaderCombo(Of Integer)
  506. '分析需要下载的 Java 文件列表
  507. Dim Address As String = Target.Java("manifest")("url")
  508. Logger.Info($"准备下载 Java {Target.Java("version")("name")}({Target.ComponentName}):{Address}")
  509. Dim ListFileStr As String = NetRequestByLoader(DlSourceOrder({Address}, {Address.Replace("piston-meta.mojang.com", "bmclapi2.bangbang93.com")}), IsJson:=True)
  510. Dim BaseDir As String = $"{Paths.AppData}.minecraft\runtime\{Target.ComponentName}\"
  511. Dim FilesToDownload As New List(Of NetFile)
  512. For Each File As JProperty In CType(ListFileStr.DeserializeJson(), JObject)("files")
  513. If CType(File.Value, JObject)("downloads")?("raw") Is Nothing Then Continue For
  514. Dim Info As JObject = CType(File.Value, JObject)("downloads")("raw")
  515. Dim Checker As New FileChecker With {.ActualSize = Info("size"), .Hash = Info("sha1")}
  516. If Checker.Hash = "12976a6c2b227cbac58969c1455444596c894656" OrElse Checker.Hash = "c80e4bab46e34d02826eab226a4441d0970f2aba" OrElse Checker.Hash = "84d2102ad171863db04e7ee22a259d1f6c5de4a5" Then
  517. Continue For '跳过 3 个无意义大量重复文件(#3827)
  518. End If
  519. Dim Url As String = Info("url")
  520. FilesToDownload.Add(New NetFile(
  521. DlSourceOrder({Url}, {Url.Replace("piston-data.mojang.com", "bmclapi2.bangbang93.com")}),
  522. BaseDir & File.Name, Checker))
  523. Next
  524. Logger.Info($"该 Java 需要下载 {FilesToDownload.Count} 个文件,目标文件夹:{BaseDir}")
  525. '构建加载器
  526. Dim Loader = New LoaderCombo(Of Integer)("下载 Java", {
  527. New LoaderDownload("下载 Java 文件", FilesToDownload) With {.ProgressWeight = 10},
  528. New LoaderWorker("刷新 Java 列表", JavaListRefreshWorker)
  529. }) With {.OnStateChanged =
  530. Sub(RawLoader As LoaderCombo(Of Integer))
  531. If RawLoader.State = LoadState.Failed OrElse RawLoader.State = LoadState.Canceled Then
  532. Logger.Warn($"由于 Java 下载未完成,清理未下载完成的 Java 文件夹:{BaseDir}")
  533. DirectoryUtils.Delete(BaseDir)
  534. End If
  535. End Sub}
  536. Return Loader
  537. End Function
  538. #End Region
  539. End Module