ModDownload.vb 71 KB

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