PageDownloadCompDetail.xaml.vb 19 KB

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