ModDownloadLib.vb 158 KB

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