ModDownloadLib.vb 132 KB

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