PageDownloadCompDetail.xaml.vb 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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 VersionFilter As String
  60. Private IsMajorVersionFilter As Boolean '是否按大版本号筛选(1.21 / 1.20 / 1.19 / ...)而非小版本号(1.21.1 / 1.21 / 1.20.4 / ...)
  61. Private Sub Load_OnFinish()
  62. '初始化筛选器
  63. Dim VersionFilters As List(Of String)
  64. '按小版本号筛选?
  65. IsMajorVersionFilter = False
  66. VersionFilters = CompFileLoader.Output.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
  67. Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
  68. '按大版本号筛选?
  69. If VersionFilters.Count >= 9 Then
  70. IsMajorVersionFilter = True
  71. VersionFilters = CompFileLoader.Output.SelectMany(Function(v) v.GameVersions).Select(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True)).
  72. Distinct.OrderByDescending(Function(s) s, New VersionComparer).ToList
  73. End If
  74. 'UI 化筛选器
  75. PanFilter.Children.Clear()
  76. If VersionFilters.Count < 2 Then
  77. CardFilter.Visibility = Visibility.Collapsed
  78. VersionFilter = Nothing
  79. Else
  80. CardFilter.Visibility = Visibility.Visible
  81. VersionFilters.Insert(0, "全部")
  82. '转化为按钮
  83. For Each Version As String In VersionFilters
  84. Dim NewButton As New MyRadioButton With {
  85. .Text = Version, .Margin = New Thickness(2, 0, 2, 0), .ColorType = MyRadioButton.ColorState.Highlight}
  86. NewButton.LabText.Margin = New Thickness(-2, 0, 8, 0)
  87. AddHandler NewButton.Check,
  88. Sub(sender As MyRadioButton, raiseByMouse As Boolean)
  89. VersionFilter = If(sender.Text = "全部", Nothing, sender.Text)
  90. UpdateFilterResult()
  91. End Sub
  92. PanFilter.Children.Add(NewButton)
  93. Next
  94. '自动选择
  95. Dim ToCheck As MyRadioButton = Nothing
  96. If TargetVersion <> "" Then
  97. Dim TargetFile = CompFileLoader.Output.FirstOrDefault(Function(v) v.GameVersions.Contains(TargetVersion))
  98. If TargetFile IsNot Nothing Then
  99. Dim TargetGroup = GetGroupedVersionName(TargetVersion, IsMajorVersionFilter, True)
  100. For Each Button As MyRadioButton In PanFilter.Children
  101. If Button.Text <> TargetGroup Then Continue For
  102. ToCheck = Button
  103. Exit For
  104. Next
  105. End If
  106. End If
  107. If ToCheck Is Nothing Then ToCheck = PanFilter.Children(0)
  108. ToCheck.Checked = True
  109. End If
  110. '更新筛选结果(文件列表 UI 化)
  111. UpdateFilterResult()
  112. End Sub
  113. Private Sub UpdateFilterResult()
  114. Dim TargetCardName As String = If(TargetVersion <> "" OrElse TargetLoader <> CompModLoaderType.Any,
  115. $"所选版本:{If(TargetLoader <> CompModLoaderType.Any, TargetLoader.ToString & " ", "")}{TargetVersion}", "")
  116. '归类到卡片下
  117. Dim Dict As New SortedDictionary(Of String, List(Of CompFile))(New CardSorter(TargetCardName))
  118. Dict.Add("其他版本", New List(Of CompFile))
  119. Dim SupportedLoaders As New List(Of Integer)([Enum].GetValues(GetType(CompModLoaderType)))
  120. For Each Version As CompFile In CompFileLoader.Output
  121. For Each GameVersion In Version.GameVersions
  122. '检查是否符合版本筛选器
  123. If VersionFilter IsNot Nothing AndAlso
  124. GetGroupedVersionName(GameVersion, IsMajorVersionFilter, True) <> VersionFilter Then Continue For
  125. '决定添加到哪个卡片
  126. Dim Ver As String = GetGroupedVersionName(GameVersion, False, False)
  127. '遍历加入的加载器列表
  128. Dim Loaders As New List(Of String)
  129. If Project.ModLoaders.Count > 1 AndAlso '至少有两个加载器
  130. Project.Type = CompType.Mod AndAlso '是 Mod
  131. Ver.StartsWith("1.") Then '不是 “快照版本” 之类的
  132. For Each Loader In Version.ModLoaders
  133. If Loader = CompModLoaderType.Quilt AndAlso Setup.Get("ToolDownloadIgnoreQuilt") Then Continue For
  134. If SupportedLoaders.Contains(Loader) Then Loaders.Add(Loader.ToString & " ")
  135. Next
  136. End If
  137. If Not Loaders.Any() Then Loaders.Add("") '保底加一个空的,确保它在一张卡片里
  138. '实际添加
  139. For Each Loader In Loaders
  140. Dim TargetCard As String = Loader & Ver
  141. If Not Dict.ContainsKey(TargetCard) Then Dict.Add(TargetCard, New List(Of CompFile))
  142. If Not Dict(TargetCard).Contains(Version) Then Dict(TargetCard).Add(Version)
  143. Next
  144. Next
  145. Next
  146. '添加筛选的版本的卡片
  147. If TargetCardName <> "" Then
  148. Dict.Add(TargetCardName, New List(Of CompFile))
  149. For Each Version As CompFile In CompFileLoader.Output
  150. If Version.GameVersions.Contains(TargetVersion) AndAlso
  151. (TargetLoader = CompModLoaderType.Any OrElse Version.ModLoaders.Contains(TargetLoader)) Then
  152. '检查是否符合版本筛选器
  153. If VersionFilter IsNot Nothing AndAlso
  154. Not Version.GameVersions.Any(Function(v) GetGroupedVersionName(v, IsMajorVersionFilter, True) = VersionFilter) Then Continue For
  155. If Not Dict(TargetCardName).Contains(Version) Then Dict(TargetCardName).Add(Version)
  156. End If
  157. Next
  158. End If
  159. '转化为 UI
  160. Try
  161. PanResults.Children.Clear()
  162. For Each Pair As KeyValuePair(Of String, List(Of CompFile)) In Dict
  163. If Not Pair.Value.Any() Then Continue For
  164. '增加卡片
  165. 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
  166. 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}
  167. NewCard.Children.Add(NewStack)
  168. NewCard.SwapControl = NewStack
  169. PanResults.Children.Add(NewCard)
  170. '确定卡片是否展开
  171. If Pair.Key = TargetCardName OrElse
  172. (FrmMain.PageCurrent.Additional IsNot Nothing AndAlso '#2761
  173. CType(FrmMain.PageCurrent.Additional(1), List(Of String)).Contains(NewCard.Title)) Then
  174. MyCard.StackInstall(NewStack, If(Project.Type = CompType.ModPack, 9, 8), Pair.Key) 'FUTURE: Res
  175. Else
  176. NewCard.IsSwaped = True
  177. End If
  178. '增加提示
  179. If Pair.Key = "其他版本" Then
  180. NewStack.Children.Add(New MyHint With {.Text = "由于版本信息更新缓慢,可能无法识别刚更新的 MC 版本,只需等待几天即可自动恢复正常。", .IsWarn = False, .Margin = New Thickness(0, 0, 0, 7)})
  181. End If
  182. Next
  183. '如果只有一张卡片,展开第一张卡片
  184. If PanResults.Children.Count = 1 Then
  185. CType(PanResults.Children(0), MyCard).IsSwaped = False
  186. End If
  187. Catch ex As Exception
  188. Log(ex, "可视化工程下载列表出错", LogLevel.Feedback)
  189. End Try
  190. End Sub
  191. Private Function GetGroupedVersionName(Name As String, MajorOnly As Boolean, FoldOldRelease As Boolean) As String
  192. If Name Is Nothing Then
  193. Return "其他版本"
  194. ElseIf Name.Contains("w") Then
  195. Return "快照版本"
  196. ElseIf Name.StartsWith("1.0") OrElse Not Name.StartsWith("1.") OrElse (FoldOldRelease AndAlso Val(Name.Split(".")(1)) < 10) Then
  197. Return "远古版本"
  198. Else
  199. Return If(MajorOnly, "1." & Name.Split(".")(1).BeforeFirst(" "), Name)
  200. End If
  201. End Function
  202. #End Region
  203. Private IsFirstInit As Boolean = True
  204. Public Sub Init() Handles Me.PageEnter
  205. AniControlEnabled += 1
  206. Project = FrmMain.PageCurrent.Additional(0)
  207. PanBack.ScrollToHome()
  208. '重启加载器
  209. If IsFirstInit Then
  210. '在 Me.Initialized 已经初始化了加载器,不再重复初始化
  211. IsFirstInit = False
  212. Else
  213. PageLoaderRestart(IsForceRestart:=True)
  214. End If
  215. '放置当前工程
  216. If CompItem IsNot Nothing Then PanIntro.Children.Remove(CompItem)
  217. CompItem = Project.ToCompItem(True, True)
  218. CompItem.CanInteraction = False
  219. CompItem.Margin = New Thickness(-7, -7, 0, 8)
  220. PanIntro.Children.Insert(0, CompItem)
  221. '决定按钮显示
  222. BtnIntroWeb.Text = If(Project.FromCurseForge, "转到 CurseForge", "转到 Modrinth")
  223. BtnIntroWiki.Visibility = If(Project.WikiId = 0, Visibility.Collapsed, Visibility.Visible)
  224. AniControlEnabled -= 1
  225. End Sub
  226. '整合包下载(安装)
  227. Public Sub Install_Click(sender As MyListItem, e As EventArgs)
  228. Try
  229. '获取基本信息
  230. Dim File As CompFile = sender.Tag
  231. Dim LoaderName As String = $"{If(Project.FromCurseForge, "CurseForge", "Modrinth")} 整合包下载:{Project.TranslatedName} "
  232. '获取版本名
  233. Dim PackName As String = Project.TranslatedName.Replace(".zip", "").Replace(".rar", "").Replace(".mrpack", "").Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  234. Dim Validate As New ValidateFolderName(PathMcFolder & "versions")
  235. If Validate.Validate(PackName) <> "" Then PackName = ""
  236. Dim VersionName As String = MyMsgBoxInput("输入版本名称", "", PackName, New ObjectModel.Collection(Of Validate) From {Validate})
  237. If String.IsNullOrEmpty(VersionName) Then Exit Sub
  238. '构造步骤加载器
  239. Dim Loaders As New List(Of LoaderBase)
  240. Dim Target As String = $"{PathMcFolder}versions\{VersionName}\原始整合包.{If(Project.FromCurseForge, "zip", "mrpack")}"
  241. Dim LogoFileAddress As String = MyImage.GetTempPath(CompItem.Logo)
  242. Loaders.Add(New LoaderDownload("下载整合包文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 10, .Block = True})
  243. Loaders.Add(New LoaderTask(Of Integer, Integer)("准备安装整合包",
  244. Sub() ModpackInstall(Target, VersionName, If(IO.File.Exists(LogoFileAddress), LogoFileAddress, Nothing))) With {.ProgressWeight = 0.1})
  245. '启动
  246. Dim Loader As New LoaderCombo(Of String)(LoaderName, Loaders) With {.OnStateChanged =
  247. Sub(MyLoader)
  248. Select Case MyLoader.State
  249. Case LoadState.Failed
  250. Hint(MyLoader.Name & "失败:" & GetExceptionSummary(MyLoader.Error), HintType.Critical)
  251. Case LoadState.Aborted
  252. Hint(MyLoader.Name & "已取消!", HintType.Info)
  253. Case LoadState.Loading
  254. Exit Sub '不重新加载版本列表
  255. End Select
  256. McInstallFailedClearFolder(MyLoader)
  257. End Sub}
  258. Loader.Start(PathMcFolder & "versions\" & VersionName & "\")
  259. LoaderTaskbarAdd(Loader)
  260. FrmMain.BtnExtraDownload.ShowRefresh()
  261. FrmMain.BtnExtraDownload.Ribble()
  262. Catch ex As Exception
  263. Log(ex, "下载资源整合包失败", LogLevel.Feedback)
  264. End Try
  265. End Sub
  266. 'Mod、资源包下载;整合包另存为
  267. Public Shared CachedFolder As String = Nothing '仅在本次缓存的下载文件夹
  268. Public Sub Save_Click(sender As Object, e As EventArgs)
  269. Dim File As CompFile = If(TypeOf sender Is MyListItem, sender, sender.Parent).Tag
  270. RunInNewThread(
  271. Sub()
  272. Try
  273. Dim Desc As String = If(Project.Type = CompType.ModPack, "整合包", If(Project.Type = CompType.Mod, "Mod ", "资源包"))
  274. '确认默认保存位置
  275. Dim DefaultFolder As String = Nothing
  276. If Project.Type = CompType.Mod Then
  277. '获取 Mod 所需的加载器种类
  278. Dim AllowForge As Boolean? = Nothing, AllowFabric As Boolean? = Nothing
  279. If File.ModLoaders.Any Then '从文件中获取
  280. AllowForge = File.ModLoaders.Contains(CompModLoaderType.Forge) OrElse File.ModLoaders.Contains(CompModLoaderType.NeoForge)
  281. AllowFabric = File.ModLoaders.Contains(CompModLoaderType.Fabric)
  282. ElseIf Project.ModLoaders.Any Then '从工程中获取
  283. AllowForge = Project.ModLoaders.Contains(CompModLoaderType.Forge) OrElse File.ModLoaders.Contains(CompModLoaderType.NeoForge)
  284. AllowFabric = Project.ModLoaders.Contains(CompModLoaderType.Fabric)
  285. End If
  286. If AllowForge IsNot Nothing AndAlso Not AllowForge AndAlso
  287. AllowFabric IsNot Nothing AndAlso Not AllowFabric Then
  288. AllowForge = Nothing : AllowFabric = Nothing
  289. End If
  290. Log("[Comp] 允许 Forge:" & If(AllowForge, "未知") & ",允许 Fabric:" & If(AllowFabric, "未知"))
  291. '判断某个版本是否符合要求
  292. Dim IsVersionSuitable As Func(Of McVersion, Boolean) =
  293. Function(Version)
  294. If Not Version.IsLoaded Then Version.Load()
  295. If Not Version.Modable Then Return False
  296. If File.GameVersions.Any(Function(v) v.Contains(".")) AndAlso
  297. Not File.GameVersions.Any(Function(v) v.Contains(".") AndAlso v = Version.Version.McName) Then Return False
  298. If AllowForge Is Nothing OrElse AllowFabric Is Nothing Then Return True
  299. If AllowForge AndAlso (Version.Version.HasForge OrElse Version.Version.HasNeoForge) Then Return True
  300. If AllowFabric AndAlso Version.Version.HasFabric Then Return True
  301. Return False
  302. End Function
  303. '获取 Mod 默认下载位置
  304. If CachedFolder IsNot Nothing Then
  305. DefaultFolder = CachedFolder
  306. Log("[Comp] 使用上次下载时的文件夹作为默认下载位置")
  307. ElseIf McVersionCurrent IsNot Nothing AndAlso IsVersionSuitable(McVersionCurrent) Then
  308. DefaultFolder = McVersionCurrent.PathIndie & "mods\"
  309. Directory.CreateDirectory(DefaultFolder)
  310. Log("[Comp] 使用当前版本的 mods 文件夹作为默认下载位置(" & McVersionCurrent.Name & ")")
  311. Else
  312. Dim NeedLoad As Boolean = McVersionListLoader.State <> LoadState.Finished
  313. If NeedLoad Then
  314. Hint("正在查找适合的游戏版本……")
  315. LoaderFolderRun(McVersionListLoader, PathMcFolder, LoaderFolderRunType.ForceRun, MaxDepth:=1, ExtraPath:="versions\", WaitForExit:=True)
  316. End If
  317. Dim SuitableVersions As New List(Of McVersion)
  318. For Each Version As McVersion In McVersionList.Values.SelectMany(Function(l) l)
  319. If IsVersionSuitable(Version) Then SuitableVersions.Add(Version)
  320. Next
  321. If Not SuitableVersions.Any() Then
  322. DefaultFolder = PathMcFolder
  323. If NeedLoad Then
  324. Hint("当前 MC 文件夹中没有找到适合这个 Mod 的版本!")
  325. Else
  326. Log("[Comp] 由于当前版本不兼容,使用当前的 MC 文件夹作为默认下载位置")
  327. End If
  328. Else '选择 Mod 数量最多的版本
  329. Dim SelectedVersion = SuitableVersions.OrderBy(
  330. Function(v)
  331. Dim Info As New DirectoryInfo(v.PathIndie & "mods\")
  332. Return If(Info.Exists, Info.GetFiles().Length, -1)
  333. End Function).LastOrDefault()
  334. DefaultFolder = SelectedVersion.PathIndie & "mods\"
  335. Directory.CreateDirectory(DefaultFolder)
  336. Log("[Comp] 使用适合的游戏版本作为默认下载位置(" & SelectedVersion.Name & ")")
  337. End If
  338. End If
  339. End If
  340. '获取基本信息
  341. Dim FileName As String
  342. If Project.TranslatedName = Project.RawName Then
  343. FileName = File.FileName
  344. Else
  345. Dim ChineseName As String = Project.TranslatedName.BeforeFirst(" (").BeforeFirst(" - ").
  346. Replace("\", "\").Replace("/", "/").Replace("|", "|").Replace(":", ":").Replace("<", "<").Replace(">", ">").Replace("*", "*").Replace("?", "?").Replace("""", "").Replace(": ", ":")
  347. Select Case Setup.Get("ToolDownloadTranslateV2")
  348. Case 0
  349. FileName = $"【{ChineseName}】{File.FileName}"
  350. Case 1
  351. FileName = $"[{ChineseName}] {File.FileName}"
  352. Case 2
  353. FileName = $"{ChineseName}-{File.FileName}"
  354. Case 3
  355. FileName = $"{File.FileName}-{ChineseName}"
  356. Case Else
  357. FileName = File.FileName
  358. End Select
  359. End If
  360. RunInUi(
  361. Sub()
  362. '弹窗要求选择保存位置
  363. Dim Target As String
  364. Target = SelectSaveFile("选择保存位置", FileName,
  365. Desc & "文件|" &
  366. If(Project.Type = CompType.Mod,
  367. If(File.FileName.EndsWith(".litemod"), "*.litemod", "*.jar"),
  368. If(File.FileName.EndsWith(".mrpack"), "*.mrpack", "*.zip")), DefaultFolder)
  369. If Not Target.Contains("\") Then Exit Sub
  370. '构造步骤加载器
  371. Dim LoaderName As String = Desc & "下载:" & GetFileNameWithoutExtentionFromPath(Target) & " "
  372. If Target <> DefaultFolder AndAlso Project.Type = CompType.Mod Then CachedFolder = GetPathFromFullPath(Target)
  373. Dim Loaders As New List(Of LoaderBase)
  374. Loaders.Add(New LoaderDownload("下载文件", New List(Of NetFile) From {File.ToNetFile(Target)}) With {.ProgressWeight = 6, .Block = True})
  375. '启动
  376. Dim Loader As New LoaderCombo(Of Integer)(LoaderName, Loaders) With {.OnStateChanged = AddressOf LoaderStateChangedHintOnly}
  377. Loader.Start(1)
  378. LoaderTaskbarAdd(Loader)
  379. FrmMain.BtnExtraDownload.ShowRefresh()
  380. FrmMain.BtnExtraDownload.Ribble()
  381. End Sub)
  382. Catch ex As Exception
  383. Log(ex, "保存资源文件失败", LogLevel.Feedback)
  384. End Try
  385. End Sub, "Download CompDetail Save")
  386. End Sub
  387. Private Sub BtnIntroWeb_Click(sender As Object, e As EventArgs) Handles BtnIntroWeb.Click
  388. OpenWebsite(Project.Website)
  389. End Sub
  390. Private Sub BtnIntroWiki_Click(sender As Object, e As EventArgs) Handles BtnIntroWiki.Click
  391. OpenWebsite("https://www.mcmod.cn/class/" & Project.WikiId & ".html")
  392. End Sub
  393. Private Sub BtnIntroCopy_Click(sender As Object, e As EventArgs) Handles BtnIntroCopy.Click
  394. ClipboardSet(CompItem.LabTitle.Text & CompItem.LabTitleRaw.Text)
  395. End Sub
  396. End Class