PageDownloadCompDetail.xaml.vb 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. Public Class PageDownloadCompDetail
  2. Private CompItem As MyCompItem = Nothing
  3. #Region "加载器"
  4. Private CompFileLoader As New LoaderTask(Of Integer, List(Of CompFile))(
  5. "Comp File", Sub(Task As LoaderTask(Of Integer, List(Of CompFile))) Task.Output = CompFilesGet(Project.Id, Project.FromCurseForge))
  6. '初始化加载器信息
  7. Private Sub PageDownloadCompDetail_Inited(sender As Object, e As EventArgs) Handles Me.Initialized
  8. Project = FrmMain.PageCurrent.Additional(0)
  9. TargetVersion = FrmMain.PageCurrent.Additional(2)
  10. TargetLoader = FrmMain.PageCurrent.Additional(3)
  11. PageLoaderInit(Load, PanLoad, PanMain, CardIntro, CompFileLoader, AddressOf Load_OnFinish)
  12. End Sub
  13. Private Sub PageDownloadCompDetail_Loaded(sender As Object, e As EventArgs) Handles Me.Loaded
  14. 'Initialized 只会执行一次
  15. Project = FrmMain.PageCurrent.Additional(0)
  16. TargetVersion = FrmMain.PageCurrent.Additional(2)
  17. TargetLoader = FrmMain.PageCurrent.Additional(3)
  18. End Sub
  19. Private Project As CompProject
  20. Private TargetVersion As String, TargetLoader As CompModLoaderType
  21. '自动重试
  22. Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
  23. Select Case CompFileLoader.State
  24. Case LoadState.Failed
  25. Dim ErrorMessage As String = ""
  26. If CompFileLoader.Error IsNot Nothing Then ErrorMessage = CompFileLoader.Error.Message
  27. If ErrorMessage.Contains("不是有效的 json 文件") Then
  28. Log("[Comp] 下载的文件 json 列表损坏,已自动重试", LogLevel.Debug)
  29. PageLoaderRestart()
  30. End If
  31. End Select
  32. End Sub
  33. '结果 UI 化
  34. Private Class CardSorter
  35. Implements IComparer(Of String)
  36. Public Topmost As String = ""
  37. Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
  38. '相同
  39. If x = y Then Return 0
  40. '置顶
  41. If x = Topmost Then Return -1
  42. If y = Topmost Then Return 1
  43. '特殊版本
  44. Dim IsXSpecial As Boolean = x.EndsWithF("版本")
  45. Dim IsYSpecial As Boolean = y.EndsWithF("版本")
  46. If IsXSpecial AndAlso IsYSpecial Then Return x.CompareTo(y)
  47. If IsXSpecial Then Return 1
  48. If IsYSpecial Then Return -1
  49. '比较版本号
  50. Dim VersionCodeSort = -VersionSortInteger(x.Replace(x.BeforeFirst(" ") & " ", ""), y.Replace(y.BeforeFirst(" ") & " ", ""))
  51. If VersionCodeSort <> 0 Then Return VersionCodeSort
  52. '比较全部
  53. Return -VersionSortInteger(x, y)
  54. End Function
  55. Public Sub New(Optional Topmost As String = "")
  56. Me.Topmost = If(Topmost, "")
  57. End Sub
  58. End Class
  59. Private Sub Load_OnFinish()
  60. Dim TargetCardName As String = If(TargetVersion <> "" OrElse TargetLoader <> CompModLoaderType.Any,
  61. $"所选版本:{If(TargetLoader <> CompModLoaderType.Any, TargetLoader.ToString & " ", "")}{TargetVersion}", "")
  62. '初始化字典
  63. Dim Dict As New SortedDictionary(Of String, List(Of CompFile))(New CardSorter(TargetCardName))
  64. Dict.Add("未知版本", New List(Of CompFile))
  65. Dim SupportedLoaders As New List(Of Integer)([Enum].GetValues(GetType(CompModLoaderType)))
  66. For Each Version As CompFile In CompFileLoader.Output
  67. For Each GameVersion In Version.GameVersions
  68. '决定添加到哪个卡片
  69. Dim Ver As String
  70. If GameVersion Is Nothing Then
  71. Ver = "未知版本"
  72. ElseIf GameVersion.Contains("w") OrElse GameVersion.Contains("pre") OrElse GameVersion.Contains("rc") Then
  73. Ver = "快照版本"
  74. ElseIf GameVersion.StartsWithF("1.0") Then
  75. Ver = "远古版本"
  76. Else
  77. Ver = GameVersion
  78. End If
  79. '遍历加入的加载器列表
  80. Dim Loaders As New List(Of String)
  81. If Project.Type = CompType.Mod Then
  82. For Each Loader In Version.ModLoaders
  83. If SupportedLoaders.Contains(Loader) Then Loaders.Add(Loader.ToString & " ")
  84. Next
  85. End If
  86. If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
  87. '实际添加
  88. For Each Loader In Loaders
  89. Dim TargetCard As String = Loader & Ver
  90. If Not Dict.ContainsKey(TargetCard) Then Dict.Add(TargetCard, New List(Of CompFile))
  91. If Not Dict(TargetCard).Contains(Version) Then Dict(TargetCard).Add(Version)
  92. Next
  93. Next
  94. Next
  95. '添加筛选的版本的卡片
  96. If TargetCardName <> "" Then
  97. Dict.Add(TargetCardName, New List(Of CompFile))
  98. For Each Version As CompFile In CompFileLoader.Output
  99. If Version.GameVersions.Contains(TargetVersion) AndAlso
  100. (TargetLoader = CompModLoaderType.Any OrElse Version.ModLoaders.Contains(TargetLoader)) Then
  101. If Not Dict(TargetCardName).Contains(Version) Then Dict(TargetCardName).Add(Version)
  102. End If
  103. Next
  104. End If
  105. #Region "转化为 UI"
  106. Try
  107. '清空当前
  108. PanMain.Children.Clear()
  109. '转化为 UI
  110. For Each Pair As KeyValuePair(Of String, List(Of CompFile)) In Dict
  111. If Not Pair.Value.Any() Then Continue For
  112. '增加卡片
  113. Dim NewCard As New MyCard With {.Title = Pair.Key, .Margin = New Thickness(0, 0, 0, 15), .SwapType = If(Project.Type = CompType.ModPack, 9, 8)} 'FUTURE: Res
  114. Dim NewStack As New StackPanel With {.Margin = New Thickness(20, MyCard.SwapedHeight, 18, 0), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0), .Tag = Pair.Value}
  115. NewCard.Children.Add(NewStack)
  116. NewCard.SwapControl = NewStack
  117. PanMain.Children.Add(NewCard)
  118. '确定卡片是否展开
  119. If Pair.Key = TargetCardName OrElse
  120. (FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
  121. CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
  122. MyCard.StackInstall(NewStack, If(Project.Type = CompType.ModPack, 9, 8), Pair.Key) 'FUTURE: Res
  123. Else
  124. NewCard.IsSwaped = True
  125. End If
  126. '增加提示
  127. If Pair.Key = "未知版本" Then
  128. NewStack.Children.Add(New MyHint With {.Text = "由于 API 的版本信息更新缓慢,可能无法识别刚更新不久的 MC 版本,只需等待几天即可自动恢复正常。", .IsWarn = False, .Margin = New Thickness(0, 0, 0, 7)})
  129. End If
  130. Next
  131. '如果只有一张卡片,展开第一张卡片
  132. If PanMain.Children.Count = 1 Then
  133. CType(PanMain.Children(0), MyCard).IsSwaped = False
  134. End If
  135. Catch ex As Exception
  136. Log(ex, "可视化工程下载列表出错", LogLevel.Feedback)
  137. End Try
  138. #End Region
  139. End Sub
  140. #End Region
  141. Private IsFirstInit As Boolean = True
  142. Public Sub Init() Handles Me.PageEnter
  143. AniControlEnabled += 1
  144. Project = FrmMain.PageCurrent.Additional(0)
  145. PanBack.ScrollToHome()
  146. '重启加载器
  147. If IsFirstInit Then
  148. '在 Me.Initialized 已经初始化了加载器,不再重复初始化
  149. IsFirstInit = False
  150. Else
  151. PageLoaderRestart(IsForceRestart:=True)
  152. End If
  153. '放置当前工程
  154. If CompItem IsNot Nothing Then PanIntro.Children.Remove(CompItem)
  155. CompItem = Project.ToCompItem(True, True)
  156. CompItem.CanInteraction = False
  157. CompItem.Margin = New Thickness(-7, -7, 0, 8)
  158. PanIntro.Children.Insert(0, CompItem)
  159. '决定按钮显示
  160. BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
  161. BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
  162. AniControlEnabled -= 1
  163. End Sub
  164. '整合包下载(安装)
  165. Public Sub Install_Click(sender As MyListItem, e As EventArgs)
  166. Try
  167. '获取基本信息
  168. Dim File As CompFile = sender.Tag
  169. Dim LoaderName As String = $"{If(Project.FromCurseForge, "CurseForge", "Modrinth")} 整合包下载:{Project.TranslatedName} "
  170. '获取版本名
  171. Dim PackName As String = Project.TranslatedName.Replace(".zip", "").Replace(".rar", "").Replace(".mrpack", "").Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  172. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  173. If Validate.Validate(PackName) <> "" Then PackName = ""
  174. Dim VersionName As String = MyMsgBoxInput("输入版本名称", "", PackName, New ObjectModel.Collection(Of Validate) From {Validate})
  175. If String.IsNullOrEmpty(VersionName) Then Exit Sub
  176. '构造步骤加载器
  177. Dim Loaders As New List(Of LoaderBase)
  178. Dim Target As String = $"{PathMcFolder}versions\{VersionName}\原始整合包.{If(Project.FromCurseForge, "zip", "mrpack")}"
  179. Dim LogoFileAddress As String = MyImage.GetTempPath(CompItem.Logo)
  180. Loaders.Add(New LoaderDownload("下载整合包文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 10, .Block = True})
  181. Loaders.Add(New LoaderTask(Of Integer, Integer)("准备安装整合包",
  182. Sub() ModpackInstall(Target, VersionName, If(IO.File.Exists(LogoFileAddress), LogoFileAddress, Nothing))) With {.ProgressWeight = 0.1})
  183. '启动
  184. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged =
  185. Sub(MyLoader)
  186. Select Case MyLoader.State
  187. Case LoadState.Failed
  188. Hint(MyLoader.Name & "失败:" & GetExceptionSummary(MyLoader.Error), HintType.Critical)
  189. Case LoadState.Aborted
  190. Hint(MyLoader.Name & "已取消!", HintType.Info)
  191. Case LoadState.Loading
  192. Exit Sub '不重新加载版本列表
  193. End Select
  194. McInstallFailedClearFolder(MyLoader)
  195. End Sub}
  196. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  197. LoaderTaskbarAdd(Loader)
  198. FrmMain.BtnExtraDownload.ShowRefresh()
  199. FrmMain.BtnExtraDownload.Ribble()
  200. Catch ex As Exception
  201. Log(ex, "下载资源整合包失败", LogLevel.Feedback)
  202. End Try
  203. End Sub
  204. 'Mod、资源包下载;整合包另存为
  205. Public Shared CachedFolder As String = Nothing '仅在本次缓存的下载文件夹
  206. Public Sub Save_Click(sender As Object, e As EventArgs)
  207. Dim File As CompFile = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag
  208. RunInNewThread(
  209. Sub()
  210. Try
  211. Dim Desc As String = If(Project.Type = CompType.ModPack, "整合包", If(Project.Type = CompType.Mod, "Mod ", "资源包"))
  212. '确认默认保存位置
  213. Dim DefaultFolder As String = Nothing
  214. If Project.Type = CompType.Mod Then
  215. '获取 Mod 所需的加载器种类
  216. Dim AllowForge As Boolean? = Nothing, AllowFabric As Boolean? = Nothing
  217. If File.ModLoaders.Any Then '从文件中获取
  218. AllowForge = File.ModLoaders.Contains(CompModLoaderType.Forge) OrElse File.ModLoaders.Contains(CompModLoaderType.NeoForge)
  219. AllowFabric = File.ModLoaders.Contains(CompModLoaderType.Fabric)
  220. ElseIf Project.ModLoaders.Any Then '从工程中获取
  221. AllowForge = Project.ModLoaders.Contains(CompModLoaderType.Forge) OrElse File.ModLoaders.Contains(CompModLoaderType.NeoForge)
  222. AllowFabric = Project.ModLoaders.Contains(CompModLoaderType.Fabric)
  223. End If
  224. If AllowForge IsNot Nothing AndAlso Not AllowForge AndAlso
  225. AllowFabric IsNot Nothing AndAlso Not AllowFabric Then
  226. AllowForge = Nothing : AllowFabric = Nothing
  227. End If
  228. Log("[Comp] 允许 Forge:" & If(AllowForge, "未知") & ",允许 Fabric:" & If(AllowFabric, "未知"))
  229. '判断某个版本是否符合要求
  230. Dim IsVersionSuitable As Func(Of McVersion, Boolean) =
  231. Function(Version)
  232. If Not Version.IsLoaded Then Version.Load()
  233. If Not Version.Modable Then Return False
  234. If File.GameVersions.Any(Function(v) v.Contains(".")) AndAlso
  235. Not File.GameVersions.Any(Function(v) v.Contains(".") AndAlso v.Split(".")(1) = Version.Version.McCodeMain.ToString) Then Return False
  236. If AllowForge Is Nothing OrElse AllowFabric Is Nothing Then Return True
  237. If AllowForge AndAlso (Version.Version.HasForge OrElse Version.Version.HasNeoForge) Then Return True
  238. If AllowFabric AndAlso Version.Version.HasFabric Then Return True
  239. Return False
  240. End Function
  241. '获取 Mod 默认下载位置
  242. If CachedFolder IsNot Nothing Then
  243. DefaultFolder = CachedFolder
  244. Log("[Comp] 使用上次下载时的文件夹作为默认下载位置")
  245. ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then
  246. DefaultFolder = McVersionCurrent.PathIndie & "mods\"
  247. Directory.CreateDirectory(DefaultFolder)
  248. Log("[Comp] 使用当前版本的 mods 文件夹作为默认下载位置(" & McVersionCurrent.Name & ")")
  249. Else
  250. Dim NeedLoad As Boolean = McVersionListLoader.State <> LoadState.Finished
  251. If NeedLoad Then
  252. Hint("正在查找适合的游戏版本……")
  253. LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
  254. End If
  255. Dim SuitableVersions As New List(Of McVersion)
  256. For Each Version As McVersion In McVersionList.Values.SelectMany(Function(l) l)
  257. If IsVersionSuitable(Version) Then SuitableVersions.Add(Version)
  258. Next
  259. If Not SuitableVersions.Any() Then
  260. DefaultFolder = PathMcFolder
  261. If NeedLoad Then
  262. Hint("当前 MC 文件夹中没有找到适合这个 Mod 的版本!")
  263. Else
  264. Log("[Comp] 由于当前版本不兼容,使用当前的 MC 文件夹作为默认下载位置")
  265. End If
  266. Else '选择 Mod 数量最多的版本
  267. Dim SelectedVersion = SuitableVersions.OrderBy(
  268. Function(v)
  269. Dim Info As New DirectoryInfo(v.PathIndie & "mods\")
  270. Return If(Info.Exists, Info.GetFiles().Length, -1)
  271. End Function).LastOrDefault()
  272. DefaultFolder = SelectedVersion.PathIndie & "mods\"
  273. Directory.CreateDirectory(DefaultFolder)
  274. Log("[Comp] 使用适合的游戏版本作为默认下载位置(" & SelectedVersion.Name & ")")
  275. End If
  276. End If
  277. End If
  278. '获取基本信息
  279. Dim FileName As String
  280. If Project.TranslatedName = Project.RawName Then
  281. FileName = File.FileName
  282. Else
  283. Dim ChineseName As String = Project.TranslatedName.BeforeFirst(" (").BeforeFirst(" - ").
  284. Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  285. Select Case Setup.Get("ToolDownloadTranslate")
  286. Case 0
  287. FileName = $"[{ChineseName}] {File.FileName}"
  288. Case 1
  289. FileName = $"{ChineseName}-{File.FileName}"
  290. Case 2
  291. FileName = $"{File.FileName}-{ChineseName}"
  292. Case Else
  293. FileName = File.FileName
  294. End Select
  295. End If
  296. RunInUi(
  297. Sub()
  298. '弹窗要求选择保存位置
  299. Dim Target As String
  300. Target = SelectAs("选择保存位置", FileName,
  301. Desc & "文件|" &
  302. If(Project.Type = CompType.Mod,
  303. If(File.FileName.EndsWith(".litemod"), "*.litemod", "*.jar"),
  304. If(File.FileName.EndsWith(".mrpack"), "*.mrpack", "*.zip")), DefaultFolder)
  305. If Not Target.Contains("\") Then Exit Sub
  306. '构造步骤加载器
  307. Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
  308. If Target <> DefaultFolder AndAlso Project.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target)
  309. Dim Loaders As New List(Of LoaderBase)
  310. Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
  311. '启动
  312. Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf DownloadStateSave}
  313. Loader.Start(1)
  314. LoaderTaskbarAdd(Loader)
  315. FrmMain.BtnExtraDownload.ShowRefresh()
  316. FrmMain.BtnExtraDownload.Ribble()
  317. End Sub)
  318. Catch ex As Exception
  319. Log(ex, "保存资源文件失败", LogLevel.Feedback)
  320. End Try
  321. End Sub, "Download CompDetail Save")
  322. End Sub
  323. Private Sub BtnIntroWeb_Click(sender As Object, e As EventArgs) Handles BtnIntroWeb.Click
  324. OpenWebsite(Project.Website)
  325. End Sub
  326. Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
  327. OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
  328. End Sub
  329. Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
  330. ClipboardSet(CompItem.LabTitle.Text)
  331. End Sub
  332. End Class