ModModpack.vb 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. Public Module ModModpack
  2. '触发整合包安装的外部接口
  3. ''' <summary>
  4. ''' 弹窗要求选择一个整合包文件并进行安装。
  5. ''' </summary>
  6. Public Sub ModpackInstall()
  7. Dim File As String = SelectFile("压缩包文件(*.rar;*.zip)|*.rar;*.zip", "选择整合包压缩文件") '选择整合包文件
  8. If String.IsNullOrEmpty(File) Then Exit Sub
  9. RunInThread(Sub() ModpackInstall(File))
  10. End Sub
  11. ''' <summary>
  12. ''' 安装一个给定的整合包文件,返回是否安装成功。必须在工作线程执行。
  13. ''' </summary>
  14. Public Function ModpackInstall(File As String, Optional VersionName As String = Nothing, Optional ShowHint As Boolean = True, Optional Logo As String = Nothing) As Boolean
  15. Log("[ModPack] 整合包安装请求:" & If(File, "null"))
  16. Dim Archive As Compression.ZipArchive = Nothing
  17. Dim ArchiveBaseFolder As String = ""
  18. Try
  19. '获取整合包种类与关键 Json
  20. Dim PackType As Integer = -1
  21. Try
  22. Archive = New Compression.ZipArchive(New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  23. '从根目录判断整合包类型
  24. If Archive.GetEntry("mcbbs.packmeta") IsNot Nothing Then PackType = 3 : Exit Try 'MCBBS 整合包(优先于 manifest.json 判断)
  25. If Archive.GetEntry("manifest.json") IsNot Nothing Then
  26. Dim Json As JObject = GetJson(ReadFile(Archive.GetEntry("manifest.json").Open, Encoding.UTF8))
  27. If Json("addons") Is Nothing Then
  28. PackType = 0 : Exit Try 'CurseForge 整合包
  29. Else
  30. PackType = 3 : Exit Try 'MCBBS 整合包
  31. End If
  32. End If
  33. If Archive.GetEntry("modpack.json") IsNot Nothing Then PackType = 1 : Exit Try 'HMCL 整合包
  34. If Archive.GetEntry("mmc-pack.json") IsNot Nothing Then PackType = 2 : Exit Try 'MMC 整合包
  35. '从一级目录判断整合包类型
  36. For Each Entry In Archive.Entries
  37. Dim FullNames As String() = Entry.FullName.Split("/")
  38. ArchiveBaseFolder = FullNames(0) & "/"
  39. If Entry.FullName.EndsWith("/versions/") AndAlso FullNames.Count = 3 Then PackType = 9 : Exit Try '压缩包
  40. '确定为一级目录下
  41. If FullNames.Count <> 2 Then Continue For
  42. '判断是否为关键文件
  43. If FullNames(1) = "mcbbs.packmeta" Then PackType = 3 : Exit Try 'MCBBS 整合包(优先于 manifest.json 判断)
  44. If FullNames(1) = "manifest.json" Then
  45. Dim Json As JObject = GetJson(ReadFile(Entry.Open, Encoding.UTF8))
  46. If Json("addons") Is Nothing Then
  47. PackType = 0 : Exit Try 'CurseForge 整合包
  48. Else
  49. PackType = 3 : ArchiveBaseFolder = "overrides/" : Exit Try 'MCBBS 整合包
  50. End If
  51. End If
  52. If FullNames(1) = "modpack.json" Then PackType = 1 : Exit Try 'HMCL 整合包
  53. If FullNames(1) = "mmc-pack.json" Then PackType = 2 : Exit Try 'MMC 整合包
  54. Next
  55. Catch ex As Exception
  56. If GetExceptionDetail(ex, True).Contains("Error.WinIOError") Then
  57. Log(ex, "打开整合包文件失败", If(ShowHint, LogLevel.Hint, LogLevel.Normal))
  58. Return False
  59. ElseIf File.ToLower.EndsWith(".rar") Then
  60. Log(ex, "PCL2 无法处理 rar 格式的压缩包,请在解压后重新压缩为 zip 格式再试", If(ShowHint, LogLevel.Hint, LogLevel.Normal))
  61. Return False
  62. Else
  63. Log(ex, "打开整合包文件失败,文件可能损坏或为不支持的压缩包格式", If(ShowHint, LogLevel.Hint, LogLevel.Normal))
  64. Return False
  65. End If
  66. End Try
  67. '执行对应的安装方法
  68. Select Case PackType
  69. Case 0
  70. Log("[ModPack] 整合包种类:CurseForge")
  71. InstallPackCurseForge(File, Archive, ArchiveBaseFolder, VersionName, Logo)
  72. Case 1
  73. Log("[ModPack] 整合包种类:HMCL")
  74. InstallPackHMCL(File, Archive, ArchiveBaseFolder)
  75. Case 2
  76. Log("[ModPack] 整合包种类:MMC")
  77. InstallPackMMC(File, Archive, ArchiveBaseFolder)
  78. Case 3
  79. Log("[ModPack] 整合包种类:MCBBS")
  80. InstallPackMCBBS(File, Archive, ArchiveBaseFolder)
  81. Case 9
  82. Log("[ModPack] 整合包种类:压缩包")
  83. Archive.Dispose()
  84. Archive = Nothing
  85. InstallPackCompress(File, ArchiveBaseFolder)
  86. Case Else
  87. If ShowHint Then
  88. Hint("未能识别该整合包的种类,无法安装!", HintType.Critical)
  89. Else
  90. Log("[ModPack] 未能识别该整合包的种类,无法安装!")
  91. End If
  92. Return False
  93. End Select
  94. Return True
  95. Catch ex As Exception
  96. Log(ex, "准备安装整合包失败", LogLevel.Feedback)
  97. Return False
  98. Finally
  99. If Archive IsNot Nothing Then Archive.Dispose()
  100. End Try
  101. End Function
  102. '整合包缓存清理
  103. Private IsInstallCacheCleared As Boolean = False
  104. Private IsInstallCacheClearing As Boolean = False
  105. Private Sub UnpackFiles(InstallTemp As String, FileAddress As String)
  106. '清理缓存文件夹
  107. If Not IsInstallCacheCleared Then
  108. IsInstallCacheCleared = True
  109. IsInstallCacheClearing = True
  110. Try
  111. Log("[ModPack] 开始清理整合包安装缓存")
  112. DeleteDirectory(PathTemp & "PackInstall\")
  113. Log("[ModPack] 已清理整合包安装缓存")
  114. Catch ex As Exception
  115. Log(ex, "清理整合包安装缓存失败")
  116. Finally
  117. IsInstallCacheClearing = False
  118. End Try
  119. ElseIf IsInstallCacheClearing Then
  120. '等待另一个整合包安装的清理步骤完成
  121. Do While IsInstallCacheClearing
  122. Thread.Sleep(1)
  123. Loop
  124. End If
  125. '解压文件
  126. Dim RetryCount As Integer = 1
  127. Dim Encode = Encoding.GetEncoding("GB18030")
  128. Try
  129. Retry:
  130. '完全不知道为啥会出现文件正在被另一进程使用的问题,总之多试试
  131. Directory.CreateDirectory(InstallTemp)
  132. DeleteDirectory(InstallTemp)
  133. Compression.ZipFile.ExtractToDirectory(FileAddress, InstallTemp, Encode)
  134. Catch ex As Exception
  135. Log(ex, "第 " & RetryCount & " 次解压尝试失败")
  136. If TypeOf ex Is ArgumentException Then
  137. Encode = Encoding.UTF8
  138. Log("[ModPack] 已切换压缩包解压编码为 UTF8")
  139. End If
  140. If RetryCount < 5 Then
  141. Thread.Sleep(RetryCount * 2000)
  142. RetryCount += 1
  143. GoTo Retry
  144. Else
  145. Throw
  146. End If
  147. End Try
  148. End Sub
  149. #Region "不同类型整合包的安装方法"
  150. 'CurseForge
  151. ''' <summary>
  152. ''' 获取安装 CurseForge 整合包的加载器,若失败或跳过则返回 Nothing。
  153. ''' 加载器以安装目标版本文件夹为输入。
  154. ''' </summary>
  155. Private Function InstallPackCurseForgeLoader(FileAddress As String, Archive As Compression.ZipArchive, ArchiveBaseFolder As String, VersionName As String, Optional Logo As String = Nothing) As LoaderCombo(Of String)
  156. '读取 Json 文件
  157. Dim Json As JObject
  158. Try
  159. Json = GetJson(ReadFile(Archive.GetEntry(ArchiveBaseFolder & "manifest.json").Open))
  160. If Json("minecraft") Is Nothing OrElse Json("minecraft")("version") Is Nothing Then Throw New Exception("整合包未提供 Minecraft 版本信息")
  161. Catch ex As Exception
  162. Log(ex, "整合包安装信息存在问题", LogLevel.Hint)
  163. Return Nothing
  164. End Try
  165. '获取 Mod API 版本信息
  166. Dim ForgeVersion As String = Nothing
  167. Dim FabricVersion As String = Nothing
  168. For Each Entry In If(Json("minecraft")("modLoaders"), {})
  169. Dim Id As String = If(Entry("id"), "").ToString.ToLower
  170. If Id.StartsWith("forge-") Then
  171. 'Forge 指定
  172. If Id.Contains("recommended") Then
  173. Log("[ModPack] 该整合包版本过老,已不支持进行安装!", LogLevel.Hint)
  174. Return Nothing
  175. End If
  176. Try
  177. Log("[ModPack] 整合包 Forge 版本:" & Id)
  178. ForgeVersion = Id.Split("-")(1)
  179. Exit For
  180. Catch ex As Exception
  181. Log(ex, "读取整合包 Forge 版本失败:" & Id)
  182. End Try
  183. ElseIf Id.StartsWith("fabric-") Then
  184. 'Fabric 指定
  185. Try
  186. Log("[ModPack] 整合包 Fabric 版本:" & Id)
  187. FabricVersion = Id.Split("-")(1)
  188. Exit For
  189. Catch ex As Exception
  190. Log(ex, "读取整合包 Fabric 版本失败:" & Id)
  191. End Try
  192. End If
  193. Next
  194. '解压与配置文件
  195. Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
  196. Dim InstallLoaders As New List(Of LoaderBase)
  197. Dim OverrideHome As String = If(Json("overrides"), "")
  198. If OverrideHome <> "" Then
  199. InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
  200. Sub(Task As LoaderTask(Of String, Integer))
  201. UnpackFiles(InstallTemp, FileAddress)
  202. Task.Progress = 0.5
  203. '复制结果
  204. If Directory.Exists(InstallTemp & ArchiveBaseFolder & OverrideHome) Then
  205. My.Computer.FileSystem.CopyDirectory(InstallTemp & ArchiveBaseFolder & OverrideHome, PathMcFolder & "versions\" & VersionName)
  206. Else
  207. Log("[ModPack] 整合包中未找到 override 目录,已跳过")
  208. End If
  209. Task.Progress = 0.9
  210. '开启版本隔离
  211. WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
  212. End Sub) With {
  213. .ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
  214. End If
  215. '获取 Mod 列表
  216. Dim ModFileList As New List(Of Integer)
  217. For Each ModEntry In If(Json("files"), {})
  218. If ModEntry("projectID") Is Nothing OrElse ModEntry("fileID") Is Nothing Then
  219. Hint("某项 Mod 缺少必要信息,已跳过:" & ModEntry.ToString)
  220. Continue For
  221. End If
  222. If ModEntry("required") IsNot Nothing AndAlso Not ModEntry("required").ToObject(Of Boolean) Then Continue For
  223. ModFileList.Add(ModEntry("fileID"))
  224. Next
  225. If ModFileList.Count > 0 Then
  226. '获取 Mod 下载信息
  227. InstallLoaders.Add(New LoaderTask(Of Integer, JArray)("获取 Mod 下载信息",
  228. Sub(Task As LoaderTask(Of Integer, JArray))
  229. Task.Output = GetJson(NetRequestRetry("https://api.curseforge.com/v1/mods/files", "POST", "{""fileIds"": [" & Join(ModFileList, ",") & "]}", "application/json"))("data")
  230. '如果文件已被删除,则 API 会跳过那一项
  231. If ModFileList.Count > Task.Output.Count Then Throw New Exception("整合包所需要的部分 Mod 版本已被 Mod 作者删除,因此无法完成整合包安装,请联系整合包作者更新整合包中的 Mod 版本")
  232. End Sub) With {.ProgressWeight = ModFileList.Count / 10}) '每 10 Mod 需要 1s
  233. '构造 NetFile
  234. InstallLoaders.Add(New LoaderTask(Of JArray, List(Of NetFile))("构造 Mod 下载信息",
  235. Sub(Task As LoaderTask(Of JArray, List(Of NetFile)))
  236. Dim FileList As New Dictionary(Of Integer, NetFile)
  237. For Each ModJson In Task.Input
  238. '跳过重复的 Mod(疑似 CurseForge Bug)
  239. If FileList.ContainsKey(ModJson("id").ToObject(Of Integer)) Then Continue For
  240. '根据 modules 判断是资源包还是 Mod
  241. Dim ModuleNames = CType(ModJson("modules"), JArray).Select(Function(l) l("name").ToString).ToList
  242. Dim IsResourcePack As Boolean = Not ModuleNames.Contains("META-INF") AndAlso ModuleNames.Contains("pack.mcmeta")
  243. '实际的添加
  244. FileList.Add(ModJson("id"), New DlCfFile(ModJson, False).GetDownloadFile(
  245. PathMcFolder & "versions\" & VersionName & "\" & If(IsResourcePack, "resourcepacks", "mods") & "\", False))
  246. Task.Progress += 1 / (1 + ModFileList.Count)
  247. Next
  248. Task.Output = FileList.Values.ToList
  249. End Sub) With {.ProgressWeight = ModFileList.Count / 200, .Show = False}) '每 200 Mod 需要 1s
  250. '下载 Mod 文件
  251. InstallLoaders.Add(New LoaderDownload("下载 Mod", New List(Of NetFile)) With {.ProgressWeight = ModFileList.Count * 1.5}) '每个 Mod 需要 1.5s
  252. End If
  253. '构造加载器
  254. Dim Request As New McInstallRequest With {
  255. .TargetVersionName = VersionName,
  256. .MinecraftName = Json("minecraft")("version").ToString,
  257. .ForgeVersion = ForgeVersion,
  258. .FabricVersion = FabricVersion
  259. }
  260. Dim InstallExpectTime As Double = 0
  261. For Each InstallLoader In InstallLoaders
  262. InstallExpectTime += InstallLoader.ProgressWeight
  263. Next
  264. Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
  265. If MergeLoaders Is Nothing Then Return Nothing
  266. Dim MergeExpectTime As Double = 0
  267. For Each MergeLoader In MergeLoaders
  268. MergeExpectTime += MergeLoader.ProgressWeight
  269. Next
  270. '构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
  271. Dim LoadersLib As New List(Of LoaderBase)
  272. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
  273. LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
  274. '构造总加载器
  275. Dim Loaders As New List(Of LoaderBase)
  276. Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .Block = False, .ProgressWeight = InstallExpectTime})
  277. Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .ProgressWeight = MergeExpectTime})
  278. Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
  279. Loaders.Add(New LoaderTask(Of String, String)("最终整理文件",
  280. Sub(Task As LoaderTask(Of String, String))
  281. '设置图标
  282. Dim VersionFolder As String = PathMcFolder & "versions\" & VersionName & "\"
  283. If Logo IsNot Nothing AndAlso File.Exists(Logo) Then
  284. File.Copy(Logo, VersionFolder & "PCL\Logo.png", True)
  285. WriteIni(VersionFolder & "PCL\Setup.ini", "Logo", "PCL\Logo.png")
  286. WriteIni(VersionFolder & "PCL\Setup.ini", "LogoCustom", "True")
  287. Log("[Download] 已设置整合包 Logo:" & Logo)
  288. End If
  289. '删除原始整合包文件
  290. Dim Target As String = VersionFolder & "原始整合包.zip"
  291. If Not Setup.Get("ToolDownloadKeepModpack") AndAlso File.Exists(Target) Then
  292. Log("[Download] 根据设置要求删除原始整合包文件:" & Target)
  293. File.Delete(Target)
  294. End If
  295. End Sub) With {.ProgressWeight = 0.1, .Show = False})
  296. '重复任务检查
  297. Dim LoaderName As String = "CurseForge 整合包安装:" & VersionName & " "
  298. SyncLock LoaderTaskbarLock
  299. For i = 0 To LoaderTaskbar.Count - 1
  300. If LoaderTaskbar(i).Name = LoaderName Then
  301. Hint("该整合包正在安装中!", HintType.Critical)
  302. Return Nothing
  303. End If
  304. Next
  305. End SyncLock
  306. '启动
  307. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
  308. Return Loader
  309. End Function
  310. Private Sub InstallPackCurseForge(FileAddress As String, Archive As Compression.ZipArchive, ArchiveBaseFolder As String, Optional VersionName As String = Nothing, Optional Logo As String = Nothing)
  311. '获取版本名
  312. Dim ShowRibble As Boolean = VersionName Is Nothing
  313. If VersionName Is Nothing Then
  314. Dim Json As JObject
  315. Try
  316. Json = GetJson(ReadFile(Archive.GetEntry(ArchiveBaseFolder & "manifest.json").Open))
  317. If Json("minecraft") Is Nothing OrElse Json("minecraft")("version") Is Nothing Then Throw New Exception("整合包未提供 Minecraft 版本信息")
  318. Catch ex As Exception
  319. Log(ex, "整合包安装信息存在问题", LogLevel.Hint)
  320. Exit Sub
  321. End Try
  322. Dim PackName As String = If(Json("name"), "")
  323. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  324. If Validate.Validate(PackName) <> "" Then PackName = ""
  325. VersionName = MyMsgBoxInput(PackName, New ObjectModel.Collection(Of Validate) From {Validate},
  326. Title:="输入版本名", Button2:="取消")
  327. If String.IsNullOrEmpty(VersionName) Then Exit Sub
  328. End If
  329. '启动加载器
  330. Dim Loader = InstallPackCurseForgeLoader(FileAddress, Archive, ArchiveBaseFolder, VersionName, Logo)
  331. If Loader Is Nothing Then Exit Sub
  332. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  333. LoaderTaskbarAdd(Loader)
  334. FrmMain.BtnExtraDownload.ShowRefresh()
  335. If ShowRibble Then FrmMain.BtnExtraDownload.Ribble()
  336. End Sub
  337. 'HMCL
  338. Private Sub InstallPackHMCL(FileAddress As String, Archive As Compression.ZipArchive, ArchiveBaseFolder As String)
  339. '读取 Json 文件
  340. Dim Json As JObject
  341. Try
  342. Json = GetJson(ReadFile(Archive.GetEntry(ArchiveBaseFolder & "modpack.json").Open, Encoding.UTF8))
  343. Catch ex As Exception
  344. Log(ex, "整合包安装信息存在问题", LogLevel.Hint)
  345. Exit Sub
  346. End Try
  347. '获取版本名
  348. Dim PackName As String = If(Json("name"), "")
  349. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  350. If Validate.Validate(PackName) <> "" Then PackName = ""
  351. Dim VersionName As String = MyMsgBoxInput(PackName, New ObjectModel.Collection(Of Validate) From {Validate},
  352. Title:="输入版本名", Button2:="取消")
  353. If VersionName Is Nothing Then Exit Sub
  354. '解压与配置文件
  355. Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
  356. Dim InstallLoaders As New List(Of LoaderBase)
  357. InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
  358. Sub(Task As LoaderTask(Of String, Integer))
  359. UnpackFiles(InstallTemp, FileAddress)
  360. Task.Progress = 0.5
  361. '复制结果
  362. If Directory.Exists(InstallTemp & ArchiveBaseFolder & "minecraft") Then
  363. My.Computer.FileSystem.CopyDirectory(InstallTemp & ArchiveBaseFolder & "minecraft", PathMcFolder & "versions\" & VersionName)
  364. Else
  365. Log("[ModPack] 整合包中未找到 minecraft override 目录,已跳过")
  366. End If
  367. Task.Progress = 0.9
  368. '开启版本隔离
  369. WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
  370. End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
  371. '构造加载器
  372. If Json("gameVersion") Is Nothing Then Throw New Exception("整合包未提供游戏版本信息")
  373. Dim Request As New McInstallRequest With {
  374. .TargetVersionName = VersionName,
  375. .MinecraftName = Json("gameVersion").ToString
  376. }
  377. Dim InstallExpectTime As Double = 0
  378. For Each InstallLoader In InstallLoaders
  379. InstallExpectTime += InstallLoader.ProgressWeight
  380. Next
  381. Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
  382. If MergeLoaders Is Nothing Then Exit Sub
  383. Dim MergeExpectTime As Double = 0
  384. For Each MergeLoader In MergeLoaders
  385. MergeExpectTime += MergeLoader.ProgressWeight
  386. Next
  387. '构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
  388. Dim LoadersLib As New List(Of LoaderBase)
  389. LoadersLib.Add(New LoaderTask(Of String, String)("重命名版本 Json(副加载器)",
  390. Sub()
  391. Dim RealFileName As String = PathMcFolder & "versions\" & VersionName & "\" & VersionName & ".json"
  392. Dim OldFileName As String = PathMcFolder & "versions\" & VersionName & "\pack.json"
  393. If File.Exists(OldFileName) Then
  394. '修改 id
  395. Dim FileJson = GetJson(ReadFile(OldFileName))
  396. FileJson("id") = VersionName
  397. '替换文件
  398. File.Delete(OldFileName)
  399. WriteFile(RealFileName, FileJson.ToString)
  400. Log("[ModPack] 已重命名版本 Json:" & RealFileName)
  401. End If
  402. End Sub) With {.ProgressWeight = 0.1, .Show = False})
  403. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
  404. LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
  405. '构造总加载器
  406. Dim Loaders As New List(Of LoaderBase) From {
  407. New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime},
  408. New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime},
  409. New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8}
  410. }
  411. '重复任务检查
  412. Dim LoaderName As String = "HMCL 整合包安装:" & VersionName & " "
  413. SyncLock LoaderTaskbarLock
  414. For i = 0 To LoaderTaskbar.Count - 1
  415. If LoaderTaskbar(i).Name = LoaderName Then
  416. Hint("该整合包正在安装中!", HintType.Critical)
  417. Exit Sub
  418. End If
  419. Next
  420. End SyncLock
  421. '启动
  422. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
  423. 'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
  424. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  425. LoaderTaskbarAdd(Loader)
  426. FrmMain.BtnExtraDownload.ShowRefresh()
  427. FrmMain.BtnExtraDownload.Ribble()
  428. End Sub
  429. 'MMC
  430. Private Sub InstallPackMMC(FileAddress As String, Archive As Compression.ZipArchive, ArchiveBaseFolder As String)
  431. '读取 Json 文件
  432. Dim PackJson As JObject, PackInstance As String
  433. Try
  434. PackJson = GetJson(ReadFile(Archive.GetEntry(ArchiveBaseFolder & "mmc-pack.json").Open, Encoding.UTF8))
  435. PackInstance = ReadFile(Archive.GetEntry(ArchiveBaseFolder & "instance.cfg").Open, Encoding.UTF8)
  436. Catch ex As Exception
  437. Log(ex, "整合包安装信息存在问题", LogLevel.Hint)
  438. Exit Sub
  439. End Try
  440. '获取版本名
  441. Dim PackName As String = If(RegexSeek(PackInstance, "(?<=\nname\=)[^\n]+"), "")
  442. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  443. If Validate.Validate(PackName) <> "" Then PackName = ""
  444. Dim VersionName As String = MyMsgBoxInput(PackName, New ObjectModel.Collection(Of Validate) From {Validate},
  445. Title:="输入版本名", Button2:="取消")
  446. If VersionName Is Nothing Then Exit Sub
  447. '解压与配置文件
  448. Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
  449. Dim InstallLoaders As New List(Of LoaderBase)
  450. InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
  451. Sub(Task As LoaderTask(Of String, Integer))
  452. UnpackFiles(InstallTemp, FileAddress)
  453. Task.Progress = 0.5
  454. '复制结果
  455. If Directory.Exists(InstallTemp & ArchiveBaseFolder & ".minecraft") Then
  456. My.Computer.FileSystem.CopyDirectory(InstallTemp & ArchiveBaseFolder & ".minecraft", PathMcFolder & "versions\" & VersionName)
  457. Else
  458. Log("[ModPack] 整合包中未找到 override .minecraft 目录,已跳过")
  459. End If
  460. Task.Progress = 0.9
  461. '开启版本隔离
  462. WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
  463. End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
  464. '构造版本安装请求
  465. If PackJson("components") Is Nothing Then Throw New Exception("整合包未提供游戏版本信息")
  466. Dim Request As New McInstallRequest With {.TargetVersionName = VersionName}
  467. For Each Component In PackJson("components")
  468. Select Case If(Component("uid"), "").ToString
  469. Case "org.lwjgl"
  470. Log("[ModPack] 已跳过 LWJGL 项")
  471. Case "net.minecraft"
  472. Request.MinecraftName = Component("version")
  473. Case "net.minecraftforge"
  474. Request.ForgeVersion = Component("version")
  475. End Select
  476. Next
  477. '构造加载器
  478. Dim InstallExpectTime As Double = 0
  479. For Each InstallLoader In InstallLoaders
  480. InstallExpectTime += InstallLoader.ProgressWeight
  481. Next
  482. Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
  483. If MergeLoaders Is Nothing Then Exit Sub
  484. Dim MergeExpectTime As Double = 0
  485. For Each MergeLoader In MergeLoaders
  486. MergeExpectTime += MergeLoader.ProgressWeight
  487. Next
  488. '构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
  489. Dim LoadersLib As New List(Of LoaderBase)
  490. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
  491. LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
  492. '构造总加载器
  493. Dim Loaders As New List(Of LoaderBase)
  494. Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime})
  495. Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime})
  496. Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
  497. '重复任务检查
  498. Dim LoaderName As String = "MMC 整合包安装:" & VersionName & " "
  499. SyncLock LoaderTaskbarLock
  500. For i = 0 To LoaderTaskbar.Count - 1
  501. If LoaderTaskbar(i).Name = LoaderName Then
  502. Hint("该整合包正在安装中!", HintType.Critical)
  503. Exit Sub
  504. End If
  505. Next
  506. End SyncLock
  507. '启动
  508. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
  509. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  510. LoaderTaskbarAdd(Loader)
  511. FrmMain.BtnExtraDownload.ShowRefresh()
  512. FrmMain.BtnExtraDownload.Ribble()
  513. End Sub
  514. 'MCBBS
  515. Private Sub InstallPackMCBBS(FileAddress As String, Archive As Compression.ZipArchive, ArchiveBaseFolder As String)
  516. '读取 Json 文件
  517. Dim Json As JObject
  518. Try
  519. Dim Entry = If(Archive.GetEntry(ArchiveBaseFolder & "mcbbs.packmeta"), Archive.GetEntry(ArchiveBaseFolder & "manifest.json"))
  520. Json = GetJson(ReadFile(Entry.Open, Encoding.UTF8))
  521. Catch ex As Exception
  522. Log(ex, "整合包安装信息存在问题", LogLevel.Hint)
  523. Exit Sub
  524. End Try
  525. '获取版本名
  526. Dim PackName As String = If(Json("name"), "")
  527. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  528. If Validate.Validate(PackName) <> "" Then PackName = ""
  529. Dim VersionName As String = MyMsgBoxInput(PackName, New ObjectModel.Collection(Of Validate) From {Validate},
  530. Title:="输入版本名", Button2:="取消")
  531. If VersionName Is Nothing Then Exit Sub
  532. '解压与配置文件
  533. Dim InstallTemp As String = PathTemp & "PackInstall\" & RandomInteger(0, 100000) & "\"
  534. Dim InstallLoaders As New List(Of LoaderBase)
  535. InstallLoaders.Add(New LoaderTask(Of String, Integer)("解压整合包文件",
  536. Sub(Task As LoaderTask(Of String, Integer))
  537. UnpackFiles(InstallTemp, FileAddress)
  538. Task.Progress = 0.5
  539. '复制结果
  540. If Directory.Exists(InstallTemp & ArchiveBaseFolder & "overrides") Then
  541. My.Computer.FileSystem.CopyDirectory(InstallTemp & ArchiveBaseFolder & "overrides", PathMcFolder & "versions\" & VersionName)
  542. Else
  543. Log("[ModPack] 整合包中未找到 overrides 目录,已跳过")
  544. End If
  545. Task.Progress = 0.9
  546. '开启版本隔离
  547. WriteIni(PathMcFolder & "versions\" & VersionName & "\PCL\Setup.ini", "VersionArgumentIndie", 1)
  548. End Sub) With {.ProgressWeight = New FileInfo(FileAddress).Length / 1024 / 1024 / 6, .Block = False}) '每 6M 需要 1s
  549. '构造加载器
  550. If Json("addons") Is Nothing Then Throw New Exception("整合包未提供游戏版本信息")
  551. Dim Addons As New Dictionary(Of String, String)
  552. For Each Entry In Json("addons")
  553. Addons.Add(Entry("id"), Entry("version"))
  554. Next
  555. If Not Addons.ContainsKey("game") Then Throw New Exception("整合包未提供游戏版本信息")
  556. Dim Request As New McInstallRequest With {
  557. .TargetVersionName = VersionName,
  558. .MinecraftName = Addons("game"),
  559. .OptiFineVersion = If(Addons.ContainsKey("optifine"), Addons("optifine"), Nothing),
  560. .ForgeVersion = If(Addons.ContainsKey("forge"), Addons("forge"), Nothing),
  561. .FabricVersion = If(Addons.ContainsKey("fabric"), Addons("fabric"), Nothing)
  562. }
  563. Dim InstallExpectTime As Double = 0
  564. For Each InstallLoader In InstallLoaders
  565. InstallExpectTime += InstallLoader.ProgressWeight
  566. Next
  567. Dim MergeLoaders As List(Of LoaderBase) = McInstallLoader(Request, True)
  568. If MergeLoaders Is Nothing Then Exit Sub
  569. Dim MergeExpectTime As Double = 0
  570. For Each MergeLoader In MergeLoaders
  571. MergeExpectTime += MergeLoader.ProgressWeight
  572. Next
  573. '构造 Libraries 加载器(为了使得 Mods 下载结束后再构造,这样才会下载 JumpLoader 文件)
  574. Dim LoadersLib As New List(Of LoaderBase)
  575. LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析游戏支持库文件(副加载器)", Sub(Task As LoaderTask(Of String, List(Of NetFile))) Task.Output = McLibFix(New McVersion(VersionName))) With {.ProgressWeight = 1, .Show = False})
  576. LoadersLib.Add(New LoaderDownload("下载游戏支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 7, .Show = False})
  577. '构造总加载器
  578. Dim Loaders As New List(Of LoaderBase)
  579. Loaders.Add(New LoaderCombo(Of String)("游戏安装", MergeLoaders) With {.Show = False, .Block = False, .ProgressWeight = MergeExpectTime})
  580. Loaders.Add(New LoaderCombo(Of String)("整合包安装", InstallLoaders) With {.Show = False, .ProgressWeight = InstallExpectTime})
  581. Loaders.Add(New LoaderCombo(Of String)("下载游戏支持库文件", LoadersLib) With {.ProgressWeight = 8})
  582. '重复任务检查
  583. Dim LoaderName As String = "MCBBS 整合包安装:" & VersionName & " "
  584. SyncLock LoaderTaskbarLock
  585. For i = 0 To LoaderTaskbar.Count - 1
  586. If LoaderTaskbar(i).Name = LoaderName Then
  587. Hint("该整合包正在安装中!", HintType.Critical)
  588. Exit Sub
  589. End If
  590. Next
  591. End SyncLock
  592. '启动
  593. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged = AddressOf McInstallState}
  594. 'If Archive IsNot Nothing Then Archive.Dispose() '解除占用,以免在加载器中触发 “正由另一进程使用,因此该进程无法访问此文件”
  595. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  596. LoaderTaskbarAdd(Loader)
  597. FrmMain.BtnExtraDownload.ShowRefresh()
  598. FrmMain.BtnExtraDownload.Ribble()
  599. End Sub
  600. '普通压缩包
  601. Private Sub InstallPackCompress(FileAddress As String, ArchiveBaseFolder As String)
  602. MyMsgBox("请在接下来打开的窗口中选择安装目标文件夹,它必须是一个空文件夹。", "安装提示", "继续", ForceWait:=True)
  603. '获取解压路径
  604. Dim TargetFolder As String = SelectFolder("选择安装目标(必须是一个空文件夹)")
  605. If String.IsNullOrEmpty(TargetFolder) Then Exit Sub
  606. If TargetFolder.Contains("!") OrElse TargetFolder.Contains(";") Then Hint("Minecraft 文件夹路径中不能含有感叹号或分号!", HintType.Critical) : Exit Sub
  607. If Directory.GetFileSystemEntries(TargetFolder).Length > 0 Then Hint("请选择一个空文件夹作为安装目标!", HintType.Critical) : Exit Sub
  608. '要求显示名称
  609. Dim NewName As String = MyMsgBoxInput(GetFolderNameFromPath(TargetFolder), New ObjectModel.Collection(Of Validate) From {
  610. New ValidateNullOrWhiteSpace, New ValidateLength(1, 30), New ValidateExcept({">", "|"})
  611. },, "输入它在列表中的显示名称",, "取消")
  612. If String.IsNullOrWhiteSpace(NewName) Then Exit Sub
  613. '解压
  614. Hint("正在解压压缩包……")
  615. UnpackFiles(TargetFolder, FileAddress)
  616. '加入文件夹列表
  617. PageSelectLeft.AddFolder(TargetFolder, NewName, False)
  618. Hint("已加入游戏文件夹列表:" & NewName, HintType.Finish)
  619. End Sub
  620. #End Region
  621. End Module