ModDownload.vb 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. Public Module ModDownload
  2. #Region "DlClient* | Minecraft 客户端"
  3. ''' <summary>
  4. ''' 返回某 Minecraft 版本对应的原版主 Jar 文件的下载信息,要求对应依赖版本已存在。
  5. ''' 失败则抛出异常,不需要下载则返回 Nothing。
  6. ''' </summary>
  7. Public Function DlClientJarGet(Version As McVersion, ReturnNothingOnFileUseable As Boolean) As NetFile
  8. '获取底层继承版本
  9. Do While Not String.IsNullOrEmpty(Version.InheritVersion)
  10. Version = New McVersion(Version.InheritVersion)
  11. Loop
  12. '检查 Json 是否标准
  13. If Version.JsonObject("downloads") Is Nothing OrElse Version.JsonObject("downloads")("client") Is Nothing OrElse Version.JsonObject("downloads")("client")("url") Is Nothing Then
  14. Throw New Exception("底层版本 " & Version.Name & " 中无 jar 文件下载信息")
  15. End If
  16. '检查文件
  17. Dim Checker As New FileChecker(MinSize:=1024, ActualSize:=If(Version.JsonObject("downloads")("client")("size"), -1), Hash:=Version.JsonObject("downloads")("client")("sha1"))
  18. If ReturnNothingOnFileUseable Then
  19. '是否跳过
  20. Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
  21. Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
  22. Case 0 '使用全局设置
  23. Case 1 '开启
  24. IsSetupSkip = False
  25. Case 2 '关闭
  26. IsSetupSkip = True
  27. End Select
  28. If IsSetupSkip AndAlso File.Exists(Version.Path & Version.Name & ".jar") Then Return Nothing '跳过校验
  29. If Checker.Check(Version.Path & Version.Name & ".jar") Is Nothing Then Return Nothing '通过校验
  30. End If
  31. '返回下载信息
  32. Dim JarUrl As String = Version.JsonObject("downloads")("client")("url")
  33. Return New NetFile(DlSourceLauncherOrMetaGet(JarUrl), Version.Path & Version.Name & ".jar", Checker)
  34. End Function
  35. ''' <summary>
  36. ''' 返回某 Minecraft 版本对应的原版主 AssetIndex 文件的下载信息,要求对应依赖版本已存在。
  37. ''' 若未找到,则会返回 Legacy 资源文件或 Nothing。
  38. ''' </summary>
  39. Public Function DlClientAssetIndexGet(Version As McVersion) As NetFile
  40. '获取底层继承版本
  41. Do While Not String.IsNullOrEmpty(Version.InheritVersion)
  42. Version = New McVersion(Version.InheritVersion)
  43. Loop
  44. '获取信息
  45. Dim IndexInfo = McAssetsGetIndex(Version, True, True)
  46. Dim IndexAddress As String = PathMcFolder & "assets\indexes\" & IndexInfo("id").ToString & ".json"
  47. Log("[Download] 版本 " & Version.Name & " 对应的资源文件索引为 " & IndexInfo("id").ToString)
  48. Dim IndexUrl As String = If(IndexInfo("url"), "")
  49. If IndexUrl = "" Then
  50. Return Nothing
  51. Else
  52. Return New NetFile(DlSourceLauncherOrMetaGet(IndexUrl, False), IndexAddress, New FileChecker(CanUseExistsFile:=False, IsJson:=True))
  53. End If
  54. End Function
  55. ''' <summary>
  56. ''' 构造补全某 Minecraft 版本的所有文件的加载器列表。失败会抛出异常。
  57. ''' </summary>
  58. Public Function DlClientFix(Version As McVersion, CheckAssetsHash As Boolean, AssetsIndexBehaviour As AssetsIndexExistsBehaviour, SkipAssetsDownloadWhileSetupRequired As Boolean) As List(Of LoaderBase)
  59. Dim Loaders As New List(Of LoaderBase)
  60. #Region "下载支持库文件"
  61. Dim LoadersLib As New List(Of LoaderBase) From {
  62. New LoaderTask(Of String, List(Of NetFile))("分析缺失支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(Version)) With {.ProgressWeight = 1},
  63. New LoaderDownload("下载支持库文件", New List(Of NetFile)) With {.ProgressWeight = 15}
  64. }
  65. '构造加载器
  66. Loaders.Add(New LoaderCombo(Of String)("下载支持库文件(主加载器)", LoadersLib) With {.Block = False, .Show = False, .ProgressWeight = 16})
  67. #End Region
  68. #Region "下载资源文件"
  69. Dim IsSetupSkip As Boolean = Setup.Get("LaunchAdvanceAssets")
  70. Select Case Setup.Get("VersionAdvanceAssets", Version:=Version)
  71. Case 0
  72. '使用全局设置
  73. Case 1
  74. '开启
  75. IsSetupSkip = False
  76. Case 2
  77. '关闭
  78. IsSetupSkip = True
  79. End Select
  80. If IsSetupSkip Then Log("[Download] 已跳过 Assets 下载")
  81. If (Not SkipAssetsDownloadWhileSetupRequired) OrElse Not IsSetupSkip Then
  82. Dim LoadersAssets As New List(Of LoaderBase)
  83. '获取资源文件索引地址
  84. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址",
  85. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  86. Try
  87. Dim IndexFile = DlClientAssetIndexGet(Version)
  88. Dim IndexFileInfo As New FileInfo(IndexFile.LocalPath)
  89. If AssetsIndexBehaviour <> AssetsIndexExistsBehaviour.AlwaysDownload AndAlso IndexFile.Check.Check(IndexFile.LocalPath) Is Nothing Then
  90. Task.Output = New List(Of NetFile)
  91. Else
  92. Task.Output = New List(Of NetFile) From {IndexFile}
  93. End If
  94. Catch ex As Exception
  95. Throw New Exception("分析资源文件索引地址失败", ex)
  96. End Try
  97. End Sub) With {.ProgressWeight = 0.5, .Show = False})
  98. '下载资源文件索引
  99. LoadersAssets.Add(New LoaderDownload("下载资源文件索引", New List(Of NetFile)) With {.ProgressWeight = 2})
  100. '要求独立更新索引
  101. If AssetsIndexBehaviour = AssetsIndexExistsBehaviour.DownloadInBackground Then
  102. Dim LoadersAssetsUpdate As New List(Of LoaderBase)
  103. Dim TempAddress As String = Nothing
  104. Dim RealAddress As String = Nothing
  105. LoadersAssetsUpdate.Add(New LoaderTask(Of String, List(Of NetFile))("后台分析资源文件索引地址", Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  106. Dim BackAssetsFile As NetFile = DlClientAssetIndexGet(Version)
  107. RealAddress = BackAssetsFile.LocalPath
  108. TempAddress = PathTemp & "Cache\" & BackAssetsFile.LocalName
  109. BackAssetsFile.LocalPath = TempAddress
  110. Task.Output = New List(Of NetFile) From {BackAssetsFile}
  111. End Sub))
  112. LoadersAssetsUpdate.Add(New LoaderDownload("后台下载资源文件索引", New List(Of NetFile)))
  113. LoadersAssetsUpdate.Add(New LoaderTask(Of List(Of NetFile), String)("后台复制资源文件索引", Sub(Task As LoaderTask(Of List(Of NetFile), String))
  114. CopyFile(TempAddress, RealAddress)
  115. McLaunchLog("后台更新资源文件索引成功:" & TempAddress)
  116. End Sub))
  117. Dim Updater As New LoaderCombo(Of String)("后台更新资源文件索引", LoadersAssetsUpdate)
  118. Log("[Download] 开始后台更新资源文件索引")
  119. Updater.Start()
  120. End If
  121. '获取资源文件地址
  122. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析缺失资源文件",
  123. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  124. Task.Output = McAssetsFixList(McAssetsGetIndexName(Version), CheckAssetsHash, Task)
  125. End Sub) With {.ProgressWeight = 3})
  126. '下载资源文件
  127. LoadersAssets.Add(New LoaderDownload("下载资源文件", New List(Of NetFile)) With {.ProgressWeight = 25})
  128. '构造加载器
  129. Loaders.Add(New LoaderCombo(Of String)("下载资源文件(主加载器)", LoadersAssets) With {.Block = False, .Show = False, .ProgressWeight = 30.5})
  130. End If
  131. #End Region
  132. Return Loaders
  133. End Function
  134. Public Enum AssetsIndexExistsBehaviour
  135. ''' <summary>
  136. ''' 如果文件存在,则不进行下载。
  137. ''' </summary>
  138. DontDownload
  139. ''' <summary>
  140. ''' 如果文件存在,则启动新的下载加载器进行独立的更新。
  141. ''' </summary>
  142. DownloadInBackground
  143. ''' <summary>
  144. ''' 如果文件存在,也同样进行下载。
  145. ''' </summary>
  146. AlwaysDownload
  147. End Enum
  148. #End Region
  149. #Region "DlClientList | Minecraft 客户端 版本列表"
  150. '主加载器
  151. Public Structure DlClientListResult
  152. ''' <summary>
  153. ''' 数据来源名称,如“Mojang”,“BMCLAPI”。
  154. ''' </summary>
  155. Public SourceName As String
  156. ''' <summary>
  157. ''' 是否为官方的实时数据。
  158. ''' </summary>
  159. Public IsOfficial As Boolean
  160. ''' <summary>
  161. ''' 获取到的 Json 数据。
  162. ''' </summary>
  163. Public Value As JObject
  164. '''' <summary>
  165. '''' 官方源的失败原因。若没有则为 Nothing。
  166. '''' </summary>
  167. 'Public OfficialError As Exception
  168. End Structure
  169. ''' <summary>
  170. ''' Minecraft 客户端 版本列表,主加载器。
  171. ''' </summary>
  172. Public DlClientListLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Main", AddressOf DlClientListMain)
  173. Private Sub DlClientListMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  174. Select Case Setup.Get("ToolDownloadVersion")
  175. Case 0
  176. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  177. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 30),
  178. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 60)
  179. }, Loader.IsForceRestarting)
  180. Case 1
  181. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  182. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 5),
  183. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 35)
  184. }, Loader.IsForceRestarting)
  185. Case Else
  186. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)) From {
  187. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListMojangLoader, 60),
  188. New KeyValuePair(Of LoaderTask(Of Integer, DlClientListResult), Integer)(DlClientListBmclapiLoader, 60)
  189. }, Loader.IsForceRestarting)
  190. End Select
  191. End Sub
  192. '各个下载源的分加载器
  193. ''' <summary>
  194. ''' Minecraft 客户端 版本列表,Mojang 官方源加载器。
  195. ''' </summary>
  196. Public DlClientListMojangLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Mojang", AddressOf DlClientListMojangMain)
  197. Private IsNewClientVersionHinted As Boolean = False
  198. Private Sub DlClientListMojangMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  199. Dim Json As JObject = NetGetCodeByRequestRetry("https://launchermeta.mojang.com/mc/game/version_manifest.json", IsJson:=True)
  200. Try
  201. Dim Versions As JArray = Json("versions")
  202. If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
  203. '添加 PCL 特供项
  204. If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json")))
  205. '返回
  206. Loader.Output = New DlClientListResult With {.IsOfficial = True, .SourceName = "Mojang 官方源", .Value = Json}
  207. '解析更新提示(Release)
  208. Dim Version As String = Json("latest")("release")
  209. If Setup.Get("ToolUpdateRelease") AndAlso Not Setup.Get("ToolUpdateReleaseLast") = "" AndAlso Version IsNot Nothing AndAlso Not Setup.Get("ToolUpdateReleaseLast") = Version Then
  210. McDownloadClientUpdateHint(Version, Json)
  211. IsNewClientVersionHinted = True
  212. End If
  213. McVersionHighest = Version.Split(".")(1)
  214. Setup.Set("ToolUpdateReleaseLast", Version)
  215. '解析更新提示(Snapshot)
  216. Version = Json("latest")("snapshot")
  217. If Setup.Get("ToolUpdateSnapshot") AndAlso Not Setup.Get("ToolUpdateSnapshotLast") = "" AndAlso Version IsNot Nothing AndAlso Not Setup.Get("ToolUpdateSnapshotLast") = Version AndAlso Not IsNewClientVersionHinted Then
  218. McDownloadClientUpdateHint(Version, Json)
  219. End If
  220. Setup.Set("ToolUpdateSnapshotLast", If(Version, "Nothing"))
  221. Catch ex As Exception
  222. Throw New Exception("版本列表解析失败", ex)
  223. End Try
  224. End Sub
  225. ''' <summary>
  226. ''' Minecraft 客户端 版本列表,BMCLAPI 源加载器。
  227. ''' </summary>
  228. Public DlClientListBmclapiLoader As New LoaderTask(Of Integer, DlClientListResult)("DlClientList Bmclapi", AddressOf DlClientListBmclapiMain)
  229. Private Sub DlClientListBmclapiMain(Loader As LoaderTask(Of Integer, DlClientListResult))
  230. Dim Json As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/mc/game/version_manifest.json",
  231. BackupUrl:="https://download.mcbbs.net/mc/game/version_manifest.json",
  232. IsJson:=True)
  233. Try
  234. Dim Versions As JArray = Json("versions")
  235. If Versions.Count < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Json.ToString & ")")
  236. '添加 PCL 特供项
  237. If File.Exists(PathTemp & "Cache\download.json") Then Versions.Merge(GetJson(ReadFile(PathTemp & "Cache\download.json")))
  238. '返回
  239. Loader.Output = New DlClientListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Json}
  240. Catch ex As Exception
  241. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  242. End Try
  243. End Sub
  244. ''' <summary>
  245. ''' 获取某个版本的 Json 下载地址,若失败则返回 Nothing。必须在工作线程执行。
  246. ''' </summary>
  247. Public Function DlClientListGet(Id As String)
  248. Try
  249. '确认 Minecraft 版本列表已完成获取
  250. Select Case DlClientListLoader.State
  251. Case LoadState.Loading
  252. DlClientListLoader.WaitForExit()
  253. Case LoadState.Failed, LoadState.Aborted, LoadState.Waiting
  254. DlClientListLoader.WaitForExit(IsForceRestart:=True)
  255. End Select
  256. '确认版本格式标准
  257. Id = Id.Replace("_", "-") '1.7.10_pre4 在版本列表中显示为 1.7.10-pre4
  258. If Id <> "1.0" AndAlso Id.EndsWith(".0") Then Id = Left(Id, Id.Length - 2) 'OptiFine 1.8 的下载会触发此问题,显示版本为 1.8.0
  259. '查找版本并开始
  260. For Each Version As JObject In DlClientListLoader.Output.Value("versions")
  261. If Version("id") = Id Then
  262. Return Version("url").ToString
  263. End If
  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.EndsWith(".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, 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, 35)
  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)
  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. '获取所有版本信息
  358. Dim Forge As List(Of String) = RegexSearch(Result, "(?<=colForge'>)[^<]*")
  359. Dim ReleaseTime As List(Of String) = RegexSearch(Result, "(?<=colDate'>)[^<]+")
  360. Dim Name As List(Of String) = RegexSearch(Result, "(?<=OptiFine_)[0-9A-Za-z_.]+(?=.jar"")")
  361. If Not ReleaseTime.Count = Name.Count Then Throw New Exception("版本与发布时间数据无法对应")
  362. If Not Forge.Count = Name.Count Then Throw New Exception("版本与 Forge 兼容数据无法对应")
  363. If ReleaseTime.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  364. '转化为列表输出
  365. Dim Versions As New List(Of DlOptiFineListEntry)
  366. For i = 0 To ReleaseTime.Count - 1
  367. Name(i) = Name(i).Replace("_", " ")
  368. Dim Entry As New DlOptiFineListEntry With {
  369. .NameDisplay = Name(i).Replace("HD U ", "").Replace(".0 ", " "),
  370. .ReleaseTime = Join({ReleaseTime(i).Split(".")(2), ReleaseTime(i).Split(".")(1), ReleaseTime(i).Split(".")(0)}, "/"),
  371. .IsPreview = Name(i).ToLower.Contains("pre"),
  372. .Inherit = Name(i).ToString.Split(" ")(0),
  373. .NameFile = If(Name(i).ToLower.Contains("pre"), "preview_", "") & "OptiFine_" & Name(i).Replace(" ", "_") & ".jar",
  374. .RequiredForgeVersion = Forge(i).Replace("Forge ", "").Replace("#", "")}
  375. If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
  376. Entry.NameVersion = Entry.Inherit & "-OptiFine_" & Name(i).ToString.Replace(" ", "_").Replace(Entry.Inherit & "_", "")
  377. Versions.Add(Entry)
  378. Next
  379. Loader.Output = New DlOptiFineListResult With {.IsOfficial = True, .SourceName = "OptiFine 官方源", .Value = Versions}
  380. End Sub
  381. ''' <summary>
  382. ''' OptiFine 版本列表,BMCLAPI。
  383. ''' </summary>
  384. Public DlOptiFineListBmclapiLoader As New LoaderTask(Of Integer, DlOptiFineListResult)("DlOptiFineList Bmclapi", AddressOf DlOptiFineListBmclapiMain)
  385. Private Sub DlOptiFineListBmclapiMain(Loader As LoaderTask(Of Integer, DlOptiFineListResult))
  386. Dim Json As JArray = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/optifine/versionList",
  387. BackupUrl:="https://download.mcbbs.net/optifine/versionList", IsJson:=True)
  388. Try
  389. Dim Versions As New List(Of DlOptiFineListEntry)
  390. For Each Token As JObject In Json
  391. Dim Entry As New DlOptiFineListEntry With {
  392. .NameDisplay = (Token("mcversion").ToString & Token("type").ToString.Replace("HD_U", "").Replace("_", " ") & " " & Token("patch").ToString).Replace(".0 ", " "),
  393. .ReleaseTime = "",
  394. .IsPreview = Token("patch").ToString.ToLower.Contains("pre"),
  395. .Inherit = Token("mcversion").ToString,
  396. .NameFile = Token("filename").ToString,
  397. .RequiredForgeVersion = If(Token("forge"), "").ToString.Replace("Forge ", "").Replace("#", "")
  398. }
  399. If Entry.RequiredForgeVersion.Contains("N/A") Then Entry.RequiredForgeVersion = Nothing
  400. Entry.NameVersion = Entry.Inherit & "-OptiFine_" & (Token("type").ToString & " " & Token("patch").ToString).Replace(".0 ", " ").Replace(" ", "_").Replace(Entry.Inherit & "_", "")
  401. Versions.Add(Entry)
  402. Next
  403. Loader.Output = New DlOptiFineListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Versions}
  404. Catch ex As Exception
  405. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  406. End Try
  407. End Sub
  408. #End Region
  409. #Region "DlForgeList | Forge Minecraft 版本列表"
  410. Public Structure DlForgeListResult
  411. ''' <summary>
  412. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  413. ''' </summary>
  414. Public SourceName As String
  415. ''' <summary>
  416. ''' 是否为官方的实时数据。
  417. ''' </summary>
  418. Public IsOfficial As Boolean
  419. ''' <summary>
  420. ''' 获取到的数据。
  421. ''' </summary>
  422. Public Value As List(Of String)
  423. End Structure
  424. ''' <summary>
  425. ''' Forge 版本列表,主加载器。
  426. ''' </summary>
  427. Public DlForgeListLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Main", AddressOf DlForgeListMain)
  428. Private Sub DlForgeListMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  429. Select Case Setup.Get("ToolDownloadVersion")
  430. Case 0
  431. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  432. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 30),
  433. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 60)
  434. }, Loader.IsForceRestarting)
  435. Case 1
  436. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  437. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 5),
  438. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 35)
  439. }, Loader.IsForceRestarting)
  440. Case Else
  441. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)) From {
  442. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListOfficialLoader, 60),
  443. New KeyValuePair(Of LoaderTask(Of Integer, DlForgeListResult), Integer)(DlForgeListBmclapiLoader, 60)
  444. }, Loader.IsForceRestarting)
  445. End Select
  446. End Sub
  447. ''' <summary>
  448. ''' Forge 版本列表,官方源。
  449. ''' </summary>
  450. Public DlForgeListOfficialLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Official", AddressOf DlForgeListOfficialMain)
  451. Private Sub DlForgeListOfficialMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  452. Dim Result As String = NetGetCodeByRequestRetry("http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.2.4.html", Encoding.Default, "text/html")
  453. If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  454. '获取所有版本信息
  455. Dim Names As List(Of String) = RegexSearch(Result, "(?<=a href=""index_)[0-9.]+(_pre[0-9]?)?(?=.html)")
  456. Names.Add("1.2.4") '1.2.4 不会被匹配上
  457. If Names.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  458. Loader.Output = New DlForgeListResult With {.IsOfficial = True, .SourceName = "Forge 官方源", .Value = Names}
  459. End Sub
  460. ''' <summary>
  461. ''' Forge 版本列表,BMCLAPI。
  462. ''' </summary>
  463. Public DlForgeListBmclapiLoader As New LoaderTask(Of Integer, DlForgeListResult)("DlForgeList Bmclapi", AddressOf DlForgeListBmclapiMain)
  464. Private Sub DlForgeListBmclapiMain(Loader As LoaderTask(Of Integer, DlForgeListResult))
  465. Dim Result As String = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/forge/minecraft", Encoding.Default,
  466. BackupUrl:="https://download.mcbbs.net/forge/minecraft")
  467. If Result.Length < 200 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  468. '获取所有版本信息
  469. Dim Names As List(Of String) = RegexSearch(Result, "[0-9.]+(_pre[0-9]?)?")
  470. If Names.Count < 10 Then Throw New Exception("获取到的版本数量不足(" & Result & ")")
  471. Loader.Output = New DlForgeListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Names}
  472. End Sub
  473. #End Region
  474. #Region "DlForgeVersion | Forge 版本列表"
  475. Public Class DlForgeVersionEntry
  476. ''' <summary>
  477. ''' 完整的版本名,如 “14.22.1.2478”。
  478. ''' </summary>
  479. Public Version As String
  480. ''' <summary>
  481. ''' 对应的 Minecraft 版本,如“1.12.2”。
  482. ''' </summary>
  483. Public Inherit As String
  484. ''' <summary>
  485. ''' 发布时间,格式为“yyyy/MM/dd HH:mm”。
  486. ''' </summary>
  487. Public ReleaseTime As String
  488. ''' <summary>
  489. ''' 文件的 MD5 或 SHA1(BMCLAPI 的老版本是 MD5,新版本是 SHA1;官方源总是 MD5)。
  490. ''' </summary>
  491. Public Hash As String = Nothing
  492. ''' <summary>
  493. ''' 是否为推荐版本。
  494. ''' </summary>
  495. Public IsRecommended As Boolean
  496. ''' <summary>
  497. ''' 安装类型。有 installer、client、universal 三种。
  498. ''' </summary>
  499. Public Category As String
  500. ''' <summary>
  501. ''' 版本分支。若无分支则为 Nothing。
  502. ''' </summary>
  503. Public Branch As String = Nothing
  504. ''' <summary>
  505. ''' 是否为新版 Forge。(即为 Minecraft 1.13+)
  506. ''' </summary>
  507. Public ReadOnly Property IsNewType As Boolean
  508. Get
  509. Return Version.Split(".")(0) >= 20
  510. End Get
  511. End Property
  512. ''' <summary>
  513. ''' 构建数。
  514. ''' </summary>
  515. Public ReadOnly Property Build As Integer
  516. Get
  517. Dim Version = Me.Version.Split(".")
  518. If Version(0) < 15 Then
  519. Return Version(Version.Count - 1)
  520. Else
  521. Return Version(Version.Count - 1) + 10000
  522. End If
  523. End Get
  524. End Property
  525. ''' <summary>
  526. ''' 用于下载的文件版本名。可能在 Version 的基础上添加了分支。
  527. ''' </summary>
  528. Public ReadOnly Property FileVersion As String
  529. Get
  530. Return Version & If(Branch Is Nothing, "", "-" & Branch)
  531. End Get
  532. End Property
  533. ''' <summary>
  534. ''' 即将下载的文件全名。
  535. ''' </summary>
  536. Public ReadOnly Property FileName As String
  537. Get
  538. Return "forge-" & Inherit & "-" & FileVersion & "-" & Category & "." & FileSuffix
  539. End Get
  540. End Property
  541. ''' <summary>
  542. ''' 文件扩展名。
  543. ''' </summary>
  544. Public ReadOnly Property FileSuffix As String
  545. Get
  546. If Category = "installer" Then
  547. Return "jar"
  548. Else
  549. Return "zip"
  550. End If
  551. End Get
  552. End Property
  553. End Class
  554. ''' <summary>
  555. ''' Forge 版本列表,主加载器。
  556. ''' </summary>
  557. Public Sub DlForgeVersionMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
  558. Dim DlForgeVersionOfficialLoader As New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Official", AddressOf DlForgeVersionOfficialMain)
  559. Dim DlForgeVersionBmclapiLoader As New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Bmclapi", AddressOf DlForgeVersionBmclapiMain)
  560. Select Case Setup.Get("ToolDownloadVersion")
  561. Case 0
  562. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  563. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 30),
  564. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 60)
  565. }, Loader.IsForceRestarting)
  566. Case 1
  567. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  568. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 5),
  569. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 35)
  570. }, Loader.IsForceRestarting)
  571. Case Else
  572. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)) From {
  573. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionOfficialLoader, 60),
  574. New KeyValuePair(Of LoaderTask(Of String, List(Of DlForgeVersionEntry)), Integer)(DlForgeVersionBmclapiLoader, 60)
  575. }, Loader.IsForceRestarting)
  576. End Select
  577. End Sub
  578. ''' <summary>
  579. ''' Forge 版本列表,官方源。
  580. ''' </summary>
  581. Public Sub DlForgeVersionOfficialMain(Loader As LoaderTask(Of String, List(Of DlForgeVersionEntry)))
  582. Dim Result As String
  583. Try
  584. Result = NetGetCodeByDownload("http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" & Loader.Input & ".html")
  585. Catch ex As Exception
  586. If GetExceptionSummary(ex).Contains("(404)") Then
  587. Throw New Exception("没有可用版本")
  588. Else
  589. Throw
  590. End If
  591. End Try
  592. If Result.Length < 1000 Then Throw New Exception("获取到的版本列表长度不足(" & Result & ")")
  593. Dim Versions As New List(Of DlForgeVersionEntry)
  594. Try
  595. '分割版本信息
  596. Dim VersionCodes = Mid(Result, 1, Result.LastIndexOf("</table>")).Replace("<td class=""download-version", "¨").Split("¨")
  597. '获取所有版本信息
  598. For i = 1 To VersionCodes.Count - 1
  599. Dim VersionCode = VersionCodes(i)
  600. Try
  601. '基础信息获取
  602. Dim Name As String = RegexSeek(VersionCode, "(?<=[^(0-9)]+)[0-9\.]+")
  603. Dim IsRecommended As Boolean = VersionCode.Contains("fa promo-recommended")
  604. Dim Inherit As String = Loader.Input
  605. '分支获取
  606. Dim Branch As String = RegexSeek(VersionCode, "(?<=Branch:</strong>[\s]*)[^<]+")
  607. Branch = RegexSeek(If(Branch, ""), "[^\s]+")
  608. If String.IsNullOrWhiteSpace(Branch) Then Branch = Nothing
  609. If Name = "11.15.1.2318" OrElse Name = "11.15.1.1902" OrElse Name = "11.15.1.1890" Then Branch = "1.8.9"
  610. If Branch Is Nothing AndAlso Inherit = "1.7.10" AndAlso Name.Split(".")(3) >= 1300 Then Branch = "1.7.10"
  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.IndexOf("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.IndexOf("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.IndexOf("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 With {.Category = Category, .Version = Name, .IsRecommended = IsRecommended, .Hash = MD5.Trim(vbCr, vbLf), .Inherit = Inherit, .ReleaseTime = ReleaseTime, .Branch = Branch})
  641. Catch ex As Exception
  642. Throw New Exception("版本信息提取失败(" & VersionCode & ")", ex)
  643. End Try
  644. Next
  645. Catch ex As Exception
  646. Throw New Exception("版本列表解析失败(" & Result & ")", ex)
  647. End Try
  648. If Versions.Count = 0 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/" & Loader.Input,
  656. BackupUrl:="https://download.mcbbs.net/forge/minecraft/" & Loader.Input, IsJson:=True)
  657. Dim Versions As New List(Of DlForgeVersionEntry)
  658. Try
  659. Dim Recommended As String = McDownloadForgeRecommendedGet(Loader.Input)
  660. For Each Token As JObject In Json
  661. '分类与 Hash 获取
  662. Dim Hash As String = Nothing, Category As String = "unknown", Proi As Integer = -1
  663. For Each File As JObject In Token("files")
  664. Select Case File("category").ToString
  665. Case "installer"
  666. If File("format").ToString = "jar" Then
  667. '类型为 installer.jar,支持范围 ~753 (~ 1.6.1 部分), 738~684 (1.5.2 全部)
  668. Hash = File("hash")
  669. Category = "installer"
  670. Proi = 2
  671. End If
  672. Case "universal"
  673. If Proi <= 1 AndAlso File("format").ToString = "zip" Then
  674. '类型为 universal.zip,支持范围 751~449 (1.6.1 部分), 682~183 (1.5.1 ~ 1.3.2 部分)
  675. Hash = File("hash")
  676. Category = "universal"
  677. Proi = 1
  678. End If
  679. Case "client"
  680. If Proi <= 0 AndAlso File("format").ToString = "zip" Then
  681. '类型为 client.zip,支持范围 182~ (1.3.2 部分 ~)
  682. Hash = File("hash")
  683. Category = "client"
  684. Proi = 0
  685. End If
  686. End Select
  687. Next
  688. '获取 Entry
  689. Dim Inherit As String = Loader.Input
  690. Dim Branch As String = Token("branch")
  691. Dim Name As String = Token("version")
  692. If Name = "11.15.1.2318" OrElse Name = "11.15.1.1902" OrElse Name = "11.15.1.1890" Then Branch = "1.8.9"
  693. If Branch Is Nothing AndAlso Inherit = "1.7.10" AndAlso Name.Split(".")(3) >= 1300 Then Branch = "1.7.10"
  694. '基础信息获取
  695. Dim Entry = New DlForgeVersionEntry With {.Hash = Hash, .Category = Category, .Version = Name, .Branch = Branch, .Inherit = Inherit, .IsRecommended = Recommended = Name}
  696. Dim TimeSplit = Token("modified").ToString.Split("-"c, "T"c, ":"c, "."c, " "c, "/"c)
  697. Entry.ReleaseTime = Token("modified").ToObject(Of Date).ToLocalTime.ToString("yyyy/MM/dd HH:mm")
  698. '添加项
  699. Versions.Add(Entry)
  700. Next
  701. Catch ex As Exception
  702. Throw New Exception("版本列表解析失败(" & Json.ToString & ")", ex)
  703. End Try
  704. If Versions.Count = 0 Then Throw New Exception("没有可用版本")
  705. Loader.Output = Versions
  706. End Sub
  707. #End Region
  708. #Region "DlLiteLoaderList | LiteLoader 版本列表"
  709. Public Structure DlLiteLoaderListResult
  710. ''' <summary>
  711. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  712. ''' </summary>
  713. Public SourceName As String
  714. ''' <summary>
  715. ''' 是否为官方的实时数据。
  716. ''' </summary>
  717. Public IsOfficial As Boolean
  718. ''' <summary>
  719. ''' 获取到的数据。
  720. ''' </summary>
  721. Public Value As List(Of DlLiteLoaderListEntry)
  722. ''' <summary>
  723. ''' 官方源的失败原因。若没有则为 Nothing。
  724. ''' </summary>
  725. Public OfficialError As Exception
  726. End Structure
  727. Public Class DlLiteLoaderListEntry
  728. ''' <summary>
  729. ''' 实际的文件名,如“liteloader-installer-1.12-00-SNAPSHOT.jar”。
  730. ''' </summary>
  731. Public FileName As String
  732. ''' <summary>
  733. ''' 是否为测试版。
  734. ''' </summary>
  735. Public IsPreview As Boolean
  736. ''' <summary>
  737. ''' 对应的 Minecraft 版本,如“1.12.2”。
  738. ''' </summary>
  739. Public Inherit As String
  740. ''' <summary>
  741. ''' 是否为 1.7 及更早的远古版。
  742. ''' </summary>
  743. Public IsLegacy As Boolean
  744. ''' <summary>
  745. ''' 发布时间,格式为“yyyy/mm/dd HH:mm”。
  746. ''' </summary>
  747. Public ReleaseTime As String
  748. ''' <summary>
  749. ''' 文件的 MD5。
  750. ''' </summary>
  751. Public MD5 As String
  752. ''' <summary>
  753. ''' 对应的 Json 项。
  754. ''' </summary>
  755. Public JsonToken As JToken
  756. End Class
  757. ''' <summary>
  758. ''' LiteLoader 版本列表,主加载器。
  759. ''' </summary>
  760. Public DlLiteLoaderListLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Main", AddressOf DlLiteLoaderListMain)
  761. Private Sub DlLiteLoaderListMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  762. Select Case Setup.Get("ToolDownloadVersion")
  763. Case 0
  764. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  765. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 30),
  766. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 60)
  767. }, Loader.IsForceRestarting)
  768. Case 1
  769. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  770. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 5),
  771. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 35)
  772. }, Loader.IsForceRestarting)
  773. Case Else
  774. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)) From {
  775. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListOfficialLoader, 60),
  776. New KeyValuePair(Of LoaderTask(Of Integer, DlLiteLoaderListResult), Integer)(DlLiteLoaderListBmclapiLoader, 60)
  777. }, Loader.IsForceRestarting)
  778. End Select
  779. End Sub
  780. ''' <summary>
  781. ''' LiteLoader 版本列表,官方源。
  782. ''' </summary>
  783. Public DlLiteLoaderListOfficialLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Official", AddressOf DlLiteLoaderListOfficialMain)
  784. Private Sub DlLiteLoaderListOfficialMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  785. Dim Result As JObject = NetGetCodeByRequestRetry("http://dl.liteloader.com/versions/versions.json", IsJson:=True)
  786. Try
  787. Dim Json As JObject = Result("versions")
  788. Dim Versions As New List(Of DlLiteLoaderListEntry)
  789. For Each Pair As KeyValuePair(Of String, JToken) In Json
  790. If Pair.Key.StartsWith("1.6") OrElse Pair.Key.StartsWith("1.5") Then Continue For
  791. Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
  792. Versions.Add(New DlLiteLoaderListEntry With {
  793. .Inherit = Pair.Key,
  794. .IsLegacy = Pair.Key.Split(".")(1) < 8,
  795. .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
  796. .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
  797. .MD5 = RealEntry("md5"),
  798. .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
  799. .JsonToken = RealEntry
  800. })
  801. Next
  802. Loader.Output = New DlLiteLoaderListResult With {.IsOfficial = True, .SourceName = "LiteLoader 官方源", .Value = Versions}
  803. Catch ex As Exception
  804. Throw New Exception("版本列表解析失败(" & Result.ToString & ")", ex)
  805. End Try
  806. End Sub
  807. ''' <summary>
  808. ''' LiteLoader 版本列表,BMCLAPI。
  809. ''' </summary>
  810. Public DlLiteLoaderListBmclapiLoader As New LoaderTask(Of Integer, DlLiteLoaderListResult)("DlLiteLoaderList Bmclapi", AddressOf DlLiteLoaderListBmclapiMain)
  811. Private Sub DlLiteLoaderListBmclapiMain(Loader As LoaderTask(Of Integer, DlLiteLoaderListResult))
  812. Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json",
  813. BackupUrl:="https://download.mcbbs.net/maven/com/mumfrey/liteloader/versions.json", IsJson:=True)
  814. Try
  815. Dim Json As JObject = Result("versions")
  816. Dim Versions As New List(Of DlLiteLoaderListEntry)
  817. For Each Pair As KeyValuePair(Of String, JToken) In Json
  818. If Pair.Key.StartsWith("1.6") OrElse Pair.Key.StartsWith("1.5") Then Continue For
  819. Dim RealEntry As JToken = If(Pair.Value("artefacts"), Pair.Value("snapshots"))("com.mumfrey:liteloader")("latest")
  820. Versions.Add(New DlLiteLoaderListEntry With {
  821. .Inherit = Pair.Key,
  822. .IsLegacy = Pair.Key.Split(".")(1) < 8,
  823. .IsPreview = RealEntry("stream").ToString.ToLower = "snapshot",
  824. .FileName = "liteloader-installer-" & Pair.Key & If(Pair.Key = "1.8" OrElse Pair.Key = "1.9", ".0", "") & "-00-SNAPSHOT.jar",
  825. .MD5 = RealEntry("md5"),
  826. .ReleaseTime = GetLocalTime(GetDate(RealEntry("timestamp"))).ToString("yyyy/MM/dd HH:mm"),
  827. .JsonToken = RealEntry
  828. })
  829. Next
  830. Loader.Output = New DlLiteLoaderListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Versions}
  831. Catch ex As Exception
  832. Throw New Exception("版本列表解析失败(" & Result.ToString & ")", ex)
  833. End Try
  834. End Sub
  835. #End Region
  836. #Region "DlFabricList | Fabric 列表"
  837. Public Structure DlFabricListResult
  838. ''' <summary>
  839. ''' 数据来源名称,如“Official”,“BMCLAPI”。
  840. ''' </summary>
  841. Public SourceName As String
  842. ''' <summary>
  843. ''' 是否为官方的实时数据。
  844. ''' </summary>
  845. Public IsOfficial As Boolean
  846. ''' <summary>
  847. ''' 获取到的数据。
  848. ''' </summary>
  849. Public Value As JObject
  850. End Structure
  851. ''' <summary>
  852. ''' Fabric 列表,主加载器。
  853. ''' </summary>
  854. Public DlFabricListLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Main", AddressOf DlFabricListMain)
  855. Private Sub DlFabricListMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  856. Select Case Setup.Get("ToolDownloadVersion")
  857. Case 0
  858. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  859. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 30),
  860. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 60)
  861. }, Loader.IsForceRestarting)
  862. Case 1
  863. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  864. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 5),
  865. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 35)
  866. }, Loader.IsForceRestarting)
  867. Case Else
  868. DlSourceLoader(Loader, New List(Of KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)) From {
  869. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListOfficialLoader, 60),
  870. New KeyValuePair(Of LoaderTask(Of Integer, DlFabricListResult), Integer)(DlFabricListBmclapiLoader, 60)
  871. }, Loader.IsForceRestarting)
  872. End Select
  873. End Sub
  874. ''' <summary>
  875. ''' Fabric 列表,官方源。
  876. ''' </summary>
  877. Public DlFabricListOfficialLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Official", AddressOf DlFabricListOfficialMain)
  878. Private Sub DlFabricListOfficialMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  879. Dim Result As JObject = NetGetCodeByRequestRetry("https://meta.fabricmc.net/v2/versions", IsJson:=True)
  880. Try
  881. Dim Output = New DlFabricListResult With {.IsOfficial = True, .SourceName = "Fabric 官方源", .Value = Result}
  882. If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
  883. Loader.Output = Output
  884. Catch ex As Exception
  885. Throw New Exception("列表解析失败(" & Result.ToString & ")", ex)
  886. End Try
  887. End Sub
  888. ''' <summary>
  889. ''' Fabric 列表,BMCLAPI。
  890. ''' </summary>
  891. Public DlFabricListBmclapiLoader As New LoaderTask(Of Integer, DlFabricListResult)("DlFabricList Bmclapi", AddressOf DlFabricListBmclapiMain)
  892. Private Sub DlFabricListBmclapiMain(Loader As LoaderTask(Of Integer, DlFabricListResult))
  893. Dim Result As JObject = NetGetCodeByRequestRetry("https://bmclapi2.bangbang93.com/fabric-meta/v2/versions",
  894. BackupUrl:="https://download.mcbbs.net/fabric-meta/v2/versions", IsJson:=True)
  895. Try
  896. Dim Output = New DlFabricListResult With {.IsOfficial = False, .SourceName = "BMCLAPI", .Value = Result}
  897. If Output.Value("game") Is Nothing OrElse Output.Value("loader") Is Nothing OrElse Output.Value("installer") Is Nothing Then Throw New Exception("获取到的列表缺乏必要项")
  898. Loader.Output = Output
  899. Catch ex As Exception
  900. Throw New Exception("列表解析失败(" & Result.ToString & ")", ex)
  901. End Try
  902. End Sub
  903. ''' <summary>
  904. ''' Fabric API 列表,官方源。
  905. ''' </summary>
  906. Public DlFabricApiLoader As New LoaderTask(Of Integer, List(Of CompFile))("Fabric API List Loader",
  907. Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet("fabric-api", False))
  908. ''' <summary>
  909. ''' OptiFabric 列表,官方源。
  910. ''' </summary>
  911. Public DlOptiFabricLoader As New LoaderTask(Of Integer, List(Of CompFile))("OptiFabric List Loader",
  912. Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet("322385", True))
  913. #End Region
  914. #Region "DlSource | 镜像下载源"
  915. Public Function DlSourceResourceGet(MojangBase As String) As String()
  916. MojangBase = MojangBase.Replace("http://resources.download.minecraft.net", "https://resources.download.minecraft.net")
  917. Return {MojangBase.Replace("https://resources.download.minecraft.net", "https://download.mcbbs.net/assets"),
  918. MojangBase,
  919. MojangBase.Replace("https://resources.download.minecraft.net", "https://bmclapi2.bangbang93.com/assets")
  920. }
  921. End Function
  922. Public Function DlSourceLibraryGet(MojangBase As String) As String()
  923. Return {MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/maven"),
  924. MojangBase.Replace("https://libraries.minecraft.net", "https://download.mcbbs.net/libraries"),
  925. MojangBase,
  926. MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/maven"),
  927. MojangBase.Replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
  928. }
  929. End Function
  930. Public Function DlSourceLauncherOrMetaGet(MojangBase As String, Optional IsStatic As Boolean = True) As String()
  931. If MojangBase Is Nothing Then Throw New Exception("无对应的 json 下载地址")
  932. If IsStatic Then
  933. Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
  934. MojangBase,
  935. MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
  936. }
  937. Else
  938. Return {MojangBase.Replace("https://launcher.mojang.com", "https://download.mcbbs.net").Replace("https://launchermeta.mojang.com", "https://download.mcbbs.net"),
  939. MojangBase,
  940. MojangBase.Replace("https://launcher.mojang.com", "https://bmclapi2.bangbang93.com").Replace("https://launchermeta.mojang.com", "https://bmclapi2.bangbang93.com")
  941. }
  942. End If
  943. End Function
  944. Private Sub DlSourceLoader(Of InputType, OutputType)(MainLoader As LoaderTask(Of InputType, OutputType),
  945. LoaderList As List(Of KeyValuePair(Of LoaderTask(Of InputType, OutputType), Integer)),
  946. Optional IsForceRestart As Boolean = False)
  947. Dim WaitCycle As Integer = 0
  948. Do While True
  949. '检查加载结束
  950. Dim IsAllFailed As Boolean = True
  951. For Each SubLoader In LoaderList
  952. If WaitCycle = 0 Then
  953. '如果要强制刷新,就不使用已经加载好的值
  954. If IsForceRestart Then Exit For
  955. '如果输入不一样,就不使用已经加载好的值
  956. If (SubLoader.Key.Input Is Nothing Xor MainLoader.Input Is Nothing) OrElse
  957. (SubLoader.Key.Input IsNot Nothing AndAlso Not SubLoader.Key.Input.Equals(MainLoader.Input)) Then Continue For
  958. End If
  959. If SubLoader.Key.State <> LoadState.Failed Then IsAllFailed = False
  960. If SubLoader.Key.State = LoadState.Finished Then
  961. MainLoader.Output = SubLoader.Key.Output
  962. DlSourceLoaderAbort(LoaderList)
  963. Exit Sub
  964. ElseIf IsAllFailed Then
  965. If WaitCycle < SubLoader.Value * 100 Then WaitCycle = SubLoader.Value * 100
  966. End If
  967. '由于 Forge BMCLAPI 没有可用版本导致强制失败
  968. '在没有可用版本时,官方源会一直卡住,直接使用 BMCLAPI 判定失败即可
  969. If SubLoader.Key.Error IsNot Nothing AndAlso SubLoader.Key.Error.Message.Contains("没有可用版本") Then
  970. For Each SubLoader2 In LoaderList
  971. If WaitCycle < SubLoader2.Value * 100 Then WaitCycle = SubLoader2.Value * 100
  972. Next
  973. End If
  974. Next
  975. '启动加载源
  976. If WaitCycle = 0 Then
  977. '启动第一个源
  978. LoaderList(0).Key.Start(MainLoader.Input, IsForceRestart)
  979. '将其他源标记为未启动,以确保可以切换下载源(#184)
  980. For i = 1 To LoaderList.Count - 1
  981. LoaderList(i).Key.State = LoadState.Waiting
  982. Next
  983. End If
  984. For i = 0 To LoaderList.Count - 1
  985. If WaitCycle = LoaderList(i).Value * 100 Then
  986. If i < LoaderList.Count - 1 Then
  987. '启动下一个源
  988. LoaderList(i + 1).Key.Start(MainLoader.Input, IsForceRestart)
  989. Else
  990. '失败
  991. Dim ErrorInfo As Exception = Nothing
  992. For ii = 0 To LoaderList.Count - 1
  993. LoaderList(ii).Key.Input = Nothing '重置输入,以免以同样的输入“重试加载”时直接失败
  994. If LoaderList(ii).Key.Error IsNot Nothing Then
  995. If ErrorInfo Is Nothing OrElse LoaderList(ii).Key.Error.Message.Contains("没有可用版本") Then
  996. ErrorInfo = LoaderList(ii).Key.Error
  997. End If
  998. End If
  999. Next
  1000. If ErrorInfo Is Nothing Then ErrorInfo = New TimeoutException("下载源连接超时")
  1001. DlSourceLoaderAbort(LoaderList)
  1002. Throw ErrorInfo
  1003. End If
  1004. Exit For
  1005. End If
  1006. Next
  1007. '计时
  1008. If MainLoader.IsAborted Then
  1009. DlSourceLoaderAbort(LoaderList)
  1010. Exit Sub
  1011. End If
  1012. Thread.Sleep(10)
  1013. WaitCycle += 1
  1014. Loop
  1015. End Sub
  1016. Private Sub DlSourceLoaderAbort(Of InputType, OutputType)(LoaderList As List(Of KeyValuePair(Of LoaderTask(Of InputType, OutputType), Integer)))
  1017. For Each Loader In LoaderList
  1018. If Loader.Key.State = LoadState.Loading Then Loader.Key.Abort()
  1019. Next
  1020. End Sub
  1021. #End Region
  1022. End Module