ModDownloadLib.vb 128 KB

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