PageVersionMod.xaml.vb 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. Public Class PageVersionMod
  2. Implements IRefreshable
  3. #Region "初始化"
  4. Private IsLoad As Boolean = False
  5. Public Sub PageOther_Loaded() Handles Me.Loaded
  6. If FrmMain.PageLast.Page <> FormMain.PageType.CompDetail Then PanBack.ScrollToHome()
  7. AniControlEnabled += 1
  8. SelectedMods.Clear()
  9. ReloadModList()
  10. ChangeAllSelected(False)
  11. AniControlEnabled -= 1
  12. '非重复加载部分
  13. If IsLoad Then Exit Sub
  14. IsLoad = True
  15. '调整按钮边距(这玩意儿没法从 XAML 改)
  16. For Each Btn As MyRadioButton In PanFilter.Children
  17. Btn.LabText.Margin = New Thickness(-2, 0, 8, 0)
  18. Next
  19. #If DEBUG Then
  20. BtnManageCheck.Visibility = Visibility.Visible
  21. #End If
  22. End Sub
  23. ''' <summary>
  24. ''' 刷新 Mod 列表。
  25. ''' </summary>
  26. Public Sub ReloadModList(Optional ForceReload As Boolean = False)
  27. If LoaderRun(If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then
  28. Log("[System] 已刷新 Mod 列表")
  29. Filter = FilterType.All
  30. PanBack.ScrollToHome()
  31. SearchBox.Text = ""
  32. End If
  33. End Sub
  34. '强制刷新
  35. Private Sub RefreshSelf() Implements IRefreshable.Refresh
  36. Refresh()
  37. End Sub
  38. Public Shared Sub Refresh()
  39. '强制刷新
  40. Try
  41. CompProjectCache.Clear()
  42. CompFilesCache.Clear()
  43. File.Delete(PathTemp & "Cache\LocalMod.json")
  44. Log("[Mod] 由于点击刷新按钮,清理本地 Mod 信息缓存")
  45. Catch ex As Exception
  46. Log(ex, "强制刷新时清理本地 Mod 信息缓存失败")
  47. End Try
  48. If FrmVersionMod IsNot Nothing Then FrmVersionMod.ReloadModList(True) '无需 Else,还没加载刷个鬼的新
  49. FrmVersionLeft.ItemMod.Checked = True
  50. Hint("正在刷新……", Log:=False)
  51. End Sub
  52. Private Sub LoaderInit() Handles Me.Initialized
  53. PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf LoadUIFromLoaderOutput, AutoRun:=False)
  54. End Sub
  55. Private Sub Load_Click(sender As Object, e As MouseButtonEventArgs) Handles Load.Click
  56. If McModLoader.State = LoadState.Failed Then
  57. LoaderRun(LoaderFolderRunType.ForceRun)
  58. End If
  59. End Sub
  60. Public Function LoaderRun(Type As LoaderFolderRunType) As Boolean
  61. Return LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", Type)
  62. End Function
  63. #End Region
  64. #Region "UI 化"
  65. ''' <summary>
  66. ''' 已加载的 Mod UI 缓存,不确保按显示顺序排列。Key 为 Mod 的 RawFileName。
  67. ''' </summary>
  68. Public ModItems As New Dictionary(Of String, MyLocalModItem)
  69. ''' <summary>
  70. ''' 将加载器结果的 Mod 列表加载为 UI。
  71. ''' </summary>
  72. Private Sub LoadUIFromLoaderOutput()
  73. Try
  74. '判断应该显示哪一个页面
  75. If McModLoader.Output.Any() Then
  76. PanBack.Visibility = Visibility.Visible
  77. PanEmpty.Visibility = Visibility.Collapsed
  78. Else
  79. PanEmpty.Visibility = Visibility.Visible
  80. PanBack.Visibility = Visibility.Collapsed
  81. Exit Sub
  82. End If
  83. '修改缓存
  84. ModItems.Clear()
  85. For Each ModEntity As McMod In McModLoader.Output
  86. ModItems(ModEntity.RawFileName) = McModListItem(ModEntity)
  87. Next
  88. '显示结果
  89. Filter = FilterType.All
  90. SearchBox.Text = "" '这会触发结果刷新,所以需要在 ModItems 更新之后,详见 #3124 的视频
  91. RefreshUI()
  92. Catch ex As Exception
  93. Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
  94. End Try
  95. End Sub
  96. Private Function McModListItem(Entry As McMod) As MyLocalModItem
  97. AniControlEnabled += 1
  98. Dim NewItem As New MyLocalModItem With {.SnapsToDevicePixels = True, .Entry = Entry,
  99. .ButtonHandler = AddressOf McModContent, .Checked = SelectedMods.Contains(Entry.RawFileName)}
  100. AddHandler Entry.OnCompUpdate, AddressOf NewItem.Refresh
  101. NewItem.Refresh()
  102. AniControlEnabled -= 1
  103. Return NewItem
  104. End Function
  105. Private Sub McModContent(sender As MyLocalModItem, e As EventArgs)
  106. '点击事件
  107. AddHandler sender.Changed, AddressOf CheckChanged
  108. AddHandler sender.Click, Sub(ss As MyLocalModItem, ee As EventArgs) ss.Checked = Not ss.Checked
  109. '图标按钮
  110. Dim BtnOpen As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonOpen, .Tag = sender}
  111. BtnOpen.ToolTip = "打开文件位置"
  112. ToolTipService.SetPlacement(BtnOpen, Primitives.PlacementMode.Center)
  113. ToolTipService.SetVerticalOffset(BtnOpen, 30)
  114. ToolTipService.SetHorizontalOffset(BtnOpen, 2)
  115. AddHandler BtnOpen.Click, AddressOf Open_Click
  116. Dim BtnCont As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonInfo, .Tag = sender}
  117. BtnCont.ToolTip = "详情"
  118. ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
  119. ToolTipService.SetVerticalOffset(BtnCont, 30)
  120. ToolTipService.SetHorizontalOffset(BtnCont, 2)
  121. AddHandler BtnCont.Click, AddressOf Info_Click
  122. AddHandler sender.MouseRightButtonUp, AddressOf Info_Click
  123. Dim BtnDelete As New MyIconButton With {.LogoScale = 1, .Logo = Logo.IconButtonDelete, .Tag = sender}
  124. BtnDelete.ToolTip = "删除"
  125. ToolTipService.SetPlacement(BtnDelete, Primitives.PlacementMode.Center)
  126. ToolTipService.SetVerticalOffset(BtnDelete, 30)
  127. ToolTipService.SetHorizontalOffset(BtnDelete, 2)
  128. AddHandler BtnDelete.Click, AddressOf Delete_Click
  129. Dim BtnED As New MyIconButton With {.LogoScale = 1, .Logo = If(sender.Entry.State = McMod.McModState.Fine, Logo.IconButtonStop, Logo.IconButtonCheck), .Tag = sender}
  130. BtnED.ToolTip = If(sender.Entry.State = McMod.McModState.Fine, "禁用", "启用")
  131. ToolTipService.SetPlacement(BtnED, Primitives.PlacementMode.Center)
  132. ToolTipService.SetVerticalOffset(BtnED, 30)
  133. ToolTipService.SetHorizontalOffset(BtnED, 2)
  134. AddHandler BtnED.Click, AddressOf ED_Click
  135. If sender.Entry.State = McMod.McModState.Unavailable Then
  136. sender.Buttons = {BtnCont, BtnOpen, BtnDelete}
  137. Else
  138. sender.Buttons = {BtnCont, BtnOpen, BtnED, BtnDelete}
  139. End If
  140. End Sub
  141. ''' <summary>
  142. ''' 刷新整个 UI。
  143. ''' </summary>
  144. Public Sub RefreshUI()
  145. If PanList Is Nothing Then Exit Sub
  146. Dim ShowingMods = If(IsSearching, SearchResult, If(McModLoader.Output, New List(Of McMod))).Where(Function(m) CanPassFilter(m)).ToList
  147. '重新列出列表
  148. AniControlEnabled += 1
  149. If ShowingMods.Any() Then
  150. PanList.Visibility = Visibility.Visible
  151. PanList.Children.Clear()
  152. For Each TargetMod In ShowingMods
  153. Dim Item As MyLocalModItem = ModItems(TargetMod.RawFileName)
  154. Item.Checked = SelectedMods.Contains(TargetMod.RawFileName) '更新选中状态
  155. PanList.Children.Add(Item)
  156. Next
  157. Else
  158. PanList.Visibility = Visibility.Collapsed
  159. End If
  160. AniControlEnabled -= 1
  161. SelectedMods = SelectedMods.Where(Function(m) ShowingMods.Any(Function(s) s.RawFileName = m)).ToList '取消选中已经不显示的 Mod
  162. RefreshBars()
  163. End Sub
  164. ''' <summary>
  165. ''' 刷新顶栏和底栏显示。
  166. ''' </summary>
  167. Public Sub RefreshBars()
  168. '-----------------
  169. ' 顶部栏
  170. '-----------------
  171. '计数
  172. Dim AnyCount As Integer = 0
  173. Dim EnabledCount As Integer = 0
  174. Dim DisabledCount As Integer = 0
  175. Dim UpdateCount As Integer = 0
  176. Dim UnavalialeCount As Integer = 0
  177. For Each ModItem In If(IsSearching, SearchResult, If(McModLoader.Output, New List(Of McMod)))
  178. AnyCount += 1
  179. If ModItem.CanUpdate Then UpdateCount += 1
  180. If ModItem.State.Equals(McMod.McModState.Fine) Then EnabledCount += 1
  181. If ModItem.State.Equals(McMod.McModState.Disabled) Then DisabledCount += 1
  182. If ModItem.State.Equals(McMod.McModState.Unavailable) Then UnavalialeCount += 1
  183. Next
  184. '显示
  185. BtnFilterAll.Text = If(IsSearching, "搜索结果", "全部") & $" ({AnyCount})"
  186. BtnFilterCanUpdate.Text = $"可更新 ({UpdateCount})"
  187. BtnFilterCanUpdate.Visibility = If(Filter = FilterType.CanUpdate OrElse UpdateCount > 0, Visibility.Visible, Visibility.Collapsed)
  188. BtnFilterEnabled.Text = $"启用 ({EnabledCount})"
  189. BtnFilterEnabled.Visibility = If(Filter = FilterType.Enabled OrElse (EnabledCount > 0 AndAlso EnabledCount < AnyCount), Visibility.Visible, Visibility.Collapsed)
  190. BtnFilterDisabled.Text = $"禁用 ({DisabledCount})"
  191. BtnFilterDisabled.Visibility = If(Filter = FilterType.Disabled OrElse DisabledCount > 0, Visibility.Visible, Visibility.Collapsed)
  192. BtnFilterError.Text = $"错误 ({UnavalialeCount})"
  193. BtnFilterError.Visibility = If(Filter = FilterType.Unavailable OrElse UnavalialeCount > 0, Visibility.Visible, Visibility.Collapsed)
  194. '-----------------
  195. ' 底部栏
  196. '-----------------
  197. '计数
  198. Dim NewCount As Integer = SelectedMods.Count
  199. Dim Selected = NewCount > 0
  200. If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
  201. '按钮可用性
  202. If Selected Then
  203. Dim HasUpdate As Boolean = False
  204. Dim HasEnabled As Boolean = False
  205. Dim HasDisabled As Boolean = False
  206. For Each ModEntity In McModLoader.Output
  207. If SelectedMods.Contains(ModEntity.RawFileName) Then
  208. If ModEntity.CanUpdate Then HasUpdate = True
  209. If ModEntity.State = McMod.McModState.Fine Then
  210. HasEnabled = True
  211. ElseIf ModEntity.State = McMod.McModState.Disabled Then
  212. HasDisabled = True
  213. End If
  214. End If
  215. Next
  216. BtnSelectDisable.IsEnabled = HasEnabled
  217. BtnSelectEnable.IsEnabled = HasDisabled
  218. BtnSelectUpdate.IsEnabled = HasUpdate
  219. End If
  220. '更新显示状态
  221. If AniControlEnabled = 0 Then
  222. PanListBack.Margin = New Thickness(0, 0, 0, If(Selected, 95, 15))
  223. If Selected Then
  224. '仅在数量增加时播放出现/跳跃动画
  225. If BottomBarShownCount >= NewCount Then
  226. BottomBarShownCount = NewCount
  227. Return
  228. Else
  229. BottomBarShownCount = NewCount
  230. End If
  231. '出现/跳跃动画
  232. CardSelect.Visibility = Visibility.Visible
  233. AniStart({
  234. AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
  235. AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
  236. AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
  237. AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
  238. }, "Mod Sidebar")
  239. Else
  240. '不重复播放隐藏动画
  241. If BottomBarShownCount = 0 Then Return
  242. BottomBarShownCount = 0
  243. '隐藏动画
  244. AniStart({
  245. AaOpacity(CardSelect, -CardSelect.Opacity, 90),
  246. AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
  247. AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
  248. }, "Mod Sidebar")
  249. End If
  250. Else
  251. AniStop("Mod Sidebar")
  252. BottomBarShownCount = NewCount
  253. If Selected Then
  254. CardSelect.Visibility = Visibility.Visible
  255. CardSelect.Opacity = 1
  256. TransSelect.Y = -25
  257. Else
  258. CardSelect.Visibility = Visibility.Collapsed
  259. CardSelect.Opacity = 0
  260. TransSelect.Y = -10
  261. End If
  262. End If
  263. End Sub
  264. Private BottomBarShownCount As Integer = 0
  265. #End Region
  266. #Region "管理"
  267. ''' <summary>
  268. ''' 打开 Mods 文件夹。
  269. ''' </summary>
  270. Private Sub BtnManageOpen_Click(sender As Object, e As EventArgs) Handles BtnManageOpen.Click, BtnHintOpen.Click
  271. Try
  272. Directory.CreateDirectory(PageVersionLeft.Version.PathIndie & "mods\")
  273. OpenExplorer(PageVersionLeft.Version.PathIndie & "mods\")
  274. Catch ex As Exception
  275. Log(ex, "打开 Mods 文件夹失败", LogLevel.Msgbox)
  276. End Try
  277. End Sub
  278. #If DEBUG Then
  279. ''' <summary>
  280. ''' 检查 Mod。
  281. ''' </summary>
  282. Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
  283. Try
  284. Dim Result = McModCheck(PageVersionLeft.Version, McModLoader.Output)
  285. If Result.Any Then
  286. MyMsgBox(Join(Result, vbCrLf & vbCrLf), "Mod 检查结果")
  287. Else
  288. Hint("Mod 检查完成,未发现任何问题!", HintType.Finish)
  289. End If
  290. Catch ex As Exception
  291. Log(ex, "进行 Mod 检查时出错", LogLevel.Feedback)
  292. End Try
  293. End Sub
  294. #End If
  295. ''' <summary>
  296. ''' 全选。
  297. ''' </summary>
  298. Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
  299. ChangeAllSelected(SelectedMods.Count < PanList.Children.Count)
  300. End Sub
  301. ''' <summary>
  302. ''' 安装 Mod。
  303. ''' </summary>
  304. Private Sub BtnManageInstall_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageInstall.Click, BtnHintInstall.Click
  305. Dim FileList = SelectFiles("Mod 文件(*.jar;*.litemod;*.disabled;*.old)|*.jar;*.litemod;*.disabled;*.old", "选择要安装的 Mod")
  306. If Not FileList.Any Then Return
  307. InstallMods(FileList)
  308. End Sub
  309. ''' <summary>
  310. ''' 尝试安装 Mod。
  311. ''' 返回输入的文件是否为一个 Mod 文件,仅用于判断拖拽行为。
  312. ''' </summary>
  313. Public Shared Function InstallMods(FilePathList As IEnumerable(Of String)) As Boolean
  314. Dim Extension As String = FilePathList.First.AfterLast(".").ToLower
  315. '检查文件扩展名
  316. If Not {"jar", "litemod", "disabled", "old"}.Any(Function(t) t = Extension) Then Return False
  317. Log("[System] 文件为 jar/litemod 格式,尝试作为 Mod 安装")
  318. '检查回收站:回收站中的文件有错误的文件名
  319. If FilePathList.First.Contains(":\$RECYCLE.BIN\") Then
  320. Hint("请先将文件从回收站还原,再尝试安装!", HintType.Critical)
  321. Return True
  322. End If
  323. '获取并检查目标版本
  324. Dim TargetVersion As McVersion = McVersionCurrent
  325. If FrmMain.PageCurrent = FormMain.PageType.VersionSetup Then TargetVersion = PageVersionLeft.Version
  326. If FrmMain.PageCurrent = FormMain.PageType.VersionSelect OrElse TargetVersion Is Nothing OrElse Not TargetVersion.Modable Then
  327. '正在选择版本,或当前版本不能安装 Mod
  328. Hint("若要安装 Mod,请先选择一个可以安装 Mod 的版本!")
  329. ElseIf Not (FrmMain.PageCurrent = FormMain.PageType.VersionSetup AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.VersionMod) Then
  330. '未处于 Mod 管理页面
  331. If MyMsgBox($"是否要将这{If(FilePathList.Count = 1, "个", "些")}文件作为 Mod 安装到 {TargetVersion.Name}?", "Mod 安装确认", "确定", "取消") = 1 Then GoTo Install
  332. Else
  333. '处于 Mod 管理页面
  334. Install:
  335. Try
  336. For Each ModFile In FilePathList
  337. Dim NewFileName = GetFileNameFromPath(ModFile).Replace(".disabled", "").Replace(".old", "")
  338. If Not NewFileName.Contains(".") Then NewFileName += ".jar" '#4227
  339. CopyFile(ModFile, TargetVersion.PathIndie & "mods\" & NewFileName)
  340. Next
  341. If FilePathList.Count = 1 Then
  342. Hint($"已安装 {GetFileNameFromPath(FilePathList.First).Replace(".disabled", "").Replace(".old", "")}!", HintType.Finish)
  343. Else
  344. Hint($"已安装 {FilePathList.Count} 个 Mod!", HintType.Finish)
  345. End If
  346. '刷新列表
  347. If FrmMain.PageCurrent = FormMain.PageType.VersionSetup AndAlso FrmMain.PageCurrentSub = FormMain.PageSubType.VersionMod Then
  348. LoaderFolderRun(McModLoader, TargetVersion.PathIndie & "mods\", LoaderFolderRunType.ForceRun)
  349. End If
  350. Catch ex As Exception
  351. Log(ex, "复制 Mod 文件失败", LogLevel.Msgbox)
  352. End Try
  353. End If
  354. Return True
  355. End Function
  356. ''' <summary>
  357. ''' 下载 Mod。
  358. ''' </summary>
  359. Private Sub BtnManageDownload_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageDownload.Click, BtnHintDownload.Click
  360. PageDownloadMod.TargetVersion = PageVersionLeft.Version '将当前版本设置为筛选器
  361. FrmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadMod)
  362. End Sub
  363. #End Region
  364. #Region "选择"
  365. '选择的 Mod 的路径(不含 .disabled 和 .old)
  366. Public SelectedMods As New List(Of String)
  367. '单项切换选择状态
  368. Public Sub CheckChanged(sender As MyLocalModItem, e As RouteEventArgs)
  369. If AniControlEnabled <> 0 Then Return
  370. '更新选择了的内容
  371. Dim SelectedKey As String = sender.Entry.RawFileName
  372. If sender.Checked Then
  373. If Not SelectedMods.Contains(SelectedKey) Then SelectedMods.Add(SelectedKey)
  374. Else
  375. SelectedMods.Remove(SelectedKey)
  376. End If
  377. RefreshBars()
  378. End Sub
  379. '切换所有项的选择状态
  380. Private Sub ChangeAllSelected(Value As Boolean)
  381. AniControlEnabled += 1
  382. SelectedMods.Clear()
  383. For Each Item As MyLocalModItem In ModItems.Values
  384. '#4992,Mod 从过滤器看可能不应在列表中,但因为刚切换状态所以依然保留在列表中,所以应该从列表 UI 判断,而非从过滤器判断
  385. Dim ShouldSelected As Boolean = Value AndAlso PanList.Children.Contains(Item)
  386. Item.Checked = ShouldSelected
  387. If ShouldSelected Then SelectedMods.Add(Item.Entry.RawFileName)
  388. Next
  389. AniControlEnabled -= 1
  390. RefreshBars()
  391. End Sub
  392. Private Sub UnselectedAllWithAnimation() Handles Load.StateChanged, Me.PageExit
  393. Dim CacheAniControlEnabled = AniControlEnabled
  394. AniControlEnabled = 0
  395. ChangeAllSelected(False)
  396. AniControlEnabled += CacheAniControlEnabled
  397. End Sub
  398. Private Sub PageVersionMod_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
  399. If My.Computer.Keyboard.CtrlKeyDown AndAlso e.Key = Key.A Then ChangeAllSelected(True)
  400. End Sub
  401. #End Region
  402. #Region "筛选"
  403. Private _Filter As FilterType = FilterType.All
  404. Private Property Filter As FilterType
  405. Get
  406. Return _Filter
  407. End Get
  408. Set(value As FilterType)
  409. If _Filter = value Then Return
  410. _Filter = value
  411. Select Case value
  412. Case FilterType.All
  413. BtnFilterAll.Checked = True
  414. Case FilterType.Enabled
  415. BtnFilterEnabled.Checked = True
  416. Case FilterType.Disabled
  417. BtnFilterDisabled.Checked = True
  418. Case FilterType.CanUpdate
  419. BtnFilterCanUpdate.Checked = True
  420. Case Else
  421. BtnFilterError.Checked = True
  422. End Select
  423. RefreshUI()
  424. End Set
  425. End Property
  426. Private Enum FilterType As Integer
  427. All = 0
  428. Enabled = 1
  429. Disabled = 2
  430. CanUpdate = 3
  431. Unavailable = 4
  432. End Enum
  433. ''' <returns>
  434. ''' True - 当前显示的模组列表视图需要在从网络获取模组详细信息(例如是否有更新版本)后同步
  435. ''' </returns>
  436. Public Function CurrentViewNeedSyncOnModDetailLoaded() As Boolean
  437. Return Filter = FilterType.CanUpdate
  438. End Function
  439. ''' <summary>
  440. ''' 检查该 Mod 项是否符合当前筛选的类别。
  441. ''' </summary>
  442. Private Function CanPassFilter(CheckingMod As McMod) As Boolean
  443. Select Case Filter
  444. Case FilterType.All
  445. Return True
  446. Case FilterType.Enabled
  447. Return CheckingMod.State = McMod.McModState.Fine
  448. Case FilterType.Disabled
  449. Return CheckingMod.State = McMod.McModState.Disabled
  450. Case FilterType.CanUpdate
  451. Return CheckingMod.CanUpdate
  452. Case FilterType.Unavailable
  453. Return CheckingMod.State = McMod.McModState.Unavailable
  454. Case Else
  455. Return False
  456. End Select
  457. End Function
  458. '点击筛选项触发的改变
  459. Private Sub ChangeFilter(sender As MyRadioButton, raiseByMouse As Boolean) Handles BtnFilterAll.Check, BtnFilterCanUpdate.Check, BtnFilterDisabled.Check, BtnFilterEnabled.Check, BtnFilterError.Check
  460. Filter = sender.Tag
  461. RefreshUI()
  462. End Sub
  463. #End Region
  464. #Region "下边栏"
  465. '启用 / 禁用
  466. Private Sub BtnSelectED_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
  467. EDMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)),
  468. Not sender.Equals(BtnSelectDisable))
  469. ChangeAllSelected(False)
  470. End Sub
  471. Private Sub EDMods(ModList As IEnumerable(Of McMod), IsEnable As Boolean)
  472. Dim IsSuccessful As Boolean = True
  473. For Each ModE In ModList.ToList
  474. Dim ModEntity = ModE '仅用于去除迭代变量无法修改的限制
  475. Dim NewPath As String = Nothing
  476. If ModEntity.State = McMod.McModState.Fine AndAlso Not IsEnable Then
  477. '禁用
  478. NewPath = ModEntity.Path & If(File.Exists(ModEntity.Path & ".old"), ".old", ".disabled")
  479. ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso IsEnable Then
  480. '启用
  481. NewPath = ModEntity.RawPath
  482. Else
  483. Continue For
  484. End If
  485. '重命名
  486. Try
  487. If File.Exists(NewPath) Then
  488. If File.Exists(ModEntity.Path) Then
  489. '同时存在两个名称的 Mod
  490. If GetFileMD5(ModEntity.Path) <> GetFileMD5(NewPath) Then
  491. MyMsgBox($"目前同时存在启用和禁用的两个 Mod 文件:{vbCrLf} - {NewPath}{vbCrLf} - {ModEntity.Path}{vbCrLf}{vbCrLf}注意,这两个文件的内容并不相同。{vbCrLf}在手动删除或重命名其中一个文件后,才能继续操作。", "存在文件冲突")
  492. Continue For
  493. End If
  494. Else
  495. '已经重命名过了
  496. Log("[Mod] Mod 的状态已被切换", LogLevel.Debug)
  497. Continue For
  498. End If
  499. End If
  500. File.Delete(NewPath)
  501. FileSystem.Rename(ModEntity.Path, NewPath)
  502. Catch ex As FileNotFoundException
  503. Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
  504. ReloadModList(True)
  505. Return
  506. Catch ex As Exception
  507. Log(ex, $"重命名 Mod 失败({If(ModEntity.Path, "null")})")
  508. IsSuccessful = False
  509. End Try
  510. '更改 Loader 中的列表
  511. Dim NewModEntity As New McMod(NewPath)
  512. NewModEntity.FromJson(ModEntity.ToJson)
  513. If McModLoader.Output.Contains(ModEntity) Then
  514. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  515. McModLoader.Output.RemoveAt(IndexOfLoader)
  516. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  517. End If
  518. If SearchResult IsNot Nothing AndAlso SearchResult.Contains(ModEntity) Then '#4862
  519. Dim IndexOfResult As Integer = SearchResult.IndexOf(ModEntity)
  520. SearchResult.Remove(ModEntity)
  521. SearchResult.Insert(IndexOfResult, NewModEntity)
  522. End If
  523. '更改 UI 中的列表
  524. Dim NewItem As MyLocalModItem = McModListItem(NewModEntity)
  525. ModItems(ModEntity.RawFileName) = NewItem
  526. Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry Is ModEntity))
  527. If IndexOfUi = -1 Then Continue For '因为未知原因 Mod 的状态已经切换完了
  528. PanList.Children.RemoveAt(IndexOfUi)
  529. PanList.Children.Insert(IndexOfUi, NewItem)
  530. Next
  531. If IsSuccessful Then
  532. RefreshBars()
  533. Else
  534. Hint("由于文件被占用,Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  535. ReloadModList(True)
  536. End If
  537. LoaderRun(LoaderFolderRunType.UpdateOnly)
  538. End Sub
  539. '更新
  540. Private Sub BtnSelectUpdate_Click() Handles BtnSelectUpdate.Click
  541. Dim UpdateList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName) AndAlso m.CanUpdate).ToList()
  542. If Not UpdateList.Any() Then Return
  543. UpdateMods(UpdateList)
  544. ChangeAllSelected(False)
  545. End Sub
  546. ''' <summary>
  547. ''' 记录正在进行 Mod 更新的 mods 文件夹路径。
  548. ''' </summary>
  549. Public Shared UpdatingVersions As New List(Of String)
  550. Public Sub UpdateMods(ModList As IEnumerable(Of McMod))
  551. '更新前警告
  552. If Not Setup.Get("HintUpdateMod") OrElse ModList.Count >= 15 Then
  553. If MyMsgBox($"新版本 Mod 可能不兼容旧存档或者其他 Mod,这可能导致游戏崩溃,甚至永久损坏存档!{vbCrLf}如果你在游玩整合包,请千万不要自行更新 Mod!{vbCrLf}{vbCrLf}在更新前,请先备份存档,并检查 Mod 的更新日志。{vbCrLf}如果更新后出现问题,你也可以在回收站找回更新前的 Mod。", "Mod 更新警告", "我已了解风险,继续更新", "取消", IsWarn:=True) = 1 Then
  554. Setup.Set("HintUpdateMod", True)
  555. Else
  556. Exit Sub
  557. End If
  558. End If
  559. Try
  560. '构造下载信息
  561. ModList = ModList.ToList() '防止刷新影响迭代器
  562. Dim FileList As New List(Of NetFile)
  563. Dim FileCopyList As New Dictionary(Of String, String)
  564. For Each Entry As McMod In ModList
  565. Dim File As CompFile = Entry.UpdateFile
  566. If Not File.Available Then Continue For
  567. '确认更新后的文件名
  568. Dim CurrentReplaceName = Entry.CompFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
  569. Dim NewestReplaceName = Entry.UpdateFile.FileName.Replace(".jar", "").Replace(".old", "").Replace(".disabled", "")
  570. Dim CurrentSegs = CurrentReplaceName.Split("-"c).ToList()
  571. Dim NewestSegs = NewestReplaceName.Split("-"c).ToList()
  572. Dim Shortened As Boolean = False
  573. Do While True '移除前导相同部分(不能移除所有相同项,这会导致例如 1.2-forge-2 和 1.3-forge-3 中间的 forge 被去掉,导致尝试替换 1.2-2)
  574. If Not CurrentSegs.Any() OrElse Not NewestSegs.Any() Then Exit Do
  575. If CurrentSegs.First <> NewestSegs.First Then Exit Do
  576. CurrentSegs.RemoveAt(0)
  577. NewestSegs.RemoveAt(0)
  578. Shortened = True
  579. Loop
  580. Do While True '移除后导相同部分
  581. If Not CurrentSegs.Any() OrElse Not NewestSegs.Any() Then Exit Do
  582. If CurrentSegs.Last <> NewestSegs.Last Then Exit Do
  583. CurrentSegs.RemoveAt(CurrentSegs.Count - 1)
  584. NewestSegs.RemoveAt(NewestSegs.Count - 1)
  585. Shortened = True
  586. Loop
  587. If Shortened AndAlso CurrentSegs.Any() AndAlso NewestSegs.Any() Then
  588. CurrentReplaceName = Join(CurrentSegs, "-")
  589. NewestReplaceName = Join(NewestSegs, "-")
  590. End If
  591. '添加到下载列表
  592. Dim TempAddress As String = PathTemp & "DownloadedMods\" & Entry.FileName.Replace(CurrentReplaceName, NewestReplaceName)
  593. Dim RealAddress As String = GetPathFromFullPath(Entry.Path) & Entry.FileName.Replace(CurrentReplaceName, NewestReplaceName)
  594. FileList.Add(File.ToNetFile(TempAddress))
  595. FileCopyList(TempAddress) = RealAddress
  596. Next
  597. '构造加载器
  598. Dim InstallLoaders As New List(Of LoaderBase)
  599. Dim FinishedFileNames As New List(Of String)
  600. InstallLoaders.Add(New LoaderDownload("下载新版 Mod 文件", FileList) With {.ProgressWeight = ModList.Count * 1.5}) '每个 Mod 需要 1.5s
  601. InstallLoaders.Add(New LoaderTask(Of Integer, Integer)("替换旧版 Mod 文件",
  602. Sub()
  603. Try
  604. For Each Entry As McMod In ModList
  605. If File.Exists(Entry.Path) Then
  606. My.Computer.FileSystem.DeleteFile(Entry.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  607. Else
  608. Log($"[Mod] 未找到更新前的 Mod 文件,跳过对它的删除:{Entry.Path}", LogLevel.Debug)
  609. End If
  610. Next
  611. For Each Entry As KeyValuePair(Of String, String) In FileCopyList
  612. If File.Exists(Entry.Value) Then
  613. My.Computer.FileSystem.DeleteFile(Entry.Value, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  614. Log($"[Mod] 更新后的 Mod 文件已存在,将会把它放入回收站:{Entry.Value}", LogLevel.Debug)
  615. End If
  616. If Directory.Exists(GetPathFromFullPath(Entry.Value)) Then
  617. File.Move(Entry.Key, Entry.Value)
  618. FinishedFileNames.Add(GetFileNameFromPath(Entry.Value))
  619. Else
  620. Log($"[Mod] 更新后的目标文件夹已被删除:{Entry.Value}", LogLevel.Debug)
  621. End If
  622. Next
  623. Catch ex As OperationCanceledException
  624. Log(ex, "替换旧版 Mod 文件时被主动取消")
  625. End Try
  626. End Sub))
  627. '结束处理
  628. Dim Loader As New LoaderCombo(Of IEnumerable(Of McMod))("Mod 更新:" & PageVersionLeft.Version.Name, InstallLoaders)
  629. Dim PathMods As String = PageVersionLeft.Version.PathIndie & "mods\"
  630. Loader.OnStateChanged =
  631. Sub()
  632. '结果提示
  633. Select Case Loader.State
  634. Case LoadState.Finished
  635. Select Case FinishedFileNames.Count
  636. Case 0 '一般是由于 Mod 文件被占用,然后玩家主动取消
  637. Log($"[Mod] 没有 Mod 被成功更新")
  638. Case 1
  639. Hint($"已成功更新 {FinishedFileNames.Single}!", HintType.Finish)
  640. Case Else
  641. Hint($"已成功更新 {FinishedFileNames.Count} 个 Mod!", HintType.Finish)
  642. End Select
  643. Case LoadState.Failed
  644. Hint("Mod 更新失败:" & GetExceptionSummary(Loader.Error), HintType.Critical)
  645. Case LoadState.Aborted
  646. Hint("Mod 更新已中止!", HintType.Info)
  647. Case Else
  648. Exit Sub
  649. End Select
  650. Log($"[Mod] 已从正在进行 Mod 更新的文件夹列表移除:{PathMods}")
  651. UpdatingVersions.Remove(PathMods)
  652. '清理缓存
  653. RunInNewThread(
  654. Sub()
  655. Try
  656. For Each TempFile In FileCopyList.Keys
  657. If File.Exists(TempFile) Then File.Delete(TempFile)
  658. Next
  659. Catch ex As Exception
  660. Log(ex, "清理 Mod 更新缓存失败")
  661. End Try
  662. End Sub, "Clean Mod Update Cache", ThreadPriority.BelowNormal)
  663. End Sub
  664. '启动加载器
  665. Log($"[Mod] 开始更新 {ModList.Count} 个 Mod:{PathMods}")
  666. UpdatingVersions.Add(PathMods)
  667. Loader.Start()
  668. LoaderTaskbarAdd(Loader)
  669. FrmMain.BtnExtraDownload.ShowRefresh()
  670. FrmMain.BtnExtraDownload.Ribble()
  671. ReloadModList(True)
  672. Catch ex As Exception
  673. Log(ex, "初始化 Mod 更新失败")
  674. End Try
  675. End Sub
  676. '删除
  677. Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
  678. DeleteMods(McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)))
  679. ChangeAllSelected(False)
  680. End Sub
  681. Private Sub DeleteMods(ModList As IEnumerable(Of McMod))
  682. Try
  683. Dim IsSuccessful As Boolean = True
  684. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  685. '确认需要删除的文件
  686. ModList = ModList.SelectMany(
  687. Function(Target As McMod)
  688. If Target.State = McMod.McModState.Fine Then
  689. Return {Target.Path, Target.Path & If(File.Exists(Target.Path & ".old"), ".old", ".disabled")}
  690. Else
  691. Return {Target.Path, Target.RawPath}
  692. End If
  693. End Function).Distinct.Where(Function(m) File.Exists(m)).Select(Function(m) New McMod(m)).ToList()
  694. '实际删除文件
  695. For Each ModEntity In ModList
  696. '删除
  697. Try
  698. If IsShiftPressed Then
  699. File.Delete(ModEntity.Path)
  700. Else
  701. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  702. End If
  703. Catch ex As OperationCanceledException
  704. Log(ex, "删除 Mod 被主动取消")
  705. ReloadModList(True)
  706. Return
  707. Catch ex As Exception
  708. Log(ex, $"删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
  709. IsSuccessful = False
  710. End Try
  711. '取消选中
  712. SelectedMods.Remove(ModEntity.RawFileName)
  713. '更改 Loader 和 UI 中的列表
  714. McModLoader.Output.Remove(ModEntity)
  715. SearchResult?.Remove(ModEntity)
  716. ModItems.Remove(ModEntity.RawFileName)
  717. Dim IndexOfUi As Integer = PanList.Children.IndexOf(PanList.Children.OfType(Of MyLocalModItem).FirstOrDefault(Function(i) i.Entry.Equals(ModEntity)))
  718. If IndexOfUi >= 0 Then PanList.Children.RemoveAt(IndexOfUi)
  719. Next
  720. RefreshBars()
  721. If Not IsSuccessful Then
  722. Hint("由于文件被占用,Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  723. ReloadModList(True)
  724. ElseIf PanList.Children.Count = 0 Then
  725. ReloadModList(True) '删除了全部文件
  726. Else
  727. RefreshBars()
  728. End If
  729. '显示结果提示
  730. If Not IsSuccessful Then Exit Sub
  731. If IsShiftPressed Then
  732. If ModList.Count = 1 Then
  733. Hint($"已彻底删除 {ModList.Single.FileName}!", HintType.Finish)
  734. Else
  735. Hint($"已彻底删除 {ModList.Count} 个文件!", HintType.Finish)
  736. End If
  737. Else
  738. If ModList.Count = 1 Then
  739. Hint($"已将 {ModList.Single.FileName} 删除到回收站!", HintType.Finish)
  740. Else
  741. Hint($"已将 {ModList.Count} 个文件删除到回收站!", HintType.Finish)
  742. End If
  743. End If
  744. Catch ex As OperationCanceledException
  745. Log(ex, "删除 Mod 被主动取消")
  746. ReloadModList(True)
  747. Catch ex As Exception
  748. Log(ex, "删除 Mod 出现未知错误", LogLevel.Feedback)
  749. ReloadModList(True)
  750. End Try
  751. LoaderRun(LoaderFolderRunType.UpdateOnly)
  752. End Sub
  753. '取消选择
  754. Private Sub BtnSelectCancel_Click() Handles BtnSelectCancel.Click
  755. ChangeAllSelected(False)
  756. End Sub
  757. #End Region
  758. #Region "单个 Mod 项"
  759. '详情
  760. Public Sub Info_Click(sender As Object, e As EventArgs)
  761. Try
  762. Dim ModEntry As McMod = CType(If(TypeOf sender Is MyIconButton, sender.Tag, sender), MyLocalModItem).Entry
  763. '加载失败信息
  764. If ModEntry.State = McMod.McModState.Unavailable Then
  765. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntry.FileUnavailableReason), "Mod 读取失败")
  766. Return
  767. End If
  768. If ModEntry.Comp IsNot Nothing Then
  769. '跳转到 Mod 下载页面
  770. FrmMain.PageChange(New FormMain.PageStackData With {.Page = FormMain.PageType.CompDetail,
  771. .Additional = {ModEntry.Comp, New List(Of String), PageVersionLeft.Version.Version.McName,
  772. If(PageVersionLeft.Version.Version.HasForge, CompModLoaderType.Forge,
  773. If(PageVersionLeft.Version.Version.HasNeoForge, CompModLoaderType.NeoForge,
  774. If(PageVersionLeft.Version.Version.HasFabric, CompModLoaderType.Fabric, CompModLoaderType.Any)))}})
  775. Else
  776. '获取信息
  777. Dim ContentLines As New List(Of String)
  778. If ModEntry.Description IsNot Nothing Then ContentLines.Add(ModEntry.Description & vbCrLf)
  779. If ModEntry.Authors IsNot Nothing Then ContentLines.Add("作者:" & ModEntry.Authors)
  780. ContentLines.Add("文件:" & ModEntry.FileName & "(" & GetString(New FileInfo(ModEntry.Path).Length) & ")")
  781. If ModEntry.Version IsNot Nothing Then ContentLines.Add("版本:" & ModEntry.Version)
  782. Dim DebugInfo As New List(Of String)
  783. If ModEntry.ModId IsNot Nothing Then
  784. DebugInfo.Add("Mod ID:" & ModEntry.ModId)
  785. End If
  786. If ModEntry.Dependencies.Any Then
  787. DebugInfo.Add("依赖于:")
  788. For Each Dep In ModEntry.Dependencies
  789. DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
  790. Next
  791. End If
  792. If DebugInfo.Any Then
  793. ContentLines.Add("")
  794. ContentLines.AddRange(DebugInfo)
  795. End If
  796. '获取用于搜索的 Mod 名称
  797. Dim ModOriginalName As String = ModEntry.Name.Replace(" ", "+")
  798. Dim ModSearchName As String = ModOriginalName.Substring(0, 1)
  799. For i = 1 To ModOriginalName.Count - 1
  800. Dim IsLastLower As Boolean = ModOriginalName(i - 1).ToString.ToLower.Equals(ModOriginalName(i - 1).ToString)
  801. Dim IsCurrentLower As Boolean = ModOriginalName(i).ToString.ToLower.Equals(ModOriginalName(i).ToString)
  802. If IsLastLower AndAlso Not IsCurrentLower Then
  803. '上一个字母为小写,这一个字母为大写
  804. ModSearchName += "+"
  805. End If
  806. ModSearchName += ModOriginalName(i)
  807. Next
  808. ModSearchName = ModSearchName.Replace("++", "+").Replace("pti+Fine", "ptiFine")
  809. '显示
  810. If ModEntry.Url Is Nothing Then
  811. If MyMsgBox(Join(ContentLines, vbCrLf), ModEntry.Name, "百科搜索", "返回") = 1 Then
  812. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  813. End If
  814. Else
  815. Select Case MyMsgBox(Join(ContentLines, vbCrLf), ModEntry.Name, "打开官网", "百科搜索", "返回")
  816. Case 1
  817. OpenWebsite(ModEntry.Url)
  818. Case 2
  819. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  820. End Select
  821. End If
  822. End If
  823. Catch ex As Exception
  824. Log(ex, "获取 Mod 详情失败", LogLevel.Feedback)
  825. End Try
  826. End Sub
  827. '打开文件所在的位置
  828. Public Sub Open_Click(sender As MyIconButton, e As EventArgs)
  829. Try
  830. Dim ListItem As MyLocalModItem = sender.Tag
  831. OpenExplorer(ListItem.Entry.Path)
  832. Catch ex As Exception
  833. Log(ex, "打开 Mod 文件位置失败", LogLevel.Feedback)
  834. End Try
  835. End Sub
  836. '删除
  837. Public Sub Delete_Click(sender As MyIconButton, e As EventArgs)
  838. Dim ListItem As MyLocalModItem = sender.Tag
  839. DeleteMods({ListItem.Entry})
  840. End Sub
  841. '启用 / 禁用
  842. Public Sub ED_Click(sender As MyIconButton, e As EventArgs)
  843. Dim ListItem As MyLocalModItem = sender.Tag
  844. EDMods({ListItem.Entry}, ListItem.Entry.State = McMod.McModState.Disabled)
  845. End Sub
  846. #End Region
  847. #Region "搜索"
  848. Public ReadOnly Property IsSearching As Boolean
  849. Get
  850. Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
  851. End Get
  852. End Property
  853. Private SearchResult As List(Of McMod)
  854. Public Sub SearchRun() Handles SearchBox.TextChanged
  855. If IsSearching Then
  856. '构造请求
  857. Dim QueryList As New List(Of SearchEntry(Of McMod))
  858. For Each Entry As McMod In McModLoader.Output
  859. Dim SearchSource As New List(Of KeyValuePair(Of String, Double))
  860. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Name, 1))
  861. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.FileName, 1))
  862. If Entry.Version IsNot Nothing Then
  863. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Version, 0.2))
  864. End If
  865. If Entry.Description IsNot Nothing AndAlso Entry.Description <> "" Then
  866. SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Description, 0.4))
  867. End If
  868. If Entry.Comp IsNot Nothing Then
  869. If Entry.Comp.RawName <> Entry.Name Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.RawName, 1))
  870. If Entry.Comp.TranslatedName <> Entry.Comp.RawName Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.TranslatedName, 1))
  871. If Entry.Comp.Description <> Entry.Description Then SearchSource.Add(New KeyValuePair(Of String, Double)(Entry.Comp.Description, 0.4))
  872. SearchSource.Add(New KeyValuePair(Of String, Double)(String.Join("", Entry.Comp.Tags), 0.2))
  873. End If
  874. QueryList.Add(New SearchEntry(Of McMod) With {.Item = Entry, .SearchSource = SearchSource})
  875. Next
  876. '进行搜索
  877. SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=6, MinBlurSimilarity:=0.35).Select(Function(r) r.Item).ToList
  878. End If
  879. RefreshUI()
  880. End Sub
  881. #End Region
  882. End Class