PageDownloadCfDetail.xaml.vb 19 KB

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