PageVersionMod.xaml.vb 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. Imports System.Security.Principal
  2. Imports PCL.MyLoading
  3. Public Class PageVersionMod
  4. #Region "初始化"
  5. Private IsLoad As Boolean = False
  6. Public Sub PageOther_Loaded() Handles Me.Loaded
  7. PanBack.ScrollToHome()
  8. AniControlEnabled += 1
  9. SelectedMods.Clear()
  10. RefreshList()
  11. ChangeAllSelected(False)
  12. AniControlEnabled -= 1
  13. '非重复加载部分
  14. If IsLoad Then Exit Sub
  15. IsLoad = True
  16. #If DEBUG Then
  17. BtnManageCheck.Visibility = Visibility.Visible
  18. #End If
  19. End Sub
  20. ''' <summary>
  21. ''' 刷新 Mod 列表。
  22. ''' </summary>
  23. Public Sub RefreshList(Optional ForceReload As Boolean = False)
  24. If McModLoader.State = LoadState.Loading Then Exit Sub
  25. If LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", If(ForceReload, LoaderFolderRunType.ForceRun, LoaderFolderRunType.RunOnUpdated)) Then
  26. Log("[System] 已刷新 Mod 列表")
  27. PanBack.ScrollToHome()
  28. SearchBox.Text = ""
  29. End If
  30. End Sub
  31. Private Sub LoaderInit() Handles Me.Initialized
  32. PageLoaderInit(Load, PanLoad, PanAllBack, Nothing, McModLoader, AddressOf Load_Finish, AutoRun:=False)
  33. End Sub
  34. Private Sub Load_Click(sender As Object, e As MouseButtonEventArgs) Handles Load.Click
  35. If McModLoader.State = LoadState.Failed Then
  36. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.ForceRun)
  37. End If
  38. End Sub
  39. #End Region
  40. #Region "UI 化"
  41. Private PanItems As StackPanel
  42. ''' <summary>
  43. ''' 将 Mod 列表加载为 UI。
  44. ''' </summary>
  45. Private Sub Load_Finish(Loader As LoaderTask(Of String, List(Of McMod)))
  46. Dim List As List(Of McMod) = Loader.Output
  47. Try
  48. PanList.Children.Clear()
  49. '判断应该显示哪一个页面
  50. If List.Count = 0 Then
  51. PanEmpty.Visibility = Visibility.Visible
  52. PanBack.Visibility = Visibility.Collapsed
  53. Exit Sub
  54. Else
  55. PanBack.Visibility = Visibility.Visible
  56. PanEmpty.Visibility = Visibility.Collapsed
  57. End If
  58. SearchBox.Text = ""
  59. '建立 StackPanel
  60. PanItems = New StackPanel With {.Margin = New Thickness(18, MyCard.SwapedHeight, 18, If(List.Count > 0, 20, 0)), .VerticalAlignment = VerticalAlignment.Top, .RenderTransform = New TranslateTransform(0, 0)}
  61. For Each ModEntity As McMod In List
  62. PanItems.Children.Add(McModListItem(ModEntity))
  63. Next
  64. '建立 MyCard
  65. Dim NewCard As New MyCard With {.Title = McModGetTitle(List), .Margin = New Thickness(0, 0, 0, 15)}
  66. NewCard.Children.Add(PanItems)
  67. PanList.Children.Add(NewCard)
  68. '显示提示
  69. If List.Count > 0 AndAlso Not Setup.Get("HintModDisable") Then
  70. Setup.Set("HintModDisable", True)
  71. Hint("直接点击某个 Mod 项即可将它禁用!")
  72. End If
  73. Catch ex As Exception
  74. Log(ex, "加载 Mod 列表 UI 失败", LogLevel.Feedback)
  75. End Try
  76. End Sub
  77. ''' <summary>
  78. ''' 获取 Card 的标题。
  79. ''' </summary>
  80. Private Function McModGetTitle(List As List(Of McMod)) As String
  81. Dim Counter = {0, 0, 0}
  82. For Each ModEntity As McMod In List
  83. Counter(ModEntity.State) += 1
  84. Next
  85. If List.Count = 0 Then Return "未找到任何 Mod"
  86. Dim TypeList As New List(Of String)
  87. If Counter(McMod.McModState.Fine) > 0 Then TypeList.Add("启用 " & Counter(McMod.McModState.Fine))
  88. If Counter(McMod.McModState.Disabled) > 0 Then TypeList.Add("禁用 " & Counter(McMod.McModState.Disabled))
  89. If Counter(McMod.McModState.Unavaliable) > 0 Then TypeList.Add("错误 " & Counter(McMod.McModState.Unavaliable))
  90. Return "Mod 列表(" & Join(TypeList, ",") & ")"
  91. End Function
  92. Private Function McModListItem(Entry As McMod) As MyListItem
  93. '图标
  94. Dim Logo As String
  95. Select Case Entry.State
  96. Case McMod.McModState.Fine
  97. Logo = "pack://application:,,,/images/Blocks/RedstoneLampOn.png"
  98. Case McMod.McModState.Disabled
  99. Logo = "pack://application:,,,/images/Blocks/RedstoneLampOff.png"
  100. Case Else '出错
  101. Logo = "pack://application:,,,/images/Blocks/RedstoneBlock.png"
  102. End Select
  103. '文本
  104. Dim Title As String
  105. If Entry.State = McMod.McModState.Disabled Then
  106. Title = GetFileNameWithoutExtentionFromPath(Entry.Path.Substring(0, Entry.Path.Count - ".disabled".Count)) & "(已禁用)"
  107. Else
  108. Title = GetFileNameWithoutExtentionFromPath(Entry.Path)
  109. End If
  110. Dim Desc As String
  111. If Entry.Version Is Nothing OrElse Entry.Description IsNot Nothing Then
  112. Desc = Entry.Name & If(Entry.Version Is Nothing, "", " (" & Entry.Version & ")") & " : " & If(Entry.Description, Entry.Path)
  113. ElseIf Entry.IsFileAvailable Then
  114. Desc = Entry.Path
  115. Else
  116. Desc = "存在错误 : " & Entry.Path
  117. End If
  118. 'Desc = If(Entry.ModId, "无可用名称") & " (" & If(Entry.Version, "无可用版本") & ")"
  119. 'If Entry.Dependencies.Count > 0 Then
  120. ' Dim DepList As New List(Of String)
  121. ' For Each Dep In Entry.Dependencies
  122. ' DepList.Add(Dep.Key & If(Dep.Value Is Nothing, "", "@" & Dep.Value))
  123. ' Next
  124. ' Desc += " : " & Join(DepList, ", ")
  125. 'End If
  126. '实例化
  127. AniControlEnabled += 1
  128. Dim NewItem As New MyListItem With {
  129. .LogoClickable = True, .Logo = Logo, .SnapsToDevicePixels = True, .Title = Title, .Info = Desc, .Height = 42, .Tag = Entry,
  130. .Type = MyListItem.CheckType.CheckBox, .IsScaleAnimationEnabled = False,
  131. .PaddingRight = 73,
  132. .ContentHandler = AddressOf McModContent, .Checked = SelectedMods.Contains(Entry.RawFileName)
  133. }
  134. AniControlEnabled -= 1
  135. Return NewItem
  136. End Function
  137. Private Sub McModContent(sender As MyListItem, e As EventArgs)
  138. Dim ModEntity As McMod = sender.Tag
  139. '注册点击事件
  140. AddHandler sender.Changed, AddressOf CheckChanged
  141. AddHandler sender.LogoClick, AddressOf ItemLogo_Click
  142. '图标按钮
  143. Dim BtnDel As New MyIconButton With {.LogoScale = 1.1, .Logo = Logo.IconButtonDelete, .Tag = sender}
  144. BtnDel.ToolTip = "删除"
  145. ToolTipService.SetPlacement(BtnDel, Primitives.PlacementMode.Center)
  146. ToolTipService.SetVerticalOffset(BtnDel, 30)
  147. ToolTipService.SetHorizontalOffset(BtnDel, 2)
  148. AddHandler BtnDel.Click, AddressOf Delete_Click
  149. Dim BtnOpen As New MyIconButton With {.LogoScale = 1.15, .Logo = Logo.IconButtonOpen, .Tag = sender}
  150. BtnOpen.ToolTip = "打开文件位置"
  151. ToolTipService.SetPlacement(BtnOpen, Primitives.PlacementMode.Center)
  152. ToolTipService.SetVerticalOffset(BtnOpen, 30)
  153. ToolTipService.SetHorizontalOffset(BtnOpen, 2)
  154. AddHandler BtnOpen.Click, AddressOf Open_Click
  155. Dim BtnCont As New MyIconButton With {.LogoScale = 1.05, .Logo = Logo.IconButtonInfo, .Tag = sender}
  156. BtnCont.ToolTip = "详情"
  157. ToolTipService.SetPlacement(BtnCont, Primitives.PlacementMode.Center)
  158. ToolTipService.SetVerticalOffset(BtnCont, 30)
  159. ToolTipService.SetHorizontalOffset(BtnCont, 2)
  160. AddHandler BtnCont.Click, AddressOf Info_Click
  161. AddHandler sender.MouseRightButtonDown, AddressOf Info_Click
  162. sender.Buttons = {BtnCont, BtnOpen, BtnDel}
  163. End Sub
  164. #End Region
  165. #Region "管理"
  166. ''' <summary>
  167. ''' 打开 Mods 文件夹。
  168. ''' </summary>
  169. Private Sub BtnManageOpen_Click(sender As Object, e As EventArgs) Handles BtnManageOpen.Click, BtnHintOpen.Click
  170. Try
  171. Directory.CreateDirectory(PageVersionLeft.Version.PathIndie & "mods\")
  172. OpenExplorer("""" & PageVersionLeft.Version.PathIndie & "mods\""")
  173. Catch ex As Exception
  174. Log(ex, "打开 Mods 文件夹失败", LogLevel.Msgbox)
  175. End Try
  176. End Sub
  177. #If DEBUG Then
  178. ''' <summary>
  179. ''' 检查 Mod。
  180. ''' </summary>
  181. Private Sub BtnManageCheck_Click(sender As Object, e As EventArgs) Handles BtnManageCheck.Click
  182. Try
  183. Dim Result = McModCheck(PageVersionLeft.Version, McModLoader.Output)
  184. If Result.Count > 0 Then
  185. MyMsgBox(Join(Result, vbCrLf & vbCrLf), "Mod 检查结果")
  186. Else
  187. Hint("Mod 检查完成,未发现任何问题!", HintType.Finish)
  188. End If
  189. Catch ex As Exception
  190. Log(ex, "进行 Mod 检查时出错", LogLevel.Feedback)
  191. End Try
  192. End Sub
  193. #End If
  194. ''' <summary>
  195. ''' 全选。
  196. ''' </summary>
  197. Private Sub BtnManageSelectAll_Click(sender As Object, e As MouseButtonEventArgs) Handles BtnManageSelectAll.Click
  198. ChangeAllSelected(True)
  199. End Sub
  200. #End Region
  201. #Region "选择"
  202. '选择的 Mod 的路径(不含 .disabled)
  203. Public SelectedMods As New List(Of String)
  204. '单项切换选择状态
  205. Public Sub CheckChanged(sender As MyListItem, e As RouteEventArgs)
  206. If AniControlEnabled <> 0 Then Return
  207. '更新选择了的内容
  208. Dim SelectedKey As String = CType(sender.Tag, McMod).RawFileName
  209. If sender.Checked Then
  210. If Not SelectedMods.Contains(SelectedKey) Then SelectedMods.Add(SelectedKey)
  211. Else
  212. SelectedMods.Remove(SelectedKey)
  213. End If
  214. '更新下边栏 UI
  215. RefreshBottomBar()
  216. End Sub
  217. '改变下边栏状态
  218. Private ShownCount As Integer = 0
  219. Private Sub RefreshBottomBar()
  220. '计数
  221. Dim NewCount As Integer = SelectedMods.Count
  222. Dim Selected = NewCount > 0
  223. If Selected Then LabSelect.Text = $"已选择 {NewCount} 个文件" '取消所有选择时不更新数字
  224. '按钮可用性
  225. If Selected Then
  226. Dim HasEnabled As Boolean = False
  227. Dim HasDisabled As Boolean = False
  228. For Each ModEntity In McModLoader.Output
  229. If SelectedMods.Contains(ModEntity.RawFileName) Then
  230. If ModEntity.State = McMod.McModState.Fine Then
  231. HasEnabled = True
  232. ElseIf ModEntity.State = McMod.McModState.Disabled Then
  233. HasDisabled = True
  234. End If
  235. End If
  236. Next
  237. BtnSelectDisable.IsEnabled = HasEnabled
  238. BtnSelectEnable.IsEnabled = HasDisabled
  239. End If
  240. '更新显示状态
  241. CardSelect.IsHitTestVisible = Selected
  242. If AniControlEnabled = 0 Then
  243. If Selected Then
  244. '仅在数量增加时播放出现/跳跃动画
  245. If ShownCount >= NewCount Then
  246. ShownCount = NewCount
  247. Return
  248. Else
  249. ShownCount = NewCount
  250. End If
  251. '出现/跳跃动画
  252. CardSelect.Visibility = Visibility.Visible
  253. AniStart({
  254. AaOpacity(CardSelect, 1 - CardSelect.Opacity, 60),
  255. AaTranslateY(CardSelect, -27 - TransSelect.Y, 120, Ease:=New AniEaseOutFluent(AniEasePower.Weak)),
  256. AaTranslateY(CardSelect, 3, 150, 120, Ease:=New AniEaseInoutFluent(AniEasePower.Weak)),
  257. AaTranslateY(CardSelect, -1, 90, 270, Ease:=New AniEaseInoutFluent(AniEasePower.Weak))
  258. }, "Mod Sidebar")
  259. Else
  260. '不重复播放隐藏动画
  261. If ShownCount = 0 Then Return
  262. ShownCount = 0
  263. '隐藏动画
  264. AniStart({
  265. AaOpacity(CardSelect, -CardSelect.Opacity, 90),
  266. AaTranslateY(CardSelect, -10 - TransSelect.Y, 90, Ease:=New AniEaseInFluent(AniEasePower.Weak)),
  267. AaCode(Sub() CardSelect.Visibility = Visibility.Collapsed, After:=True)
  268. }, "Mod Sidebar")
  269. End If
  270. Else
  271. AniStop("Mod Sidebar")
  272. ShownCount = NewCount
  273. If Selected Then
  274. CardSelect.Visibility = Visibility.Visible
  275. CardSelect.Opacity = 1
  276. TransSelect.Y = -25
  277. Else
  278. CardSelect.Visibility = Visibility.Collapsed
  279. CardSelect.Opacity = 0
  280. TransSelect.Y = -10
  281. End If
  282. End If
  283. End Sub
  284. '切换所有项的选择状态
  285. Private Sub ChangeAllSelected(Value As Boolean)
  286. AniControlEnabled += 1
  287. SelectedMods.Clear()
  288. If IsSearching Then
  289. '搜索中
  290. For Each Item As MyListItem In PanSearchList.Children
  291. Item.Checked = Value
  292. If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
  293. Next
  294. If Not Value Then '只取消选择
  295. If PanItems IsNot Nothing Then
  296. For Each Item As MyListItem In PanItems.Children
  297. Item.Checked = Value
  298. Next
  299. End If
  300. End If
  301. Else
  302. '非搜索中
  303. If PanItems IsNot Nothing Then
  304. For Each Item As MyListItem In PanItems.Children
  305. Item.Checked = Value
  306. If Value Then SelectedMods.Add(CType(Item.Tag, McMod).RawFileName)
  307. Next
  308. End If
  309. End If
  310. AniControlEnabled -= 1
  311. '更新下边栏 UI
  312. RefreshBottomBar()
  313. End Sub
  314. Private Sub Load_State(sender As Object, newState As MyLoadingState, oldState As MyLoadingState) Handles Load.StateChanged
  315. ChangeAllSelected(False)
  316. End Sub
  317. #End Region
  318. #Region "下边栏"
  319. '启用 / 禁用
  320. Private Sub BtnSelectEorD_Click(sender As MyIconTextButton, e As RouteEventArgs) Handles BtnSelectEnable.Click, BtnSelectDisable.Click
  321. Dim IsSuccessful As Boolean = True
  322. Dim IsDisable As Boolean = sender.Equals(BtnSelectDisable)
  323. For Each ModEntity In McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
  324. Dim NewPath As String = Nothing
  325. If ModEntity.State = McMod.McModState.Fine And IsDisable Then
  326. '禁用
  327. NewPath = ModEntity.Path & ".disabled"
  328. ElseIf ModEntity.State = McMod.McModState.Disabled AndAlso Not IsDisable Then
  329. '启用
  330. NewPath = ModEntity.Path.Substring(0, ModEntity.Path.Count - ".disabled".Count)
  331. Else
  332. Continue For
  333. End If
  334. '重命名
  335. Try
  336. If File.Exists(NewPath) AndAlso Not File.Exists(ModEntity.Path) Then Continue For '因为未知原因 Mod 的状态已经切换完了
  337. File.Delete(NewPath)
  338. FileSystem.Rename(ModEntity.Path, NewPath)
  339. Catch ex As FileNotFoundException
  340. Log(ex, $"未找到需要重命名的 Mod({If(ModEntity.Path, "null")})", LogLevel.Feedback)
  341. RefreshList(True)
  342. Return
  343. Catch ex As Exception
  344. Log(ex, $"批量重命名 Mod 失败({If(ModEntity.Path, "null")})")
  345. IsSuccessful = False
  346. End Try
  347. '更改 Loader 和 UI 中的列表
  348. Dim NewModEntity As New McMod(NewPath)
  349. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  350. McModLoader.Output.RemoveAt(IndexOfLoader)
  351. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  352. Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
  353. Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyListItem).First(Function(i) CType(i.Tag, McMod) Is ModEntity))
  354. Parent.Children.RemoveAt(IndexOfUi)
  355. Parent.Children.Insert(IndexOfUi, McModListItem(NewModEntity))
  356. Next
  357. If Not IsSearching Then
  358. '改变禁用数量的显示
  359. CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  360. '更新加载器状态
  361. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  362. End If
  363. If Not IsSuccessful Then
  364. Hint("由于文件被占用,部分 Mod 的状态切换失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  365. RefreshList(True)
  366. Else
  367. ChangeAllSelected(False)
  368. End If
  369. End Sub
  370. '删除
  371. Private Sub BtnSelectDelete_Click() Handles BtnSelectDelete.Click
  372. Dim IsSuccessful As Boolean = True
  373. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  374. Dim DeleteList As List(Of McMod) = McModLoader.Output.Where(Function(m) SelectedMods.Contains(m.RawFileName)).ToList()
  375. For Each ModEntity In DeleteList
  376. '删除
  377. Try
  378. If IsShiftPressed Then
  379. File.Delete(ModEntity.Path)
  380. Else
  381. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  382. End If
  383. Catch ex As OperationCanceledException
  384. Log(ex, "批量删除 Mod 被主动取消")
  385. RefreshList(True)
  386. Return
  387. Catch ex As Exception
  388. Log(ex, $"批量删除 Mod 失败({ModEntity.Path})", LogLevel.Msgbox)
  389. IsSuccessful = False
  390. End Try
  391. '更改 Loader 和 UI 中的列表
  392. McModLoader.Output.Remove(ModEntity)
  393. Dim Parent As StackPanel = If(IsSearching, PanSearchList, PanItems)
  394. Dim IndexOfUi As Integer = Parent.Children.IndexOf(Parent.Children.OfType(Of MyListItem).First(Function(i) CType(i.Tag, McMod) Is ModEntity))
  395. Parent.Children.RemoveAt(IndexOfUi)
  396. Next
  397. If Not IsSearching Then
  398. '改变禁用数量的显示
  399. CType(PanItems.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  400. '更新加载器状态
  401. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  402. End If
  403. If Not IsSuccessful Then
  404. Hint("由于文件被占用,部分 Mod 删除失败,请尝试关闭正在运行的游戏后再试!", HintType.Critical)
  405. RefreshList(True)
  406. ElseIf If(IsSearching, PanSearchList, PanItems).Children.Count = 0 Then
  407. RefreshList(True)
  408. Else
  409. ChangeAllSelected(False)
  410. End If
  411. If IsSuccessful Then
  412. If IsShiftPressed Then
  413. Hint($"已彻底删除 {DeleteList.Count} 个文件!", HintType.Finish)
  414. Else
  415. Hint($"已将 {DeleteList.Count} 个文件删除到回收站!", HintType.Finish)
  416. End If
  417. End If
  418. End Sub
  419. '取消选择
  420. Private Sub BtnSelectCancel_Click() Handles BtnSelectCancel.Click
  421. ChangeAllSelected(False)
  422. End Sub
  423. #End Region
  424. #Region "单个 Mod 项"
  425. '点击 Logo:切换可用状态 / 显示错误原因
  426. Public Sub ItemLogo_Click(sender As MyListItem, e As EventArgs)
  427. Try
  428. Dim ModEntity As McMod = sender.Tag
  429. Dim NewPath As String = Nothing
  430. Select Case ModEntity.State
  431. Case McMod.McModState.Fine
  432. '前置检测警告
  433. If ModEntity.IsPresetMod Then
  434. If MyMsgBox("该 Mod 可能为其他 Mod 的前置,如果禁用可能导致其他 Mod 无法使用。" & vbCrLf & "你确定要继续禁用吗?", "警告", "禁用", "取消") = 2 Then Exit Sub
  435. End If
  436. NewPath = ModEntity.Path & ".disabled"
  437. Case McMod.McModState.Disabled
  438. NewPath = ModEntity.Path.Substring(0, ModEntity.Path.Count - ".disabled".Count)
  439. Case McMod.McModState.Unavaliable
  440. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntity.FileUnavailableReason), "Mod 读取失败")
  441. Exit Sub
  442. End Select
  443. '重命名
  444. Dim NewModEntity As New McMod(NewPath)
  445. Try
  446. File.Delete(NewPath)
  447. FileSystem.Rename(ModEntity.Path, NewPath)
  448. Catch ex As FileNotFoundException
  449. Log(ex, "未找到理应存在的 Mod 文件(" & ModEntity.Path & ")")
  450. FileSystem.Rename(NewPath, ModEntity.Path)
  451. NewModEntity = New McMod(ModEntity.Path)
  452. End Try
  453. '更改 Loader 中的列表
  454. Dim IndexOfLoader As Integer = McModLoader.Output.IndexOf(ModEntity)
  455. McModLoader.Output.RemoveAt(IndexOfLoader)
  456. McModLoader.Output.Insert(IndexOfLoader, NewModEntity)
  457. '更改 UI 中的列表
  458. Dim Parent As StackPanel = sender.Parent
  459. Dim IndexOfUi As Integer = Parent.Children.IndexOf(sender)
  460. Parent.Children.RemoveAt(IndexOfUi)
  461. Parent.Children.Insert(IndexOfUi, McModListItem(NewModEntity))
  462. '仅在非搜索页面才执行,以确保搜索页内外显示一致
  463. If Not IsSearching Then
  464. '改变禁用数量的显示
  465. CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  466. '更新加载器状态
  467. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  468. End If
  469. RefreshBottomBar()
  470. Catch ex As Exception
  471. Log(ex, "单个状态改变中重命名 Mod 失败")
  472. Hint("切换 Mod 状态失败,请尝试关闭正在运行的游戏后再试!")
  473. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  474. End Try
  475. End Sub
  476. '删除
  477. Public Sub Delete_Click(sender As MyIconButton, e As EventArgs)
  478. Try
  479. Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
  480. Dim ListItem As MyListItem = sender.Tag
  481. Dim ModEntity As McMod = ListItem.Tag
  482. '前置检测警告
  483. If ModEntity.IsPresetMod Then
  484. If MyMsgBox("该 Mod 可能为其他 Mod 的前置,如果删除可能导致其他 Mod 无法使用。" & vbCrLf & "你确定要继续删除吗?", "警告", "删除", "取消", IsWarn:=True) = 2 Then Exit Sub
  485. End If
  486. '删除
  487. If File.Exists(ModEntity.Path) Then
  488. If IsShiftPressed Then
  489. File.Delete(ModEntity.Path)
  490. Else
  491. My.Computer.FileSystem.DeleteFile(ModEntity.Path, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.SendToRecycleBin)
  492. End If
  493. Else
  494. '在不明原因下删除的文件可能还在列表里,如果玩家又点了一次就会出错,总之加个判断也不碍事
  495. Log("[System] 需要删除的 Mod 文件不存在(" & ModEntity.Path & ")", LogLevel.Hint)
  496. Exit Sub
  497. End If
  498. '更改 Loader 中的列表
  499. McModLoader.Output.Remove(ModEntity)
  500. '更改 UI 中的列表
  501. Dim Parent As StackPanel = ListItem.Parent
  502. Parent.Children.Remove(ListItem)
  503. If Parent.Children.Count = 0 Then
  504. RefreshList(True)
  505. Else
  506. CType(Parent.Parent, MyCard).Title = McModGetTitle(McModLoader.Output)
  507. End If
  508. '显示提示
  509. If IsShiftPressed Then
  510. Hint("已删除 " & ModEntity.FileName & "!", HintType.Finish)
  511. Else
  512. Hint("已将 " & ModEntity.FileName & " 删除到回收站!", HintType.Finish)
  513. End If
  514. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.UpdateOnly)
  515. Catch ex As OperationCanceledException
  516. Log(ex, "删除 Mod 被主动取消")
  517. Catch ex As Exception
  518. Log(ex, "删除 Mod 失败", LogLevel.Feedback)
  519. End Try
  520. End Sub
  521. '详情
  522. Public Sub Info_Click(sender As Object, e As EventArgs)
  523. Try
  524. Dim ListItem As MyListItem
  525. ListItem = If(TypeOf sender Is MyIconButton, sender.Tag, sender)
  526. Dim ModEntity As McMod = ListItem.Tag
  527. '加载失败信息
  528. If ModEntity.State = McMod.McModState.Unavaliable Then
  529. MyMsgBox("无法读取此 Mod 的信息。" & vbCrLf & vbCrLf & "详细的错误信息:" & GetExceptionDetail(ModEntity.FileUnavailableReason), "Mod 读取失败")
  530. Return
  531. End If
  532. '获取信息
  533. Dim ContentLines As New List(Of String)
  534. If ModEntity.Description IsNot Nothing Then ContentLines.Add(ModEntity.Description & vbCrLf)
  535. If ModEntity.Authors IsNot Nothing Then ContentLines.Add("作者:" & ModEntity.Authors)
  536. ContentLines.Add("文件:" & ModEntity.FileName & "(" & GetString(New FileInfo(ModEntity.Path).Length) & ")")
  537. If ModEntity.Version IsNot Nothing Then ContentLines.Add("版本:" & ModEntity.Version)
  538. Dim DebugInfo As New List(Of String)
  539. If ModEntity.ModId IsNot Nothing Then
  540. DebugInfo.Add("Mod ID:" & ModEntity.ModId)
  541. End If
  542. If ModEntity.Dependencies.Count > 0 Then
  543. DebugInfo.Add("依赖于:")
  544. For Each Dep In ModEntity.Dependencies
  545. DebugInfo.Add(" - " & Dep.Key & If(Dep.Value Is Nothing, "", ",版本:" & Dep.Value))
  546. Next
  547. End If
  548. If DebugInfo.Count > 0 Then
  549. ContentLines.Add("")
  550. ContentLines.AddRange(DebugInfo)
  551. End If
  552. '获取用于搜索的 Mod 名称
  553. Dim ModOriginalName As String = ModEntity.Name.Replace(" ", "+")
  554. Dim ModSearchName As String = ModOriginalName.Substring(0, 1)
  555. For i = 1 To ModOriginalName.Count - 1
  556. Dim IsLastLower As Boolean = ModOriginalName(i - 1).ToString.ToLower.Equals(ModOriginalName(i - 1).ToString)
  557. Dim IsCurrentLower As Boolean = ModOriginalName(i).ToString.ToLower.Equals(ModOriginalName(i).ToString)
  558. If IsLastLower AndAlso Not IsCurrentLower Then
  559. '上一个字母为小写,这一个字母为大写
  560. ModSearchName += "+"
  561. End If
  562. ModSearchName += ModOriginalName(i)
  563. Next
  564. ModSearchName = ModSearchName.Replace("++", "+").Replace("pti+Fine", "ptiFine")
  565. '显示
  566. If ModEntity.Url Is Nothing Then
  567. If MyMsgBox(Join(ContentLines, vbCrLf), ModEntity.Name, "百科搜索", "返回") = 1 Then
  568. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  569. End If
  570. Else
  571. Select Case MyMsgBox(Join(ContentLines, vbCrLf), ModEntity.Name, "打开官网", "百科搜索", "返回")
  572. Case 1
  573. OpenWebsite(ModEntity.Url)
  574. Case 2
  575. OpenWebsite("https://www.mcmod.cn/s?key=" & ModSearchName & "&site=all&filter=0")
  576. End Select
  577. End If
  578. Catch ex As Exception
  579. Log(ex, "获取 Mod 详情失败", LogLevel.Feedback)
  580. End Try
  581. End Sub
  582. '打开文件所在的位置
  583. Public Sub Open_Click(sender As MyIconButton, e As EventArgs)
  584. Try
  585. Dim ListItem As MyListItem = sender.Tag
  586. Dim ModEntity As McMod = ListItem.Tag
  587. OpenExplorer("/select,""" & ModEntity.Path & """")
  588. Catch ex As Exception
  589. Log(ex, "打开 Mod 文件位置失败", LogLevel.Feedback)
  590. End Try
  591. End Sub
  592. #End Region
  593. Public ReadOnly Property IsSearching As Boolean
  594. Get
  595. Return Not String.IsNullOrWhiteSpace(SearchBox.Text)
  596. End Get
  597. End Property
  598. Public Sub SearchRun() Handles SearchBox.TextChanged
  599. ChangeAllSelected(False)
  600. If IsSearching Then
  601. '构造请求
  602. Dim QueryList As New List(Of SearchEntry(Of McMod))
  603. For Each Entry As McMod In McModLoader.Output
  604. QueryList.Add(New SearchEntry(Of McMod) With {
  605. .Item = Entry,
  606. .SearchSource = New List(Of KeyValuePair(Of String, Double)) From {
  607. New KeyValuePair(Of String, Double)(Entry.Name, 1),
  608. New KeyValuePair(Of String, Double)(Entry.FileName, 1),
  609. New KeyValuePair(Of String, Double)(If(Entry.Description, ""), 0.5)
  610. }
  611. })
  612. Next
  613. '进行搜索,构造列表
  614. Dim SearchResult = Search(QueryList, SearchBox.Text, MaxBlurCount:=5, MinBlurSimilarity:=0.35)
  615. PanSearchList.Children.Clear()
  616. If SearchResult.Count = 0 Then
  617. PanSearch.Title = "无搜索结果"
  618. PanSearchList.Visibility = Visibility.Collapsed
  619. Else
  620. PanSearch.Title = "搜索结果"
  621. For Each Result In SearchResult
  622. Dim Item = McModListItem(Result.Item)
  623. If ModeDebug Then Item.Info = If(Result.AbsoluteRight, "完全匹配,", "") & "相似度:" & Math.Round(Result.Similarity, 3) & "," & Item.Info
  624. PanSearchList.Children.Add(Item)
  625. Next
  626. PanSearchList.Visibility = Visibility.Visible
  627. End If
  628. '显示
  629. AniStart({
  630. AaOpacity(PanList, -PanList.Opacity, 100),
  631. AaCode(Sub()
  632. PanList.Visibility = Visibility.Collapsed
  633. PanSearch.Visibility = Visibility.Visible
  634. PanSearch.TriggerForceResize()
  635. End Sub,, True),
  636. AaOpacity(PanSearch, 1 - PanSearch.Opacity, 200, 60)
  637. }, "FrmVersionMod Search Switch", True)
  638. Else
  639. '隐藏
  640. LoaderFolderRun(McModLoader, PageVersionLeft.Version.PathIndie & "mods\", LoaderFolderRunType.RunOnUpdated)
  641. AniStart({
  642. AaOpacity(PanSearch, -PanSearch.Opacity, 100),
  643. AaCode(Sub()
  644. PanSearch.Height = 0
  645. PanSearch.Visibility = Visibility.Collapsed
  646. PanList.Visibility = Visibility.Visible
  647. End Sub,, True),
  648. AaOpacity(PanList, 1 - PanList.Opacity, 150, 30)
  649. }, "FrmVersionMod Search Switch", True)
  650. End If
  651. End Sub
  652. End Class