ModDownloadLib.vb 128 KB

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