ModDownload.vb 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  1. Public Module ModDownload
  2. #Region "DlClient* | Minecraft 客户端"
  3. ''' <summary>
  4. ''' 返回某 Minecraft 版本对应的原版主 Jar 文件的下载信息,要求对应依赖版本已存在。
  5. ''' 失败则抛出异常,不需要下载则返回 Nothing。
  6. ''' </summary>
  7. Public Function DlClientJarGet(Version As McVersion, ReturnNothingOnFileUseable As Boolean) As NetFile
  8. '获取底层继承版本
  9. Do While Not String.IsNullOrEmpty(Version.InheritVersion)
  10. Version = New McVersion(Version.InheritVersion)
  11. Loop
  12. '检查 Json 是否标准
  13. If Version.JsonObject("downloads") Is Nothing OrElse Version.JsonObject("downloads")("client") Is Nothing OrElse Version.JsonObject("downloads")("client")("url") Is Nothing Then
  14. Throw New Exception("底层版本 " & Version.Name & " 中无 jar 文件下载信息")
  15. End If
  16. '检查文件
  17. Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1"))
  18. If ReturnNothingOnFileUseable Then
  19. '是否跳过
  20. Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
  21. If IsSetupSkip AndAlso File.Exists(Version.Path & Version.Name & ".jar") Then Return Nothing '跳过校验
  22. If Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
  23. End If
  24. '返回下载信息
  25. Dim JarUrl As String = Version.JsonObject("downloads")("client")("url")
  26. Return New NetFile(DlSourceLauncherOrMetaGet(JarUrl), Version.Path & Version.Name & ".jar", Checker)
  27. End Function
  28. ''' <summary>
  29. ''' 返回某 Minecraft 版本对应的原版主 AssetIndex 文件的下载信息,要求对应依赖版本已存在。
  30. ''' 若未找到,则会返回 Legacy 资源文件或 Nothing。
  31. ''' </summary>
  32. Public Function DlClientAssetIndexGet(Version As McVersion) As NetFile
  33. '获取底层继承版本
  34. Do While Not String.IsNullOrEmpty(Version.InheritVersion)
  35. Version = New McVersion(Version.InheritVersion)
  36. Loop
  37. '获取信息
  38. Dim IndexInfo = McAssetsGetIndex(Version, True, True)
  39. Dim IndexAddress As String = PathMcFolder & "assets\indexes\" & IndexInfo("id").ToString & ".json"
  40. Log("[Download] 版本 " & Version.Name & " 对应的资源文件索引为 " & IndexInfo("id").ToString)
  41. Dim IndexUrl As String = If(IndexInfo("url"), "")
  42. If IndexUrl = "" Then
  43. Return Nothing
  44. Else
  45. Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
  46. End If
  47. End Function
  48. ''' <summary>
  49. ''' 构造补全某 Minecraft 版本的所有文件的加载器列表。失败会抛出异常。
  50. ''' </summary>
  51. Public Function DlClientFix(Version As McVersion, CheckAssetsHash As Boolean, AssetsIndexBehaviour As AssetsIndexExistsBehaviour, SkipAssetsDownloadWhileSetupRequired As Boolean) As List(Of LoaderBase)
  52. Dim Loaders As New List(Of LoaderBase)
  53. #Region "下载支持库文件"
  54. Dim LoadersLib As New List(Of LoaderBase) From {
  55. New LoaderTask(Of String, List(Of NetFile))("分析缺失支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(Version)) With {.ProgressWeight = 1},
  56. New LoaderDownload("下载支持库文件", New List(Of NetFile)) With {.ProgressWeight = 15}
  57. }
  58. '构造加载器
  59. Loaders.Add(New LoaderCombo(Of String)("下载支持库文件(主加载器)", LoadersLib) With {.Block = False, .Show = False, .ProgressWeight = 16})
  60. #End Region
  61. #Region "下载资源文件"
  62. Dim IsSetupSkip As Boolean = ShouldIgnoreFileCheck(Version)
  63. If IsSetupSkip Then Log("[Download] 已跳过 Assets 下载")
  64. If (Not SkipAssetsDownloadWhileSetupRequired) OrElse Not IsSetupSkip Then
  65. Dim LoadersAssets As New List(Of LoaderBase)
  66. '获取资源文件索引地址
  67. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址",
  68. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  69. Try
  70. Dim IndexFile = DlClientAssetIndexGet(Version)
  71. Dim IndexFileInfo As New FileInfo(IndexFile.LocalPath)
  72. If AssetsIndexBehaviour <> AssetsIndexExistsBehaviour.AlwaysDownload AndAlso IndexFile.Check.Check(IndexFile.LocalPath) Is Nothing Then
  73. Task.Output = New List(Of NetFile)
  74. Else
  75. Task.Output = New List(Of NetFile) From {IndexFile}
  76. End If
  77. Catch ex As Exception
  78. Throw New Exception("分析资源文件索引地址失败", ex)
  79. End Try
  80. End Sub) With {.ProgressWeight = 0.5, .Show = False})
  81. '下载资源文件索引
  82. LoadersAssets.Add(New LoaderDownload("下载资源文件索引", New List(Of NetFile)) With {.ProgressWeight = 2})
  83. '要求独立更新索引
  84. If AssetsIndexBehaviour = AssetsIndexExistsBehaviour.DownloadInBackground Then
  85. Dim LoadersAssetsUpdate As New List(Of LoaderBase)
  86. Dim TempAddress As String = Nothing
  87. Dim RealAddress As String = Nothing
  88. LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址",
  89. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  90. Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Version)
  91. RealAddress = BackAssetsFile.LocalPath
  92. TempAddress = PathTemp & "Cache\" & BackAssetsFile.LocalName
  93. BackAssetsFile.LocalPath = TempAddress
  94. Task.Output = New List(Of NetFile) From {BackAssetsFile}
  95. '检查是否需要更新:每天只更新一次
  96. If File.Exists(RealAddress) AndAlso Math.Abs((File.GetLastWriteTime(RealAddress).Date - Now.Date).TotalDays) < 1 Then
  97. Log("[Download] 无需更新资源文件索引")
  98. Task.Abort()
  99. End If
  100. End Sub))
  101. LoadersAssetsUpdate.Add(New LoaderDownload("后台下载资源文件索引", New List(Of NetFile)))
  102. LoadersAssetsUpdate.Add(New LoaderTask(Of List(Of NetFile), String)("后台复制资源文件索引",
  103. Sub(Task As LoaderTask(Of List(Of NetFile), String))
  104. CopyFile(TempAddress, RealAddress)
  105. McLaunchLog("后台更新资源文件索引成功:" & TempAddress)
  106. End Sub))
  107. Dim Updater As New LoaderCombo(Of String)("后台更新资源文件索引", LoadersAssetsUpdate)
  108. Log("[Download] 开始后台检查资源文件索引")
  109. Updater.Start()
  110. End If
  111. '获取资源文件地址
  112. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析缺失资源文件",
  113. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  114. Task.Output = McAssetsFixList(McAssetsGetIndexName(Version), CheckAssetsHash, Task)
  115. End Sub) With {.ProgressWeight = 3})
  116. '下载资源文件
  117. LoadersAssets.Add(New LoaderDownload("下载资源文件", New List(Of NetFile)) With {.ProgressWeight = 25})
  118. '构造加载器
  119. Loaders.Add(New LoaderCombo(Of String)("下载资源文件(主加载器)", LoadersAssets) With {.Block = False, .Show = False, .ProgressWeight = 30.5})
  120. End If
  121. #End Region
  122. Return Loaders
  123. End Function
  124. Public Enum AssetsIndexExistsBehaviour
  125. ''' <summary>
  126. ''' 如果文件存在,则不进行下载。
  127. ''' </summary>
  128. DontDownload
  129. ''' <summary>
  130. ''' 如果文件存在,则启动新的下载加载器进行独立的更新。
  131. ''' </summary>
  132. DownloadInBackground
  133. ''' <summary>
  134. ''' 如果文件存在,也同样进行下载。
  135. ''' </summary>
  136. AlwaysDownload
  137. End Enum
  138. #End Region
  139. #Region "DlClientList | Minecraft 客户端 版本列表"
  140. '主加载器
  141. Public Structure DlClientListResult
  142. ''' <summary>
  143. ''' 数据来源名称,如“Mojang”,“BMCLAPI”。
  144. ''' </summary>
  145. Public SourceName As String
  146. ''' <summary>
  147. ''' 是否为官方的实时数据。
  148. ''' </summary>
  149. Public IsOfficial As Boolean
  150. ''' <summary>
  151. ''' 获取到的 Json 数据。
  152. ''' </summary>
  153. Public Value As JObject
  154. '''' <summary>
  155. '''' 官方源的失败原因。若没有则为 Nothing。
  156. '''' </summary>
  157. 'Public OfficialError As Exception
  158. End Structure
  159. ''' <summary>
  160. ''' Minecraft 客户端 版本列表,主加载器。
  161. ''' </summary>
  162. Public DlClientListLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Main", AddressOf DlClientListMain)
  163. Private Sub DlClientListMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  164. Select Case Setup.Get("ToolDownloadVersion")
  165. Case 0
  166. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  167. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 30),
  168. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 60)
  169. }, Loader.IsForceRestarting)
  170. Case 1
  171. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  172. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 5),
  173. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 35)
  174. }, Loader.IsForceRestarting)
  175. Case Else
  176. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  177. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 60),
  178. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 60)
  179. }, Loader.IsForceRestarting)
  180. End Select
  181. End Sub
  182. '各个下载源的分加载器
  183. ''' <summary>
  184. ''' Minecraft 客户端 版本列表,Mojang 官方源加载器。
  185. ''' </summary>
  186. Public DlClientListMojangLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Mojang", AddressOf DlClientListMojangMain)
  187. Private IsNewClientVersionHinted As Boolean = False
  188. Private Sub DlClientListMojangMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  189. Dim Json As JObject = NetGetCodeByRequestRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", IsJson:=True)
  190. Try
  191. Dim Versions As JArray = Json("versions")
  192. If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
  193. '添加 PCL 特供项
  194. If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json")))
  195. '返回
  196. Loader.Output = New DlClientListResult With {.IsOfficial = True, .SourceName = "Mojang 官方源", .Value = Json}
  197. '解析更新提示(Release)
  198. Dim Version As String = Json("latest")("release")
  199. If Setup.Get("ToolUpdateRelease") AndAlso Not Setup.Get("ToolUpdateReleaseLast") = "" AndAlso Version IsNot Nothing AndAlso Not Setup.Get("ToolUpdateReleaseLast") = Version Then
  200. McDownloadClientUpdateHint(Version, Json)
  201. IsNewClientVersionHinted = True
  202. End If
  203. McVersionHighest = Version.Split(".")(1)
  204. Setup.Set("ToolUpdateReleaseLast", Version)
  205. '解析更新提示(Snapshot)
  206. Version = Json("latest")("snapshot")
  207. If Setup.Get("ToolUpdateSnapshot") AndAlso Not Setup.Get("ToolUpdateSnapshotLast") = "" AndAlso Version IsNot Nothing AndAlso Not Setup.Get("ToolUpdateSnapshotLast") = Version AndAlso Not IsNewClientVersionHinted Then
  208. McDownloadClientUpdateHint(Version, Json)
  209. End If
  210. Setup.Set("ToolUpdateSnapshotLast", If(Version, "Nothing"))
  211. Catch ex As Exception
  212. Throw New Exception("版本列表解析失败", ex)
  213. End Try
  214. End Sub
  215. ''' <summary>
  216. ''' Minecraft 客户端 版本列表,BMCLAPI 源加载器。
  217. ''' </summary>
  218. Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
  219. Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  220. Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json", IsJson:=True)
  221. Try
  222. Dim Versions As JArray = Json("versions")
  223. If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
  224. '添加 PCL 特供项
  225. If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json")))
  226. '返回
  227. Loader.Output = New DlClientListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Json}
  228. Catch ex As Exception
  229. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  230. End Try
  231. End Sub
  232. ''' <summary>
  233. ''' 获取某个版本的 Json 下载地址,若失败则返回 Nothing。必须在工作线程执行。
  234. ''' </summary>
  235. Public Function DlClientListGet(Id As String)
  236. Try
  237. '确认 Minecraft 版本列表已完成获取
  238. Select Case DlClientListLoader.State
  239. Case LoadState.Loading
  240. DlClientListLoader.WaitForExit()
  241. Case LoadState.Failed, LoadState.Aborted, LoadState.Waiting
  242. DlClientListLoader.WaitForExit(IsForceRestart:=True)
  243. End Select
  244. '确认版本格式标准
  245. Id = Id.Replace("_", "-") '1.7.10_pre4 在版本列表中显示为 1.7.10-pre4
  246. If Id <> "1.0" AndAlso Id.EndsWithF(".0") Then Id = Left(Id, Id.Length - 2) 'OptiFine 1.8 的下载会触发此问题,显示版本为 1.8.0
  247. '查找版本并开始
  248. For Each Version As JObject In DlClientListLoader.Output.Value("versions")
  249. If Version("id") = Id Then
  250. Return Version("url").ToString
  251. End If
  252. Next
  253. Log("未发现版本 " & Id & " 的 json 下载地址,版本列表返回为:" & vbCrLf & DlClientListLoader.Output.Value.ToString, LogLevel.Debug)
  254. Return Nothing
  255. Catch ex As Exception
  256. Log(ex, "获取版本 " & Id & " 的 json 下载地址失败")
  257. Return Nothing
  258. End Try
  259. End Function
  260. #End Region
  261. #Region "DlOptiFineList | OptiFine 版本列表"
  262. Public Structure DlOptiFineListResult
  263. ''' <summary>
  264. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  265. ''' </summary>
  266. Public SourceName As String
  267. ''' <summary>
  268. ''' 是否为官方的实时数据。
  269. ''' </summary>
  270. Public IsOfficial As Boolean
  271. ''' <summary>
  272. ''' 获取到的数据。
  273. ''' </summary>
  274. Public Value As List(Of DlOptiFineListEntry)
  275. End Structure
  276. Public Class DlOptiFineListEntry
  277. ''' <summary>
  278. ''' 显示名称,已去除 HD_U 字样,如“1.12.2 C8”。
  279. ''' </summary>
  280. Public NameDisplay As String
  281. ''' <summary>
  282. ''' 原始文件名称,如“preview_OptiFine_1.11_HD_U_E1_pre.jar”。
  283. ''' </summary>
  284. Public NameFile As String
  285. ''' <summary>
  286. ''' 对应的版本名称,如“1.13.2-OptiFine_HD_U_E6”。
  287. ''' </summary>
  288. Public NameVersion As String
  289. ''' <summary>
  290. ''' 是否为测试版。
  291. ''' </summary>
  292. Public IsPreview As Boolean
  293. ''' <summary>
  294. ''' 对应的 Minecraft 版本,如“1.12.2”。
  295. ''' </summary>
  296. Public Property Inherit As String
  297. Get
  298. Return _inherit
  299. End Get
  300. Set(value As String)
  301. If value.EndsWithF(".0") Then value = Left(value, value.Length - 2)
  302. _inherit = value
  303. End Set
  304. End Property
  305. Private _inherit As String
  306. ''' <summary>
  307. ''' 发布时间,格式为“yyyy/mm/dd”。OptiFine 源无此数据。
  308. ''' </summary>
  309. Public ReleaseTime As String
  310. ''' <summary>
  311. ''' 需要的最低 Forge 版本。空字符串为无限制,Nothing 为不兼容,“28.1.56” 表示版本号,“1161” 表示版本号的最后一位。
  312. ''' </summary>
  313. Public RequiredForgeVersion As String
  314. End Class
  315. ''' <summary>
  316. ''' OptiFine 版本列表,主加载器。
  317. ''' </summary>
  318. Public DlOptiFineListLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Main", AddressOf DlOptiFineListMain)
  319. Private Sub DlOptiFineListMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
  320. Select Case Setup.Get("ToolDownloadVersion")
  321. Case 0
  322. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)) From {
  323. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListBmclapiLoader, 30),
  324. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListOfficialLoader, 60)
  325. }, Loader.IsForceRestarting)
  326. Case 1
  327. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)) From {
  328. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListOfficialLoader, 5),
  329. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListBmclapiLoader, 35)
  330. }, Loader.IsForceRestarting)
  331. Case Else
  332. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)) From {
  333. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListOfficialLoader, 60),
  334. New KeyValuePair(Of LoaderTask(Of Integer, DlOptiFineListResult), Integer)(DlOptiFineListBmclapiLoader, 60)
  335. }, Loader.IsForceRestarting)
  336. End Select
  337. End Sub
  338. ''' <summary>
  339. ''' OptiFine 版本列表,官方源。
  340. ''' </summary>
  341. Public DlOptiFineListOfficialLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Official", AddressOf DlOptiFineListOfficialMain)
  342. Private Sub DlOptiFineListOfficialMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
  343. Dim Result As String = NetGetCodeByClient("https://optifine.net/downloads", Encoding.Default)
  344. If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  345. '获取所有版本信息
  346. Dim Forge As List(Of String) = RegexSearch(Result, "(?<=colForge'>)[^<]*")
  347. Dim ReleaseTime As List(Of String) = RegexSearch(Result, "(?<=colDate'>)[^<]+")
  348. Dim Name As List(Of String) = RegexSearch(Result, "(?<=OptiFine_)[0-9A-Za-z_.]+(?=.jar"")")
  349. If Not ReleaseTime.Count = Name.Count Then Throw New Exception("版本与发布时间数据无法对应")
  350. If Not Forge.Count = Name.Count Then Throw New Exception("版本与 Forge 兼容数据无法对应")
  351. If ReleaseTime.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  352. '转化为列表输出
  353. Dim Versions As New List(Of DlOptiFineListEntry)
  354. For i = 0 To ReleaseTime.Count - 1
  355. Name(i) = Name(i).Replace("_", " ")
  356. Dim Entry As New DlOptiFineListEntry With {
  357. .NameDisplay = Name(i).Replace("HD U ", "").Replace(".0 ", " "),
  358. .ReleaseTime = Join({ReleaseTime(i).Split(".")(2), ReleaseTime(i).Split(".")(1), ReleaseTime(i).Split(".")(0)}, "/"),
  359. .IsPreview = Name(i).ContainsF("pre", True),
  360. .Inherit = Name(i).ToString.Split(" ")(0),
  361. .NameFile = If(Name(i).ContainsF("pre", True), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
  362. .RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")}
  363. If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
  364. Entry.NameVersion = Entry.Inherit & "-OptiFine_" & Name(i).ToString.Replace(" ", "_").Replace(Entry.Inherit & "_", "")
  365. Versions.Add(Entry)
  366. Next
  367. Loader.Output = New DlOptiFineListResult With {.IsOfficial = True, .SourceName = "OptiFine 官方源", .Value = Versions}
  368. End Sub
  369. ''' <summary>
  370. ''' OptiFine 版本列表,BMCLAPI。
  371. ''' </summary>
  372. Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
  373. Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
  374. Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList", IsJson:=True)
  375. Try
  376. Dim Versions As New List(Of DlOptiFineListEntry)
  377. For Each Token As JObject In Json
  378. Dim Entry As New DlOptiFineListEntry With {
  379. .NameDisplay = (Token("mcversion").ToString & Token("type").ToString.Replace("HD_U", "").Replace("_", " ") & " " & Token("patch").ToString).Replace(".0 ", " "),
  380. .ReleaseTime = "",
  381. .IsPreview = Token("patch").ToString.ContainsF("pre", True),
  382. .Inherit = Token("mcversion").ToString,
  383. .NameFile = Token("filename").ToString,
  384. .RequiredForgeVersion = If(Token("forge"), "").ToString.Replace("Forge ", "").Replace("#", "")
  385. }
  386. If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
  387. Entry.NameVersion = Entry.Inherit & "-OptiFine_" & (Token("type").ToString & " " & Token("patch").ToString).Replace(".0 ", " ").Replace(" ", "_").Replace(Entry.Inherit & "_", "")
  388. Versions.Add(Entry)
  389. Next
  390. Loader.Output = New DlOptiFineListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Versions}
  391. Catch ex As Exception
  392. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  393. End Try
  394. End Sub
  395. #End Region
  396. #Region "DlForgeList | Forge Minecraft 版本列表"
  397. Public Structure DlForgeListResult
  398. ''' <summary>
  399. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  400. ''' </summary>
  401. Public SourceName As String
  402. ''' <summary>
  403. ''' 是否为官方的实时数据。
  404. ''' </summary>
  405. Public IsOfficial As Boolean
  406. ''' <summary>
  407. ''' 获取到的数据。
  408. ''' </summary>
  409. Public Value As List(Of String)
  410. End Structure
  411. ''' <summary>
  412. ''' Forge 版本列表,主加载器。
  413. ''' </summary>
  414. Public DlForgeListLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Main", AddressOf DlForgeListMain)
  415. Private Sub DlForgeListMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  416. Select Case Setup.Get("ToolDownloadVersion")
  417. Case 0
  418. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  419. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 30),
  420. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 60)
  421. }, Loader.IsForceRestarting)
  422. Case 1
  423. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  424. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 5),
  425. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 35)
  426. }, Loader.IsForceRestarting)
  427. Case Else
  428. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  429. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 60),
  430. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 60)
  431. }, Loader.IsForceRestarting)
  432. End Select
  433. End Sub
  434. ''' <summary>
  435. ''' Forge 版本列表,官方源。
  436. ''' </summary>
  437. Public DlForgeListOfficialLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Official", AddressOf DlForgeListOfficialMain)
  438. Private Sub DlForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  439. Dim Result As String = NetGetCodeByRequestRetry("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html", UseBrowserUserAgent:=True)
  440. If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  441. '获取所有版本信息
  442. Dim Names As List(Of String) = RegexSearch(Result, "(?<=a href=""index_)[0-9.]+(_pre[0-9]?)?(?=.html)")
  443. Names.Add("1.2.4") '1.2.4 不会被匹配上
  444. If Names.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  445. Loader.Output = New DlForgeListResult With {.IsOfficial = True, .SourceName = "Forge 官方源", .Value = Names}
  446. End Sub
  447. ''' <summary>
  448. ''' Forge 版本列表,BMCLAPI。
  449. ''' </summary>
  450. Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
  451. Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  452. Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default)
  453. If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  454. '获取所有版本信息
  455. Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
  456. If Names.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  457. Loader.Output = New DlForgeListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Names}
  458. End Sub
  459. #End Region
  460. #Region "DlForgeVersion | Forge 版本列表"
  461. Public Class DlForgeVersionEntry
  462. ''' <summary>
  463. ''' 完整的版本名,如 “14.22.1.2478”。
  464. ''' </summary>
  465. Public Version As String
  466. ''' <summary>
  467. ''' 对应的 Minecraft 版本,如“1.12.2”。
  468. ''' </summary>
  469. Public Inherit As String
  470. ''' <summary>
  471. ''' 发布时间,格式为“yyyy/MM/dd HH:mm”。
  472. ''' </summary>
  473. Public ReleaseTime As String
  474. ''' <summary>
  475. ''' 文件的 MD5 或 SHA1(BMCLAPI 的老版本是 MD5,新版本是 SHA1;官方源总是 MD5)。
  476. ''' </summary>
  477. Public Hash As String = Nothing
  478. ''' <summary>
  479. ''' 是否为推荐版本。
  480. ''' </summary>
  481. Public IsRecommended As Boolean
  482. ''' <summary>
  483. ''' 安装类型。有 installer、client、universal 三种。
  484. ''' </summary>
  485. Public Category As String
  486. ''' <summary>
  487. ''' 版本分支。若无分支则为 Nothing。
  488. ''' </summary>
  489. Public Branch As String = Nothing
  490. ''' <summary>
  491. ''' 是否为新版 Forge。(即为 Minecraft 1.13+)
  492. ''' </summary>
  493. Public ReadOnly Property IsNewType As Boolean
  494. Get
  495. Return Version.Split(".")(0) >= 20
  496. End Get
  497. End Property
  498. ''' <summary>
  499. ''' 构建数。
  500. ''' </summary>
  501. Public ReadOnly Property Build As Integer
  502. Get
  503. Dim Version = Me.Version.Split(".")
  504. If Version(0) < 15 Then
  505. Return Version(Version.Count - 1)
  506. Else
  507. Return Version(Version.Count - 1) + 10000
  508. End If
  509. End Get
  510. End Property
  511. ''' <summary>
  512. ''' 用于下载的文件版本名。可能在 Version 的基础上添加了分支。
  513. ''' </summary>
  514. Public ReadOnly Property FileVersion As String
  515. Get
  516. Return Version & If(Branch Is Nothing, "", "-" & Branch)
  517. End Get
  518. End Property
  519. ''' <summary>
  520. ''' 即将下载的文件全名。
  521. ''' </summary>
  522. Public ReadOnly Property FileName As String
  523. Get
  524. Return "forge-" & Inherit & "-" & FileVersion & "-" & Category & "." & FileSuffix
  525. End Get
  526. End Property
  527. ''' <summary>
  528. ''' 文件扩展名。
  529. ''' </summary>
  530. Public ReadOnly Property FileSuffix As String
  531. Get
  532. If Category = "installer" Then
  533. Return "jar"
  534. Else
  535. Return "zip"
  536. End If
  537. End Get
  538. End Property
  539. End Class
  540. ''' <summary>
  541. ''' Forge 版本列表,主加载器。
  542. ''' </summary>
  543. Public Sub DlForgeVersionMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
  544. Dim DlForgeVersionOfficialLoader As New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Official", AddressOf DlForgeVersionOfficialMain)
  545. Dim DlForgeVersionBmclapiLoader As New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Bmclapi", AddressOf DlForgeVersionBmclapiMain)
  546. Select Case Setup.Get("ToolDownloadVersion")
  547. Case 0
  548. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  549. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 30),
  550. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 60)
  551. }, Loader.IsForceRestarting)
  552. Case 1
  553. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  554. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 5),
  555. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 35)
  556. }, Loader.IsForceRestarting)
  557. Case Else
  558. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  559. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 60),
  560. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 60)
  561. }, Loader.IsForceRestarting)
  562. End Select
  563. End Sub
  564. ''' <summary>
  565. ''' Forge 版本列表,官方源。
  566. ''' </summary>
  567. Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
  568. Dim Result As String
  569. Try
  570. Result = NetGetCodeByDownload("https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" & Loader.Input & ".html", UseBrowserUserAgent:=True)
  571. Catch ex As Exception
  572. If GetExceptionSummary(ex).Contains("(404)") Then
  573. Throw New Exception("没有可用版本")
  574. Else
  575. Throw
  576. End If
  577. End Try
  578. If Result.Length < 1000 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  579. Dim Versions As New List(Of DlForgeVersionEntry)
  580. Try
  581. '分割版本信息
  582. Dim VersionCodes = Mid(Result, 1, Result.LastIndexOfF("</table>")).Split("<td class=""download-version")
  583. '获取所有版本信息
  584. For i = 1 To VersionCodes.Count - 1
  585. Dim VersionCode = VersionCodes(i)
  586. Try
  587. '基础信息获取
  588. Dim Name As String = RegexSeek(VersionCode, "(?<=[^(0-9)]+)[0-9\.]+")
  589. Dim IsRecommended As Boolean = VersionCode.Contains("fa promo-recommended")
  590. Dim Inherit As String = Loader.Input
  591. '分支获取
  592. Dim Branch As String = RegexSeek(VersionCode, $"(?<=-{Name}-)[^-""]+(?=-[a-z]+.[a-z]{{3}})")
  593. If String.IsNullOrWhiteSpace(Branch) Then Branch = Nothing
  594. If Name = "11.15.1.2318" OrElse Name = "11.15.1.1902" OrElse Name = "11.15.1.1890" Then Branch = "1.8.9"
  595. If Branch Is Nothing AndAlso Inherit = "1.7.10" AndAlso Name.Split(".")(3) >= 1300 Then Branch = "1.7.10"
  596. '发布时间获取
  597. Dim ReleaseTimeOriginal = RegexSeek(VersionCode, "(?<=""download-time"" title="")[^""]+")
  598. Dim ReleaseTimeSplit = ReleaseTimeOriginal.Split(" -:".ToCharArray) '原格式:"2021-02-15 03:24:02"
  599. Dim ReleaseDate As New Date(ReleaseTimeSplit(0), ReleaseTimeSplit(1), ReleaseTimeSplit(2), '年月日
  600. ReleaseTimeSplit(3), ReleaseTimeSplit(4), ReleaseTimeSplit(5), '时分秒
  601. 0, DateTimeKind.Utc) '以 UTC 时间作为标准
  602. Dim ReleaseTime As String = ReleaseDate.ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm") '时区与格式转换
  603. '分类与 MD5 获取
  604. Dim MD5 As String, Category As String
  605. If VersionCode.Contains("classifier-installer""") Then
  606. '类型为 installer.jar,支持范围 ~753 (~ 1.6.1 部分), 738~684 (1.5.2 全部)
  607. VersionCode = VersionCode.Substring(VersionCode.IndexOfF("installer.jar"))
  608. MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
  609. Category = "installer"
  610. ElseIf VersionCode.Contains("classifier-universal""") Then
  611. '类型为 universal.zip,支持范围 751~449 (1.6.1 部分), 682~183 (1.5.1 ~ 1.3.2 部分)
  612. VersionCode = VersionCode.Substring(VersionCode.IndexOfF("universal.zip"))
  613. MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
  614. Category = "universal"
  615. ElseIf VersionCode.Contains("client.zip") Then
  616. '类型为 client.zip,支持范围 182~ (1.3.2 部分 ~)
  617. VersionCode = VersionCode.Substring(VersionCode.IndexOfF("client.zip"))
  618. MD5 = RegexSeek(VersionCode, "(?<=MD5:</strong> )[^<]+")
  619. Category = "client"
  620. Else
  621. '没有任何下载(1.6.4 有一部分这种情况)
  622. Continue For
  623. End If
  624. '添加进列表
  625. Versions.Add(New DlForgeVersionEntry With {.Category = Category, .Version = Name, .IsRecommended = IsRecommended, .Hash = MD5.Trim(vbCr, vbLf), .Inherit = Inherit, .ReleaseTime = ReleaseTime, .Branch = Branch})
  626. Catch ex As Exception
  627. Throw New Exception("版本信息提取失败(" & VersionCode & ")", ex)
  628. End Try
  629. Next
  630. Catch ex As Exception
  631. Throw New Exception("版本列表解析失败(" & Result & ")", ex)
  632. End Try
  633. If Not Versions.Any() Then Throw New Exception("没有可用版本")
  634. Loader.Output = Versions
  635. End Sub
  636. ''' <summary>
  637. ''' Forge 版本列表,BMCLAPI。
  638. ''' </summary>
  639. Public Sub DlForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
  640. Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft/" & Loader.Input, IsJson:=True)
  641. Dim Versions As New List(Of DlForgeVersionEntry)
  642. Try
  643. Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
  644. For Each Token As JObject In Json
  645. '分类与 Hash 获取
  646. Dim Hash As String = Nothing, Category As String = "unknown", Proi As Integer = -1
  647. For Each File As JObject In Token("files")
  648. Select Case File("category").ToString
  649. Case "installer"
  650. If File("format").ToString = "jar" Then
  651. '类型为 installer.jar,支持范围 ~753 (~ 1.6.1 部分), 738~684 (1.5.2 全部)
  652. Hash = File("hash")
  653. Category = "installer"
  654. Proi = 2
  655. End If
  656. Case "universal"
  657. If Proi <= 1 AndAlso File("format").ToString = "zip" Then
  658. '类型为 universal.zip,支持范围 751~449 (1.6.1 部分), 682~183 (1.5.1 ~ 1.3.2 部分)
  659. Hash = File("hash")
  660. Category = "universal"
  661. Proi = 1
  662. End If
  663. Case "client"
  664. If Proi <= 0 AndAlso File("format").ToString = "zip" Then
  665. '类型为 client.zip,支持范围 182~ (1.3.2 部分 ~)
  666. Hash = File("hash")
  667. Category = "client"
  668. Proi = 0
  669. End If
  670. End Select
  671. Next
  672. '获取 Entry
  673. Dim Inherit As String = Loader.Input
  674. Dim Branch As String = Token("branch")
  675. Dim Name As String = Token("version")
  676. If Name = "11.15.1.2318" OrElse Name = "11.15.1.1902" OrElse Name = "11.15.1.1890" Then Branch = "1.8.9"
  677. If Branch Is Nothing AndAlso Inherit = "1.7.10" AndAlso Name.Split(".")(3) >= 1300 Then Branch = "1.7.10"
  678. '基础信息获取
  679. Dim Entry = New DlForgeVersionEntry With {.Hash = Hash, .Category = Category, .Version = Name, .Branch = Branch, .Inherit = Inherit, .IsRecommended = Recommended = Name}
  680. Dim TimeSplit = Token("modified").ToString.Split("-"c, "T"c, ":"c, "."c, " "c, "/"c)
  681. Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy'/'MM'/'dd HH':'mm")
  682. '添加项
  683. Versions.Add(Entry)
  684. Next
  685. Catch ex As Exception
  686. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  687. End Try
  688. If Not Versions.Any() Then Throw New Exception("没有可用版本")
  689. Loader.Output = Versions
  690. End Sub
  691. #End Region
  692. #Region "DlNeoForgeList | NeoForge Minecraft 版本列表"
  693. Public Structure DlNeoForgeListResult
  694. ''' <summary>
  695. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  696. ''' </summary>
  697. Public SourceName As String
  698. ''' <summary>
  699. ''' 是否为官方的实时数据。
  700. ''' </summary>
  701. Public IsOfficial As Boolean
  702. ''' <summary>
  703. ''' 获取到的数据。
  704. ''' </summary>
  705. Public Value As List(Of String)
  706. End Structure
  707. ''' <summary>
  708. ''' NeoForge 版本列表,主加载器。
  709. ''' </summary>
  710. Public DlNeoForgeListLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Main", AddressOf DlNeoForgeListMain)
  711. Private Sub DlNeoForgeListMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
  712. Select Case Setup.Get("ToolDownloadVersion")
  713. Case 0
  714. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)) From {
  715. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListBmclapiLoader, 30),
  716. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListOfficialLoader, 60)
  717. }, Loader.IsForceRestarting)
  718. Case 1
  719. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)) From {
  720. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListOfficialLoader, 5),
  721. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListBmclapiLoader, 35)
  722. }, Loader.IsForceRestarting)
  723. Case Else
  724. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)) From {
  725. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListOfficialLoader, 60),
  726. New KeyValuePair(Of LoaderTask(Of Integer, DlNeoForgeListResult), Integer)(DlNeoForgeListBmclapiLoader, 60)
  727. }, Loader.IsForceRestarting)
  728. End Select
  729. End Sub
  730. ''' <summary>
  731. ''' NeoForge 版本列表,官方源。
  732. ''' </summary>
  733. Public DlNeoForgeListOfficialLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Official", AddressOf DlNeoForgeListOfficialMain)
  734. Private Sub DlNeoForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
  735. Dim ResultLatest As String
  736. Dim VersionsJArray As JArray
  737. Try
  738. ResultLatest = NetGetCodeByDownload("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True)
  739. Catch ex As Exception
  740. If GetExceptionSummary(ex).Contains("(404)") Then
  741. Throw New Exception("没有可用版本")
  742. Else
  743. Throw
  744. End If
  745. End Try
  746. If ResultLatest.Length < 50 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
  747. VersionsJArray = GetJson(ResultLatest)("versions")
  748. Dim Versions As List(Of String) = New List(Of String)
  749. Versions.Add("1.20.1")
  750. Try
  751. For Each Token As String In VersionsJArray
  752. Dim Version As String = Token.ToString().Replace("neoforge-", "")
  753. Dim Inherit As String = "1." & Version.ToString().Split(".")(0) & "." & Token.ToString().Split(".")(1)
  754. If Inherit.EndsWith(".0") Then
  755. Inherit = Inherit.Replace(".0", "")
  756. End If
  757. If Versions.Contains(Inherit) Then
  758. Continue For
  759. Else
  760. Versions.Add(Inherit)
  761. End If
  762. Next
  763. Catch ex As Exception
  764. MyMsgBox(ex.ToString(), "错误")
  765. Throw New Exception("版本列表解析失败(" & VersionsJArray.ToString & ")", ex)
  766. End Try
  767. If Not Versions.Any() Then Throw New Exception("没有可用版本")
  768. Loader.Output = New DlNeoForgeListResult With {.IsOfficial = True, .SourceName = "NeoForge 官方源", .Value = Versions}
  769. End Sub
  770. ''' <summary>
  771. ''' NeoForge 版本列表,BMCLAPI。
  772. ''' </summary>
  773. Public DlNeoForgeListBmclapiLoader As New LoaderTask(Of Integer, DlNeoForgeListResult)("DlNeoForgeList Bmclapi", AddressOf DlNeoForgeListBmclapiMain)
  774. Private Sub DlNeoForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlNeoForgeListResult))
  775. Dim Versions As List(Of String) = New List(Of String)
  776. Dim ClientVersions As JArray = DlClientListLoader.Output.Value("versions") 'BMCLAPI 不能返回所有 NeoForge 版本,获取 MC 版本列表进行遍历
  777. Dim ClientReleases As List(Of String) = New List(Of String)
  778. For Each Client In ClientVersions '排除快照版或远古版等特殊版本
  779. Dim ClientVersionString = Client("id").ToString()
  780. If Not (ClientVersionString.ContainsF("a") OrElse ClientVersionString.ContainsF("b") OrElse ClientVersionString.ContainsF("c") OrElse ClientVersionString.ContainsF("w") OrElse ClientVersionString.ContainsF("pre") OrElse ClientVersionString.ContainsF("Pre") OrElse ClientVersionString.ContainsF("rc") OrElse ClientVersionString.ContainsF("b") OrElse ClientVersionString.ContainsF("rd") OrElse ClientVersionString.ContainsF("inf")) Then
  781. ClientReleases.Add(ClientVersionString)
  782. End If
  783. Next
  784. For Each Release In ClientReleases
  785. If Int(Release.Split(".")(1)) > 19 Then '如果游戏版本低于 1.20 就不进行请求
  786. Dim Json As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/neoforge/list/" & Release)
  787. If Not Json.Contains("[]") Then '如果这个版本不支持 NeoForge,BMCLAPI 会直接返回一个空的 Json 序列,这里直接用字符串处理了
  788. Versions.Add(Release)
  789. End If
  790. End If
  791. Next
  792. Loader.Output = New DlNeoForgeListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Versions}
  793. End Sub
  794. #End Region
  795. #Region "DlNeoForgeVersion | NeoForge 版本列表"
  796. Public Class DlNeoForgeVersionEntry
  797. ''' <summary>
  798. ''' 完整的版本名,如 “neoforge-20.4.30-beta”。
  799. ''' </summary>
  800. Public Version As String
  801. ''' <summary>
  802. ''' 对应的 Minecraft 版本,如“1.12.2”。
  803. ''' </summary>
  804. Public Inherit As String
  805. ''' <summary>
  806. ''' 发布时间,格式为“yyyy/MM/dd HH:mm”。
  807. ''' </summary>
  808. Public ReleaseTime As String
  809. ''' <summary>
  810. ''' 文件的 MD5 或 SHA1(BMCLAPI 的老版本是 MD5,新版本是 SHA1;官方源总是 MD5)。
  811. ''' </summary>
  812. Public Hash As String = Nothing
  813. ''' <summary>
  814. ''' 安装类型。有 installer、client、universal 三种。
  815. ''' </summary>
  816. Public Category As String
  817. ''' <summary>
  818. ''' 版本分支。若无分支则为 Nothing。
  819. ''' </summary>
  820. Public Branch As String = Nothing
  821. ''' <summary>
  822. ''' 是否是 Beta 版。若未提供则默认为 False。
  823. ''' </summary>
  824. Public IsBeta As Boolean = False
  825. ''' <summary>
  826. ''' 标准的版本号,如 “20.4.30”。
  827. ''' </summary>
  828. Public StdVersion As String
  829. Public Structure DlNeoForgeListResult
  830. ''' <summary>
  831. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  832. ''' </summary>
  833. Public SourceName As String
  834. ''' <summary>
  835. ''' 是否为官方的实时数据。
  836. ''' </summary>
  837. Public IsOfficial As Boolean
  838. ''' <summary>
  839. ''' 获取到的数据。
  840. ''' </summary>
  841. Public Value As JObject
  842. End Structure
  843. ''' <summary>
  844. ''' 构建数。
  845. ''' </summary>
  846. Public ReadOnly Property Build As Integer
  847. Get
  848. Dim Version = Me.Version.Split(".")
  849. If Version(0) < 15 Then
  850. Return Version(Version.Count - 1)
  851. Else
  852. Return Version(Version.Count - 1) + 10000
  853. End If
  854. End Get
  855. End Property
  856. ''' <summary>
  857. ''' 用于下载的文件版本名。可能在 Version 的基础上添加了分支。
  858. ''' </summary>
  859. Public ReadOnly Property FileVersion As String
  860. Get
  861. Return Version & If(Branch Is Nothing, "", "-" & Branch)
  862. End Get
  863. End Property
  864. ''' <summary>
  865. ''' 即将下载的文件全名。
  866. ''' </summary>
  867. Public ReadOnly Property FileName As String
  868. Get
  869. If StdVersion.StartsWith("47.") Then 'NeoForge 1.20.1 的版本命名有些特殊...
  870. Return "forge-" & "1.20.1" & "-" & StdVersion & If(IsBeta, "-beta", "") & "-" & "installer" & "." & "jar"
  871. Else
  872. Return "neoforge-" & StdVersion & If(IsBeta, "-beta", "") & "-" & "installer" & "." & "jar"
  873. End If
  874. End Get
  875. End Property
  876. ''' <summary>
  877. ''' 文件扩展名。
  878. ''' </summary>
  879. Public FileSuffix As String = "jar"
  880. End Class
  881. ''' <summary>
  882. ''' NeoForge 版本列表,主加载器。
  883. ''' </summary>
  884. Public Sub DlNeoForgeVersionMain(Loader As LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)))
  885. Dim DlNeoForgeVersionOfficialLoader As New LoaderTask(Of String, List(Of DlNeoForgeVersionEntry))("DlNeoForgeVersion Official", AddressOf DlNeoForgeVersionOfficialMain)
  886. Dim DlNeoForgeVersionBmclapiLoader As New LoaderTask(Of String, List(Of DlNeoForgeVersionEntry))("DlNeoForgeVersion Bmclapi", AddressOf DlNeoForgeVersionBmclapiMain)
  887. Select Case Setup.Get("ToolDownloadVersion")
  888. Case 0
  889. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)) From {
  890. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionBmclapiLoader, 30),
  891. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionOfficialLoader, 60)
  892. }, Loader.IsForceRestarting)
  893. Case 1
  894. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)) From {
  895. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionOfficialLoader, 5),
  896. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionBmclapiLoader, 35)
  897. }, Loader.IsForceRestarting)
  898. Case Else
  899. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)) From {
  900. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionOfficialLoader, 60),
  901. New KeyValuePair(Of LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)), Integer)(DlNeoForgeVersionBmclapiLoader, 60)
  902. }, Loader.IsForceRestarting)
  903. End Select
  904. End Sub
  905. ''' <summary>
  906. ''' NeoForge 版本列表,官方源。
  907. ''' </summary>
  908. Public Sub DlNeoForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)))
  909. Dim ResultLatest As String
  910. Dim ResultLegacy As String
  911. Dim ResultLatestJson As JObject
  912. Dim ResultLegacyJson As JObject
  913. Dim VersionsJArray As JArray
  914. Dim IsLegacyNeo As Boolean = False
  915. Try
  916. ResultLatest = NetGetCodeByDownload("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge", UseBrowserUserAgent:=True)
  917. ResultLegacy = NetGetCodeByDownload("https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge", UseBrowserUserAgent:=True)
  918. Catch ex As Exception
  919. If GetExceptionSummary(ex).Contains("(404)") Then
  920. Throw New Exception("没有可用版本")
  921. Else
  922. Throw
  923. End If
  924. End Try
  925. If ResultLatest.Length < 50 AndAlso ResultLegacy.Length < 50 Then Throw New Exception("获取到的版本列表长度不足(" & ResultLatest & ")")
  926. ResultLatestJson = GetJson(ResultLatest)
  927. ResultLegacyJson = GetJson(ResultLegacy)
  928. If Loader.Input IsNot Nothing Then
  929. If Loader.Input.ToString().Contains("1.20.1") Then
  930. VersionsJArray = ResultLegacyJson("versions")
  931. IsLegacyNeo = True
  932. Else
  933. VersionsJArray = ResultLatestJson("versions")
  934. End If
  935. Else
  936. VersionsJArray = ResultLatestJson("versions")
  937. End If
  938. Dim Versions As New List(Of DlNeoForgeVersionEntry)
  939. Try
  940. For Each Token As String In VersionsJArray
  941. Dim StdVersion As String
  942. Dim IsBeta As Boolean
  943. Dim rawVersion As String = Token
  944. If rawVersion.Contains("-beta") Then
  945. StdVersion = Token.Replace("neoforge-", "").Replace("-beta", "").Replace("1.20.1-", "")
  946. IsBeta = True
  947. Else
  948. StdVersion = Token.Replace("neoforge-", "").Replace("1.20.1-", "")
  949. IsBeta = False
  950. End If
  951. Dim Inherit As String = "1." & StdVersion.Split(".")(0) & "." & StdVersion.Split(".")(1)
  952. Inherit = Inherit.Replace(".0", "")
  953. If IsLegacyNeo Then Inherit = "1.20.1"
  954. Dim Entry = New DlNeoForgeVersionEntry
  955. If Loader.Input IsNot Nothing Then
  956. If IsLegacyNeo Then
  957. Entry = New DlNeoForgeVersionEntry With {.Version = rawVersion, .Inherit = Inherit, .IsBeta = IsBeta, .StdVersion = StdVersion}
  958. Versions.Add(Entry)
  959. Else
  960. If Inherit.Contains(Loader.Input.ToString().Replace("1.", "")) Then
  961. Entry = New DlNeoForgeVersionEntry With {.Version = rawVersion, .Inherit = Inherit, .IsBeta = IsBeta, .StdVersion = StdVersion}
  962. Versions.Add(Entry)
  963. End If
  964. End If
  965. Else
  966. Entry = New DlNeoForgeVersionEntry With {.Version = rawVersion, .Inherit = Inherit, .IsBeta = IsBeta, .StdVersion = StdVersion}
  967. Versions.Add(Entry)
  968. End If
  969. Next
  970. Catch ex As Exception
  971. MyMsgBox(ex.ToString(), "错误")
  972. Throw New Exception("版本列表解析失败(" & VersionsJArray.ToString & ")", ex)
  973. End Try
  974. If Not Versions.Any() Then Throw New Exception("没有可用版本")
  975. Dim VersionsArray = Versions.ToList()
  976. VersionsArray.Reverse()
  977. Loader.Output = VersionsArray
  978. End Sub
  979. ''' <summary>
  980. ''' NeoForge 版本列表,BMCLAPI。
  981. ''' </summary>
  982. Public Sub DlNeoForgeVersionBmclapiMain(Loader As LoaderTask(Of String, List(Of DlNeoForgeVersionEntry)))
  983. Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/neoforge/list/" & Loader.Input, IsJson:=True)
  984. Dim Versions As New List(Of DlNeoForgeVersionEntry)
  985. Try
  986. For Each Token As JObject In Json
  987. Dim StdVersion As String
  988. Dim IsBeta As Boolean
  989. Dim rawVersion As String = Token("rawVersion")
  990. If rawVersion.Contains("-beta") Then
  991. StdVersion = Token("version").ToString().Replace("neoforge-", "").Replace("-beta", "").Replace("1.20.1-", "")
  992. IsBeta = True
  993. Else
  994. StdVersion = Token("version").ToString().Replace("neoforge-", "").Replace("1.20.1-", "")
  995. IsBeta = False
  996. End If
  997. Dim Inherit As String = Token("mcversion")
  998. Dim Entry = New DlNeoForgeVersionEntry With {.Version = rawVersion, .Inherit = Inherit, .IsBeta = IsBeta, .StdVersion = StdVersion}
  999. Versions.Add(Entry)
  1000. Next
  1001. Catch ex As Exception
  1002. MyMsgBox(ex.ToString(), "错误")
  1003. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  1004. End Try
  1005. If Not Versions.Any() Then Throw New Exception("没有可用版本")
  1006. Dim VersionsArray = Versions.ToList()
  1007. VersionsArray.Reverse()
  1008. Loader.Output = VersionsArray
  1009. End Sub
  1010. #End Region
  1011. #Region "DlLiteLoaderList | LiteLoader 版本列表"
  1012. Public Structure DlLiteLoaderListResult
  1013. ''' <summary>
  1014. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  1015. ''' </summary>
  1016. Public SourceName As String
  1017. ''' <summary>
  1018. ''' 是否为官方的实时数据。
  1019. ''' </summary>
  1020. Public IsOfficial As Boolean
  1021. ''' <summary>
  1022. ''' 获取到的数据。
  1023. ''' </summary>
  1024. Public Value As List(Of DlLiteLoaderListEntry)
  1025. ''' <summary>
  1026. ''' 官方源的失败原因。若没有则为 Nothing。
  1027. ''' </summary>
  1028. Public OfficialError As Exception
  1029. End Structure
  1030. Public Class DlLiteLoaderListEntry
  1031. ''' <summary>
  1032. ''' 实际的文件名,如“liteloader-installer-1.12-00-SNAPSHOT.jar”。
  1033. ''' </summary>
  1034. Public FileName As String
  1035. ''' <summary>
  1036. ''' 是否为测试版。
  1037. ''' </summary>
  1038. Public IsPreview As Boolean
  1039. ''' <summary>
  1040. ''' 对应的 Minecraft 版本,如“1.12.2”。
  1041. ''' </summary>
  1042. Public Inherit As String
  1043. ''' <summary>
  1044. ''' 是否为 1.7 及更早的远古版。
  1045. ''' </summary>
  1046. Public IsLegacy As Boolean
  1047. ''' <summary>
  1048. ''' 发布时间,格式为“yyyy/mm/dd HH:mm”。
  1049. ''' </summary>
  1050. Public ReleaseTime As String
  1051. ''' <summary>
  1052. ''' 文件的 MD5。
  1053. ''' </summary>
  1054. Public MD5 As String
  1055. ''' <summary>
  1056. ''' 对应的 Json 项。
  1057. ''' </summary>
  1058. Public JsonToken As JToken
  1059. End Class
  1060. ''' <summary>
  1061. ''' LiteLoader 版本列表,主加载器。
  1062. ''' </summary>
  1063. Public DlLiteLoaderListLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Main", AddressOf DlLiteLoaderListMain)
  1064. Private Sub DlLiteLoaderListMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  1065. Select Case Setup.Get("ToolDownloadVersion")
  1066. Case 0
  1067. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  1068. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 30),
  1069. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 60)
  1070. }, Loader.IsForceRestarting)
  1071. Case 1
  1072. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  1073. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 5),
  1074. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 35)
  1075. }, Loader.IsForceRestarting)
  1076. Case Else
  1077. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  1078. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 60),
  1079. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 60)
  1080. }, Loader.IsForceRestarting)
  1081. End Select
  1082. End Sub
  1083. ''' <summary>
  1084. ''' LiteLoader 版本列表,官方源。
  1085. ''' </summary>
  1086. Public DlLiteLoaderListOfficialLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Official", AddressOf DlLiteLoaderListOfficialMain)
  1087. Private Sub DlLiteLoaderListOfficialMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  1088. Dim Result As JObject = NetGetCodeByRequestRetry("https://dl.liteloader.com/versions/versions.json", IsJson:=True)
  1089. Try
  1090. Dim Json As JObject = Result("versions")
  1091. Dim Versions As New List(Of DlLiteLoaderListEntry)
  1092. For Each Pair As KeyValuePair(Of String, JToken) In Json
  1093. If Pair.Key.StartsWithF("1.6") OrElse Pair.Key.StartsWithF("1.5") Then Continue For
  1094. Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
  1095. Versions.Add(New DlLiteLoaderListEntry With {
  1096. .Inherit = Pair.Key,
  1097. .IsLegacy = Pair.Key.Split(".")(1) < 8,
  1098. .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
  1099. .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
  1100. .MD5 = RealEntry("md5"),
  1101. .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
  1102. .JsonToken = RealEntry
  1103. })
  1104. Next
  1105. Loader.Output = New DlLiteLoaderListResult With {.IsOfficial = True, .SourceName = "LiteLoader 官方源", .Value = Versions}
  1106. Catch ex As Exception
  1107. Throw New Exception("版本列表解析失败(" & Result.ToString & ")", ex)
  1108. End Try
  1109. End Sub
  1110. ''' <summary>
  1111. ''' LiteLoader 版本列表,BMCLAPI。
  1112. ''' </summary>
  1113. Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
  1114. Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  1115. Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
  1116. Try
  1117. Dim Json As JObject = Result("versions")
  1118. Dim Versions As New List(Of DlLiteLoaderListEntry)
  1119. For Each Pair As KeyValuePair(Of String, JToken) In Json
  1120. If Pair.Key.StartsWithF("1.6") OrElse Pair.Key.StartsWithF("1.5") Then Continue For
  1121. Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
  1122. Versions.Add(New DlLiteLoaderListEntry With {
  1123. .Inherit = Pair.Key,
  1124. .IsLegacy = Pair.Key.Split(".")(1) < 8,
  1125. .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
  1126. .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
  1127. .MD5 = RealEntry("md5"),
  1128. .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy'/'MM'/'dd HH':'mm"),
  1129. .JsonToken = RealEntry
  1130. })
  1131. Next
  1132. Loader.Output = New DlLiteLoaderListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Versions}
  1133. Catch ex As Exception
  1134. Throw New Exception("版本列表解析失败(" & Result.ToString & ")", ex)
  1135. End Try
  1136. End Sub
  1137. #End Region
  1138. #Region "DlFabricList | Fabric 列表"
  1139. Public Structure DlFabricListResult
  1140. ''' <summary>
  1141. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  1142. ''' </summary>
  1143. Public SourceName As String
  1144. ''' <summary>
  1145. ''' 是否为官方的实时数据。
  1146. ''' </summary>
  1147. Public IsOfficial As Boolean
  1148. ''' <summary>
  1149. ''' 获取到的数据。
  1150. ''' </summary>
  1151. Public Value As JObject
  1152. End Structure
  1153. ''' <summary>
  1154. ''' Fabric 列表,主加载器。
  1155. ''' </summary>
  1156. Public DlFabricListLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Main", AddressOf DlFabricListMain)
  1157. Private Sub DlFabricListMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  1158. Select Case Setup.Get("ToolDownloadVersion")
  1159. Case 0
  1160. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  1161. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 30),
  1162. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 60)
  1163. }, Loader.IsForceRestarting)
  1164. Case 1
  1165. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  1166. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 5),
  1167. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 35)
  1168. }, Loader.IsForceRestarting)
  1169. Case Else
  1170. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  1171. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 60),
  1172. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 60)
  1173. }, Loader.IsForceRestarting)
  1174. End Select
  1175. End Sub
  1176. ''' <summary>
  1177. ''' Fabric 列表,官方源。
  1178. ''' </summary>
  1179. Public DlFabricListOfficialLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Official", AddressOf DlFabricListOfficialMain)
  1180. Private Sub DlFabricListOfficialMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  1181. Dim Result As JObject = NetGetCodeByRequestRetry("https://meta.fabricmc.net/v2/versions", IsJson:=True)
  1182. Try
  1183. Dim Output = New DlFabricListResult With {.IsOfficial = True, .SourceName = "Fabric 官方源", .Value = Result}
  1184. If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
  1185. Loader.Output = Output
  1186. Catch ex As Exception
  1187. Throw New Exception("列表解析失败(" & Result.ToString & ")", ex)
  1188. End Try
  1189. End Sub
  1190. ''' <summary>
  1191. ''' Fabric 列表,BMCLAPI。
  1192. ''' </summary>
  1193. Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
  1194. Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  1195. Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions", IsJson:=True)
  1196. Try
  1197. Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
  1198. If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
  1199. Loader.Output = Output
  1200. Catch ex As Exception
  1201. Throw New Exception("列表解析失败(" & Result.ToString & ")", ex)
  1202. End Try
  1203. End Sub
  1204. ''' <summary>
  1205. ''' Fabric API 列表,官方源。
  1206. ''' </summary>
  1207. Public DlFabricApiLoader As New LoaderTask(Of Integer, List(Of CompFile))("Fabric API List Loader",
  1208. Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet("fabric-api", False))
  1209. ''' <summary>
  1210. ''' OptiFabric 列表,官方源。
  1211. ''' </summary>
  1212. Public DlOptiFabricLoader As New LoaderTask(Of Integer, List(Of CompFile))("OptiFabric List Loader",
  1213. Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet("322385", True))
  1214. #End Region
  1215. #Region "DlSource | 镜像下载源"
  1216. Public Function DlSourceResourceGet(MojangBase As String) As String()
  1217. MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
  1218. Return {
  1219. MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/assets").Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets"),
  1220. MojangBase
  1221. }
  1222. End Function
  1223. Public Function DlSourceLibraryGet(MojangBase As String) As String()
  1224. Return {
  1225. MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/maven").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
  1226. MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com/libraries").Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries"),
  1227. MojangBase
  1228. }
  1229. End Function
  1230. Public Function DlSourceLauncherOrMetaGet(MojangBase As String) As String()
  1231. If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
  1232. Return {
  1233. MojangBase.Replace("https://piston-data.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://piston-meta.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com"),
  1234. MojangBase
  1235. }
  1236. End Function
  1237. Private Sub DlSourceLoader(Of InputType, OutputType)(MainLoader As LoaderTask(Of InputType, OutputType),
  1238. LoaderList As List(Of KeyValuePair(Of LoaderTask(Of InputType, OutputType), Integer)),
  1239. Optional IsForceRestart As Boolean = False)
  1240. Dim WaitCycle As Integer = 0
  1241. Do While True
  1242. '检查加载结束
  1243. Dim IsAllFailed As Boolean = True
  1244. For Each SubLoader In LoaderList
  1245. If WaitCycle = 0 Then
  1246. '如果要强制刷新,就不使用已经加载好的值
  1247. If IsForceRestart Then Exit For
  1248. '如果输入不一样,就不使用已经加载好的值
  1249. If (SubLoader.Key.Input Is Nothing Xor MainLoader.Input Is Nothing) OrElse
  1250. (SubLoader.Key.Input IsNot Nothing AndAlso Not SubLoader.Key.Input.Equals(MainLoader.Input)) Then Continue For
  1251. End If
  1252. If SubLoader.Key.State <> LoadState.Failed Then IsAllFailed = False
  1253. If SubLoader.Key.State = LoadState.Finished Then
  1254. MainLoader.Output = SubLoader.Key.Output
  1255. DlSourceLoaderAbort(LoaderList)
  1256. Exit Sub
  1257. ElseIf IsAllFailed Then
  1258. If WaitCycle < SubLoader.Value * 100 Then WaitCycle = SubLoader.Value * 100
  1259. End If
  1260. '由于 Forge BMCLAPI 没有可用版本导致强制失败
  1261. '在没有可用版本时,官方源会一直卡住,直接使用 BMCLAPI 判定失败即可
  1262. If SubLoader.Key.Error IsNot Nothing AndAlso SubLoader.Key.Error.Message.Contains("没有可用版本") Then
  1263. For Each SubLoader2 In LoaderList
  1264. If WaitCycle < SubLoader2.Value * 100 Then WaitCycle = SubLoader2.Value * 100
  1265. Next
  1266. End If
  1267. Next
  1268. '启动加载源
  1269. If WaitCycle = 0 Then
  1270. '启动第一个源
  1271. LoaderList(0).Key.Start(MainLoader.Input, IsForceRestart)
  1272. '将其他源标记为未启动,以确保可以切换下载源(#184)
  1273. For i = 1 To LoaderList.Count - 1
  1274. LoaderList(i).Key.State = LoadState.Waiting
  1275. Next
  1276. End If
  1277. For i = 0 To LoaderList.Count - 1
  1278. If WaitCycle = LoaderList(i).Value * 100 Then
  1279. If i < LoaderList.Count - 1 Then
  1280. '启动下一个源
  1281. LoaderList(i + 1).Key.Start(MainLoader.Input, IsForceRestart)
  1282. Else
  1283. '失败
  1284. Dim ErrorInfo As Exception = Nothing
  1285. For ii = 0 To LoaderList.Count - 1
  1286. LoaderList(ii).Key.Input = Nothing '重置输入,以免以同样的输入“重试加载”时直接失败
  1287. If LoaderList(ii).Key.Error IsNot Nothing Then
  1288. If ErrorInfo Is Nothing OrElse LoaderList(ii).Key.Error.Message.Contains("没有可用版本") Then
  1289. ErrorInfo = LoaderList(ii).Key.Error
  1290. End If
  1291. End If
  1292. Next
  1293. If ErrorInfo Is Nothing Then ErrorInfo = New TimeoutException("下载源连接超时")
  1294. DlSourceLoaderAbort(LoaderList)
  1295. Throw ErrorInfo
  1296. End If
  1297. Exit For
  1298. End If
  1299. Next
  1300. '计时
  1301. If MainLoader.IsAborted Then
  1302. DlSourceLoaderAbort(LoaderList)
  1303. Exit Sub
  1304. End If
  1305. Thread.Sleep(10)
  1306. WaitCycle += 1
  1307. Loop
  1308. End Sub
  1309. Private Sub DlSourceLoaderAbort(Of InputType, OutputType)(LoaderList As List(Of KeyValuePair(Of LoaderTask(Of InputType, OutputType), Integer)))
  1310. For Each Loader In LoaderList
  1311. If Loader.Key.State = LoadState.Loading Then Loader.Key.Abort()
  1312. Next
  1313. End Sub
  1314. #End Region
  1315. End Module