2.13.0.0
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
''' <summary>
|
||||
''' <summary>
|
||||
''' 本地的社区资源文件。
|
||||
''' </summary>
|
||||
Public Class LocalResourceFile
|
||||
@@ -106,235 +106,6 @@ Public Class LocalResourceFile
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "从 JAR 获取信息"
|
||||
|
||||
''' <summary>
|
||||
''' 从 JAR 文件中获取 Mod 信息。
|
||||
''' </summary>
|
||||
Public Sub LoadMetadataFromJar()
|
||||
Static IsLoaded As Boolean = False
|
||||
If IsLoaded Then Return
|
||||
IsLoaded = True
|
||||
Dim Jar As ZipArchive = Nothing
|
||||
Try
|
||||
'基础可用性检查
|
||||
If Not FileUtils.Exists(File.FullName) Then Throw New FileNotFoundException("未找到 Mod 文件(" & File.FullName & ")")
|
||||
'打开 Jar 文件
|
||||
Jar = FileUtils.OpenZip(File.FullName)
|
||||
'信息获取
|
||||
LoadMetadataFromJar(Jar)
|
||||
Catch ex As UnauthorizedAccessException
|
||||
Logger.Warn(ex, $"Mod 文件由于无权限无法打开({File.FullName})")
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"Mod 文件无法打开({File.FullName})")
|
||||
Finally
|
||||
If Jar IsNot Nothing Then Jar.Dispose()
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub LoadMetadataFromJar(Jar As ZipArchive)
|
||||
|
||||
#Region "尝试使用 mcmod.info"
|
||||
Try
|
||||
'获取信息文件
|
||||
Dim InfoEntry As ZipArchiveEntry = Jar.GetEntry("mcmod.info")
|
||||
Dim InfoString As String = Nothing
|
||||
If InfoEntry IsNot Nothing Then
|
||||
InfoString = InfoEntry.Open().ReadString()
|
||||
If InfoString.Length < 15 Then InfoString = Nothing
|
||||
End If
|
||||
If InfoString Is Nothing Then Exit Try
|
||||
'获取可用 Json 项
|
||||
Dim InfoObject As JObject
|
||||
Dim JsonObject = GetJson(InfoString)
|
||||
If JsonObject.Type = JTokenType.Array Then
|
||||
InfoObject = JsonObject(0)
|
||||
Else
|
||||
InfoObject = JsonObject("modList")(0)
|
||||
End If
|
||||
'从文件中获取 Mod 信息项
|
||||
Display = InfoObject("name")
|
||||
Description = InfoObject("description")
|
||||
Version = InfoObject("version")
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"读取 mcmod.info 时出现未知错误({File.FullName})")
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 fabric.mod.json"
|
||||
Try
|
||||
'获取 fabric.mod.json 文件
|
||||
Dim FabricEntry As ZipArchiveEntry = Jar.GetEntry("fabric.mod.json")
|
||||
Dim FabricText As String = Nothing
|
||||
If FabricEntry IsNot Nothing Then
|
||||
FabricText = FabricEntry.Open().ReadString(Encoding.UTF8)
|
||||
If Not FabricText.Contains("schemaVersion") Then FabricText = Nothing
|
||||
End If
|
||||
If FabricText Is Nothing Then Exit Try
|
||||
Dim FabricObject As JObject = GetJson(FabricText)
|
||||
GotFabric:
|
||||
'从文件中获取 Mod 信息项
|
||||
If FabricObject.ContainsKey("name") Then Display = FabricObject("name")
|
||||
If FabricObject.ContainsKey("version") Then Version = FabricObject("version")
|
||||
If FabricObject.ContainsKey("description") Then Description = FabricObject("description")
|
||||
'加载成功
|
||||
GoTo Finished
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"读取 fabric.mod.json 时出现未知错误({File.FullName})")
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 mods.toml"
|
||||
Try
|
||||
'获取 mods.toml 文件
|
||||
Dim TomlEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/mods.toml")
|
||||
Dim TomlText As String = Nothing
|
||||
If TomlEntry IsNot Nothing Then
|
||||
TomlText = TomlEntry.Open().ReadString()
|
||||
If TomlText.Length < 15 Then TomlText = Nothing
|
||||
End If
|
||||
If TomlText Is Nothing Then Exit Try
|
||||
'文件标准化:统一换行符为 vbLf,去除注释、头尾的空格、空行
|
||||
Dim Lines As New List(Of String)
|
||||
For Each Line In TomlText.SplitLines(True)
|
||||
If Line.StartsWithF("#") Then '去除注释
|
||||
Continue For
|
||||
ElseIf Line.Contains("#") Then
|
||||
Line = Line.Substring(0, Line.IndexOfF("#"))
|
||||
End If
|
||||
Line = Line.Trim(New Char() {" "c, " "c, " "c}) '去除头尾的空格
|
||||
If Line.Any Then Lines.Add(Line) '去除空行
|
||||
Next
|
||||
'读取文件数据
|
||||
Dim TomlData As New List(Of KeyValuePair(Of String, Dictionary(Of String, Object))) From {New KeyValuePair(Of String, Dictionary(Of String, Object))("", New Dictionary(Of String, Object))}
|
||||
For i = 0 To Lines.Count - 1
|
||||
Dim Line As String = Lines(i)
|
||||
If Line.StartsWithF("[") AndAlso Line.EndsWithF("]") Then
|
||||
'段落标记
|
||||
Dim Header = Line.Trim("[]".ToCharArray)
|
||||
TomlData.Add(New KeyValuePair(Of String, Dictionary(Of String, Object))(Header, New Dictionary(Of String, Object)))
|
||||
ElseIf Line.Contains("=") Then
|
||||
'字段标记
|
||||
Dim Key As String = Line.Substring(0, Line.IndexOfF("=")).TrimEnd(New Char() {" "c, " "c, " "c})
|
||||
Dim RawValue As String = Line.Substring(Line.IndexOfF("=") + 1).TrimStart(New Char() {" "c, " "c, " "c})
|
||||
Dim Value As Object
|
||||
If RawValue.StartsWithF("""") AndAlso RawValue.EndsWithF("""") Then
|
||||
'单行字符串
|
||||
Value = RawValue.Trim("""")
|
||||
ElseIf RawValue.StartsWithF("'''") Then
|
||||
'多行字符串
|
||||
Dim ValueLines As New List(Of String) From {RawValue.TrimStart("'")}
|
||||
If ValueLines(0).EndsWithF("'''") Then '把多行字符串按单行写法写的错误处理(#2732)
|
||||
ValueLines(0) = ValueLines(0).TrimEnd("'")
|
||||
Else
|
||||
Do Until i >= Lines.Count - 1
|
||||
i += 1
|
||||
Dim ValueLine As String = Lines(i)
|
||||
If ValueLine.EndsWithF("'''") Then
|
||||
ValueLines.Add(ValueLine.TrimEnd("'"))
|
||||
Exit Do
|
||||
Else
|
||||
ValueLines.Add(ValueLine)
|
||||
End If
|
||||
Loop
|
||||
End If
|
||||
Value = ValueLines.Join(vbLf).Trim(vbLf).ReplaceLineEndings(vbCrLf)
|
||||
ElseIf RawValue.Lower = "true" OrElse RawValue.Lower = "false" Then
|
||||
'布尔型
|
||||
Value = (RawValue.Lower = "true")
|
||||
ElseIf Val(RawValue).ToString = RawValue Then
|
||||
'数字型
|
||||
Value = Val(RawValue)
|
||||
Else
|
||||
'不知道是个啥玩意儿,直接存储
|
||||
Value = RawValue
|
||||
End If
|
||||
TomlData.Last.Value(Key) = Value
|
||||
Else
|
||||
'不知道是个啥玩意儿
|
||||
Exit Try
|
||||
End If
|
||||
Next
|
||||
'从文件数据中获取信息
|
||||
Dim ModEntry As Dictionary(Of String, Object) = Nothing
|
||||
For Each TomlSubData In TomlData
|
||||
If TomlSubData.Key = "mods" Then
|
||||
ModEntry = TomlSubData.Value
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
If ModEntry Is Nothing OrElse Not ModEntry.ContainsKey("modId") Then Exit Try
|
||||
If ModEntry.ContainsKey("displayName") Then Display = ModEntry("displayName")
|
||||
If ModEntry.ContainsKey("description") Then Description = ModEntry("description")
|
||||
If ModEntry.ContainsKey("version") Then Version = ModEntry("version")
|
||||
'加载成功
|
||||
GoTo Finished
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"读取 mods.toml 时出现未知错误({File.FullName})")
|
||||
End Try
|
||||
#End Region
|
||||
#Region "尝试使用 fml_cache_annotation.json"
|
||||
Try
|
||||
'获取 fml_cache_annotation.json 文件
|
||||
Dim FmlEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/fml_cache_annotation.json")
|
||||
Dim FmlText As String = Nothing
|
||||
If FmlEntry IsNot Nothing Then
|
||||
FmlText = FmlEntry.Open().ReadString(Encoding.UTF8)
|
||||
If Not FmlText.Contains("Lnet/minecraftforge/fml/common/Mod;") Then FmlText = Nothing
|
||||
End If
|
||||
If FmlText Is Nothing Then Exit Try
|
||||
Dim FmlJson As JObject = GetJson(FmlText)
|
||||
'获取可用 Json 项
|
||||
Dim FmlObject As JObject = Nothing
|
||||
For Each ModFilePair In FmlJson
|
||||
Dim ModFileAnnos As JArray = ModFilePair.Value("annotations")
|
||||
If ModFileAnnos IsNot Nothing Then
|
||||
'先获取 Mod
|
||||
For Each ModFileAnno In ModFileAnnos
|
||||
Dim Name As String = If(ModFileAnno("name"), "")
|
||||
If Name = "Lnet/minecraftforge/fml/common/Mod;" Then
|
||||
FmlObject = ModFileAnno("values")
|
||||
GoTo Got
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Next
|
||||
Exit Try
|
||||
Got:
|
||||
'从文件中获取 Mod 信息项
|
||||
If FmlObject.ContainsKey("name") Then Display = FmlObject("name")("value")
|
||||
If FmlObject.ContainsKey("version") Then Version = FmlObject("version")("value")
|
||||
'加载成功
|
||||
GoTo Finished
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"读取 fml_cache_annotation.json 时出现未知错误({File.FullName})")
|
||||
End Try
|
||||
#End Region
|
||||
Return '没能成功获取任何信息
|
||||
|
||||
Finished:
|
||||
#Region "将 Version 代号转换为 META-INF 中的版本"
|
||||
If _Version = "version" Then
|
||||
Try
|
||||
Dim MetaEntry As ZipArchiveEntry = Jar.GetEntry("META-INF/MANIFEST.MF")
|
||||
If MetaEntry IsNot Nothing Then
|
||||
Dim MetaString As String = MetaEntry.Open().ReadString().Replace(" :", ":").Replace(": ", ":")
|
||||
If MetaString.Contains("Implementation-Version:") Then
|
||||
MetaString = MetaString.Substring(MetaString.IndexOfF("Implementation-Version:") + "Implementation-Version:".Count)
|
||||
MetaString = MetaString.Substring(0, MetaString.IndexOfAny(vbCrLf.ToCharArray)).Trim
|
||||
Version = MetaString
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Logger.Warn($"获取 META-INF 中的版本信息失败({File.FullName})")
|
||||
Version = Nothing
|
||||
End Try
|
||||
End If
|
||||
If _Version IsNot Nothing AndAlso Not (_Version.Contains(".") OrElse _Version.Contains("-")) Then Version = Nothing
|
||||
#End Region
|
||||
RaiseEvent OnUpdate(Me)
|
||||
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "网络信息"
|
||||
|
||||
''' <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Public Module LocalResourceLoaders
|
||||
Public Module LocalResourceLoaders
|
||||
|
||||
'加载 Mod 列表
|
||||
Public LocalResourceLoader As New LoaderTask(Of String, List(Of LocalResourceFile))("LocalResourceLoader", AddressOf LocalResourceFileLoad)
|
||||
@@ -12,7 +12,7 @@
|
||||
Try
|
||||
RunInUiWait(Sub() If FrmInstanceMod IsNot Nothing Then FrmInstanceMod.Load.Text = "正在更新 Mod")
|
||||
Do Until Not PageInstanceMod.UpdatingInstanceModFolders.Contains(Loader.Input)
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
Thread.Sleep(100)
|
||||
Loop
|
||||
Finally
|
||||
@@ -25,7 +25,7 @@
|
||||
Dim ModFileList As New List(Of String)
|
||||
If DirectoryUtils.Exists(Loader.Input) Then
|
||||
Dim RawName As String = Loader.Input.Lower
|
||||
For Each File In DirectoryUtils.GetFiles(Loader.Input)
|
||||
For Each File In DirectoryUtils.EnumerateFiles(Loader.Input, True)
|
||||
If File.BeforeLast("\").Lower & "\" <> RawName Then
|
||||
'仅当 Forge 1.13- 且文件夹名与版本号相同时,才加载该子文件夹下的 Mod
|
||||
If Not (PageInstanceLeft.Instance IsNot Nothing AndAlso PageInstanceLeft.Instance.Version.HasForge AndAlso
|
||||
@@ -53,8 +53,8 @@
|
||||
Try
|
||||
Dim CacheContent As String = FileUtils.TryReadAsString(CachePath)
|
||||
If Not String.IsNullOrWhiteSpace(CacheContent) Then
|
||||
Cache = GetJson(CacheContent)
|
||||
If Not Cache.ContainsKey("version") OrElse Cache("version").ToObject(Of Integer) <> Versions.LOCAL_MOD_CACHE_VERSION Then
|
||||
Cache = CacheContent.DeserializeJson()
|
||||
If Not Cache.ContainsKey("version") OrElse Cache("version").ToObject(Of Integer) <> Versions.LocalModCacheVersion Then
|
||||
Logger.Warn($"本地 Mod 信息缓存版本已过期,将弃用这些缓存信息")
|
||||
Cache = New JObject
|
||||
End If
|
||||
@@ -63,12 +63,12 @@
|
||||
Logger.Warn(ex, "读取本地 Mod 信息缓存失败,已重置")
|
||||
Cache = New JObject
|
||||
End Try
|
||||
Cache("version") = Versions.LOCAL_MOD_CACHE_VERSION
|
||||
Cache("version") = Versions.LocalModCacheVersion
|
||||
|
||||
'加载 Mod 列表
|
||||
Dim ModList As New List(Of LocalResourceFile)
|
||||
For Each ModFile In ModFileList
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
Dim ModEntry As New LocalResourceFile(ModFile)
|
||||
Dim DumpMod As LocalResourceFile = ModList.FirstOrDefault(Function(m) m.EnabledName = ModEntry.EnabledName) '存在两个文件,名称相同,但一个启用一个禁用
|
||||
If DumpMod IsNot Nothing Then
|
||||
@@ -88,7 +88,7 @@
|
||||
ModList = ModList.OrderBy(Function(m) m.File.Name).ToList
|
||||
|
||||
'回设
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
Loader.Output = ModList
|
||||
|
||||
'开始联网加载
|
||||
@@ -109,7 +109,7 @@
|
||||
Dim ModLoaders = Loader.Input.Target.ModLoaders
|
||||
'读取缓存,获取需要更新的 Mod 列表
|
||||
For Each ModEntry As LocalResourceFile In Loader.Input.ModList
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
Dim CacheKey = ModEntry.ModrinthHash & Loader.Input.Target.VanillaName & ModLoaders
|
||||
If Cache.ContainsKey(CacheKey) Then
|
||||
ModEntry.FromJson(Cache(CacheKey))
|
||||
@@ -119,10 +119,7 @@
|
||||
Mods.Add(ModEntry)
|
||||
Next
|
||||
Logger.Info($"有 {Mods.Where(Function(m) m.Project Is Nothing).Count} 个 Mod 需要联网获取信息,{Mods.Where(Function(m) m.Project IsNot Nothing).Count} 个 Mod 需要更新信息")
|
||||
If Not Mods.Any Then
|
||||
Loader.Input.ModList.Where(Function(m) m.Version Is Nothing).ForAll(Sub(m) m.LoadMetadataFromJar()) '从 JAR 中获取缺失的版本信息(下面有另一个分支)
|
||||
Return
|
||||
End If
|
||||
If Not Mods.Any Then Return
|
||||
'获取作为检查目标的加载器和版本
|
||||
'此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
|
||||
Dim TargetMcVersion As McVersion = Loader.Input.Target
|
||||
@@ -158,7 +155,7 @@
|
||||
Entry.ProjectVersion.Version = File.Version '使用来自 Modrinth 的版本号
|
||||
End If
|
||||
Next
|
||||
If Loader.IsInterruptedWithThread(CurrentTaskThread) Then Return
|
||||
If Loader.IsCanceledWithThread(CurrentTaskThread) Then Return
|
||||
Logger.Info($"需要从 Modrinth 获取 {ModrinthMapping.Count} 个本地 Mod 的工程信息")
|
||||
'步骤 3:获取工程信息
|
||||
If Not ModrinthMapping.Any() Then Return
|
||||
@@ -211,7 +208,7 @@
|
||||
Dim Hashes As New List(Of UInteger)
|
||||
For Each Entry In Mods
|
||||
Hashes.Add(Entry.CurseForgeHash)
|
||||
If Loader.IsInterruptedWithThread(CurrentTaskThread) Then Return
|
||||
If Loader.IsCanceledWithThread(CurrentTaskThread) Then Return
|
||||
Next
|
||||
Dim CurseForgeRaw As JContainer = DlModRequest("https://api.curseforge.com/v1/fingerprints/432", HttpMethod.Post,
|
||||
$"{{""fingerprints"": [{Hashes.Join(",")}]}}", "application/json")("data")("exactMatches")
|
||||
@@ -232,7 +229,7 @@
|
||||
If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then Entry.ProjectVersion = File
|
||||
Next
|
||||
Next
|
||||
If Loader.IsInterruptedWithThread(CurrentTaskThread) Then Return
|
||||
If Loader.IsCanceledWithThread(CurrentTaskThread) Then Return
|
||||
Logger.Info($"需要从 CurseForge 获取 {ProjectIdToLocalFiles.Count} 个本地 Mod 的工程信息")
|
||||
If Not ProjectIdToLocalFiles.Any() Then Return
|
||||
|
||||
@@ -313,25 +310,23 @@
|
||||
EndedThreadCount += 1
|
||||
End Try
|
||||
End Sub, "Mod List Detail Loader CurseForge")
|
||||
'从 JAR 中获取缺失的版本信息(上面有另一个分支)
|
||||
Loader.Input.ModList.Where(Function(m) m.Version Is Nothing).ForAll(Sub(m) m.LoadMetadataFromJar())
|
||||
'等待线程结束
|
||||
Do Until EndedThreadCount = 2
|
||||
Thread.Sleep(10)
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
Loop
|
||||
'保存缓存
|
||||
Mods = Mods.Where(Function(m) m.Project IsNot Nothing).ToList()
|
||||
Logger.Info($"联网获取本地 Mod 信息完成,为 {Mods.Count} 个 Mod 更新缓存")
|
||||
If Not Mods.Any() Then Return
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
For Each Entry In Mods
|
||||
Entry.OnlineDataLoaded = Not IsFailed
|
||||
Cache(Entry.ModrinthHash & VanillaVersion & ModLoaders) = Entry.ToJson()
|
||||
Next
|
||||
FileUtils.Write(PathTemp & "Cache\LocalMod.json", Cache.ToString(If(ModeDebug, Newtonsoft.Json.Formatting.Indented, Newtonsoft.Json.Formatting.None)))
|
||||
'刷新边栏
|
||||
If Loader.IsInterrupted Then Return
|
||||
If Loader.IsCanceled Then Return
|
||||
If FrmInstanceMod?.Filter = PageInstanceMod.FilterType.CanUpdate Then
|
||||
RunInUi(Sub() FrmInstanceMod?.RefreshUI()) '同步 “可更新” 列表 (#4677)
|
||||
Else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
''' <summary>
|
||||
''' <summary>
|
||||
''' 社区资源的项目信息。
|
||||
''' </summary>
|
||||
Public Class ResourceProject
|
||||
@@ -88,7 +88,7 @@ Public Class ResourceProject
|
||||
If Not LoadedDatabase Then
|
||||
LoadedDatabase = True
|
||||
If Types.HasFlag(ResourceTypes.Mod) OrElse Types.HasFlag(ResourceTypes.DataPack) Then
|
||||
_WikiEntry = WikiEntry.All.FirstOrDefault(Function(c) If(Platform = ResourcePlatforms.CurseForge, c.CurseForgeSlug, c.ModrinthSlug) = Slug)
|
||||
_WikiEntry = WikiEntry.All.Value.FirstOrDefault(Function(c) c.Slugs.GetOrDefault(Platform) = Slug)
|
||||
End If
|
||||
End If
|
||||
Return _WikiEntry
|
||||
@@ -111,7 +111,7 @@ Public Class ResourceProject
|
||||
''' </summary>
|
||||
Public ReadOnly Property TranslatedName As String
|
||||
Get
|
||||
Return If(WikiEntry Is Nothing OrElse WikiEntry.ChineseName = "", RawName, WikiEntry.ChineseName)
|
||||
Return If(WikiEntry?.ChineseName, RawName)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
@@ -441,7 +441,7 @@ Public Class ResourceProject
|
||||
Json("Website") = Website
|
||||
Json("LastUpdate") = LastUpdate
|
||||
Json("DownloadCount") = DownloadCount
|
||||
If ModLoaders <> ModLoaders.None Then Json("ModLoaders") = ModLoaders
|
||||
If ModLoaders <> ModLoaders.None Then Json("ModLoaders") = CInt(ModLoaders)
|
||||
Json("Tags") = New JArray(Tags)
|
||||
If LogoUrl IsNot Nothing Then Json("LogoUrl") = LogoUrl
|
||||
If Drops.Any Then Json("Drops") = New JArray(Drops)
|
||||
@@ -561,7 +561,7 @@ Public Class ResourceProject
|
||||
NewItem.LabVersion.Text = ModLoaderDescriptionFull
|
||||
End If
|
||||
NewItem.LabSource.Text = Platform.ToString
|
||||
NewItem.LabTime.Text = GetTimeSpanString(LastUpdate - Date.Now, True)
|
||||
NewItem.LabTime.Text = StringUtils.FormatTimeSpan(LastUpdate - Date.Now, True)
|
||||
NewItem.LabDownload.Text =
|
||||
If(DownloadCount > 100000000, Math.Round(DownloadCount / 100000000, 2) & " 亿",
|
||||
If(DownloadCount > 100000, Math.Floor(DownloadCount / 10000) & " 万", DownloadCount))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Public Class ResourceSearcher
|
||||
Public Class ResourceSearcher
|
||||
Private Const RESULT_PAGE_SIZE = 40
|
||||
Private Const MSG_NO_CHINESE_RESULT As String = "无搜索结果,请尝试搜索其英文名称"
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
If Tag <> "" Then Address += "&categoryId=" & Tag.BeforeFirst("/")
|
||||
If ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter Then Address += "&modLoaderType=" & Resource.ToCurseForgeModLoaderType(ModLoaders.Flags.First)
|
||||
If Not String.IsNullOrEmpty(GameVersion) Then Address += "&gameVersion=" & GameVersion
|
||||
If Not String.IsNullOrEmpty(SearchText) Then Address += "&searchFilter=" & EscapeUtils.UrlEscape(SearchText)
|
||||
If Not String.IsNullOrEmpty(SearchText) Then Address += "&searchFilter=" & StringUtils.UrlEscape(SearchText)
|
||||
If Storage.CurseForgeOffset > 0 Then Address += "&index=" & Storage.CurseForgeOffset
|
||||
Return Address
|
||||
End Function
|
||||
@@ -97,7 +97,7 @@
|
||||
If Tag.EndsWithF("/") Then Storage.ModrinthTotal = 0
|
||||
'应用筛选参数
|
||||
Dim Address As String = $"https://api.modrinth.com/v2/search?limit={RESULT_PAGE_SIZE}&index=relevance"
|
||||
If Not String.IsNullOrEmpty(SearchText) Then Address += "&query=" & EscapeUtils.UrlEscape(SearchText)
|
||||
If Not String.IsNullOrEmpty(SearchText) Then Address += "&query=" & StringUtils.UrlEscape(SearchText)
|
||||
If Storage.ModrinthOffset > 0 Then Address += "&offset=" & Storage.ModrinthOffset
|
||||
'facets=[["categories:'game-mechanics'"],["categories:'forge',categories:'fabric'"],["versions:1.19.3"],["project_type:mod"]]
|
||||
Dim Facets As New List(Of String)
|
||||
@@ -189,9 +189,9 @@
|
||||
#Region "中文搜索"
|
||||
|
||||
Dim RawSearchText As String = If(Request.SearchText, "").Trim
|
||||
RawSearchText = StrConv(RawSearchText, VbStrConv.SimplifiedChinese) '繁体转简体
|
||||
RawSearchText = RawSearchText.Lower
|
||||
Logger.Info($"工程列表搜索原始文本:{RawSearchText}")
|
||||
RawSearchText = StrConv(RawSearchText, VbStrConv.SimplifiedChinese) '繁体转简体
|
||||
|
||||
Dim IsChineseSearch As Boolean =
|
||||
RawSearchText.RegexCheck("[\u4e00-\u9fbb]") AndAlso Not String.IsNullOrEmpty(RawSearchText) AndAlso
|
||||
@@ -203,14 +203,14 @@
|
||||
Function(Result As SearchEntry(Of WikiEntry), Source As ResourcePlatforms) As IEnumerable(Of String)
|
||||
'从各个可能的来源提取候选
|
||||
Dim Candidates As New List(Of String)
|
||||
If Result.Item.CurseForgeSlug IsNot Nothing AndAlso Source = ResourcePlatforms.CurseForge Then
|
||||
Candidates.Add(Result.Item.CurseForgeSlug.Replace("-", " ").Replace("/", " "))
|
||||
If Result.Item.Slugs.ContainsKey(Source) Then
|
||||
Candidates.Add(Result.Item.Slugs(Source).
|
||||
Replace("-", " ").Replace("/", " "))
|
||||
End If
|
||||
If Result.Item.ModrinthSlug IsNot Nothing AndAlso Source = ResourcePlatforms.Modrinth Then
|
||||
Candidates.Add(Result.Item.ModrinthSlug.Replace("-", " ").Replace("/", " "))
|
||||
If Result.Item.ChineseName IsNot Nothing Then
|
||||
Candidates.Add(Result.Item.ChineseName.
|
||||
AfterLast(" (").TrimEnd(") ").BeforeFirst(" - ").Replace("-", " ").Replace("/", " ").Replace(":", " ").Replace("(", " ").Replace(")", ""))
|
||||
End If
|
||||
Candidates.Add(Result.Item.ChineseName.AfterLast(" (").TrimEnd(") ").BeforeFirst(" - ").
|
||||
Replace("-", " ").Replace("/", " ").Replace(":", " ").Replace("(", " ").Replace(")", ""))
|
||||
'分词、清洗、去重
|
||||
Candidates = Candidates.
|
||||
SelectMany(Function(c) c.Split(" ")).
|
||||
@@ -220,6 +220,7 @@
|
||||
If w.Length <= 1 Then Return False '单字或空白
|
||||
If {"the", "of", "mod", "and", "forge", "fabric", "for", "quilt", "neoforge"}.Contains(w) Then Return False '常见词
|
||||
If Val(w) > 0 Then Return False '数字
|
||||
If Not w.IsAsciiOnly Then Return False '非纯英文
|
||||
Return True
|
||||
End Function).Distinct.ToList
|
||||
'如果一个词可以由其他词拼成,则去掉这个词(例如将 ender io enderio 的 enderio 剔除,只保留 ender io)
|
||||
@@ -228,17 +229,26 @@
|
||||
Candidates = Candidates.Where(Function(w) Not Candidates.Any(Function(c) c.Length < w.Length AndAlso w.StartsWith(c) AndAlso CanForm(w.Substring(c.Length)))).ToList()
|
||||
Return Candidates
|
||||
End Function
|
||||
Dim GetSearchEntries =
|
||||
Iterator Function(Source As ResourcePlatforms)
|
||||
For Each Entry In WikiEntry.All.Value
|
||||
If Not Entry.Slugs.ContainsKey(Source) Then Continue For
|
||||
If Entry.ChineseName IsNot Nothing Then
|
||||
Yield New SearchEntry(Of WikiEntry) With {.Item = Entry, .SearchSource = New List(Of SearchSource) From {
|
||||
New SearchSource(StrConv(Entry.ChineseName.BeforeFirst(" ("), VbStrConv.SimplifiedChinese).Split("/"c, True), 1), '部分 Mod 有别名
|
||||
New SearchSource(StrConv(Entry.ChineseName.AfterFirst(" ("), VbStrConv.SimplifiedChinese) & Entry.Slugs(Source), 0.5)
|
||||
}}
|
||||
Else
|
||||
Yield New SearchEntry(Of WikiEntry) With {.Item = Entry, .SearchSource = New List(Of SearchSource) From {
|
||||
New SearchSource(Entry.Slugs(Source), 0.5)
|
||||
}}
|
||||
End If
|
||||
Next
|
||||
End Function
|
||||
'CurseForge
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) Then
|
||||
'数据库搜索
|
||||
Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) =
|
||||
WikiEntry.All.Where(Function(Entry) Entry.CurseForgeSlug IsNot Nothing).Select(
|
||||
Function(Entry) New SearchEntry(Of WikiEntry) With {
|
||||
.Item = Entry,
|
||||
.SearchSource = New List(Of SearchSource) From {
|
||||
New SearchSource(Entry.ChineseName.BeforeFirst(" (").Split("/"c, True), 1), '部分 Mod 有别名
|
||||
New SearchSource(Entry.ChineseName.AfterFirst(" (") & Entry.CurseForgeSlug, 0.5)}
|
||||
}).ToList
|
||||
Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.CurseForge).ToList()
|
||||
Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
|
||||
If CurseForgeSearchResults.Any Then
|
||||
'选取目标(CurseForge 要求每个词都必须匹配上,所以只能选择一个 Mod 进行搜索)
|
||||
@@ -255,14 +265,7 @@
|
||||
'Modrinth
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) Then
|
||||
'数据库搜索
|
||||
Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) =
|
||||
WikiEntry.All.Where(Function(Entry) Entry.ModrinthSlug IsNot Nothing).Select(
|
||||
Function(Entry) New SearchEntry(Of WikiEntry) With {
|
||||
.Item = Entry,
|
||||
.SearchSource = New List(Of SearchSource) From {
|
||||
New SearchSource(Entry.ChineseName.BeforeFirst(" (").Split("/"c, True), 1), '部分 Mod 有别名
|
||||
New SearchSource(Entry.ChineseName.AfterFirst(" (") & Entry.ModrinthSlug, 0.5)}
|
||||
}).ToList
|
||||
Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.Modrinth).ToList()
|
||||
Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
|
||||
If ModrinthSearchResults.Any Then
|
||||
'分词
|
||||
@@ -277,7 +280,7 @@
|
||||
ModrinthAltSearchText = WordWeights.MaxBy(Function(w) w.Value).Key
|
||||
Logger.Warn($"中文搜索关键词(Modrinth):{ModrinthAltSearchText}")
|
||||
'直接请求工程
|
||||
ModrinthSlugs = ModrinthSearchResults.Take(100).Select(Function(r) r.Item.ModrinthSlug).ToList
|
||||
ModrinthSlugs = ModrinthSearchResults.Take(100).Select(Function(r) r.Item.Slugs(ResourcePlatforms.Modrinth)).ToList
|
||||
End If
|
||||
End If
|
||||
'结束
|
||||
@@ -291,16 +294,17 @@
|
||||
|
||||
Dim RealResults As New List(Of ResourceProject)
|
||||
NextPage:
|
||||
Dim RawResults As New ConcurrentList(Of ResourceProject)
|
||||
Dim RawResults As New List(Of ResourceProject)
|
||||
|
||||
#Region "从 CurseForge 和 Modrinth 获取结果列表,存储于 RawResults"
|
||||
|
||||
Dim WorkThreads As New List(Of Thread)
|
||||
Dim Errors As New ConcurrentList(Of (Ex As Exception, Source As ResourcePlatforms))
|
||||
Dim Errors As New ConcurrentBag(Of (Ex As Exception, Source As ResourcePlatforms))
|
||||
|
||||
'在 1.14-,部分老 Mod 没有设置支持的加载器,因此添加 Forge 筛选就会出现遗漏
|
||||
'所以,在发起请求时不筛选加载器,然后在返回的结果中自行筛除不是 Forge 的 Mod
|
||||
Dim IgnoreModLoaderFilter = Request.ModLoaders = ModLoaders.Forge AndAlso McVersion.VersionToDrop(Request.GameVersion) < 140
|
||||
Dim SearchResults As New ConcurrentBag(Of ResourceProject)
|
||||
Try
|
||||
|
||||
'CurseForge 搜索
|
||||
@@ -322,7 +326,7 @@ NextPage:
|
||||
ProjectList.Add(Project)
|
||||
Next
|
||||
'更新结果
|
||||
ProjectList.ForEach(Sub(p) RawResults.Add(p))
|
||||
ProjectList.ForEach(Sub(p) SearchResults.Add(p))
|
||||
Storage.CurseForgeOffset += RequestResult("data").Count
|
||||
Storage.CurseForgeTotal = RequestResult("pagination")("totalCount").ToObject(Of Integer)
|
||||
Logger.Info($"从 CurseForge 搜索到了 {ProjectList.Count} 个工程(总计已获取 {Storage.CurseForgeOffset} 个,共 {Storage.CurseForgeTotal} 个)")
|
||||
@@ -350,7 +354,7 @@ NextPage:
|
||||
ProjectList.Add(New ResourceProject(JsonEntry))
|
||||
Next
|
||||
'更新结果
|
||||
ProjectList.ForEach(Sub(p) RawResults.Add(p))
|
||||
ProjectList.ForEach(Sub(p) SearchResults.Add(p))
|
||||
Storage.ModrinthOffset += RequestResult("hits").Count
|
||||
Storage.ModrinthTotal = RequestResult("total_hits").ToObject(Of Integer)
|
||||
Logger.Info($"从 Modrinth 搜索到了 {ProjectList.Count} 个工程(总计已获取 {Storage.ModrinthOffset} 个,共 {Storage.ModrinthTotal} 个)")
|
||||
@@ -386,7 +390,7 @@ NextPage:
|
||||
ProjectList.Add(Project)
|
||||
Next
|
||||
'更新结果
|
||||
ProjectList.ForEach(Sub(p) RawResults.Add(p))
|
||||
ProjectList.ForEach(Sub(p) SearchResults.Add(p))
|
||||
Logger.Info($"从 Modrinth 直接获取到了 {ProjectList.Count} 个工程")
|
||||
ModrinthSlugs.Clear() '防止重试/加载下一页时重复获取
|
||||
Catch ex As Exception
|
||||
@@ -400,8 +404,9 @@ NextPage:
|
||||
'等待线程结束
|
||||
For Each Thread In WorkThreads
|
||||
Thread.Join()
|
||||
If Task.IsInterrupted Then Return '会自动触发 Finally 以清理线程
|
||||
If Task.IsCanceled Then Return '会自动触发 Finally 以清理线程
|
||||
Next
|
||||
RawResults = New List(Of ResourceProject)(SearchResults)
|
||||
|
||||
'仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
|
||||
If IgnoreModLoaderFilter Then
|
||||
@@ -510,7 +515,7 @@ NextPage:
|
||||
Next
|
||||
End If
|
||||
'根据排序分得出结果并添加
|
||||
If Task.IsInterrupted Then Throw New ThreadInterruptedException '#8246
|
||||
If Task.IsCanceled Then Throw New OperationCanceledException '#8246
|
||||
Storage.Results.AddRange(
|
||||
Scores.OrderByDescending(Function(s) s.Value).Select(Function(r) r.Key))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
''' <summary>
|
||||
''' <summary>
|
||||
''' 社区资源的单个可下载版本。
|
||||
''' </summary>
|
||||
Public Class ResourceVersion
|
||||
@@ -132,7 +132,7 @@ Public Class ResourceVersion
|
||||
'DownloadAddress
|
||||
Dim Url = Data("downloadUrl").ToString
|
||||
If Url = "" Then Url = $"https://edge.forgecdn.net/files/{CInt(.Id.ToString.Substring(0, 4))}/{CInt(.Id.ToString.Substring(4))}/{ .FileName}"
|
||||
.DownloadUrls = ParseCurseForgeDownloadUrls(Url.Replace(.FileName, EscapeUtils.UrlEscape(.FileName))) '对脑残 CurseForge 的下载地址进行多种修正
|
||||
.DownloadUrls = ParseCurseForgeDownloadUrls(Url.Replace(.FileName, StringUtils.UrlEscape(.FileName))) '对脑残 CurseForge 的下载地址进行多种修正
|
||||
.DownloadUrls.Add(DlSourceModGet(Url)) '添加镜像源;注意 MCIM 源不支持 URL 编码后的文件名,必须传入 URL 编码前的文件名
|
||||
.DownloadUrls = .DownloadUrls.Distinct.ToList '最终去重
|
||||
'Dependencies
|
||||
@@ -178,21 +178,20 @@ Public Class ResourceVersion
|
||||
.Size = File("size")
|
||||
.Hash = File("hashes")("sha1")
|
||||
End If
|
||||
'类别
|
||||
'类别与 Loaders
|
||||
'结果可能混杂着 Mod、数据包和服务端插件
|
||||
Dim RawLoaders As List(Of String) = Data("loaders").Select(Function(v) v.ToString).ToList
|
||||
.ModLoaders = ModLoaders.None
|
||||
If .ResourceType.HasFlag(ResourceTypes.Mod) OrElse .ResourceType.HasFlag(ResourceTypes.DataPack) Then
|
||||
If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then .ResourceType = ResourceTypes.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
|
||||
If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
|
||||
If EnumUtils.GetAllFlags(Of ModLoaders)().Any() Then .ResourceType = ResourceTypes.Mod
|
||||
Else
|
||||
'使用传入的类别,不作修改(#8377)
|
||||
End If
|
||||
'Loaders
|
||||
For Each Loader As ModLoaders In EnumUtils.GetAllFlags(Of ModLoaders)()
|
||||
If RawLoaders.Contains(Loader.ToString.Lower) Then .ModLoaders = .ModLoaders Or Loader
|
||||
Next
|
||||
If .ResourceType.HasFlag(ResourceTypes.Mod) OrElse .ResourceType.HasFlag(ResourceTypes.DataPack) Then
|
||||
If RawLoaders.Intersect({"bukkit", "folia", "paper", "purpur", "spigot"}).Any() Then .ResourceType = ResourceTypes.Plugin 'Veinminer Enchantment 同时支持服务端与 Fabric
|
||||
If RawLoaders.Contains("datapack") Then .ResourceType = ResourceTypes.DataPack
|
||||
If .ModLoaders.Flags().Any() Then .ResourceType = ResourceTypes.Mod
|
||||
Else
|
||||
'使用传入的类别,不作修改(#8377)
|
||||
End If
|
||||
'Dependencies
|
||||
If Data.ContainsKey("dependencies") Then
|
||||
.RawDependencies = Data("dependencies").
|
||||
@@ -256,7 +255,7 @@ Public Class ResourceVersion
|
||||
Else 'Modrinth
|
||||
ResultJsonArray = DlModRequest($"https://api.modrinth.com/v2/project/{ProjectId}/version?include_changelog=false")
|
||||
End If
|
||||
ProjectFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) ResourceVersion.FromPlatformJson(a, TargetProject.Types)).
|
||||
ProjectFilesCache(ProjectId) = ResultJsonArray.Select(Function(a) FromPlatformJson(a, TargetProject.Types)).
|
||||
Where(Function(a) a.DownloadAvailable).
|
||||
DistinctBy(Function(a) a.Id).ToList 'CurseForge 可能会重复返回相同项(#1330)
|
||||
End If
|
||||
@@ -313,13 +312,13 @@ Public Class ResourceVersion
|
||||
Public Function ToCacheJson() As JObject
|
||||
Dim Result As New JObject
|
||||
With Result
|
||||
.Add("ResourceType", ResourceType)
|
||||
.Add("ResourceType", CInt(ResourceType))
|
||||
.Add("Id", Id)
|
||||
If Version IsNot Nothing Then .Add("Version", Version)
|
||||
.Add("DisplayName", Display)
|
||||
.Add("ReleaseDate", ReleaseDate)
|
||||
.Add("DownloadCount", DownloadCount)
|
||||
.Add("ModLoaders", ModLoaders)
|
||||
.Add("ModLoaders", CInt(ModLoaders))
|
||||
.Add("GameVersions", New JArray(GameVersions))
|
||||
.Add("ReleaseType", CInt(ReleaseType))
|
||||
If FileName IsNot Nothing Then .Add("FileName", FileName)
|
||||
@@ -359,17 +358,14 @@ Public Class ResourceVersion
|
||||
If .Title <> FileName.BeforeLast(".") Then Yield FileName.BeforeLast(".")
|
||||
If Dependencies.Any Then Yield $"{Dependencies.Count} 项前置"
|
||||
If GameVersions.All(Function(v) Not v.Contains(".") OrElse {"w", "snapshot", "rc", "pre", "experimental", "-"}.Any(Function(s) v.ContainsIgnoreCase(s))) Then Yield $"游戏版本 {GameVersions.Join("、"c)}"
|
||||
If DownloadCount > 0 Then Yield $"下载 {If(DownloadCount > 100000, Math.Round(DownloadCount / 10000) & " 万", DownloadCount & " ")}次" 'CurseForge 的下载次数经常错误地返回 0
|
||||
Yield $"更新于 {GetTimeSpanString(ReleaseDate - Date.Now, False)}"
|
||||
If DownloadCount > 0 Then Yield $"下载 {If(DownloadCount > 100000, Math.Round(DownloadCount / 10000) & " 万", DownloadCount.ToString() + " ")}次" 'CurseForge 的下载次数经常错误地返回 0
|
||||
Yield $"更新于 {StringUtils.FormatTimeSpan(ReleaseDate - Date.Now, False)}"
|
||||
If ReleaseType <> ReleaseTypes.Release Then Yield ReleaseTypeDisplay
|
||||
End Function().Join(","c)
|
||||
|
||||
'另存为按钮
|
||||
If SaveAsButtonHandler IsNot Nothing Then
|
||||
Dim BtnSave As New MyIconButton With {.Logo = Logo.IconButtonSave, .ToolTip = "另存为"}
|
||||
ToolTipService.SetPlacement(BtnSave, Primitives.PlacementMode.Center)
|
||||
ToolTipService.SetVerticalOffset(BtnSave, 30)
|
||||
ToolTipService.SetHorizontalOffset(BtnSave, 2)
|
||||
AddHandler BtnSave.Click, SaveAsButtonHandler
|
||||
.Buttons = {BtnSave}
|
||||
End If
|
||||
@@ -383,17 +379,18 @@ Public Class ResourceVersion
|
||||
''' </summary>
|
||||
''' <param name="LocalFileOrFolder">目标本地文件夹,或完整的文件路径。会自动判断类型。</param>
|
||||
Public Function ToNetFile(LocalFileOrFolder As String,
|
||||
Optional TrackDownloadReason As DownloadReason? = Nothing, Optional TrackGameVersion As String = Nothing, Optional TrackLoader As ModLoaders = ModLoaders.None) As NetFile
|
||||
Optional TrackDownloadReason As DownloadReason? = Nothing, Optional TrackGameVersion As String = Nothing, Optional TrackLoader As ModLoaders = ModLoaders.None) As NetFile
|
||||
Return New NetFile(
|
||||
ParseModrinthTrackArguments(DownloadUrls, TrackDownloadReason, TrackGameVersion, TrackLoader),
|
||||
LocalFileOrFolder & If(LocalFileOrFolder.EndsWithF("\"), FileName, ""),
|
||||
New FileChecker(Hash:=Hash, ActualSize:=Size),
|
||||
New FileChecker With {.Hash = Hash, .ActualSize = Size},
|
||||
SimulateBrowserHeaders:=True)
|
||||
End Function
|
||||
''' <summary>
|
||||
''' 添加 Modrinth 追踪参数。
|
||||
''' </summary>
|
||||
Public Shared Iterator Function ParseModrinthTrackArguments(Urls As List(Of String), Optional TrackDownloadReason As DownloadReason? = Nothing, Optional TrackGameVersion As String = Nothing, Optional TrackLoader As ModLoaders = ModLoaders.None) As IEnumerable(Of String)
|
||||
Public Shared Iterator Function ParseModrinthTrackArguments(Urls As List(Of String),
|
||||
Optional TrackDownloadReason As DownloadReason? = Nothing, Optional TrackGameVersion As String = Nothing, Optional TrackLoader As ModLoaders = ModLoaders.None) As IEnumerable(Of String)
|
||||
Dim Arguments As New Dictionary(Of String, String)
|
||||
If TrackDownloadReason IsNot Nothing Then Arguments.Add("mr_download_reason", TrackDownloadReason.ToString.Lower)
|
||||
If TrackGameVersion IsNot Nothing Then Arguments.Add("mr_game_version", TrackGameVersion)
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
'获取所有条目:WikiEntry.All
|
||||
|
||||
''' <summary>
|
||||
''' MC 百科条目。
|
||||
''' </summary>
|
||||
Public Class WikiEntry
|
||||
|
||||
'属性
|
||||
|
||||
''' <summary>
|
||||
''' 在 MC 百科中的对应 ID。
|
||||
''' </summary>
|
||||
Public Id As Integer
|
||||
''' <summary>
|
||||
''' 中文译名。空字符串代表没有翻译。
|
||||
''' </summary>
|
||||
Public ChineseName As String = ""
|
||||
''' <summary>
|
||||
''' CurseForge Slug(例如 advanced-solar-panels)。
|
||||
''' </summary>
|
||||
Public CurseForgeSlug As String = Nothing
|
||||
''' <summary>
|
||||
''' Modrinth Slug(例如 advanced-solar-panels)。
|
||||
''' </summary>
|
||||
Public ModrinthSlug As String = Nothing
|
||||
''' <summary>
|
||||
''' MC 百科的浏览量逆序排行,1 代表浏览量最低。
|
||||
''' </summary>
|
||||
Public Popularity As Integer
|
||||
Public Overrides Function ToString() As String
|
||||
Return If(CurseForgeSlug, "") & "&" & If(ModrinthSlug, "") & "|" & Id & "|" & ChineseName & ", Rank " & Popularity
|
||||
End Function
|
||||
|
||||
'读取
|
||||
|
||||
''' <summary>
|
||||
''' 内置数据库中的所有 MC 百科条目。
|
||||
''' </summary>
|
||||
Public Shared ReadOnly Property All As List(Of WikiEntry)
|
||||
Get
|
||||
Static Cache As List(Of WikiEntry) = Nothing
|
||||
If Cache IsNot Nothing Then Return Cache
|
||||
'实际加载
|
||||
Cache = New List(Of WikiEntry)
|
||||
Dim i As Integer = 0
|
||||
Dim DataLines As List(Of String) = DirectCast(My.Resources.ResourceManager.GetObject("ModData"), String).SplitLines().ToList()
|
||||
Dim Ranks = DataLines.Last.Split("|")
|
||||
DataLines.RemoveAt(DataLines.Count - 1)
|
||||
For Each Line In DataLines
|
||||
i += 1
|
||||
If Line = "" Then Continue For
|
||||
For Each EntryData As String In Line.Split("¨")
|
||||
Dim Entry = New WikiEntry
|
||||
Dim Splited = EntryData.Split("|")
|
||||
If Splited(0).StartsWithF("@") Then
|
||||
Entry.CurseForgeSlug = Nothing
|
||||
Entry.ModrinthSlug = Splited(0).Replace("@", "")
|
||||
ElseIf Splited(0).EndsWithF("@") Then
|
||||
Entry.CurseForgeSlug = Splited(0).TrimEnd("@")
|
||||
Entry.ModrinthSlug = Entry.CurseForgeSlug
|
||||
ElseIf Splited(0).Contains("@") Then
|
||||
Entry.CurseForgeSlug = Splited(0).Split("@")(0)
|
||||
Entry.ModrinthSlug = Splited(0).Split("@")(1)
|
||||
Else
|
||||
Entry.CurseForgeSlug = Splited(0)
|
||||
Entry.ModrinthSlug = Nothing
|
||||
End If
|
||||
Entry.Id = i
|
||||
Entry.Popularity = Val(Ranks(i - 1))
|
||||
If Splited.Count >= 2 Then
|
||||
Entry.ChineseName = Splited.Last
|
||||
If Entry.ChineseName.Contains("*") Then '处理 *
|
||||
Entry.ChineseName = Entry.ChineseName.Replace("*",
|
||||
$" ({If(Entry.CurseForgeSlug, Entry.ModrinthSlug).Replace("-", " ").Capitalize})")
|
||||
End If
|
||||
End If
|
||||
Cache.Add(Entry)
|
||||
Next
|
||||
Next
|
||||
Return Cache
|
||||
End Get
|
||||
End Property
|
||||
|
||||
End Class
|
||||
Reference in New Issue
Block a user