ModDownloadLib.vb 125 KB

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