ModDownloadLib.vb 130 KB

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