ModDownloadLib.vb 129 KB

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