ModDownload.vb 69 KB

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