ModDownloadLib.vb 143 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237
  1. Imports System.IO.Compression
  2. Public Module ModDownloadLib
  3. ''' <summary>
  4. ''' 如果 OptiFine 与 Forge 同时开始安装,就会导致 Forge 安装失败。
  5. ''' </summary>
  6. Private InstallSyncLock As New Object
  7. ''' <summary>
  8. ''' 如果 OptiFine 与 Forge 同时复制原版 Jar,就会导致复制文件时冲突。
  9. ''' </summary>
  10. Private VanillaSyncLock As New Object
  11. ''' <summary>
  12. ''' 最高的 Minecraft 大版本号,-1 代表尚未获取。
  13. ''' </summary>
  14. Public McVersionHighest As Integer = -1
  15. #Region "Minecraft 下载"
  16. ''' <summary>
  17. ''' 下载某个 Minecraft 版本,这会创造一个单独的下载任务,失败会跳过执行并要求反馈。
  18. ''' 返回正在下载的任务,若跳过或失败,则返回 Nothing。
  19. ''' </summary>
  20. ''' <param name="Id">所下载的 Minecraft 的版本名。</param>
  21. ''' <param name="JsonUrl">Json 文件的 Mojang 官方地址。</param>
  22. Public Function McDownloadClient(Behaviour As NetPreDownloadBehaviour, Id As String, Optional JsonUrl As String = Nothing) As LoaderCombo(Of String)
  23. Try
  24. Dim VersionFolder As String = PathMcFolder & "versions\" & Id & "\"
  25. '重复任务检查
  26. SyncLock LoaderTaskbarLock
  27. For i = 0 To LoaderTaskbar.Count - 1
  28. If LoaderTaskbar(i).Name = "Minecraft " & Id & " 下载" Then
  29. If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return LoaderTaskbar(i)
  30. Hint("该版本正在下载中!", HintType.Critical)
  31. Return LoaderTaskbar(i)
  32. End If
  33. Next
  34. End SyncLock
  35. '已有版本检查
  36. If Behaviour <> NetPreDownloadBehaviour.IgnoreCheck AndAlso File.Exists(VersionFolder & Id & ".json") AndAlso File.Exists(VersionFolder & Id & ".jar") Then
  37. If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Return Nothing
  38. If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 与 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then
  39. File.Delete(VersionFolder & Id & ".jar")
  40. File.Delete(VersionFolder & Id & ".json")
  41. Else
  42. Return Nothing
  43. End If
  44. End If
  45. '启动
  46. Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", McDownloadClientLoader(Id, JsonUrl)) With {.OnStateChanged = AddressOf McInstallState}
  47. Loader.Start(VersionFolder)
  48. LoaderTaskbarAdd(Loader)
  49. FrmMain.BtnExtraDownload.ShowRefresh()
  50. FrmMain.BtnExtraDownload.Ribble()
  51. Return Loader
  52. Catch ex As Exception
  53. Log(ex, "开始 Minecraft 下载失败", LogLevel.Feedback)
  54. Return Nothing
  55. End Try
  56. End Function
  57. ''' <summary>
  58. ''' 保存某个 Minecraft 版本的核心文件(仅 Json 与核心 Jar)。
  59. ''' </summary>
  60. ''' <param name="Id">所下载的 Minecraft 的版本名。</param>
  61. ''' <param name="JsonUrl">Json 文件的 Mojang 官方地址。</param>
  62. Public Sub McDownloadClientCore(Id As String, JsonUrl As String, Behaviour As NetPreDownloadBehaviour)
  63. Try
  64. Dim VersionFolder As String = SelectFolder()
  65. If Not VersionFolder.Contains("\") Then Exit Sub
  66. VersionFolder = VersionFolder & Id & "\"
  67. '重复任务检查
  68. SyncLock LoaderTaskbarLock
  69. For i = 0 To LoaderTaskbar.Count - 1
  70. If LoaderTaskbar(i).Name = "Minecraft " & Id & " 下载" Then
  71. If Behaviour = NetPreDownloadBehaviour.ExitWhileExistsOrDownloading Then Exit Sub
  72. Hint("该版本正在下载中!", HintType.Critical)
  73. Exit Sub
  74. End If
  75. Next
  76. End SyncLock
  77. Dim Loaders As New List(Of LoaderBase)
  78. '下载版本 Json 文件
  79. Loaders.Add(New LoaderDownload("下载版本 json 文件", New List(Of NetFile) From {
  80. New NetFile(DlSourceLauncherOrMetaGet(JsonUrl), VersionFolder & Id & ".json", New FileChecker(CanUseExistsFile:=False, IsJson:=True))
  81. }) With {.ProgressWeight = 2})
  82. '获取支持库文件地址
  83. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析核心 jar 文件下载地址",
  84. Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder), True)) With {.ProgressWeight = 0.5, .Show = False})
  85. '下载支持库文件
  86. Loaders.Add(New LoaderDownload("下载核心 jar 文件", New List(Of NetFile)) With {.ProgressWeight = 5})
  87. '启动
  88. Dim Loader As New LoaderCombo(Of String)("Minecraft " & Id & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
  89. Loader.Start(Id)
  90. LoaderTaskbarAdd(Loader)
  91. FrmMain.BtnExtraDownload.ShowRefresh()
  92. FrmMain.BtnExtraDownload.Ribble()
  93. Catch ex As Exception
  94. Log(ex, "开始 Minecraft 下载失败", LogLevel.Feedback)
  95. End Try
  96. End Sub
  97. ''' <summary>
  98. ''' 获取下载某个 Minecraft 版本的加载器列表。
  99. ''' 它必须安装到 PathMcFolder,但是可以自定义版本名(不过自定义的版本名不会修改 Json 中的 id 项)。
  100. ''' </summary>
  101. Private Function McDownloadClientLoader(Id As String, Optional JsonUrl As String = Nothing, Optional VersionName As String = Nothing) As List(Of LoaderBase)
  102. VersionName = If(VersionName, Id)
  103. Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
  104. Dim Loaders As New List(Of LoaderBase)
  105. '下载版本 Json 文件
  106. If JsonUrl Is Nothing Then
  107. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取原版 json 文件下载地址",
  108. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  109. Dim JsonAddress As String = DlClientListGet(Id)
  110. Task.Output = New List(Of NetFile) From {New NetFile(DlSourceLauncherOrMetaGet(JsonAddress), VersionFolder & VersionName & ".json")}
  111. End Sub) With {.ProgressWeight = 2, .Show = False})
  112. End If
  113. Loaders.Add(New LoaderDownload(McDownloadClientJsonName, New List(Of NetFile) From {
  114. New NetFile(DlSourceLauncherOrMetaGet(If(JsonUrl, "")), VersionFolder & VersionName & ".json", New FileChecker(CanUseExistsFile:=False, IsJson:=True))
  115. }) With {.ProgressWeight = 3})
  116. '下载支持库文件
  117. Dim LoadersLib As New List(Of LoaderBase)
  118. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
  119. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  120. Log("[Download] 开始分析原版支持库文件:" & VersionFolder)
  121. Task.Output = McLibFix(New McVersion(VersionFolder))
  122. End Sub) With {.ProgressWeight = 1, .Show = False})
  123. LoadersLib.Add(New LoaderDownload("下载原版支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 13, .Show = False})
  124. Loaders.Add(New LoaderCombo(Of String)(McDownloadClientLibName, LoadersLib) With {.Block = False, .ProgressWeight = 14})
  125. '下载资源文件
  126. Dim LoadersAssets As New List(Of LoaderBase)
  127. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址(副加载器)",
  128. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  129. Try
  130. Dim Version As New McVersion(VersionFolder)
  131. Task.Output = New List(Of NetFile) From {DlClientAssetIndexGet(Version)}
  132. Catch ex As Exception
  133. Throw New Exception("分析资源文件索引地址失败", ex)
  134. End Try
  135. '顺手添加 Json 项目
  136. Try
  137. Dim VersionJson As JObject = GetJson(ReadFile(VersionFolder & VersionName & ".json"))
  138. VersionJson.Add("clientVersion", Id)
  139. WriteFile(VersionFolder & VersionName & ".json", VersionJson.ToString)
  140. Catch ex As Exception
  141. Throw New Exception("添加客户端版本失败", ex)
  142. End Try
  143. End Sub) With {.ProgressWeight = 1, .Show = False})
  144. LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
  145. LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析所需资源文件(副加载器)",
  146. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  147. Task.Output = McAssetsFixList(McAssetsGetIndexName(New McVersion(VersionFolder)), True, Task)
  148. End Sub) With {.ProgressWeight = 3, .Show = False})
  149. LoadersAssets.Add(New LoaderDownload("下载资源文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 14, .Show = False})
  150. Loaders.Add(New LoaderCombo(Of String)("下载原版资源文件", LoadersAssets) With {.Block = False, .ProgressWeight = 21})
  151. Return Loaders
  152. End Function
  153. Private Const McDownloadClientLibName As String = "下载原版支持库文件"
  154. Private Const McDownloadClientJsonName As String = "下载原版 json 文件"
  155. #End Region
  156. #Region "Minecraft 下载菜单"
  157. Public Function McDownloadListItem(Entry As JObject, OnClick As MyListItem.ClickEventHandler, IsSaveOnly As Boolean) As MyListItem
  158. '确定图标
  159. Dim Logo As String
  160. Select Case Entry("type")
  161. Case "release"
  162. Logo = "pack://application:,,,/images/Blocks/Grass.png"
  163. Case "snapshot"
  164. Logo = "pack://application:,,,/images/Blocks/CommandBlock.png"
  165. Case "special"
  166. Logo = "pack://application:,,,/images/Blocks/GoldBlock.png"
  167. Case Else
  168. Logo = "pack://application:,,,/images/Blocks/CobbleStone.png"
  169. End Select
  170. '建立控件
  171. Dim NewItem As New MyListItem With {.Logo = Logo, .SnapsToDevicePixels = True, .Title = Entry("id").ToString, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60}
  172. If Entry("lore") Is Nothing Then
  173. NewItem.Info = Entry("releaseTime").Value(Of Date).ToString("yyyy/MM/dd HH:mm")
  174. Else
  175. NewItem.Info = Entry("lore").ToString
  176. End If
  177. If Entry("url").ToString.Contains("pcl") Then NewItem.Info = "[PCL 特供下载] " & NewItem.Info
  178. AddHandler NewItem.Click, OnClick
  179. '建立菜单
  180. If IsSaveOnly Then
  181. NewItem.ContentHandler = AddressOf McDownloadSaveMenuBuild
  182. Else
  183. NewItem.ContentHandler = AddressOf McDownloadMenuBuild
  184. End If
  185. '结束
  186. Return NewItem
  187. End Function
  188. Private Sub McDownloadSaveMenuBuild(sender As Object, e As EventArgs)
  189. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  190. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  191. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  192. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  193. AddHandler BtnInfo.Click, AddressOf McDownloadMenuLog
  194. sender.Buttons = {BtnInfo}
  195. End Sub
  196. Private Sub McDownloadMenuBuild(sender As Object, e As EventArgs)
  197. Dim BtnSave As New MyIconButton With {.Logo = Logo.IconButtonSave, .ToolTip = "另存为"}
  198. ToolTipService.SetPlacement(BtnSave, Primitives.PlacementMode.Center)
  199. ToolTipService.SetVerticalOffset(BtnSave, 30)
  200. ToolTipService.SetHorizontalOffset(BtnSave, 2)
  201. AddHandler BtnSave.Click, AddressOf McDownloadMenuSave
  202. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  203. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  204. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  205. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  206. AddHandler BtnInfo.Click, AddressOf McDownloadMenuLog
  207. sender.Buttons = {BtnSave, BtnInfo}
  208. End Sub
  209. Private Sub McDownloadMenuLog(sender As Object, e As RoutedEventArgs)
  210. Dim Version As JToken
  211. If sender.Tag IsNot Nothing Then
  212. Version = sender.Tag
  213. ElseIf sender.Parent.Tag IsNot Nothing Then
  214. Version = sender.Parent.Tag
  215. Else
  216. Version = sender.Parent.Parent.Tag
  217. End If
  218. McUpdateLogShow(Version)
  219. End Sub
  220. Public Sub McDownloadMenuSave(sender As Object, e As RoutedEventArgs)
  221. Dim Version As MyListItem
  222. If TypeOf sender Is MyListItem Then
  223. Version = sender
  224. ElseIf TypeOf sender.Parent Is MyListItem Then
  225. Version = sender.Parent
  226. Else
  227. Version = sender.Parent.Parent
  228. End If
  229. McDownloadClientCore(Version.Title, Version.Tag("url").ToString, NetPreDownloadBehaviour.HintWhileExists)
  230. End Sub
  231. ''' <summary>
  232. ''' 显示某 Minecraft 版本的更新日志。
  233. ''' </summary>
  234. ''' <param name="VersionJson">在 version_manifest.json 中的对应项。</param>
  235. Public Sub McUpdateLogShow(VersionJson As JToken)
  236. Dim WikiName As String
  237. Dim Id As String = VersionJson("id").ToString.ToLower
  238. If Id = "3d shareware v1.34" Then
  239. WikiName = "3D_Shareware_v1.34"
  240. ElseIf Id = "2.0" Then
  241. WikiName = "Java版2.0"
  242. ElseIf Id = "1.rv-pre1" Then
  243. WikiName = "Java版1.RV-Pre1"
  244. ElseIf Id = "combat test 1" OrElse Id.Contains("combat-1") OrElse Id.Contains("combat-212796") Then
  245. WikiName = "Java版1.14.3_-_Combat_Test"
  246. ElseIf Id = "combat test 2" OrElse Id.Contains("combat-2") OrElse Id.Contains("combat-0") Then
  247. WikiName = "Java版Combat_Test_2"
  248. ElseIf Id = "combat test 3" OrElse Id = "1.14_combat-3" Then
  249. WikiName = "Java版Combat_Test_3"
  250. ElseIf Id = "combat test 4" OrElse Id = "1.15_combat-1" Then
  251. WikiName = "Java版Combat_Test_4"
  252. ElseIf Id = "combat test 5" OrElse Id = "1.15_combat-6" Then
  253. WikiName = "Java版Combat_Test_5"
  254. ElseIf Id = "combat test 6" OrElse Id = "1.16_combat-0" Then
  255. WikiName = "Java版Combat_Test_6"
  256. ElseIf Id = "combat test 7c" OrElse Id = "1.16_combat-3" Then
  257. WikiName = "Java版Combat_Test_7c"
  258. ElseIf Id = "combat test 8b" OrElse Id = "1.16_combat-5" Then
  259. WikiName = "Java版Combat_Test_8b"
  260. ElseIf Id = "combat test 8c" OrElse Id = "1.16_combat-6" Then
  261. WikiName = "Java版Combat_Test_8c"
  262. ElseIf Id = "1.0.0-rc2-2" Then
  263. WikiName = "Java版RC2"
  264. ElseIf Id.StartsWith("1.19_deep_dark_experimental_snapshot-") OrElse Id.StartsWith("1_19_deep_dark_experimental_snapshot-") Then
  265. WikiName = Id.Replace("1_19", "1.19").Replace("1.19_deep_dark_experimental_snapshot-", "Java版Deep_Dark_Experimental_Snapshot_")
  266. ElseIf Id = "b1.9-pre6" Then
  267. WikiName = "Java版Beta_1.9_Prerelease_6"
  268. ElseIf Id.Contains("b1.9") Then
  269. WikiName = "Java版Beta_1.9_Prerelease"
  270. ElseIf VersionJson("type") = "release" OrElse VersionJson("type") = "snapshot" OrElse VersionJson("type") = "special" Then
  271. WikiName = If(Id.Contains("w"), "", "Java版") & Id.Replace(" Pre-Release ", "-pre")
  272. ElseIf Id.StartsWith("b") Then
  273. WikiName = "Java版" & Id.TrimEnd("a", "b", "c", "d", "e").Replace("b", "Beta_")
  274. ElseIf Id.StartsWith("a") Then
  275. WikiName = "Java版" & Id.TrimEnd("a", "b", "c", "d", "e").Replace("a", "Alpha_v")
  276. ElseIf Id = "inf-20100618" Then
  277. WikiName = "Java版Infdev_20100618"
  278. ElseIf Id = "c0.30_01c" OrElse Id = "c0.30_survival" OrElse Id.Contains("生存测试") Then
  279. WikiName = "Java版Classic_0.30(生存模式)"
  280. ElseIf Id.StartsWith("c0.31") Then
  281. WikiName = "Java版Indev_0.31_20100130"
  282. ElseIf Id.StartsWith("c") Then
  283. WikiName = "Java版" & Id.Replace("c", "Classic_")
  284. ElseIf Id.StartsWith("rd-") Then
  285. WikiName = "Java版Pre-classic_" & Id
  286. Else
  287. Log("[Error] 未知的版本格式:" & Id & "。", LogLevel.Feedback)
  288. Exit Sub
  289. End If
  290. OpenWebsite("https://minecraft.fandom.com/zh/wiki/" & WikiName.Replace("_experimental-snapshot-", "-exp"))
  291. End Sub
  292. #End Region
  293. #Region "OptiFine 下载"
  294. Public Sub McDownloadOptiFine(DownloadInfo As DlOptiFineListEntry)
  295. Try
  296. Dim Id As String = DownloadInfo.NameVersion
  297. Dim VersionFolder As String = PathMcFolder & "versions\" & Id & "\"
  298. Dim IsNewVersion As Boolean = Val(DownloadInfo.Inherit.Split(".")(1)) >= 14
  299. Dim Target As String = If(IsNewVersion,
  300. PathTemp & "Cache\Code\" & DownloadInfo.NameVersion & "_" & GetUuid(),
  301. PathMcFolder & "libraries\optifine\OptiFine\" & DownloadInfo.NameFile.Replace("OptiFine_", "").Replace(".jar", "").Replace("preview_", "") & "\" & DownloadInfo.NameFile.Replace("OptiFine_", "OptiFine-").Replace("preview_", ""))
  302. '重复任务检查
  303. SyncLock LoaderTaskbarLock
  304. For i = 0 To LoaderTaskbar.Count - 1
  305. If LoaderTaskbar(i).Name = "OptiFine " & DownloadInfo.NameDisplay & " 下载" Then
  306. Hint("该版本正在下载中!", HintType.Critical)
  307. Exit Sub
  308. End If
  309. Next
  310. End SyncLock
  311. '已有版本检查
  312. If File.Exists(VersionFolder & Id & ".json") Then
  313. If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then
  314. File.Delete(VersionFolder & Id & ".jar")
  315. File.Delete(VersionFolder & Id & ".json")
  316. Else
  317. Exit Sub
  318. End If
  319. End If
  320. '启动
  321. Dim Loader As New LoaderCombo(Of String)("OptiFine " & DownloadInfo.NameDisplay & " 下载", McDownloadOptiFineLoader(DownloadInfo)) With {.OnStateChanged = AddressOf McInstallState}
  322. Loader.Start(VersionFolder)
  323. LoaderTaskbarAdd(Loader)
  324. FrmMain.BtnExtraDownload.ShowRefresh()
  325. FrmMain.BtnExtraDownload.Ribble()
  326. Catch ex As Exception
  327. Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback)
  328. End Try
  329. End Sub
  330. Private Sub McDownloadOptiFineSave(DownloadInfo As DlOptiFineListEntry)
  331. Try
  332. Dim Id As String = DownloadInfo.NameVersion
  333. Dim Target As String = SelectAs("选择保存位置", DownloadInfo.NameFile, "OptiFine Jar (*.jar)|*.jar")
  334. If Not Target.Contains("\") Then Exit Sub
  335. '重复任务检查
  336. SyncLock LoaderTaskbarLock
  337. For i = 0 To LoaderTaskbar.Count - 1
  338. If LoaderTaskbar(i).Name = "OptiFine " & DownloadInfo.NameDisplay & " 下载" Then
  339. Hint("该版本正在下载中!", HintType.Critical)
  340. Exit Sub
  341. End If
  342. Next
  343. End SyncLock
  344. Dim Loader As New LoaderCombo(Of DlOptiFineListEntry)("OptiFine " & DownloadInfo.NameDisplay & " 下载", McDownloadOptiFineSaveLoader(DownloadInfo, Target)) With {.OnStateChanged = AddressOf DownloadStateSave}
  345. Loader.Start(DownloadInfo)
  346. LoaderTaskbarAdd(Loader)
  347. FrmMain.BtnExtraDownload.ShowRefresh()
  348. FrmMain.BtnExtraDownload.Ribble()
  349. Catch ex As Exception
  350. Log(ex, "开始 OptiFine 下载失败", LogLevel.Feedback)
  351. End Try
  352. End Sub
  353. Private Sub McDownloadOptiFineInstall(BaseMcFolderHome As String, Target As String, Task As LoaderTask(Of List(Of NetFile), Boolean), UseJavaWrapper As Boolean)
  354. '选择 Java
  355. Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
  356. If Java Is Nothing Then
  357. If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
  358. '开始自动下载
  359. Dim JavaLoader = JavaFixLoaders(17)
  360. JavaLoader.Start(17, IsForceRestart:=True)
  361. Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted
  362. Thread.Sleep(10)
  363. Loop
  364. '检查下载结果
  365. Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
  366. If Task.IsAborted Then Exit Sub
  367. If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
  368. End If
  369. '添加 Java Wrapper 作为主 Jar
  370. Dim Arguments As String
  371. If UseJavaWrapper Then
  372. Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" -jar ""{ExtractJavaWrapper()}"" optifine.Installer"
  373. Else
  374. Arguments = $"-Duser.home=""{BaseMcFolderHome}"" -cp ""{Target}"" optifine.Installer"
  375. End If
  376. If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments
  377. '开始启动
  378. SyncLock InstallSyncLock
  379. Dim Info = New ProcessStartInfo With {
  380. .FileName = Java.PathJavaw,
  381. .Arguments = Arguments,
  382. .UseShellExecute = False,
  383. .CreateNoWindow = True,
  384. .RedirectStandardError = True,
  385. .RedirectStandardOutput = True,
  386. .WorkingDirectory = BaseMcFolderHome
  387. }
  388. If Info.EnvironmentVariables.ContainsKey("appdata") Then
  389. Info.EnvironmentVariables("appdata") = BaseMcFolderHome
  390. Else
  391. Info.EnvironmentVariables.Add("appdata", BaseMcFolderHome)
  392. End If
  393. Log("[Download] 开始安装 OptiFine:" & Target)
  394. Dim TotalLength As Integer = 0
  395. Dim process As New Process With {.StartInfo = Info}
  396. Dim LastResult As String = ""
  397. Using outputWaitHandle As New AutoResetEvent(False)
  398. Using errorWaitHandle As New AutoResetEvent(False)
  399. AddHandler process.OutputDataReceived, Function(sender, e)
  400. Try
  401. If e.Data Is Nothing Then
  402. outputWaitHandle.[Set]()
  403. Else
  404. LastResult = e.Data
  405. If ModeDebug Then Log("[Installer] " & LastResult)
  406. TotalLength += 1
  407. Task.Progress += 0.9 / 7000
  408. End If
  409. Catch ex As ObjectDisposedException
  410. Catch ex As Exception
  411. Log(ex, "读取 OptiFine 安装器信息失败")
  412. End Try
  413. Try
  414. If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
  415. Log("[Installer] 由于任务取消,已中止 OptiFine 安装")
  416. process.Kill()
  417. End If
  418. Catch
  419. End Try
  420. Return Nothing
  421. End Function
  422. AddHandler process.ErrorDataReceived, Function(sender, e)
  423. Try
  424. If e.Data Is Nothing Then
  425. errorWaitHandle.[Set]()
  426. Else
  427. LastResult = e.Data
  428. If ModeDebug Then Log("[Installer] " & LastResult)
  429. TotalLength += 1
  430. Task.Progress += 0.9 / 7000
  431. End If
  432. Catch ex As ObjectDisposedException
  433. Catch ex As Exception
  434. Log(ex, "读取 OptiFine 安装器错误信息失败")
  435. End Try
  436. Try
  437. If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
  438. Log("[Installer] 由于任务取消,已中止 OptiFine 安装")
  439. process.Kill()
  440. End If
  441. Catch
  442. End Try
  443. Return Nothing
  444. End Function
  445. process.Start()
  446. process.BeginOutputReadLine()
  447. process.BeginErrorReadLine()
  448. '等待
  449. Do Until process.HasExited
  450. Thread.Sleep(10)
  451. Loop
  452. '输出
  453. outputWaitHandle.WaitOne(10000)
  454. errorWaitHandle.WaitOne(10000)
  455. process.Dispose()
  456. If TotalLength < 1000 Then Throw New Exception("安装器运行出错,末行为 " & LastResult)
  457. End Using
  458. End Using
  459. End SyncLock
  460. End Sub
  461. ''' <summary>
  462. ''' 获取下载某个 OptiFine 版本的加载器列表。
  463. ''' </summary>
  464. Private Function McDownloadOptiFineLoader(DownloadInfo As DlOptiFineListEntry, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional ClientFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
  465. '参数初始化
  466. McFolder = If(McFolder, PathMcFolder)
  467. Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder
  468. Dim Id As String = DownloadInfo.NameVersion
  469. Dim VersionFolder As String = McFolder & "versions\" & Id & "\"
  470. Dim IsNewVersion As Boolean = DownloadInfo.Inherit.Contains("w") OrElse Val(DownloadInfo.Inherit.Split(".")(1)) >= 14
  471. Dim Target As String = If(IsNewVersion,
  472. PathTemp & "Cache\Code\" & DownloadInfo.NameVersion & "_" & GetUuid(),
  473. McFolder & "libraries\optifine\OptiFine\" & DownloadInfo.NameFile.Replace("OptiFine_", "").Replace(".jar", "").Replace("preview_", "") & "\" & DownloadInfo.NameFile.Replace("OptiFine_", "OptiFine-").Replace("preview_", ""))
  474. Dim Loaders As New List(Of LoaderBase)
  475. '获取下载地址
  476. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 OptiFine 主文件下载地址",
  477. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  478. '启动依赖版本的下载
  479. If ClientDownloadLoader Is Nothing Then
  480. If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
  481. ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
  482. End If
  483. Task.Progress = 0.1
  484. Dim Sources As New List(Of String)
  485. 'BMCLAPI 源
  486. If DownloadInfo.IsPreview Then
  487. Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
  488. Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
  489. Else
  490. Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
  491. Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
  492. End If
  493. '官方源
  494. Dim PageData As String
  495. Try
  496. PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
  497. Task.Progress = 0.8
  498. Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
  499. Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
  500. Catch ex As Exception
  501. Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
  502. End Try
  503. 'OptiFine 中文镜像源
  504. Sources.Add("https://optifine.cn/download/" & DownloadInfo.NameFile)
  505. '构造文件请求
  506. Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, Target, New FileChecker(MinSize:=64 * 1024))}
  507. End Sub) With {.ProgressWeight = 8})
  508. Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 8})
  509. Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("等待原版下载",
  510. Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
  511. '等待原版文件下载完成
  512. If ClientDownloadLoader Is Nothing Then Exit Sub
  513. Dim TargetLoaders As List(Of LoaderBase) =
  514. ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName).
  515. Where(Function(l) l.State <> LoadState.Finished).ToList
  516. If TargetLoaders.Count > 0 Then Log("[Download] OptiFine 安装正在等待原版文件下载完成")
  517. Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted
  518. TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList
  519. Thread.Sleep(50)
  520. Loop
  521. If Task.IsAborted Then Exit Sub
  522. '拷贝原版文件
  523. If Not IsCustomFolder Then Exit Sub
  524. SyncLock VanillaSyncLock
  525. Dim ClientName As String = GetFolderNameFromPath(ClientFolder)
  526. Directory.CreateDirectory(McFolder & "versions\" & DownloadInfo.Inherit)
  527. If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json") Then
  528. CopyFile(ClientFolder & ClientName & ".json",
  529. McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json")
  530. End If
  531. If Not File.Exists(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar") Then
  532. CopyFile(ClientFolder & ClientName & ".jar",
  533. McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
  534. End If
  535. End SyncLock
  536. End Sub) With {.ProgressWeight = 0.1, .Show = False})
  537. '安装(新旧方式均需要原版 Jar 和 Json)
  538. If IsNewVersion Then
  539. Log("[Download] 检测为新版 OptiFine:" & DownloadInfo.Inherit)
  540. Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("安装 OptiFine(方式 A)",
  541. Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
  542. Dim BaseMcFolderHome As String = PathTemp & "InstallOptiFine" & RandomInteger(0, 100000)
  543. Dim BaseMcFolder As String = BaseMcFolderHome & "\.minecraft\"
  544. Try
  545. '准备安装环境
  546. If Directory.Exists(BaseMcFolder & "versions\" & DownloadInfo.Inherit) Then
  547. DeleteDirectory(BaseMcFolder & "versions\" & DownloadInfo.Inherit)
  548. End If
  549. Directory.CreateDirectory(BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\")
  550. McFolderLauncherProfilesJsonCreate(BaseMcFolder)
  551. CopyFile(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json",
  552. BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".json")
  553. CopyFile(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar",
  554. BaseMcFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar")
  555. Task.Progress = 0.06
  556. '进行安装
  557. Dim UseJavaWrapper As Boolean = True
  558. Retry:
  559. Try
  560. McDownloadOptiFineInstall(BaseMcFolderHome, Target, Task, UseJavaWrapper)
  561. Catch ex As Exception
  562. If UseJavaWrapper Then
  563. Log(ex, "使用 JavaWrapper 安装 OptiFine 失败,将不使用 JavaWrapper 并重试")
  564. UseJavaWrapper = False
  565. GoTo Retry
  566. Else
  567. Throw New Exception("运行 OptiFine 安装器失败", ex)
  568. End If
  569. End Try
  570. Task.Progress = 0.96
  571. '复制文件
  572. File.Delete(BaseMcFolder & "launcher_profiles.json")
  573. CopyDirectory(BaseMcFolder, McFolder)
  574. Task.Progress = 0.98
  575. '清理文件
  576. File.Delete(Target)
  577. DeleteDirectory(BaseMcFolderHome)
  578. Catch ex As Exception
  579. Throw New Exception("安装 OptiFine(方式 A)失败", ex)
  580. End Try
  581. End Sub) With {.ProgressWeight = 8})
  582. Else
  583. Log("[Download] 检测为旧版 OptiFine:" & DownloadInfo.Inherit)
  584. Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("安装 OptiFine(方式 B)",
  585. Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
  586. Try
  587. '新建版本文件夹
  588. Directory.CreateDirectory(VersionFolder)
  589. Task.Progress = 0.1
  590. '复制 Jar 文件
  591. If File.Exists(VersionFolder & Id & ".jar") Then File.Delete(VersionFolder & Id & ".jar")
  592. CopyFile(McFolder & "versions\" & DownloadInfo.Inherit & "\" & DownloadInfo.Inherit & ".jar", VersionFolder & Id & ".jar")
  593. Task.Progress = 0.7
  594. '建立 Json 文件
  595. Dim InheritVersion As New McVersion(McFolder & "versions\" & DownloadInfo.Inherit)
  596. Dim Json As String = "{
  597. ""id"": """ & Id & """,
  598. ""inheritsFrom"": """ & DownloadInfo.Inherit & """,
  599. ""time"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy-MM-dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
  600. ""releaseTime"": """ & If(DownloadInfo.ReleaseTime = "", InheritVersion.ReleaseTime.ToString("yyyy-MM-dd"), DownloadInfo.ReleaseTime.Replace("/", "-")) & "T23:33:33+08:00"",
  601. ""type"": ""release"",
  602. ""libraries"": [
  603. {""name"": ""optifine:OptiFine:" & DownloadInfo.NameFile.Replace("OptiFine_", "").Replace(".jar", "").Replace("preview_", "") & """},
  604. {""name"": ""net.minecraft:launchwrapper:1.12""}
  605. ],
  606. ""mainClass"": ""net.minecraft.launchwrapper.Launch"","
  607. Task.Progress = 0.8
  608. If InheritVersion.IsOldJson Then
  609. '输出旧版 Json 格式
  610. Json += "
  611. ""minimumLauncherVersion"": 18,
  612. ""minecraftArguments"": """ & InheritVersion.JsonObject("minecraftArguments").ToString & " --tweakClass optifine.OptiFineTweaker""
  613. }"
  614. Else
  615. '输出新版 Json 格式
  616. Json += "
  617. ""minimumLauncherVersion"": ""21"",
  618. ""arguments"": {
  619. ""game"": [
  620. ""--tweakClass"",
  621. ""optifine.OptiFineTweaker""
  622. ]
  623. }
  624. }"
  625. End If
  626. WriteFile(VersionFolder & Id & ".json", Json)
  627. Catch ex As Exception
  628. Throw New Exception("安装 OptiFine(方式 B)失败", ex)
  629. End Try
  630. End Sub) With {.ProgressWeight = 1})
  631. End If
  632. '下载支持库
  633. If FixLibrary Then
  634. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 OptiFine 支持库文件",
  635. Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
  636. Loaders.Add(New LoaderDownload("下载 OptiFine 支持库文件", New List(Of NetFile)) With {.ProgressWeight = 4})
  637. End If
  638. Return Loaders
  639. End Function
  640. ''' <summary>
  641. ''' 获取保存某个 OptiFine 版本的加载器列表。
  642. ''' </summary>
  643. Private Function McDownloadOptiFineSaveLoader(DownloadInfo As DlOptiFineListEntry, TargetFolder As String) As List(Of LoaderBase)
  644. Dim Loaders As New List(Of LoaderBase)
  645. '获取下载地址
  646. Loaders.Add(New LoaderTask(Of DlOptiFineListEntry, List(Of NetFile))("获取 OptiFine 下载地址",
  647. Sub(Task As LoaderTask(Of DlOptiFineListEntry, List(Of NetFile)))
  648. Dim Sources As New List(Of String)
  649. 'BMCLAPI 源
  650. If DownloadInfo.IsPreview Then
  651. Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
  652. Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U_" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", "").Replace(" ", "/"))
  653. Else
  654. Sources.Add("https://download.mcbbs.net/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
  655. Sources.Add("https://bmclapi2.bangbang93.com/optifine/" & DownloadInfo.Inherit & "/HD_U/" & DownloadInfo.NameDisplay.Replace(DownloadInfo.Inherit & " ", ""))
  656. End If
  657. '官方源
  658. Dim PageData As String
  659. Try
  660. PageData = NetGetCodeByClient("https://optifine.net/adloadx?f=" & DownloadInfo.NameFile, New UTF8Encoding(False), 15000, "text/html")
  661. Task.Progress = 0.8
  662. Sources.Add("https://optifine.net/" & RegexSearch(PageData, "downloadx\?f=[^""']+")(0))
  663. Log("[Download] OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址:" & Sources(0))
  664. Catch ex As Exception
  665. Log(ex, "获取 OptiFine " & DownloadInfo.NameDisplay & " 官方下载地址失败")
  666. End Try
  667. Task.Progress = 0.9
  668. '构造文件请求
  669. Task.Output = New List(Of NetFile) From {New NetFile(Sources.ToArray, TargetFolder, New FileChecker(MinSize:=64 * 1024))}
  670. End Sub) With {.ProgressWeight = 6})
  671. '下载
  672. Loaders.Add(New LoaderDownload("下载 OptiFine 主文件", New List(Of NetFile)) With {.ProgressWeight = 10, .Block = True})
  673. Return Loaders
  674. End Function
  675. #End Region
  676. #Region "OptiFine 下载菜单"
  677. Public Function OptiFineDownloadListItem(Entry As DlOptiFineListEntry, OnClick As MyListItem.ClickEventHandler, IsSaveOnly As Boolean) As MyListItem
  678. '建立控件
  679. Dim NewItem As New MyListItem With {
  680. .Title = Entry.NameDisplay, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60,
  681. .Info = If(Entry.IsPreview, "测试版", "正式版") &
  682. If(Entry.ReleaseTime = "", "", ",发布于 " & Entry.ReleaseTime) &
  683. If(Entry.RequiredForgeVersion Is Nothing, ",不兼容 Forge", If(Entry.RequiredForgeVersion = "", "", ",推荐 Forge 版本:" & Entry.RequiredForgeVersion)),
  684. .Logo = "pack://application:,,,/images/Blocks/GrassPath.png"
  685. }
  686. AddHandler NewItem.Click, OnClick
  687. '建立菜单
  688. If IsSaveOnly Then
  689. NewItem.ContentHandler = AddressOf OptiFineSaveContMenuBuild
  690. Else
  691. NewItem.ContentHandler = AddressOf OptiFineContMenuBuild
  692. End If
  693. '结束
  694. Return NewItem
  695. End Function
  696. Private Sub OptiFineSaveContMenuBuild(sender As Object, e As EventArgs)
  697. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  698. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  699. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  700. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  701. AddHandler BtnInfo.Click, AddressOf OptiFineLog_Click
  702. sender.Buttons = {BtnInfo}
  703. End Sub
  704. Private Sub OptiFineContMenuBuild(sender As Object, e As EventArgs)
  705. Dim BtnSave As New MyIconButton With {.Logo = Logo.IconButtonSave, .ToolTip = "另存为"}
  706. ToolTipService.SetPlacement(BtnSave, Primitives.PlacementMode.Center)
  707. ToolTipService.SetVerticalOffset(BtnSave, 30)
  708. ToolTipService.SetHorizontalOffset(BtnSave, 2)
  709. AddHandler BtnSave.Click, AddressOf OptiFineSave_Click
  710. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  711. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  712. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  713. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  714. AddHandler BtnInfo.Click, AddressOf OptiFineLog_Click
  715. sender.Buttons = {BtnSave, BtnInfo}
  716. End Sub
  717. Private Sub OptiFineLog_Click(sender As Object, e As RoutedEventArgs)
  718. Dim Version As DlOptiFineListEntry
  719. If sender.Tag IsNot Nothing Then
  720. Version = sender.Tag
  721. ElseIf sender.Parent.Tag IsNot Nothing Then
  722. Version = sender.Parent.Tag
  723. Else
  724. Version = sender.Parent.Parent.Tag
  725. End If
  726. OpenWebsite("https://optifine.net/changelog?f=" & Version.NameFile)
  727. End Sub
  728. Public Sub OptiFineSave_Click(sender As Object, e As RoutedEventArgs)
  729. Dim Version As DlOptiFineListEntry
  730. If sender.Tag IsNot Nothing Then
  731. Version = sender.Tag
  732. ElseIf sender.Parent.Tag IsNot Nothing Then
  733. Version = sender.Parent.Tag
  734. Else
  735. Version = sender.Parent.Parent.Tag
  736. End If
  737. McDownloadOptiFineSave(Version)
  738. End Sub
  739. #End Region
  740. #Region "LiteLoader 下载"
  741. Public Sub McDownloadLiteLoader(DownloadInfo As DlLiteLoaderListEntry)
  742. Try
  743. Dim Id As String = DownloadInfo.Inherit
  744. Dim Target As String = PathTemp & "Download\" & Id & "-Liteloader.jar"
  745. Dim VersionName As String = DownloadInfo.Inherit & "-LiteLoader"
  746. Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
  747. '重复任务检查
  748. SyncLock LoaderTaskbarLock
  749. For i = 0 To LoaderTaskbar.Count - 1
  750. If LoaderTaskbar(i).Name = "LiteLoader " & Id & " 下载" Then
  751. Hint("该版本正在下载中!", HintType.Critical)
  752. Exit Sub
  753. End If
  754. Next
  755. End SyncLock
  756. '已有版本检查
  757. If File.Exists(VersionFolder & VersionName & ".json") Then
  758. If MyMsgBox("版本 " & VersionName & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then
  759. File.Delete(VersionFolder & VersionName & ".jar")
  760. File.Delete(VersionFolder & VersionName & ".json")
  761. Else
  762. Exit Sub
  763. End If
  764. End If
  765. '启动
  766. Dim Loader As New LoaderCombo(Of String)("LiteLoader " & Id & " 下载", McDownloadLiteLoaderLoader(DownloadInfo)) With {.OnStateChanged = AddressOf McInstallState}
  767. Loader.Start(VersionFolder)
  768. LoaderTaskbarAdd(Loader)
  769. FrmMain.BtnExtraDownload.ShowRefresh()
  770. FrmMain.BtnExtraDownload.Ribble()
  771. Catch ex As Exception
  772. Log(ex, "开始 LiteLoader 下载失败", LogLevel.Feedback)
  773. End Try
  774. End Sub
  775. Private Sub McDownloadLiteLoaderSave(DownloadInfo As DlLiteLoaderListEntry)
  776. Try
  777. Dim Id As String = DownloadInfo.Inherit
  778. Dim Target As String = SelectAs("选择保存位置", DownloadInfo.FileName.Replace("-SNAPSHOT", ""), "LiteLoader 安装器 (*.jar)|*.jar")
  779. If Not Target.Contains("\") Then Exit Sub
  780. '重复任务检查
  781. SyncLock LoaderTaskbarLock
  782. For i = 0 To LoaderTaskbar.Count - 1
  783. If LoaderTaskbar(i).Name = "LiteLoader " & Id & " 下载" Then
  784. Hint("该版本正在下载中!", HintType.Critical)
  785. Exit Sub
  786. End If
  787. Next
  788. End SyncLock
  789. '构造步骤加载器
  790. Dim Loaders As New List(Of LoaderBase)
  791. '下载
  792. Dim Address As New List(Of String)
  793. If DownloadInfo.IsLegacy Then
  794. '老版本
  795. Select Case DownloadInfo.Inherit
  796. Case "1.7.10"
  797. Address.Add("http://dl.liteloader.com/redist/1.7.10/liteloader-installer-1.7.10-04.jar")
  798. Case "1.7.2"
  799. Address.Add("http://dl.liteloader.com/redist/1.7.2/liteloader-installer-1.7.2-04.jar")
  800. Case "1.6.4"
  801. Address.Add("http://dl.liteloader.com/redist/1.6.4/liteloader-installer-1.6.4-01.jar")
  802. Case "1.6.2"
  803. Address.Add("http://dl.liteloader.com/redist/1.6.2/liteloader-installer-1.6.2-04.jar")
  804. Case "1.5.2"
  805. 'Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar",
  806. ' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/1.5.2/liteloader-1.5.2.jar"})
  807. Address.Add("http://dl.liteloader.com/redist/1.5.2/liteloader-installer-1.5.2-01.jar")
  808. Case Else
  809. Throw New NotSupportedException("未知的 Minecraft 版本(" & DownloadInfo.Inherit & ")")
  810. End Select
  811. Else
  812. 'BMCLAPI 源下载的是安装后的 Jar,而不是 Installer,故无法使用
  813. ''BMCLAPI 源
  814. 'If DownloadInfo.IsPreview Then
  815. ' Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar",
  816. ' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "-SNAPSHOT/liteloader-" & DownloadInfo.Inherit & "-SNAPSHOT.jar"})
  817. 'Else
  818. ' Address.AddRange({"https://download.mcbbs.net/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar",
  819. ' "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/" & DownloadInfo.Inherit & "/liteloader-" & DownloadInfo.Inherit & ".jar"})
  820. 'End If
  821. '官方源
  822. Address.Add("http://jenkins.liteloader.com/job/LiteLoaderInstaller%20" & DownloadInfo.Inherit & "/lastSuccessfulBuild/artifact/" & If(DownloadInfo.Inherit = "1.8", "ant/dist/", "build/libs/") & DownloadInfo.FileName)
  823. End If
  824. Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 1024))}) With {.ProgressWeight = 15})
  825. '启动
  826. Dim Loader As New LoaderCombo(Of DlLiteLoaderListEntry)("LiteLoader " & Id & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
  827. Loader.Start(DownloadInfo)
  828. LoaderTaskbarAdd(Loader)
  829. FrmMain.BtnExtraDownload.ShowRefresh()
  830. FrmMain.BtnExtraDownload.Ribble()
  831. Catch ex As Exception
  832. Log(ex, "开始 LiteLoader 安装器下载失败", LogLevel.Feedback)
  833. End Try
  834. End Sub
  835. ''' <summary>
  836. ''' 获取下载某个 LiteLoader 版本的加载器列表。
  837. ''' </summary>
  838. Private Function McDownloadLiteLoaderLoader(DownloadInfo As DlLiteLoaderListEntry, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
  839. '参数初始化
  840. McFolder = If(McFolder, PathMcFolder)
  841. Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder
  842. Dim Id As String = DownloadInfo.Inherit
  843. Dim Target As String = PathTemp & "Download\" & Id & "-Liteloader.jar"
  844. Dim VersionName As String = DownloadInfo.Inherit & "-LiteLoader"
  845. Dim VersionFolder As String = McFolder & "versions\" & VersionName & "\"
  846. Dim Loaders As New List(Of LoaderBase)
  847. '启动依赖版本的下载
  848. If ClientDownloadLoader Is Nothing Then
  849. Loaders.Add(New LoaderTask(Of String, String)(
  850. "启动 LiteLoader 依赖版本下载",
  851. Sub()
  852. If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
  853. ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, DownloadInfo.Inherit)
  854. End Sub) With {.ProgressWeight = 0.2, .Show = False, .Block = False})
  855. End If
  856. '安装
  857. Loaders.Add(New LoaderTask(Of String, String)("安装 LiteLoader",
  858. Sub(Task As LoaderTask(Of String, String))
  859. Try
  860. '新建版本文件夹
  861. Directory.CreateDirectory(VersionFolder)
  862. '构造版本 Json
  863. Dim VersionJson As New JObject
  864. VersionJson.Add("id", VersionName)
  865. VersionJson.Add("time", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
  866. VersionJson.Add("releaseTime", Date.ParseExact(DownloadInfo.ReleaseTime, "yyyy/MM/dd HH:mm", Globalization.CultureInfo.CurrentCulture))
  867. VersionJson.Add("type", "release")
  868. VersionJson.Add("arguments", GetJson("{""game"":[""--tweakClass"",""" & DownloadInfo.JsonToken("tweakClass").ToString & """]}"))
  869. VersionJson.Add("libraries", DownloadInfo.JsonToken("libraries"))
  870. CType(VersionJson("libraries"), JContainer).Add(GetJson("{""name"": ""com.mumfrey:liteloader:" & DownloadInfo.JsonToken("version").ToString & """,""url"": ""http://dl.liteloader.com/versions/""}"))
  871. VersionJson.Add("mainClass", "net.minecraft.launchwrapper.Launch")
  872. VersionJson.Add("minimumLauncherVersion", 18)
  873. VersionJson.Add("inheritsFrom", DownloadInfo.Inherit)
  874. VersionJson.Add("jar", DownloadInfo.Inherit)
  875. '输出 Json 文件
  876. WriteFile(VersionFolder & VersionName & ".json", VersionJson.ToString)
  877. Catch ex As Exception
  878. Throw New Exception("安装新 LiteLoader 版本失败", ex)
  879. End Try
  880. End Sub) With {.ProgressWeight = 1})
  881. '下载支持库
  882. If FixLibrary Then
  883. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 LiteLoader 支持库文件",
  884. Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
  885. Loaders.Add(New LoaderDownload("下载 LiteLoader 支持库文件", New List(Of NetFile)) With {.ProgressWeight = 6})
  886. End If
  887. Return Loaders
  888. End Function
  889. #End Region
  890. #Region "LiteLoader 下载菜单"
  891. Public Function LiteLoaderDownloadListItem(Entry As DlLiteLoaderListEntry, OnClick As MyListItem.ClickEventHandler, IsSaveOnly As Boolean) As MyListItem
  892. '建立控件
  893. Dim NewItem As New MyListItem With {
  894. .Title = Entry.Inherit, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 30,
  895. .Info = If(Entry.IsPreview, "测试版", "稳定版") & If(Entry.ReleaseTime = "", "", ",发布于 " & Entry.ReleaseTime),
  896. .Logo = "pack://application:,,,/images/Blocks/Egg.png"
  897. }
  898. AddHandler NewItem.Click, OnClick
  899. '建立菜单
  900. If IsSaveOnly Then
  901. NewItem.ContentHandler = AddressOf LiteLoaderSaveContMenuBuild
  902. Else
  903. NewItem.ContentHandler = AddressOf LiteLoaderContMenuBuild
  904. End If
  905. '结束
  906. Return NewItem
  907. End Function
  908. Private Sub LiteLoaderSaveContMenuBuild(sender As MyListItem, e As EventArgs)
  909. If sender.Tag.IsLegacy Then
  910. sender.PaddingRight = 0
  911. sender.Buttons = {}
  912. Else
  913. sender.PaddingRight = 30
  914. Dim BtnList As New MyIconButton With {.Logo = Logo.IconButtonList, .ToolTip = "查看全部版本", .Tag = sender}
  915. ToolTipService.SetPlacement(BtnList, Primitives.PlacementMode.Center)
  916. ToolTipService.SetVerticalOffset(BtnList, 30)
  917. ToolTipService.SetHorizontalOffset(BtnList, 2)
  918. AddHandler BtnList.Click, AddressOf LiteLoaderAll_Click
  919. sender.Buttons = {BtnList}
  920. End If
  921. End Sub
  922. Private Sub LiteLoaderContMenuBuild(sender As MyListItem, e As EventArgs)
  923. Dim BtnSave As New MyIconButton With {.Logo = Logo.IconButtonSave, .ToolTip = "保存安装器", .Tag = sender}
  924. ToolTipService.SetPlacement(BtnSave, Primitives.PlacementMode.Center)
  925. ToolTipService.SetVerticalOffset(BtnSave, 30)
  926. ToolTipService.SetHorizontalOffset(BtnSave, 2)
  927. AddHandler BtnSave.Click, AddressOf LiteLoaderSave_Click
  928. If sender.Tag.IsLegacy Then
  929. sender.PaddingRight = 30
  930. sender.Buttons = {BtnSave}
  931. Else
  932. sender.PaddingRight = 60
  933. Dim BtnList As New MyIconButton With {.Logo = Logo.IconButtonList, .ToolTip = "查看全部版本", .Tag = sender}
  934. ToolTipService.SetPlacement(BtnList, Primitives.PlacementMode.Center)
  935. ToolTipService.SetVerticalOffset(BtnList, 30)
  936. ToolTipService.SetHorizontalOffset(BtnList, 2)
  937. AddHandler BtnList.Click, AddressOf LiteLoaderAll_Click
  938. sender.Buttons = {BtnSave, BtnList}
  939. End If
  940. End Sub
  941. Private Sub LiteLoaderAll_Click(sender As Object, e As RoutedEventArgs)
  942. Dim Version As DlLiteLoaderListEntry
  943. If TypeOf sender.Tag Is DlLiteLoaderListEntry Then
  944. Version = sender.Tag
  945. Else
  946. Version = sender.Tag.Tag
  947. End If
  948. OpenWebsite("http://jenkins.liteloader.com/view/" & Version.Inherit)
  949. End Sub
  950. Public Sub LiteLoaderSave_Click(sender As Object, e As RoutedEventArgs)
  951. 'ListItem 与小按钮都会调用这个方法
  952. Dim Version As DlLiteLoaderListEntry
  953. If TypeOf sender.Tag Is DlLiteLoaderListEntry Then
  954. Version = sender.Tag
  955. Else
  956. Version = sender.Tag.Tag
  957. End If
  958. McDownloadLiteLoaderSave(Version)
  959. End Sub
  960. #End Region
  961. #Region "Forge 下载"
  962. Public Sub McDownloadForge(DownloadInfo As DlForgeVersionEntry)
  963. '老版本提示
  964. If DownloadInfo.Category = "client" Then
  965. If MyMsgBox("该 Forge 版本过于古老,PCL 暂不支持该版本的自动安装。" & vbCrLf &
  966. "若你仍然希望继续,PCL 将把安装程序下载到你指定的位置,但不会进行安装。",
  967. "版本过老", "继续", "取消") = 1 Then
  968. McDownloadForgeSave(DownloadInfo)
  969. End If
  970. Exit Sub
  971. End If
  972. If DownloadInfo.Category = "universal" OrElse DownloadInfo.Inherit.StartsWith("1.5") Then '对该版本自动安装的支持将在之后加入
  973. If MyMsgBox("该 Forge 版本过于古老,PCL 暂不支持该版本的自动安装。" & vbCrLf &
  974. "若你仍然希望继续,PCL 将把安装程序下载到你指定的位置,但不会进行安装。",
  975. "版本过老", "继续", "取消") = 1 Then
  976. McDownloadForgeSave(DownloadInfo)
  977. End If
  978. Exit Sub
  979. End If
  980. '初始化参数
  981. Dim Id As String = DownloadInfo.Inherit & "-forge-" & DownloadInfo.Version
  982. Dim Target As String = PathTemp & "Cache\Code\ForgeInstall-" & DownloadInfo.Version & "_" & GetUuid() & "." & DownloadInfo.FileSuffix
  983. Dim VersionFolder As String = PathMcFolder & "versions\" & Id & "\"
  984. Dim DisplayName As String = "Forge " & DownloadInfo.Inherit & " - " & DownloadInfo.Version
  985. Try
  986. '重复任务检查
  987. SyncLock LoaderTaskbarLock
  988. For i = 0 To LoaderTaskbar.Count - 1
  989. If LoaderTaskbar(i).Name = DisplayName & " 下载" Then
  990. Hint("该版本正在下载中!", HintType.Critical)
  991. Exit Sub
  992. End If
  993. Next
  994. End SyncLock
  995. '已有版本检查
  996. If File.Exists(VersionFolder & Id & ".json") Then
  997. If MyMsgBox("版本 " & Id & " 已存在,是否重新下载?" & vbCrLf & "这会覆盖版本的 json 和 jar 文件,但不会影响版本隔离的文件。", "版本已存在", "继续", "取消") = 1 Then
  998. File.Delete(VersionFolder & Id & ".jar")
  999. File.Delete(VersionFolder & Id & ".json")
  1000. Else
  1001. Exit Sub
  1002. End If
  1003. End If
  1004. '启动
  1005. Dim Loader As New LoaderCombo(Of String)(DisplayName & " 下载", McDownloadForgeLoader(DownloadInfo.Version, DownloadInfo.Inherit, DownloadInfo)) With {.OnStateChanged = AddressOf McInstallState}
  1006. Loader.Start(VersionFolder)
  1007. LoaderTaskbarAdd(Loader)
  1008. FrmMain.BtnExtraDownload.ShowRefresh()
  1009. FrmMain.BtnExtraDownload.Ribble()
  1010. Catch ex As Exception
  1011. Log(ex, "开始 Forge 下载失败", LogLevel.Feedback)
  1012. Finally
  1013. '删除安装包
  1014. Try
  1015. If File.Exists(Target) Then File.Delete(Target)
  1016. Catch
  1017. End Try
  1018. End Try
  1019. End Sub
  1020. Public Sub McDownloadForgeSave(DownloadInfo As DlForgeVersionEntry)
  1021. Try
  1022. Dim Target As String = SelectAs("选择保存位置", DownloadInfo.FileName, "Forge 文件 (*." & DownloadInfo.FileSuffix & ")|*." & DownloadInfo.FileSuffix)
  1023. Dim DisplayName As String = "Forge " & DownloadInfo.Inherit & " - " & DownloadInfo.Version
  1024. If Not Target.Contains("\") Then Exit Sub
  1025. '重复任务检查
  1026. SyncLock LoaderTaskbarLock
  1027. For i = 0 To LoaderTaskbar.Count - 1
  1028. If LoaderTaskbar(i).Name = DisplayName & " 下载" Then
  1029. Hint("该版本正在下载中!", HintType.Critical)
  1030. Exit Sub
  1031. End If
  1032. Next
  1033. End SyncLock
  1034. '构造步骤加载器
  1035. Dim Loaders As New List(Of LoaderBase)
  1036. '获取下载地址
  1037. Loaders.Add(New LoaderTask(Of DlForgeVersionEntry, List(Of NetFile))("获取下载地址",
  1038. Sub(Task As LoaderTask(Of DlForgeVersionEntry, List(Of NetFile)))
  1039. Dim Files As New List(Of NetFile)
  1040. Files.Add(New NetFile({
  1041. "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
  1042. "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
  1043. "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" & DownloadInfo.Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
  1044. }, Target, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash)))
  1045. Task.Output = Files
  1046. End Sub) With {.ProgressWeight = 0.1, .Show = False})
  1047. '下载
  1048. Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile)) With {.ProgressWeight = 6})
  1049. '启动
  1050. Dim Loader As New LoaderCombo(Of DlForgeVersionEntry)(DisplayName & " 下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
  1051. Loader.Start(DownloadInfo)
  1052. LoaderTaskbarAdd(Loader)
  1053. FrmMain.BtnExtraDownload.ShowRefresh()
  1054. FrmMain.BtnExtraDownload.Ribble()
  1055. Catch ex As Exception
  1056. Log(ex, "开始 Forge 下载失败", LogLevel.Feedback)
  1057. End Try
  1058. End Sub
  1059. Private Sub ForgeInjector(Target As String, Task As LoaderTask(Of Boolean, Boolean), McFolder As String, UseJavaWrapper As Boolean)
  1060. '选择 Java
  1061. Dim Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
  1062. If Java Is Nothing Then
  1063. If Not JavaDownloadConfirm("Java 8 或更高版本") Then Throw New Exception("由于未找到 Java,已取消安装。")
  1064. '开始自动下载
  1065. Dim JavaLoader = JavaFixLoaders(17)
  1066. JavaLoader.Start(17, IsForceRestart:=True)
  1067. Do While JavaLoader.State = LoadState.Loading AndAlso Not Task.IsAborted
  1068. Thread.Sleep(10)
  1069. Loop
  1070. '检查下载结果
  1071. Java = JavaSelect("已取消安装。", New Version(1, 8, 0, 0))
  1072. If Task.IsAborted Then Exit Sub
  1073. If Java Is Nothing Then Throw New Exception("由于未找到 Java,已取消安装。")
  1074. End If
  1075. '添加 Java Wrapper 作为主 Jar
  1076. Dim Arguments As String
  1077. If UseJavaWrapper Then
  1078. Arguments = $"-Doolloo.jlw.tmpdir=""{GetJavaWrapperDir()}"" -cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" -jar ""{ExtractJavaWrapper()}"" com.bangbang93.ForgeInstaller ""{McFolder}"
  1079. Else
  1080. Arguments = $"-cp ""{PathTemp}Cache\forge_installer.jar;{Target}"" com.bangbang93.ForgeInstaller ""{McFolder}"
  1081. End If
  1082. If Java.VersionCode >= 9 Then Arguments = "--add-exports cpw.mods.bootstraplauncher/cpw.mods.bootstraplauncher=ALL-UNNAMED " & Arguments
  1083. '开始启动
  1084. SyncLock InstallSyncLock
  1085. Dim Info = New ProcessStartInfo With {
  1086. .FileName = Java.PathJavaw,
  1087. .Arguments = Arguments,
  1088. .UseShellExecute = False,
  1089. .CreateNoWindow = True,
  1090. .RedirectStandardError = True,
  1091. .RedirectStandardOutput = True
  1092. }
  1093. Log("[Download] 开始安装 Forge:" & Arguments)
  1094. Dim process As New Process With {.StartInfo = Info}
  1095. Dim LastResults As New Queue(Of String)
  1096. Using outputWaitHandle As New AutoResetEvent(False)
  1097. Using errorWaitHandle As New AutoResetEvent(False)
  1098. AddHandler process.OutputDataReceived, Function(sender, e)
  1099. Try
  1100. If e.Data Is Nothing Then
  1101. outputWaitHandle.[Set]()
  1102. Else
  1103. LastResults.Enqueue(e.Data)
  1104. If LastResults.Count > 100 Then LastResults.Dequeue()
  1105. ForgeInjectorLine(e.Data, Task)
  1106. End If
  1107. Catch ex As ObjectDisposedException
  1108. Catch ex As Exception
  1109. Log(ex, "读取 Forge 安装器信息失败")
  1110. End Try
  1111. Try
  1112. If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
  1113. Log("[Installer] 由于任务取消,已中止 Forge 安装")
  1114. process.Kill()
  1115. End If
  1116. Catch
  1117. End Try
  1118. Return Nothing
  1119. End Function
  1120. AddHandler process.ErrorDataReceived, Function(sender, e)
  1121. Try
  1122. If e.Data Is Nothing Then
  1123. errorWaitHandle.[Set]()
  1124. Else
  1125. LastResults.Enqueue(e.Data)
  1126. If LastResults.Count > 100 Then LastResults.Dequeue()
  1127. ForgeInjectorLine(e.Data, Task)
  1128. End If
  1129. Catch ex As ObjectDisposedException
  1130. Catch ex As Exception
  1131. Log(ex, "读取 Forge 安装器错误信息失败")
  1132. End Try
  1133. Try
  1134. If Task.State = LoadState.Aborted AndAlso Not process.HasExited Then
  1135. Log("[Installer] 由于任务取消,已中止 Forge 安装")
  1136. process.Kill()
  1137. End If
  1138. Catch
  1139. End Try
  1140. Return Nothing
  1141. End Function
  1142. process.Start()
  1143. process.BeginOutputReadLine()
  1144. process.BeginErrorReadLine()
  1145. '等待
  1146. Do Until process.HasExited
  1147. Thread.Sleep(10)
  1148. Loop
  1149. '输出
  1150. outputWaitHandle.WaitOne(10000)
  1151. errorWaitHandle.WaitOne(10000)
  1152. process.Dispose()
  1153. '检查是否安装成功:最后两行中是否有 true(true 可能在倒数第二行,见 #832)
  1154. If LastResults.Last = "true" OrElse (LastResults.Count >= 2 AndAlso LastResults(LastResults.Count - 2) = "true") Then Exit Sub
  1155. Log(Join(LastResults, vbCrLf))
  1156. Dim LastLines As String = ""
  1157. For i As Integer = Math.Max(0, LastResults.Count - 5) To LastResults.Count - 1 '最后 5 行
  1158. LastLines &= vbCrLf & LastResults(i)
  1159. Next
  1160. Throw New Exception("Forge 安装器出错,日志结束部分为:" & LastLines)
  1161. End Using
  1162. End Using
  1163. End SyncLock
  1164. End Sub
  1165. Private Sub ForgeInjectorLine(Content As String, Task As LoaderTask(Of Boolean, Boolean))
  1166. If Content.StartsWith(" Data") OrElse Content.StartsWith(" Slim") Then
  1167. If ModeDebug Then Log("[Installer] " & Content)
  1168. ElseIf Content.StartsWith(" Reading patch ") Then
  1169. If ModeDebug Then Log("[Installer] " & Content)
  1170. Task.Progress = 0.86
  1171. Else
  1172. Select Case Content
  1173. Case "Extracting json"
  1174. Log("[Installer] " & Content)
  1175. Task.Progress = 0.07
  1176. Case "Downloading libraries"
  1177. Log("[Installer] " & Content)
  1178. Task.Progress = 0.08
  1179. Case " File exists: Checksum validated."
  1180. If ModeDebug Then Log("[Installer] " & Content)
  1181. Task.Progress += 0.003
  1182. Case "Building Processors"
  1183. Task.Progress = 0.38
  1184. Case "Task: DOWNLOAD_MOJMAPS" 'B
  1185. Task.Progress = 0.4
  1186. Case "Task: MERGE_MAPPING" 'B
  1187. Task.Progress = 0.6
  1188. Case "Splitting: "
  1189. Task.Progress = 0.62
  1190. Case "Parameter Annotations" 'B
  1191. Task.Progress = 0.66
  1192. Case "Processing Complete" 'B
  1193. Task.Progress = 0.7
  1194. Case "Remapping final jar" 'A
  1195. Task.Progress = 0.72
  1196. Case "Remapping jar... 50%" 'A
  1197. Task.Progress = 0.76
  1198. Case "Remapping jar... 100%" 'A
  1199. Task.Progress = 0.81
  1200. Case "Injecting profile"
  1201. Task.Progress = 0.91
  1202. Case Else
  1203. If ModeDebug Then Log("[Installer] " & Content)
  1204. Exit Sub
  1205. End Select
  1206. Log("[Installer] " & Content)
  1207. End If
  1208. End Sub
  1209. ''' <summary>
  1210. ''' 获取下载某个 Forge 版本的加载器列表。
  1211. ''' </summary>
  1212. Private Function McDownloadForgeLoader(Version As String, Inherit As String, Optional DownloadInfo As DlForgeVersionEntry = Nothing, Optional McFolder As String = Nothing, Optional ClientDownloadLoader As LoaderCombo(Of String) = Nothing, Optional ClientFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
  1213. '参数初始化
  1214. McFolder = If(McFolder, PathMcFolder)
  1215. If Version.StartsWith("1.") AndAlso Version.Contains("-") Then
  1216. '类似 1.19.3-41.2.8 格式,优先使用 Version 中要求的版本而非 Inherit(例如 1.19.3 却使用了 1.19 的 Forge)
  1217. Inherit = Version.Split("-").First
  1218. Version = Version.Split("-").Last
  1219. End If
  1220. Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder
  1221. Dim Id As String = Inherit & "-forge-" & Version
  1222. Dim InstallerAddress As String = PathTemp & "Cache\Code\ForgeInstall-" & Version & "_" & RandomInteger(0, 100000)
  1223. Dim VersionFolder As String = McFolder & "versions\" & Id & "\"
  1224. Dim DisplayName As String = "Forge " & Inherit & " - " & Version
  1225. Dim Loaders As New List(Of LoaderBase)
  1226. Dim LibVersionFolder As String = PathMcFolder & "versions\" & Id & "\" '作为 Lib 文件目标的版本文件夹
  1227. '获取下载信息
  1228. If DownloadInfo Is Nothing Then
  1229. Loaders.Add(New LoaderTask(Of String, String)("获取 Mod 加载器详细信息",
  1230. Sub(Task As LoaderTask(Of String, String))
  1231. '获取 Forge 版本列表
  1232. Dim ForgeLoader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("McDownloadForgeLoader " & Inherit, AddressOf DlForgeVersionMain)
  1233. ForgeLoader.WaitForExit(Inherit)
  1234. Task.Progress = 0.8
  1235. '查找对应版本
  1236. For Each ForgeVersion In ForgeLoader.Output
  1237. If ForgeVersion.Version = Version Then
  1238. DownloadInfo = ForgeVersion
  1239. Exit Sub
  1240. End If
  1241. Next
  1242. Throw New Exception("未能找到 Forge " & Inherit & "-" & Version & " 的详细信息!")
  1243. End Sub) With {.ProgressWeight = 3})
  1244. End If
  1245. '下载 Forge 主文件
  1246. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("准备 Mod 加载器下载",
  1247. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  1248. '启动依赖版本的下载
  1249. If ClientDownloadLoader Is Nothing Then
  1250. If IsCustomFolder Then Throw New Exception("如果没有指定原版下载器,则不能指定 MC 安装文件夹")
  1251. ClientDownloadLoader = McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, Inherit)
  1252. End If
  1253. '添加主文件
  1254. Dim Files As New List(Of NetFile) From {New NetFile({
  1255. "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
  1256. "http://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName,
  1257. "https://download.mcbbs.net/maven/net/minecraftforge/forge/" & Inherit & "-" & DownloadInfo.FileVersion & "/" & DownloadInfo.FileName
  1258. }, InstallerAddress, New FileChecker(MinSize:=64 * 1024, Hash:=DownloadInfo.Hash))}
  1259. Task.Output = Files
  1260. End Sub) With {.ProgressWeight = 0.5, .Show = False})
  1261. Loaders.Add(New LoaderDownload("下载 Mod 加载器主文件", New List(Of NetFile)) With {.ProgressWeight = 9})
  1262. '安装(仅在新版安装时需要原版 Jar)
  1263. If Version.Split(".")(0) >= 20 Then
  1264. Log("[Download] 检测为新版 Forge:" & Version)
  1265. Dim Libs As List(Of McLibToken) = Nothing
  1266. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 Mod 加载器支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  1267. Task.Output = New List(Of NetFile)
  1268. Dim Installer As ZipArchive = Nothing
  1269. Try
  1270. '解压并获取、合并两个 Json 的信息
  1271. Installer = New ZipArchive(New FileStream(InstallerAddress, FileMode.Open))
  1272. Task.Progress = 0.2
  1273. Dim Json As JObject = GetJson(ReadFile(Installer.GetEntry("install_profile.json").Open))
  1274. Dim Json2 As JObject = GetJson(ReadFile(Installer.GetEntry("version.json").Open))
  1275. Json.Merge(Json2)
  1276. '获取 Lib 下载信息
  1277. Libs = McLibListGetWithJson(Json, True)
  1278. '添加 Mappings 下载信息
  1279. If Json("data") IsNot Nothing AndAlso Json("data")("MOJMAPS") IsNot Nothing Then
  1280. '下载原版 Json 文件
  1281. Task.Progress = 0.4
  1282. Log("[Download] 需要 Mappings 下载信息,开始获取原版 Json 文件")
  1283. Dim RawJson As JObject = GetJson(NetGetCodeByDownload(DlSourceLauncherOrMetaGet(DlClientListGet(Inherit)), IsJson:=True))
  1284. '[net.minecraft:client:1.17.1-20210706.113038:mappings@txt]
  1285. Dim OriginalName As String = Json("data")("MOJMAPS")("client").ToString.Trim("[]".ToCharArray()).Split("@")(0)
  1286. Dim Address = McLibGet(OriginalName).Replace(".jar", "-mappings.txt")
  1287. Libs.Add(New McLibToken With {.IsJumpLoader = False, .IsNatives = False, .LocalPath = Address, .OriginalName = OriginalName,
  1288. .Url = RawJson("downloads")("client_mappings")("url"), .Size = RawJson("downloads")("client_mappings")("size"), .SHA1 = RawJson("downloads")("client_mappings")("sha1")})
  1289. End If
  1290. Task.Progress = 0.8
  1291. '去除其中的原始 Forge 项
  1292. For i = 0 To Libs.Count - 1
  1293. If Libs(i).LocalPath.EndsWith("forge-" & Inherit & "-" & Version & ".jar") Then
  1294. Libs.RemoveAt(i)
  1295. Exit For
  1296. End If
  1297. Next
  1298. Task.Output = McLibFixFromLibToken(Libs, PathMcFolder)
  1299. Catch ex As Exception
  1300. Throw New Exception("获取新版 Forge 支持库列表失败", ex)
  1301. Finally
  1302. '释放文件
  1303. If Installer IsNot Nothing Then Installer.Dispose()
  1304. End Try
  1305. End Sub) With {.ProgressWeight = 2})
  1306. Loaders.Add(New LoaderDownload("下载 Mod 加载器支持库文件", New List(Of NetFile)) With {.ProgressWeight = 12})
  1307. Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("获取 Mod 下载器支持库文件",
  1308. Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
  1309. #Region "Forge 文件"
  1310. If IsCustomFolder Then
  1311. For Each LibFile As McLibToken In Libs
  1312. Dim RealPath As String = LibFile.LocalPath.Replace(PathMcFolder, McFolder)
  1313. If Not File.Exists(RealPath) Then
  1314. Directory.CreateDirectory(IO.Path.GetDirectoryName(RealPath))
  1315. CopyFile(LibFile.LocalPath, RealPath)
  1316. End If
  1317. If ModeDebug Then Log("[Download] 复制的 Forge 支持库文件:" & LibFile.LocalPath)
  1318. Next
  1319. End If
  1320. #End Region
  1321. #Region "原版文件"
  1322. '等待原版文件下载完成
  1323. If ClientDownloadLoader Is Nothing Then Exit Sub
  1324. Dim TargetLoaders As List(Of LoaderBase) =
  1325. ClientDownloadLoader.GetLoaderList.Where(Function(l) l.Name = McDownloadClientLibName OrElse l.Name = McDownloadClientJsonName).
  1326. Where(Function(l) l.State <> LoadState.Finished).ToList
  1327. If TargetLoaders.Count > 0 Then Log("[Download] Forge 安装正在等待原版文件下载完成")
  1328. Do While TargetLoaders.Count > 0 AndAlso Not Task.IsAborted
  1329. TargetLoaders = TargetLoaders.Where(Function(l) l.State <> LoadState.Finished).ToList
  1330. Thread.Sleep(50)
  1331. Loop
  1332. If Task.IsAborted Then Exit Sub
  1333. '拷贝原版文件
  1334. If Not IsCustomFolder Then Exit Sub
  1335. SyncLock VanillaSyncLock
  1336. Dim ClientName As String = GetFolderNameFromPath(ClientFolder)
  1337. Directory.CreateDirectory(McFolder & "versions\" & Inherit)
  1338. If Not File.Exists(McFolder & "versions\" & Inherit & "\" & Inherit & ".json") Then
  1339. CopyFile(ClientFolder & ClientName & ".json", McFolder & "versions\" & Inherit & "\" & Inherit & ".json")
  1340. End If
  1341. If Not File.Exists(McFolder & "versions\" & Inherit & "\" & Inherit & ".jar") Then
  1342. CopyFile(ClientFolder & ClientName & ".jar", McFolder & "versions\" & Inherit & "\" & Inherit & ".jar")
  1343. End If
  1344. End SyncLock
  1345. #End Region
  1346. End Sub) With {.ProgressWeight = 0.1, .Show = False})
  1347. Loaders.Add(New LoaderTask(Of Boolean, Boolean)("安装 Mod 加载器(方式 A)",
  1348. Sub(Task As LoaderTask(Of Boolean, Boolean))
  1349. Dim Installer As ZipArchive = Nothing
  1350. Try
  1351. Log("[Download] 开始进行新版方式 Forge 安装:" & InstallerAddress)
  1352. '记录当前文件夹列表(在新建目标文件夹之前)
  1353. Dim OldList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.Select(Function(Info As DirectoryInfo) As String
  1354. Return Info.FullName
  1355. End Function).ToList()
  1356. '解压并获取信息
  1357. Installer = New ZipArchive(New FileStream(InstallerAddress, FileMode.Open))
  1358. Dim Json As JObject = GetJson(ReadFile(Installer.GetEntry("install_profile.json").Open))
  1359. '新建目标版本文件夹
  1360. Directory.CreateDirectory(VersionFolder)
  1361. Task.Progress = 0.04
  1362. '释放 launcher_installer.json
  1363. McFolderLauncherProfilesJsonCreate(McFolder)
  1364. Task.Progress = 0.05
  1365. '运行 Forge 安装器
  1366. Dim UseJavaWrapper As Boolean = True
  1367. Retry:
  1368. Try
  1369. '释放 Forge 注入器
  1370. WriteFile(PathTemp & "Cache\forge_installer.jar", GetResources("ForgeInstaller"))
  1371. Task.Progress = 0.06
  1372. '运行注入器
  1373. ForgeInjector(InstallerAddress, Task, McFolder, UseJavaWrapper)
  1374. Task.Progress = 0.97
  1375. Catch ex As Exception
  1376. If UseJavaWrapper Then
  1377. Log(ex, "使用 JavaWrapper 安装 Forge 失败,将不使用 JavaWrapper 并重试")
  1378. UseJavaWrapper = False
  1379. GoTo Retry
  1380. Else
  1381. Throw New Exception("运行 Forge 安装器失败", ex)
  1382. End If
  1383. End Try
  1384. '拷贝新增的版本 Json
  1385. Dim DeltaList = New DirectoryInfo(McFolder & "versions\").EnumerateDirectories.SkipWhile(Function(Info As DirectoryInfo) As Boolean
  1386. Return OldList.Contains(Info.FullName)
  1387. End Function).ToList()
  1388. If DeltaList.Count > 1 Then '它可能和 OptiFine 安装同时运行,导致增加的文件不止一个(这导致了 #151)
  1389. DeltaList = New List(Of DirectoryInfo) From {DeltaList.Where(Function(l) l.Name.Contains("forge")).First}
  1390. End If
  1391. If DeltaList.Count = 1 Then '如果没有新增文件夹,那么预测的文件夹名就是正确的
  1392. Dim JsonFile As FileInfo = DeltaList(0).EnumerateFiles.First()
  1393. WriteFile(VersionFolder & Id & ".json", ReadFile(JsonFile.FullName))
  1394. End If
  1395. '新建 mods 文件夹
  1396. Directory.CreateDirectory(New McVersion(VersionFolder).GetPathIndie(True) & "mods\")
  1397. Catch ex As Exception
  1398. Throw New Exception("安装新 Forge 版本失败", ex)
  1399. Finally
  1400. '清理文件
  1401. Try
  1402. If Installer IsNot Nothing Then Installer.Dispose()
  1403. If File.Exists(InstallerAddress) Then File.Delete(InstallerAddress)
  1404. Catch ex As Exception
  1405. Log(ex, "安装 Forge 清理文件时出错")
  1406. End Try
  1407. End Try
  1408. End Sub) With {.ProgressWeight = 10})
  1409. Else
  1410. Log("[Download] 检测为非新版 Forge:" & Version)
  1411. Loaders.Add(New LoaderTask(Of List(Of NetFile), Boolean)("安装 Mod 加载器(方式 B)",
  1412. Sub(Task As LoaderTask(Of List(Of NetFile), Boolean))
  1413. Dim Installer As ZipArchive = Nothing
  1414. Try
  1415. '解压并获取信息
  1416. Installer = New ZipArchive(New FileStream(InstallerAddress, FileMode.Open))
  1417. Task.Progress = 0.2
  1418. Dim Json As JObject = GetJson(ReadFile(Installer.GetEntry("install_profile.json").Open))
  1419. Task.Progress = 0.4
  1420. '新建版本文件夹
  1421. Directory.CreateDirectory(VersionFolder)
  1422. Task.Progress = 0.5
  1423. If Json("install") Is Nothing Then
  1424. '中版格式
  1425. Log("[Download] 进行中版方式安装:" & InstallerAddress)
  1426. '建立 Json 文件
  1427. Dim JsonVersion As JObject = GetJson(ReadFile(Installer.GetEntry(Json("json").ToString.TrimStart("/")).Open))
  1428. JsonVersion("id") = Id
  1429. WriteFile(VersionFolder & Id & ".json", JsonVersion.ToString)
  1430. Task.Progress = 0.6
  1431. '解压支持库文件
  1432. Installer.Dispose()
  1433. ExtractFile(InstallerAddress, InstallerAddress & "_unrar\")
  1434. CopyDirectory(InstallerAddress & "_unrar\maven\", McFolder & "libraries\")
  1435. DeleteDirectory(InstallerAddress & "_unrar\")
  1436. Else
  1437. '旧版格式
  1438. Log("[Download] 进行旧版方式安装:" & InstallerAddress)
  1439. '解压 Jar 文件
  1440. Dim JarAddress As String = McLibGet(Json("install")("path"), CustomMcFolder:=McFolder)
  1441. If File.Exists(JarAddress) Then File.Delete(JarAddress)
  1442. WriteFile(JarAddress, Installer.GetEntry(Json("install")("filePath")).Open)
  1443. Task.Progress = 0.9
  1444. '建立 Json 文件
  1445. Json("versionInfo")("id") = Id
  1446. If Json("versionInfo")("inheritsFrom") Is Nothing Then CType(Json("versionInfo"), JObject).Add("inheritsFrom", Inherit)
  1447. WriteFile(VersionFolder & Id & ".json", Json("versionInfo").ToString)
  1448. End If
  1449. '新建 mods 文件夹
  1450. Directory.CreateDirectory(New McVersion(VersionFolder).GetPathIndie(True) & "mods\")
  1451. Catch ex As Exception
  1452. Throw New Exception("非新版方式安装 Forge 失败", ex)
  1453. Finally
  1454. Try
  1455. '清理文件
  1456. If Installer IsNot Nothing Then Installer.Dispose()
  1457. If File.Exists(InstallerAddress) Then File.Delete(InstallerAddress)
  1458. If Directory.Exists(InstallerAddress & "_unrar\") Then DeleteDirectory(InstallerAddress & "_unrar\")
  1459. Catch ex As Exception
  1460. Log(ex, "非新版方式安装 Forge 清理文件时出错")
  1461. End Try
  1462. End Try
  1463. End Sub) With {.ProgressWeight = 1})
  1464. If FixLibrary Then
  1465. If IsCustomFolder Then Throw New Exception("若需要补全支持库,就不能自定义 MC 文件夹")
  1466. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 Mod 加载器支持库文件", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
  1467. Loaders.Add(New LoaderDownload("下载 Mod 加载器支持库文件", New List(Of NetFile)) With {.ProgressWeight = 11})
  1468. End If
  1469. End If
  1470. Return Loaders
  1471. End Function
  1472. #End Region
  1473. #Region "Forge 下载菜单"
  1474. Public Sub ForgeDownloadListItemPreload(Stack As StackPanel, Entrys As List(Of DlForgeVersionEntry), OnClick As MyListItem.ClickEventHandler, IsSaveOnly As Boolean)
  1475. '获取推荐版本与最新版本
  1476. Dim FreshVersion As DlForgeVersionEntry = Nothing
  1477. If Entrys.Count > 0 Then
  1478. FreshVersion = Entrys(0)
  1479. Else
  1480. Log("[System] 未找到可用的 Forge 版本", LogLevel.Debug)
  1481. End If
  1482. Dim RecommendedVersion As DlForgeVersionEntry = Nothing
  1483. For Each Entry In Entrys
  1484. If Entry.IsRecommended Then RecommendedVersion = Entry
  1485. Next
  1486. '若推荐版本与最新版本为同一版本,则仅显示推荐版本
  1487. If FreshVersion IsNot Nothing AndAlso FreshVersion Is RecommendedVersion Then FreshVersion = Nothing
  1488. '显示各个版本
  1489. If RecommendedVersion IsNot Nothing Then
  1490. Dim Recommended = ForgeDownloadListItem(RecommendedVersion, OnClick, IsSaveOnly)
  1491. Recommended.Info = "推荐版" & If(Recommended.Info = "", "", "," & Recommended.Info)
  1492. Stack.Children.Add(Recommended)
  1493. End If
  1494. If FreshVersion IsNot Nothing Then
  1495. Dim Fresh = ForgeDownloadListItem(FreshVersion, OnClick, IsSaveOnly)
  1496. Fresh.Info = "最新版" & If(Fresh.Info = "", "", "," & Fresh.Info)
  1497. Stack.Children.Add(Fresh)
  1498. End If
  1499. '添加间隔
  1500. Stack.Children.Add(New TextBlock With {.Text = "全部版本 (" & Entrys.Count & ")", .HorizontalAlignment = HorizontalAlignment.Left, .Margin = New Thickness(6, 13, 0, 4)})
  1501. End Sub
  1502. Public Function ForgeDownloadListItem(Entry As DlForgeVersionEntry, OnClick As MyListItem.ClickEventHandler, IsSaveOnly As Boolean) As MyListItem
  1503. '建立控件
  1504. Dim NewItem As New MyListItem With {
  1505. .Title = Entry.Version, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60,
  1506. .Info = If(Entry.ReleaseTime = "",
  1507. If(ModeDebug, "种类:" & Entry.Category & If(Entry.Branch Is Nothing, "", ",开发分支:" & Entry.Branch), ""),
  1508. "发布于 " & Entry.ReleaseTime & If(ModeDebug, ",种类:" & Entry.Category & If(Entry.Branch Is Nothing, "", ",开发分支:" & Entry.Branch), "")),
  1509. .Logo = "pack://application:,,,/images/Blocks/Anvil.png"
  1510. }
  1511. AddHandler NewItem.Click, OnClick
  1512. '建立菜单
  1513. If IsSaveOnly Then
  1514. NewItem.ContentHandler = AddressOf ForgeSaveContMenuBuild
  1515. Else
  1516. NewItem.ContentHandler = AddressOf ForgeContMenuBuild
  1517. End If
  1518. '结束
  1519. Return NewItem
  1520. End Function
  1521. Private Sub ForgeContMenuBuild(sender As MyListItem, e As EventArgs)
  1522. Dim BtnSave As New MyIconButton With {.Logo = Logo.IconButtonSave, .ToolTip = "另存为"}
  1523. ToolTipService.SetPlacement(BtnSave, Primitives.PlacementMode.Center)
  1524. ToolTipService.SetVerticalOffset(BtnSave, 30)
  1525. ToolTipService.SetHorizontalOffset(BtnSave, 2)
  1526. AddHandler BtnSave.Click, AddressOf ForgeSave_Click
  1527. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  1528. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  1529. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  1530. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  1531. AddHandler BtnInfo.Click, AddressOf ForgeLog_Click
  1532. sender.Buttons = {BtnSave, BtnInfo}
  1533. End Sub
  1534. Private Sub ForgeSaveContMenuBuild(sender As MyListItem, e As EventArgs)
  1535. Dim BtnInfo As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .ToolTip = "更新日志"}
  1536. ToolTipService.SetPlacement(BtnInfo, Primitives.PlacementMode.Center)
  1537. ToolTipService.SetVerticalOffset(BtnInfo, 30)
  1538. ToolTipService.SetHorizontalOffset(BtnInfo, 2)
  1539. AddHandler BtnInfo.Click, AddressOf ForgeLog_Click
  1540. sender.Buttons = {BtnInfo}
  1541. End Sub
  1542. Private Sub ForgeLog_Click(sender As Object, e As RoutedEventArgs)
  1543. Dim Version As DlForgeVersionEntry
  1544. If sender.Tag IsNot Nothing Then
  1545. Version = sender.Tag
  1546. ElseIf sender.Parent.Tag IsNot Nothing Then
  1547. Version = sender.Parent.Tag
  1548. Else
  1549. Version = sender.Parent.Parent.Tag
  1550. End If
  1551. OpenWebsite("http://files.minecraftforge.net/maven/net/minecraftforge/forge/" & Version.Inherit & "-" & Version.Version & "/forge-" & Version.Inherit & "-" & Version.Version & "-changelog.txt")
  1552. End Sub
  1553. Public Sub ForgeSave_Click(sender As Object, e As RoutedEventArgs)
  1554. Dim Version As DlForgeVersionEntry
  1555. If sender.Tag IsNot Nothing Then
  1556. Version = sender.Tag
  1557. ElseIf sender.Parent.Tag IsNot Nothing Then
  1558. Version = sender.Parent.Tag
  1559. Else
  1560. Version = sender.Parent.Parent.Tag
  1561. End If
  1562. McDownloadForgeSave(Version)
  1563. End Sub
  1564. #End Region
  1565. #Region "Forge 推荐版本获取"
  1566. ''' <summary>
  1567. ''' 尝试刷新 Forge 推荐版本缓存。
  1568. ''' </summary>
  1569. Public Sub McDownloadForgeRecommendedRefresh()
  1570. If IsForgeRecommendedRefreshed Then Exit Sub
  1571. IsForgeRecommendedRefreshed = True
  1572. RunInNewThread(Sub()
  1573. Try
  1574. Log("[Download] 刷新 Forge 推荐版本缓存开始")
  1575. Dim Result As String = NetGetCodeByDownload({"https://bmclapi2.bangbang93.com/forge/promos", "https://download.mcbbs.net/forge/promos"})
  1576. If Result.Length < 1000 Then Throw New Exception("获取的结果过短(" & Result & ")")
  1577. Dim ResultJson As JContainer = GetJson(Result)
  1578. '获取所有推荐版本列表
  1579. Dim RecommendedList As New List(Of String)
  1580. For Each Version As JObject In ResultJson
  1581. If Version("name") Is Nothing OrElse Version("build") Is Nothing Then Continue For
  1582. Dim Name As String = Version("name")
  1583. If Not Name.EndsWith("-recommended") Then Continue For
  1584. '内容为:"1.15.2":"31.2.0"
  1585. RecommendedList.Add("""" & Name.Replace("-recommended", """:""" & Version("build")("version").ToString & """"))
  1586. Next
  1587. If RecommendedList.Count < 5 Then Throw New Exception("获取的推荐版本数过少(" & Result & ")")
  1588. '保存
  1589. Dim CacheJson As String = "{" & Join(RecommendedList, ",") & "}"
  1590. WriteFile(PathTemp & "Cache\ForgeRecommendedList.json", CacheJson)
  1591. Log("[Download] 刷新 Forge 推荐版本缓存成功")
  1592. Catch ex As Exception
  1593. Log(ex, "刷新 Forge 推荐版本缓存失败")
  1594. End Try
  1595. End Sub, "ForgeRecommendedRefresh")
  1596. End Sub
  1597. Private IsForgeRecommendedRefreshed As Boolean = False
  1598. ''' <summary>
  1599. ''' 尝试获取某个 MC 版本对应的 Forge 推荐版本。如果不可用会返回 Nothing。
  1600. ''' </summary>
  1601. Public Function McDownloadForgeRecommendedGet(McVersion As String) As String
  1602. Try
  1603. If McVersion Is Nothing Then Return Nothing
  1604. Dim List As String = ReadFile(PathTemp & "Cache\ForgeRecommendedList.json")
  1605. If List Is Nothing OrElse List = "" Then
  1606. Log("[Download] 没有 Forge 推荐版本缓存文件")
  1607. Return Nothing
  1608. End If
  1609. Dim Json As JObject = GetJson(List)
  1610. If Json Is Nothing OrElse (Not If(McVersion, "null").Contains(".")) OrElse Not Json.ContainsKey(McVersion) Then Return Nothing
  1611. Return If(Json(McVersion), "").ToString
  1612. Catch ex As Exception
  1613. Log(ex, "获取 Forge 推荐版本失败(" & If(McVersion, "null") & ")", LogLevel.Feedback)
  1614. Return Nothing
  1615. End Try
  1616. End Function
  1617. #End Region
  1618. #Region "Fabric 下载"
  1619. Public Sub McDownloadFabricLoaderSave(DownloadInfo As JObject)
  1620. Try
  1621. Dim Url As String = DownloadInfo("url").ToString
  1622. Dim FileName As String = GetFileNameFromPath(Url)
  1623. Dim Version As String = GetFileNameFromPath(DownloadInfo("version").ToString)
  1624. Dim Target As String = SelectAs("选择保存位置", FileName, "Fabric 安装器 (*.jar)|*.jar")
  1625. If Not Target.Contains("\") Then Exit Sub
  1626. '重复任务检查
  1627. SyncLock LoaderTaskbarLock
  1628. For i = 0 To LoaderTaskbar.Count - 1
  1629. If LoaderTaskbar(i).Name = "Fabric " & Version & " 安装器下载" Then
  1630. Hint("该版本正在下载中!", HintType.Critical)
  1631. Exit Sub
  1632. End If
  1633. Next
  1634. End SyncLock
  1635. '构造步骤加载器
  1636. Dim Loaders As New List(Of LoaderBase)
  1637. '下载
  1638. 'BMCLAPI 不支持 Fabric Installer 下载
  1639. Dim Address As New List(Of String)
  1640. 'Address.Add(Url.Replace("maven.fabricmc.net", "download.mcbbs.net/maven"))
  1641. Address.Add(Url)
  1642. Loaders.Add(New LoaderDownload("下载主文件", New List(Of NetFile) From {New NetFile(Address.ToArray, Target, New FileChecker(MinSize:=1024 * 64))}) With {.ProgressWeight = 15})
  1643. '启动
  1644. Dim Loader As New LoaderCombo(Of JObject)("Fabric " & Version & " 安装器下载", Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
  1645. Loader.Start(DownloadInfo)
  1646. LoaderTaskbarAdd(Loader)
  1647. FrmMain.BtnExtraDownload.ShowRefresh()
  1648. FrmMain.BtnExtraDownload.Ribble()
  1649. Catch ex As Exception
  1650. Log(ex, "开始 Fabric 安装器下载失败", LogLevel.Feedback)
  1651. End Try
  1652. End Sub
  1653. ''' <summary>
  1654. ''' 获取下载某个 Fabric 版本的加载器列表。
  1655. ''' </summary>
  1656. Private Function McDownloadFabricLoader(FabricVersion As String, MinecraftName As String, Optional McFolder As String = Nothing, Optional FixLibrary As Boolean = True) As List(Of LoaderBase)
  1657. '参数初始化
  1658. McFolder = If(McFolder, PathMcFolder)
  1659. Dim IsCustomFolder As Boolean = McFolder <> PathMcFolder
  1660. Dim Id As String = "fabric-loader-" & FabricVersion & "-" & MinecraftName
  1661. Dim VersionFolder As String = McFolder & "versions\" & Id & "\"
  1662. Dim Loaders As New List(Of LoaderBase)
  1663. '下载 Json
  1664. MinecraftName = MinecraftName.Replace("∞", "infinite") '放在 ID 后面避免影响版本文件夹名称
  1665. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("获取 Fabric 主文件下载地址",
  1666. Sub(Task As LoaderTask(Of String, List(Of NetFile)))
  1667. '启动依赖版本的下载
  1668. If FixLibrary Then
  1669. McDownloadClient(NetPreDownloadBehaviour.ExitWhileExistsOrDownloading, MinecraftName)
  1670. End If
  1671. Task.Progress = 0.5
  1672. '构造文件请求
  1673. Task.Output = New List(Of NetFile) From {New NetFile({
  1674. "https://download.mcbbs.net/fabric-meta/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json",
  1675. "https://meta.fabricmc.net/v2/versions/loader/" & MinecraftName & "/" & FabricVersion & "/profile/json"
  1676. }, VersionFolder & Id & ".json", New FileChecker(IsJson:=True))}
  1677. '新建 mods 文件夹
  1678. Directory.CreateDirectory(New McVersion(VersionFolder).GetPathIndie(True) & "mods\")
  1679. End Sub) With {.ProgressWeight = 0.5})
  1680. Loaders.Add(New LoaderDownload("下载 Fabric 主文件", New List(Of NetFile)) With {.ProgressWeight = 2.5})
  1681. '下载支持库
  1682. If FixLibrary Then
  1683. Loaders.Add(New LoaderTask(Of String, List(Of NetFile))("分析 Fabric 支持库文件",
  1684. Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionFolder))) With {.ProgressWeight = 1, .Show = False})
  1685. Loaders.Add(New LoaderDownload("下载 Fabric 支持库文件", New List(Of NetFile)) With {.ProgressWeight = 8})
  1686. End If
  1687. Return Loaders
  1688. End Function
  1689. #End Region
  1690. #Region "Fabric 下载菜单"
  1691. Public Function FabricDownloadListItem(Entry As JObject, OnClick As MyListItem.ClickEventHandler) As MyListItem
  1692. '建立控件
  1693. Dim NewItem As New MyListItem With {
  1694. .Title = Entry("version").ToString.Replace("+build", ""), .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60,
  1695. .Info = If(Entry("stable").ToObject(Of Boolean), "稳定版", "测试版"),
  1696. .Logo = "pack://application:,,,/images/Blocks/Fabric.png"
  1697. }
  1698. AddHandler NewItem.Click, OnClick
  1699. '结束
  1700. Return NewItem
  1701. End Function
  1702. Public Function FabricApiDownloadListItem(Entry As CompFile, OnClick As MyListItem.ClickEventHandler) As MyListItem
  1703. '建立控件
  1704. Dim NewItem As New MyListItem With {
  1705. .Title = Entry.DisplayName.Split("]")(1).Replace("Fabric API ", "").Replace(" build ", ".").Split("+").First.Trim, .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60,
  1706. .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy/MM/dd HH:mm"),
  1707. .Logo = "pack://application:,,,/images/Blocks/Fabric.png"
  1708. }
  1709. AddHandler NewItem.Click, OnClick
  1710. '结束
  1711. Return NewItem
  1712. End Function
  1713. Public Function OptiFabricDownloadListItem(Entry As CompFile, OnClick As MyListItem.ClickEventHandler) As MyListItem
  1714. '建立控件
  1715. Dim NewItem As New MyListItem With {
  1716. .Title = Entry.DisplayName.ToLower.Replace("optifabric-", "").Replace(".jar", "").Trim.TrimStart("v"), .SnapsToDevicePixels = True, .Height = 42, .Type = MyListItem.CheckType.Clickable, .Tag = Entry, .PaddingRight = 60,
  1717. .Info = Entry.StatusDescription & ",发布于 " & Entry.ReleaseDate.ToString("yyyy/MM/dd HH:mm"),
  1718. .Logo = "pack://application:,,,/images/Blocks/OptiFabric.png"
  1719. }
  1720. AddHandler NewItem.Click, OnClick
  1721. '结束
  1722. Return NewItem
  1723. End Function
  1724. #End Region
  1725. #Region "合并安装"
  1726. ''' <summary>
  1727. ''' 安装请求。
  1728. ''' </summary>
  1729. Public Class McInstallRequest
  1730. ''' <summary>
  1731. ''' 必填。安装目标版本名称。
  1732. ''' </summary>
  1733. Public TargetVersionName As String
  1734. ''' <summary>
  1735. ''' 必填。安装目标文件夹。
  1736. ''' </summary>
  1737. Public TargetVersionFolder As String
  1738. ''' <summary>
  1739. ''' 必填。欲下载的 Minecraft 的版本名。
  1740. ''' </summary>
  1741. Public MinecraftName As String = Nothing
  1742. ''' <summary>
  1743. ''' 可选。欲下载的 Minecraft Json 地址。
  1744. ''' </summary>
  1745. Public MinecraftJson As String = Nothing
  1746. '若要下载 OptiFine,则需要在下面两项中完成至少一项
  1747. ''' <summary>
  1748. ''' 欲下载的 OptiFine 版本名。例如 HD_U_F6_pre1。
  1749. ''' </summary>
  1750. Public OptiFineVersion As String = Nothing
  1751. ''' <summary>
  1752. ''' 欲下载的 OptiFine 详细信息。
  1753. ''' </summary>
  1754. Public OptiFineEntry As DlOptiFineListEntry = Nothing
  1755. '若要下载 Forge,则需要在下面两项中完成至少一项
  1756. ''' <summary>
  1757. ''' 欲下载的 Forge 版本名。接受例如 36.1.4 / 14.23.5.2859 / 1.19-41.1.0 的输入。
  1758. ''' </summary>
  1759. Public ForgeVersion As String = Nothing
  1760. ''' <summary>
  1761. ''' 欲下载的 Forge。
  1762. ''' </summary>
  1763. Public ForgeEntry As DlForgeVersionEntry = Nothing
  1764. ''' <summary>
  1765. ''' 欲下载的 Fabric Loader 版本名。
  1766. ''' </summary>
  1767. Public FabricVersion As String = Nothing
  1768. ''' <summary>
  1769. ''' 欲下载的 Fabric API 信息。
  1770. ''' </summary>
  1771. Public FabricApi As CompFile = Nothing
  1772. ''' <summary>
  1773. ''' 欲下载的 OptiFabric 信息。
  1774. ''' </summary>
  1775. Public OptiFabric As CompFile = Nothing
  1776. ''' <summary>
  1777. ''' 欲下载的 LiteLoader 详细信息。
  1778. ''' </summary>
  1779. Public LiteLoaderEntry As DlLiteLoaderListEntry = Nothing
  1780. End Class
  1781. ''' <summary>
  1782. ''' 安装加载器状态改变后进行提示和重载文件夹列表的方法。
  1783. ''' </summary>
  1784. Public Sub McInstallState(Loader)
  1785. Select Case Loader.State
  1786. Case LoadState.Finished
  1787. WriteIni(PathMcFolder & "PCL.ini", "VersionCache", "") '清空缓存(合并安装会先生成文件夹,这会在刷新时误判为可以使用缓存)
  1788. Hint(Loader.Name & "成功!", HintType.Finish)
  1789. Case LoadState.Failed
  1790. Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
  1791. Case LoadState.Aborted
  1792. Hint(Loader.Name & "已取消!", HintType.Info)
  1793. Case LoadState.Loading
  1794. Exit Sub '不重新加载版本列表
  1795. End Select
  1796. McInstallFailedClearFolder(Loader)
  1797. LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\")
  1798. End Sub
  1799. ''' <summary>
  1800. ''' 安装加载器状态改变后进行提示的方法。它不会在失败时删除文件夹,也不会刷新 MC 文件夹。
  1801. ''' </summary>
  1802. Public Sub DownloadStateSave(Loader)
  1803. Select Case Loader.State
  1804. Case LoadState.Finished
  1805. Hint(Loader.Name & "成功!", HintType.Finish)
  1806. Case LoadState.Failed
  1807. Hint(Loader.Name & "失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
  1808. Case LoadState.Aborted
  1809. Hint(Loader.Name & "已取消!", HintType.Info)
  1810. Case LoadState.Loading
  1811. Exit Sub '不重新加载版本列表
  1812. End Select
  1813. End Sub
  1814. Public Sub McInstallFailedClearFolder(Loader)
  1815. Try
  1816. Thread.Sleep(1000) '防止存在尚未完全释放的文件,导致清理失败(例如整合包安装)
  1817. If Loader.State = LoadState.Failed OrElse Loader.State = LoadState.Aborted Then
  1818. '删除版本文件夹
  1819. If Directory.Exists(Loader.Input & "saves\") OrElse Directory.Exists(Loader.Input & "versions\") Then
  1820. Log("[Download] 由于版本已被独立启动,不清理版本文件夹:" & Loader.Input, LogLevel.Developer)
  1821. Else
  1822. Log("[Download] 由于下载失败或取消,清理版本文件夹:" & Loader.Input, LogLevel.Developer)
  1823. DeleteDirectory(Loader.Input)
  1824. End If
  1825. End If
  1826. Catch ex As Exception
  1827. Log(ex, "下载失败或取消后清理版本文件夹失败")
  1828. End Try
  1829. End Sub
  1830. ''' <summary>
  1831. ''' 进行合并安装。返回是否已经开始安装(例如如果没有安装 Java 则会进行提示并返回 False)
  1832. ''' </summary>
  1833. Public Function McInstall(Request As McInstallRequest) As Boolean
  1834. Try
  1835. Dim SubLoaders = McInstallLoader(Request)
  1836. If SubLoaders Is Nothing Then Return False
  1837. Dim Loader As New LoaderCombo(Of String)(Request.TargetVersionName & " 安装", SubLoaders) With {.OnStateChanged = AddressOf McInstallState}
  1838. '启动
  1839. Loader.Start(Request.TargetVersionFolder)
  1840. LoaderTaskbarAdd(Loader)
  1841. FrmMain.BtnExtraDownload.ShowRefresh()
  1842. FrmMain.BtnExtraDownload.Ribble()
  1843. Return True
  1844. Catch ex As Exception
  1845. Log(ex, "开始合并安装失败", LogLevel.Feedback)
  1846. Return False
  1847. End Try
  1848. End Function
  1849. ''' <summary>
  1850. ''' 获取合并安装加载器列表,并进行前期的缓存清理与 Java 检查工作。
  1851. ''' 如果出现已知问题且已提示用户,则返回 Nothing。出现异常则直接抛出。
  1852. ''' </summary>
  1853. Public Function McInstallLoader(Request As McInstallRequest, Optional DontFixLibraries As Boolean = False) As List(Of LoaderBase)
  1854. '获取缓存目录
  1855. Dim PathInstallTemp As String
  1856. If PathTemp.Contains(" ") AndAlso Request.ForgeEntry IsNot Nothing Then
  1857. PathInstallTemp = OsDrive & "ProgramData\PCL\Install\"
  1858. Else
  1859. PathInstallTemp = PathTemp & "Install\"
  1860. End If
  1861. '清理缓存
  1862. Try
  1863. If Not IsInstallTempCleared Then
  1864. IsInstallTempCleared = True
  1865. DeleteDirectory(PathInstallTemp, True)
  1866. Log("[Download] 已清理合并安装缓存")
  1867. End If
  1868. Catch ex As Exception
  1869. Log(ex, "清理合并安装缓存失败")
  1870. End Try
  1871. '获取参数
  1872. Dim OutputFolder As String = PathMcFolder & "versions\" & Request.TargetVersionName & "\"
  1873. Dim TempMcFolder As String = PathInstallTemp & Request.TargetVersionName.GetHashCode() & "\"
  1874. If Directory.Exists(TempMcFolder) Then DeleteDirectory(TempMcFolder)
  1875. Dim OptiFineFolder As String = Nothing
  1876. If Request.OptiFineVersion IsNot Nothing Then
  1877. If Request.OptiFineVersion.Contains("_HD_U_") Then Request.OptiFineVersion = "HD_U_" & Request.OptiFineVersion.Split("_HD_U_").Last '#735
  1878. Request.OptiFineEntry = New DlOptiFineListEntry With {
  1879. .NameDisplay = Request.MinecraftName & " " & Request.OptiFineVersion.Replace("HD_U_", "").Replace("_", "").Replace("pre", " pre"),
  1880. .Inherit = Request.MinecraftName,
  1881. .IsPreview = Request.OptiFineVersion.ToLower.Contains("pre"),
  1882. .NameVersion = Request.MinecraftName & "-OptiFine_" & Request.OptiFineVersion,
  1883. .NameFile = If(Request.OptiFineVersion.ToLower.Contains("pre"), "preview_", "") &
  1884. "OptiFine_" & Request.MinecraftName & "_" & Request.OptiFineVersion & ".jar"
  1885. }
  1886. End If
  1887. If Request.OptiFineEntry IsNot Nothing Then OptiFineFolder = TempMcFolder & "versions\" & Request.OptiFineEntry.NameVersion
  1888. Dim ForgeFolder As String = Nothing
  1889. If Request.ForgeEntry IsNot Nothing Then Request.ForgeVersion = If(Request.ForgeVersion, Request.ForgeEntry.Version)
  1890. If Request.ForgeVersion IsNot Nothing Then ForgeFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-forge-" & Request.ForgeVersion
  1891. Dim FabricFolder As String = Nothing
  1892. If Request.FabricVersion IsNot Nothing Then FabricFolder = TempMcFolder & "versions\fabric-loader-" & Request.FabricVersion & "-" & Request.MinecraftName
  1893. Dim LiteLoaderFolder As String = Nothing
  1894. If Request.LiteLoaderEntry IsNot Nothing Then LiteLoaderFolder = TempMcFolder & "versions\" & Request.MinecraftName & "-LiteLoader"
  1895. 'OptiFine 是否作为 Mod 进行下载
  1896. Dim OptiFineAsMod As Boolean =
  1897. (Request.OptiFineEntry IsNot Nothing OrElse Request.OptiFineVersion IsNot Nothing) AndAlso Request.FabricVersion IsNot Nothing
  1898. If OptiFineAsMod Then
  1899. Log("[Download] OptiFine 将作为 Mod 进行下载")
  1900. OptiFineFolder = New McVersion(OutputFolder).GetPathIndie(True) & "mods\"
  1901. End If
  1902. '记录日志
  1903. If OptiFineFolder IsNot Nothing Then Log("[Download] OptiFine 缓存:" & OptiFineFolder)
  1904. If ForgeFolder IsNot Nothing Then Log("[Download] Forge 缓存:" & ForgeFolder)
  1905. If FabricFolder IsNot Nothing Then Log("[Download] Fabric 缓存:" & FabricFolder)
  1906. If LiteLoaderFolder IsNot Nothing Then Log("[Download] LiteLoader 缓存:" & LiteLoaderFolder)
  1907. Log("[Download] 对应的原版版本:" & Request.MinecraftName)
  1908. '重复版本检查
  1909. If File.Exists(OutputFolder & Request.TargetVersionName & ".json") Then
  1910. Hint("版本 " & Request.TargetVersionName & " 已经存在!", HintType.Critical)
  1911. Return Nothing
  1912. End If
  1913. Dim LoaderList As New List(Of LoaderBase)
  1914. '添加忽略标识
  1915. LoaderList.Add(New LoaderTask(Of Integer, Integer)("添加忽略标识", Sub() WriteFile(OutputFolder & ".pclignore", "该文件会使得该文件夹不在 PCL 的版本列表中显示")) With {.Show = False, .Block = False})
  1916. 'Fabric API
  1917. If Request.FabricApi IsNot Nothing Then
  1918. LoaderList.Add(New LoaderDownload("下载 Fabric API", New List(Of NetFile) From {Request.FabricApi.ToNetFile(New McVersion(OutputFolder).GetPathIndie(True) & "mods\")}) With {.ProgressWeight = 3, .Block = False})
  1919. End If
  1920. 'OptiFabric
  1921. If Request.OptiFabric IsNot Nothing Then
  1922. LoaderList.Add(New LoaderDownload("下载 OptiFabric", New List(Of NetFile) From {Request.OptiFabric.ToNetFile(New McVersion(OutputFolder).GetPathIndie(True) & "mods\")}) With {.ProgressWeight = 3, .Block = False})
  1923. End If
  1924. '原版
  1925. Dim ClientLoader = New LoaderCombo(Of String)("下载原版 " & Request.MinecraftName, McDownloadClientLoader(Request.MinecraftName, Request.MinecraftJson, Request.TargetVersionName)) With {.Show = False, .ProgressWeight = 39, .Block = Request.ForgeVersion Is Nothing AndAlso Request.OptiFineEntry Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing}
  1926. LoaderList.Add(ClientLoader)
  1927. 'OptiFine
  1928. If Request.OptiFineEntry IsNot Nothing Then
  1929. If OptiFineAsMod Then
  1930. LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineSaveLoader(Request.OptiFineEntry, OptiFineFolder & Request.OptiFineEntry.NameFile)) With {.Show = False, .ProgressWeight = 16, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
  1931. Else
  1932. LoaderList.Add(New LoaderCombo(Of String)("下载 OptiFine " & Request.OptiFineEntry.NameDisplay, McDownloadOptiFineLoader(Request.OptiFineEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 24, .Block = Request.ForgeVersion Is Nothing AndAlso Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
  1933. End If
  1934. End If
  1935. 'Forge
  1936. If Request.ForgeVersion IsNot Nothing Then
  1937. LoaderList.Add(New LoaderCombo(Of String)("下载 Forge " & Request.ForgeVersion, McDownloadForgeLoader(Request.ForgeVersion, Request.MinecraftName, Request.ForgeEntry, TempMcFolder, ClientLoader, Request.TargetVersionFolder, False)) With {.Show = False, .ProgressWeight = 25, .Block = Request.FabricVersion Is Nothing AndAlso Request.LiteLoaderEntry Is Nothing})
  1938. End If
  1939. 'LiteLoader
  1940. If Request.LiteLoaderEntry IsNot Nothing Then
  1941. LoaderList.Add(New LoaderCombo(Of String)("下载 LiteLoader " & Request.MinecraftName, McDownloadLiteLoaderLoader(Request.LiteLoaderEntry, TempMcFolder, ClientLoader, False)) With {.Show = False, .ProgressWeight = 1, .Block = Request.FabricVersion Is Nothing})
  1942. End If
  1943. 'Fabric
  1944. If Request.FabricVersion IsNot Nothing Then
  1945. LoaderList.Add(New LoaderCombo(Of String)("下载 Fabric " & Request.FabricVersion, McDownloadFabricLoader(Request.FabricVersion, Request.MinecraftName, TempMcFolder, False)) With {.Show = False, .ProgressWeight = 2, .Block = True})
  1946. End If
  1947. '合并安装
  1948. LoaderList.Add(New LoaderTask(Of String, String)("安装游戏",
  1949. Sub(Task As LoaderTask(Of String, String))
  1950. InstallMerge(OutputFolder, OutputFolder, OptiFineFolder, OptiFineAsMod, ForgeFolder, Request.ForgeVersion, FabricFolder, LiteLoaderFolder)
  1951. Task.Progress = 0.3
  1952. If Directory.Exists(TempMcFolder & "libraries") Then CopyDirectory(TempMcFolder & "libraries", PathMcFolder & "libraries")
  1953. If Directory.Exists(TempMcFolder & "mods") Then CopyDirectory(TempMcFolder & "mods", PathMcFolder & "mods")
  1954. End Sub) With {.ProgressWeight = 2, .Block = True})
  1955. '补全文件
  1956. If Not DontFixLibraries AndAlso
  1957. (Request.OptiFineEntry IsNot Nothing OrElse (Request.ForgeVersion IsNot Nothing AndAlso Request.ForgeVersion.Split(".")(0) >= 20) OrElse Request.FabricVersion IsNot Nothing OrElse Request.LiteLoaderEntry IsNot Nothing) Then
  1958. Dim LoadersLib As New List(Of LoaderBase)
  1959. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(OutputFolder))) With {.ProgressWeight = 1, .Show = False})
  1960. LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
  1961. LoaderList.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
  1962. End If
  1963. '删除忽略标识
  1964. LoaderList.Add(New LoaderTask(Of Integer, Integer)("删除忽略标识", Sub() File.Delete(OutputFolder & ".pclignore")) With {.Show = False})
  1965. '总加载器
  1966. Return LoaderList
  1967. End Function
  1968. Private IsInstallTempCleared As Boolean = False
  1969. ''' <summary>
  1970. ''' 将多个版本 Json 进行合并,如果目标已存在则直接覆盖。失败会抛出异常。
  1971. ''' </summary>
  1972. Private Sub InstallMerge(OutputFolder As String, MinecraftFolder As String, Optional OptiFineFolder As String = Nothing, Optional OptiFineAsMod As Boolean = False, Optional ForgeFolder As String = Nothing, Optional ForgeVersion As String = Nothing, Optional FabricFolder As String = Nothing, Optional LiteLoaderFolder As String = Nothing)
  1973. Log("[Download] 开始进行版本合并,输出:" & OutputFolder & ",Minecraft:" & MinecraftFolder &
  1974. If(OptiFineFolder IsNot Nothing, ",OptiFine:" & OptiFineFolder, "") &
  1975. If(ForgeFolder IsNot Nothing, ",Forge:" & ForgeFolder, "") &
  1976. If(LiteLoaderFolder IsNot Nothing, ",LiteLoader:" & LiteLoaderFolder, "") &
  1977. If(FabricFolder IsNot Nothing, ",Fabric:" & FabricFolder, ""))
  1978. Directory.CreateDirectory(OutputFolder)
  1979. Dim HasOptiFine As Boolean = OptiFineFolder IsNot Nothing AndAlso Not OptiFineAsMod, HasForge As Boolean = ForgeFolder IsNot Nothing, HasLiteLoader As Boolean = LiteLoaderFolder IsNot Nothing, HasFabric As Boolean = FabricFolder IsNot Nothing
  1980. Dim OutputName As String, MinecraftName As String, OptiFineName As String, ForgeName As String, LiteLoaderName As String, FabricName As String
  1981. Dim OutputJsonPath As String, MinecraftJsonPath As String, OptiFineJsonPath As String = Nothing, ForgeJsonPath As String = Nothing, LiteLoaderJsonPath As String = Nothing, FabricJsonPath As String = Nothing
  1982. Dim OutputJar As String, MinecraftJar As String
  1983. #Region "初始化路径信息"
  1984. If Not OutputFolder.EndsWith("\") Then OutputFolder += "\"
  1985. OutputName = GetFolderNameFromPath(OutputFolder)
  1986. OutputJsonPath = OutputFolder & OutputName & ".json"
  1987. OutputJar = OutputFolder & OutputName & ".jar"
  1988. If Not MinecraftFolder.EndsWith("\") Then MinecraftFolder += "\"
  1989. MinecraftName = GetFolderNameFromPath(MinecraftFolder)
  1990. MinecraftJsonPath = MinecraftFolder & MinecraftName & ".json"
  1991. MinecraftJar = MinecraftFolder & MinecraftName & ".jar"
  1992. If HasOptiFine Then
  1993. If Not OptiFineFolder.EndsWith("\") Then OptiFineFolder += "\"
  1994. OptiFineName = GetFolderNameFromPath(OptiFineFolder)
  1995. OptiFineJsonPath = OptiFineFolder & OptiFineName & ".json"
  1996. End If
  1997. If HasForge Then
  1998. If Not ForgeFolder.EndsWith("\") Then ForgeFolder += "\"
  1999. ForgeName = GetFolderNameFromPath(ForgeFolder)
  2000. ForgeJsonPath = ForgeFolder & ForgeName & ".json"
  2001. End If
  2002. If HasLiteLoader Then
  2003. If Not LiteLoaderFolder.EndsWith("\") Then LiteLoaderFolder += "\"
  2004. LiteLoaderName = GetFolderNameFromPath(LiteLoaderFolder)
  2005. LiteLoaderJsonPath = LiteLoaderFolder & LiteLoaderName & ".json"
  2006. End If
  2007. If HasFabric Then
  2008. If Not FabricFolder.EndsWith("\") Then FabricFolder += "\"
  2009. FabricName = GetFolderNameFromPath(FabricFolder)
  2010. FabricJsonPath = FabricFolder & FabricName & ".json"
  2011. End If
  2012. #End Region
  2013. Dim OutputJson As JObject, MinecraftJson As JObject, OptiFineJson As JObject = Nothing, ForgeJson As JObject = Nothing, LiteLoaderJson As JObject = Nothing, FabricJson As JObject = Nothing
  2014. #Region "读取文件并检查文件是否合规"
  2015. Dim MinecraftJsonText As String = ReadFile(MinecraftJsonPath)
  2016. If Not MinecraftJsonText.StartsWith("{") Then Throw New Exception("Minecraft json 有误,地址:" & MinecraftJsonPath & ",前段内容:" & MinecraftJsonText.Substring(0, Math.Min(MinecraftJsonText.Length, 1000)))
  2017. MinecraftJson = GetJson(MinecraftJsonText)
  2018. If HasOptiFine Then
  2019. Dim OptiFineJsonText As String = ReadFile(OptiFineJsonPath)
  2020. If Not OptiFineJsonText.StartsWith("{") Then Throw New Exception("OptiFine json 有误,地址:" & OptiFineJsonPath & ",前段内容:" & OptiFineJsonText.Substring(0, Math.Min(OptiFineJsonText.Length, 1000)))
  2021. OptiFineJson = GetJson(OptiFineJsonText)
  2022. End If
  2023. If HasForge Then
  2024. Dim ForgeJsonText As String = ReadFile(ForgeJsonPath)
  2025. If Not ForgeJsonText.StartsWith("{") Then Throw New Exception("Forge json 有误,地址:" & ForgeJsonPath & ",前段内容:" & ForgeJsonText.Substring(0, Math.Min(ForgeJsonText.Length, 1000)))
  2026. ForgeJson = GetJson(ForgeJsonText)
  2027. End If
  2028. If HasLiteLoader Then
  2029. Dim LiteLoaderJsonText As String = ReadFile(LiteLoaderJsonPath)
  2030. If Not LiteLoaderJsonText.StartsWith("{") Then Throw New Exception("LiteLoader json 有误,地址:" & LiteLoaderJsonPath & ",前段内容:" & LiteLoaderJsonText.Substring(0, Math.Min(LiteLoaderJsonText.Length, 1000)))
  2031. LiteLoaderJson = GetJson(LiteLoaderJsonText)
  2032. End If
  2033. If HasFabric Then
  2034. Dim FabricJsonText As String = ReadFile(FabricJsonPath)
  2035. If Not FabricJsonText.StartsWith("{") Then Throw New Exception("Fabric json 有误,地址:" & FabricJsonPath & ",前段内容:" & FabricJsonText.Substring(0, Math.Min(FabricJsonText.Length, 1000)))
  2036. FabricJson = GetJson(FabricJsonText)
  2037. End If
  2038. #End Region
  2039. #Region "处理 Json 文件"
  2040. '获取 minecraftArguments
  2041. Dim AllArguments As String =
  2042. If(MinecraftJson("minecraftArguments"), " ").ToString & " " &
  2043. If(OptiFineJson IsNot Nothing, If(OptiFineJson("minecraftArguments"), " ").ToString, " ") & " " &
  2044. If(ForgeJson IsNot Nothing, If(ForgeJson("minecraftArguments"), " ").ToString, " ") & " " &
  2045. If(LiteLoaderJson IsNot Nothing, If(LiteLoaderJson("minecraftArguments"), " ").ToString, " ")
  2046. '分割参数字符串
  2047. Dim RawArguments As List(Of String) = AllArguments.Split(" ").Where(Function(l) l <> "").Select(Function(l) l.Trim).ToList
  2048. Dim SplitArguments As New List(Of String)
  2049. For i = 0 To RawArguments.Count - 1
  2050. If RawArguments(i).StartsWith("-") Then
  2051. SplitArguments.Add(RawArguments(i))
  2052. ElseIf SplitArguments.Count > 0 AndAlso SplitArguments.Last.StartsWith("-") AndAlso Not SplitArguments.Last.Contains(" ") Then
  2053. SplitArguments(SplitArguments.Count - 1) = SplitArguments.Last & " " & RawArguments(i)
  2054. Else
  2055. SplitArguments.Add(RawArguments(i))
  2056. End If
  2057. Next
  2058. Dim RealArguments As String = Join(SplitArguments.Distinct.ToList, " ")
  2059. '合并
  2060. OutputJson = MinecraftJson
  2061. If HasOptiFine Then
  2062. '合并 OptiFine
  2063. OptiFineJson.Remove("releaseTime")
  2064. OptiFineJson.Remove("time")
  2065. OutputJson.Merge(OptiFineJson)
  2066. End If
  2067. If HasForge Then
  2068. '合并 Forge
  2069. ForgeJson.Remove("releaseTime")
  2070. ForgeJson.Remove("time")
  2071. OutputJson.Merge(ForgeJson)
  2072. End If
  2073. If HasLiteLoader Then
  2074. '合并 LiteLoader
  2075. LiteLoaderJson.Remove("releaseTime")
  2076. LiteLoaderJson.Remove("time")
  2077. OutputJson.Merge(LiteLoaderJson)
  2078. End If
  2079. If HasFabric Then
  2080. '合并 Fabric
  2081. FabricJson.Remove("releaseTime")
  2082. FabricJson.Remove("time")
  2083. OutputJson.Merge(FabricJson)
  2084. End If
  2085. '修改
  2086. If RealArguments IsNot Nothing AndAlso RealArguments.Replace(" ", "") <> "" Then OutputJson("minecraftArguments") = RealArguments
  2087. OutputJson.Remove("_comment_")
  2088. OutputJson.Remove("inheritsFrom")
  2089. OutputJson.Remove("jar")
  2090. OutputJson("id") = OutputName
  2091. #End Region
  2092. #Region "保存"
  2093. WriteFile(OutputJsonPath, OutputJson.ToString)
  2094. If MinecraftJar <> OutputJar Then '可能是同一个文件
  2095. If File.Exists(OutputJar) Then File.Delete(OutputJar)
  2096. CopyFile(MinecraftJar, OutputJar)
  2097. End If
  2098. Log("[Download] 版本合并 " & OutputName & " 完成")
  2099. #End Region
  2100. End Sub
  2101. #End Region
  2102. End Module