2.13.1.1
This commit is contained in:
+1
-1
Submodule MeloongCore updated: 8bfb4f709f...811438aa3c
+1
-1
@@ -68,7 +68,7 @@ public static class Versions {
|
||||
/// <summary>
|
||||
/// 版本管理中 Mod 信息缓存的版本号。
|
||||
/// </summary>
|
||||
public const int LocalModCacheVersion = 18;
|
||||
public const int LocalModCacheVersion = 19;
|
||||
/// <summary>
|
||||
/// Minecraft 本地实例信息缓存的版本号。
|
||||
/// </summary>
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ public static class JavaUtils {
|
||||
static bool HasReparsePoint(FileSystemInfo info) {
|
||||
if (PathUtils.IsParentOf(Paths.AppData + @".minecraft\runtime\", info.FullName)) return false; // 不筛查 PCL 的下载路径(#8928)
|
||||
do {
|
||||
if (info.Attributes.HasFlag(FileAttributes.ReparsePoint)) return true;
|
||||
if (info.Attributes.HasFlagF(FileAttributes.ReparsePoint)) return true;
|
||||
info = info is FileInfo fileInfo ? fileInfo.Directory : ((DirectoryInfo) info).Parent;
|
||||
} while (info is not null);
|
||||
return false;
|
||||
|
||||
@@ -11,11 +11,11 @@ Public Class FormMain
|
||||
Dim FeatureList As New List(Of KeyValuePair(Of Integer, String))
|
||||
'统计更新日志条目
|
||||
If BuildType = BuildTypes.Release Then
|
||||
If LastVersion < 408 Then 'Release 2.13.1.0
|
||||
If LastVersion < 408 Then 'Release 2.13.1.1
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
|
||||
FeatureCount += 21
|
||||
BugCount += 22
|
||||
FeatureCount += 23
|
||||
BugCount += 28
|
||||
End If
|
||||
If LastVersion < 406 Then 'Release 2.13.0.1
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(4, "新增:重做 Java 管理与相关设置,允许调整 Java 优先级、指定 Java 版本范围等"))
|
||||
@@ -93,6 +93,11 @@ Public Class FormMain
|
||||
'3:BUG+ IMP* FEAT-
|
||||
'2:BUG* IMP-
|
||||
'1:BUG-
|
||||
If LastVersion < 410 Then 'Snapshot 2.13.1.1
|
||||
If LastVersion = 409 Then FeatureList.Add(New KeyValuePair(Of Integer, String)(1, "修复:更新 Mod 时可能更新成另一个 Mod 加载器的版本"))
|
||||
FeatureCount += 2
|
||||
BugCount += 6
|
||||
End If
|
||||
If LastVersion < 409 Then 'Snapshot 2.13.1.0
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:版本设置中,添加打开截图文件夹的快捷方式"))
|
||||
FeatureList.Add(New KeyValuePair(Of Integer, String)(2, "优化:导出整合包时,可以勾选导出光影设置文件"))
|
||||
@@ -781,7 +786,7 @@ Public Class FormMain
|
||||
If PanMsg.Children.Any Then
|
||||
If e.Key = Key.Enter Then
|
||||
CType(PanMsg.Children(0), Object).Btn1_Click()
|
||||
ElseIf e.Key = Key.Escape Then
|
||||
ElseIf e.Key = Key.Escape OrElse (e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) Then
|
||||
Dim Msg As Object = PanMsg.Children(0)
|
||||
If TypeOf Msg IsNot MyMsgInput AndAlso TypeOf Msg IsNot MyMsgSelect AndAlso Msg.Btn3.Visibility = Visibility.Visible Then
|
||||
Msg.Btn3_Click()
|
||||
@@ -823,6 +828,11 @@ Public Class FormMain
|
||||
If TypeOf PageRight Is IRefreshable Then CType(PageRight, IRefreshable).Refresh()
|
||||
Return
|
||||
End If
|
||||
'Ctrl + w 关闭程序
|
||||
If e.Key = Key.W AndAlso Keyboard.Modifiers.HasFlag(ModifierKeys.Control) Then
|
||||
EndProgram(True)
|
||||
Return
|
||||
End If
|
||||
'调用启动游戏
|
||||
If e.Key = Key.Enter AndAlso PageCurrent = FormMain.PageType.Launch Then
|
||||
If IsAprilEnabled AndAlso Not IsAprilGiveup Then
|
||||
|
||||
@@ -6,12 +6,12 @@ Public Module ModBase
|
||||
#Region "声明"
|
||||
|
||||
'下列版本信息由更新器自动修改
|
||||
Public Const VersionBaseName As String = "2.13.1.0" '显示用版本名
|
||||
Public Const VersionBaseName As String = "2.13.1.1" '显示用版本名
|
||||
Public Const CommitHash As String = "" 'Commit Hash,由 GitHub Workflow 自动替换
|
||||
#If RELEASE Then
|
||||
Public Const VersionCode As Integer = 408 '正式版
|
||||
#Else
|
||||
Public Const VersionCode As Integer = 409 '快照版
|
||||
Public Const VersionCode As Integer = 410 '快照版
|
||||
#End If
|
||||
|
||||
'版本信息
|
||||
|
||||
@@ -217,8 +217,7 @@ Retry:
|
||||
Optional Content As Object = Nothing, Optional Headers As Dictionary(Of String, String) = Nothing,
|
||||
Optional SimulateBrowserHeaders As Boolean = False, Optional Timeout As Integer = 25000, Optional Encoding As Encoding = Nothing) As Byte()
|
||||
If RunInUi() AndAlso Not Url.Contains("//127.") Then Throw New Exception("在 UI 线程执行了网络请求")
|
||||
Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing,
|
||||
Response As HttpResponseMessage = Nothing, HostIp As String = Nothing
|
||||
Dim Request As HttpRequestMessage = Nothing, CancelToken As CancellationTokenSource = Nothing, Response As HttpResponseMessage = Nothing
|
||||
Try
|
||||
'构建 RequestClient
|
||||
Directory.CreateDirectory(PathTemp & "Cache\Http\")
|
||||
@@ -255,8 +254,6 @@ Retry:
|
||||
Retrier.Attempt(delay:=Function() TimeSpan.FromMilliseconds(200), isRetryAllowed:=Function(ex) TypeOf ex Is FileNotFoundException OrElse TypeOf ex Is IOException, action:=
|
||||
Sub(Attempt)
|
||||
CancelToken?.Dispose() : CancelToken = New CancellationTokenSource(Timeout)
|
||||
HostIp = DNSLookup(Request, CancelToken.Token)
|
||||
If HostIp IsNot Nothing Then IPReliability.GetOrAdd(HostIp, -0.01) '预先降低一点,这样快速的重复请求会使用不同的 IP 以提高成功率
|
||||
Dim ClonedRequest As HttpRequestMessage = Request.Clone() 'HttpRequestMessage 只能发送一次,重试时需要克隆一个新的
|
||||
Try
|
||||
Response = RequestClient.SendAsync(ClonedRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
|
||||
@@ -274,26 +271,22 @@ Retry:
|
||||
End Using
|
||||
End Using
|
||||
If Not Response.IsSuccessStatusCode Then
|
||||
RecordIPReliability(HostIp, -0.7)
|
||||
Dim ResponseMessage = ResponseBytes.GetString(Encoding)
|
||||
Throw New HttpRequestCodeException(
|
||||
$"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url},{HostIp}" &
|
||||
$"错误码 {Response.StatusCode} ({CInt(Response.StatusCode)}),{Method},{Url}" &
|
||||
If(String.IsNullOrEmpty(ResponseMessage), "", vbCrLf & ResponseMessage), Response.StatusCode, ResponseMessage)
|
||||
End If
|
||||
'输出
|
||||
RecordIPReliability(HostIp, 0.5)
|
||||
Return ResponseBytes
|
||||
Catch ex As HttpRequestException
|
||||
Throw
|
||||
Catch ex As TimeoutException
|
||||
Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
|
||||
Throw New WebException($"连接服务器超时,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
|
||||
Catch ex As Exception
|
||||
If TypeOf ex IsNot TaskCanceledException Then ex.ThrowIfCanceled() 'GetResultWithTimeout 会抛出 TaskCanceledException
|
||||
RecordIPReliability(HostIp, -1)
|
||||
If ex.IsBadNetwork Then
|
||||
Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url},IP:{HostIp})", WebExceptionStatus.Timeout)
|
||||
Throw New WebException($"网络请求失败,请稍后再试,或使用 VPN 改善网络环境({Method}, {Url})", WebExceptionStatus.Timeout)
|
||||
Else
|
||||
Throw New Exception($"网络请求出现意外异常({Method}, {Url},{HostIp})", ex)
|
||||
Throw New Exception($"网络请求出现意外异常({Method}, {Url})", ex)
|
||||
End If
|
||||
Finally
|
||||
Request?.Dispose()
|
||||
@@ -304,72 +297,6 @@ Retry:
|
||||
Private RequestClient As HttpClient = Nothing
|
||||
Private RequestClientLock As New Object
|
||||
|
||||
''' <summary>
|
||||
''' 进行 DNS 解析。它仅在选择的 IP 与系统默认的不一致时才对 URL 中的 Host 进行替换。
|
||||
''' 返回请求时应使用的 IP。若解析失败,则返回 Nothing。
|
||||
''' </summary>
|
||||
Private Function DNSLookup(Request As HttpRequestMessage, CancelToken As CancellationToken) As String
|
||||
Dim Host = Request.RequestUri.Host
|
||||
'不对部分有严格 SNI 限制的域名进行 DNS 解析(#8295)
|
||||
If {"mojang.com", "minecraft.net", "minecraftservices.com"}.Any(Function(h) Host.ContainsIgnoreCase(h)) Then Return Nothing
|
||||
'若一分钟内已失败过,则不再重复尝试解析,以减少断网时的 Exception 数量
|
||||
Dim LastFailureTime As Date
|
||||
If DNSFailureRecord.TryGetValue(Host, LastFailureTime) Then
|
||||
If Date.Now - LastFailureTime < New TimeSpan(0, 1, 0) Then
|
||||
Return Nothing
|
||||
Else
|
||||
DNSFailureRecord.Remove(Host)
|
||||
End If
|
||||
End If
|
||||
'初步 DNS 解析
|
||||
Dim Candidates As IPAddress()
|
||||
Try
|
||||
Dim AsyncResult = Dns.BeginGetHostAddresses(Host, Nothing, Nothing)
|
||||
If WaitHandle.WaitAny(New WaitHandle() {AsyncResult.AsyncWaitHandle, CancelToken.WaitHandle}) Then Throw New TimeoutException("DNS 解析超时")
|
||||
Candidates = Dns.EndGetHostAddresses(AsyncResult).Distinct.ToArray
|
||||
Catch ex As Exception
|
||||
Logger.Warn(ex, $"DNS 解析失败({Host})")
|
||||
DNSFailureRecord(Host) = Date.Now
|
||||
Return Nothing
|
||||
End Try
|
||||
If Not Candidates.Any Then
|
||||
Logger.Info($"DNS 解析无结果({Host})")
|
||||
Return Nothing
|
||||
End If
|
||||
'若同时存在 IPv4 和 IPv6 地址,仅选择其中一类(因为 GFW 可能只屏蔽了 IPv4 或 IPv6)
|
||||
Dim Reliabilities = Candidates.ToDictionary(Function(i) i, Function(i) IPReliability.GetOrDefault(i.ToString, 0))
|
||||
Dim IPv4Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetwork).ToArray
|
||||
Dim IPv6Targets = Candidates.Where(Function(i) i.AddressFamily = AddressFamily.InterNetworkV6).ToArray
|
||||
If IPv4Targets.Any AndAlso IPv6Targets.Any Then
|
||||
Dim IPv4Reliability = IPv4Targets.Max(Function(i) Reliabilities(i))
|
||||
Dim IPv6Reliability = IPv6Targets.Max(Function(i) Reliabilities(i))
|
||||
If Host = "api.modrinth.com" Then IPv6Reliability -= 0.1 '让 Modrinth 优先使用 IPv4 地址(#6887)
|
||||
Logger.Trace(Function() $"DNS IPv4/IPv6 选择({Host}),IPv4 {IPv4Reliability:0.000},IPv6 {IPv6Reliability:0.000}")
|
||||
Candidates = If(IPv4Reliability >= IPv6Reliability, IPv4Targets, IPv6Targets)
|
||||
End If
|
||||
'选择可靠度最高的 IP
|
||||
Dim Target As IPAddress = Candidates.MaxBy(Function(i) Reliabilities(i))
|
||||
Request.Headers.Host = Request.RequestUri.Authority
|
||||
Request.RequestUri = (New UriBuilder(Request.RequestUri) With {.Host = If(Target.AddressFamily = AddressFamily.InterNetworkV6, $"[{Target}]", Target.ToString)}).Uri
|
||||
Return Target.ToString
|
||||
End Function
|
||||
Private DNSFailureRecord As New ConcurrentDictionary(Of String, Date)
|
||||
''' <summary>
|
||||
''' 记录每个 IP 地址的请求可靠度。
|
||||
''' 通常取值 -1 ~ +0.5,越高越好。未尝试过的 IP 应视为 0。
|
||||
''' </summary>
|
||||
Private IPReliability As New ConcurrentDictionary(Of String, Double)
|
||||
''' <summary>
|
||||
''' 根据请求结果,记录 IP 地址的可靠度。
|
||||
''' </summary>
|
||||
Private Sub RecordIPReliability(IP As String, Result As Double)
|
||||
If IP Is Nothing Then Return
|
||||
IPReliability.AddOrUpdate(IP,
|
||||
Function() Result * 0.5,
|
||||
Function(k, v) v * 0.5 + Result * 0.5
|
||||
)
|
||||
End Sub
|
||||
|
||||
#End Region
|
||||
|
||||
#Region "多线程下载引擎"
|
||||
@@ -976,7 +903,7 @@ StartThread:
|
||||
Logger.Log($"{LocalName}:开始,起始点 {Th.DownloadStart},{Th.Source.Url}", If(Th.DownloadStart = 0, LogLevel.Info, LogLevel.Trace))
|
||||
Dim ResultStream As Stream = Nothing, HttpRequest As HttpRequestMessage = Nothing,
|
||||
Response As HttpResponseMessage = Nothing, ResponseStream As Stream = Nothing,
|
||||
CancelToken As CancellationTokenSource = Nothing, HostIp As String = Nothing
|
||||
CancelToken As CancellationTokenSource = Nothing
|
||||
'部分下载源真的特别慢,并且只需要一个请求,例如 Ping 为 20s,如果增长太慢,就会造成类似 2.5s 5s 7.5s 10s 12.5s... 的极大延迟
|
||||
'延迟过长会导致某些特别慢的链接迟迟不被掐死
|
||||
Dim Timeout As Integer = Math.Min(Math.Max(ConnectAverage, 15000) * (1 + Th.Source.FailCount), 30000)
|
||||
@@ -987,7 +914,6 @@ StartThread:
|
||||
HttpRequest = New HttpRequestMessage(HttpMethod.Get, Th.Source.Url)
|
||||
SecretHeadersSign(Th.Source.Url, HttpRequest, SimulateBrowserHeaders)
|
||||
CancelToken = New CancellationTokenSource(Timeout)
|
||||
HostIp = DNSLookup(HttpRequest, CancelToken.Token) 'DNS 预解析
|
||||
If Not Th.IsFirstThread Then HttpRequest.Headers.Range = New Headers.RangeHeaderValue(Th.DownloadStart, Nothing)
|
||||
Dim ContentLength As Long = 0
|
||||
Response = ThreadClient.SendAsync(HttpRequest, HttpCompletionOption.ResponseHeadersRead, CancelToken.Token).GetResultWithTimeout(CancelToken, Timeout)
|
||||
@@ -1119,15 +1045,13 @@ SourceBreak:
|
||||
'本线程完成
|
||||
Th.State = NetState.Finished
|
||||
Logger.Trace(Function() $"{LocalName}:完成,已下载 {Th.DownloadDone}({Th.DownloadStart}~{Th.DownloadEnd})")
|
||||
RecordIPReliability(HostIp, 0.5)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
If ex.IsBadNetwork OrElse TypeOf ex Is TaskCanceledException Then
|
||||
Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message},IP:{If(String.IsNullOrEmpty(HostIp), "自动", "")}"))
|
||||
Logger.Info(New TimeoutException($"{LocalName}:超时({Timeout}ms),{ex.Message}"))
|
||||
Else
|
||||
Logger.Info(ex, $"{LocalName}:出错,IP:{HostIp}")
|
||||
Logger.Info(ex, $"{LocalName}:出错")
|
||||
End If
|
||||
RecordIPReliability(HostIp, -0.7)
|
||||
SourceFail(Th, ex, False)
|
||||
Finally
|
||||
'释放资源
|
||||
|
||||
@@ -1379,11 +1379,16 @@ OnLoaded:
|
||||
If Instance.State <> McInstanceState.Error AndAlso
|
||||
ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown") <> "Unknown" Then
|
||||
'读取版本信息
|
||||
Dim ReadCachedInfo =
|
||||
Function(Key As String) As String
|
||||
Dim Value = ReadIni(Instance.PathVersion & "PCL\Setup.ini", Key, Nothing)
|
||||
Return If(Value = "", Nothing, Value) '对老版本 Setup.ini 的空值兼容
|
||||
End Function
|
||||
Dim VersionInfo As New McVersion With {
|
||||
.Fabric = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionFabric", Nothing),
|
||||
.Forge = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionForge", Nothing),
|
||||
.NeoForge = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionNeoForge", Nothing),
|
||||
.OptiFine = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionOptiFine", Nothing),
|
||||
.Fabric = ReadCachedInfo("VersionFabric"),
|
||||
.Forge = ReadCachedInfo("VersionForge"),
|
||||
.NeoForge = ReadCachedInfo("VersionNeoForge"),
|
||||
.OptiFine = ReadCachedInfo("VersionOptiFine"),
|
||||
.HasLiteLoader = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionLiteLoader", False),
|
||||
.VanillaName = ReadIni(Instance.PathVersion & "PCL\Setup.ini", "VersionVanillaName", "Unknown")
|
||||
}
|
||||
|
||||
@@ -436,8 +436,8 @@ Public Module ModModpack
|
||||
Urls = ResourceVersion.ParseModrinthTrackArguments(Urls,
|
||||
ResourceVersion.DownloadReason.ModPack, TargetVersion.VanillaName, TargetVersion.ModLoaders).ToList 'Modrinth 来源追踪信息
|
||||
Urls = Urls.Distinct.ToList()
|
||||
Dim TargetPath As String = $"{McFolderSelected}versions\{InstanceName}\{File("path")}"
|
||||
If Not Path.GetFullPath(TargetPath).StartsWithF(Path.GetFullPath($"{McFolderSelected}versions\{InstanceName}\")) Then
|
||||
Dim TargetPath As String = Path.Combine(McFolderSelected, "versions", InstanceName, File("path").ToString().TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
|
||||
If Not PathUtils.IsParentOf($"{McFolderSelected}versions\{InstanceName}\", TargetPath) Then
|
||||
MyMsgBox($"整合包的文件路径超出了版本文件夹,请向整合包作者反馈此问题!{vbCrLf}目标:{Path.GetFullPath(TargetPath)}{vbCrLf}错误的文件:{TargetPath}", "文件路径校验失败", IsWarn:=True)
|
||||
Throw New OperationCanceledException
|
||||
End If
|
||||
|
||||
@@ -120,12 +120,12 @@ Public Module LocalResourceLoaders
|
||||
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 Return
|
||||
'获取作为检查目标的加载器和版本
|
||||
'获取作为检查目标的版本
|
||||
'此处不应向下扩展检查的 MC 小版本,例如 Mod 在更新 1.16.5 后,对早期的 1.16.2 版本发布了修补补丁,这会导致 PCL 将 1.16.5 版本的 Mod 降级到 1.16.2
|
||||
Dim TargetMcVersion As McVersion = Loader.Input.Target
|
||||
Dim VanillaVersion = TargetMcVersion.VanillaName
|
||||
'开始网络获取
|
||||
Logger.Info($"目标加载器:{ModLoaders},版本:{VanillaVersion}")
|
||||
Logger.Info($"正在检查 Mod 信息的版本:{VanillaVersion},加载器 {ModLoaders}")
|
||||
Dim EndedThreadCount As Integer = 0, IsFailed As Boolean = False
|
||||
Dim CurrentTaskThread As Thread = Thread.CurrentThread
|
||||
'从 Modrinth 获取信息
|
||||
@@ -140,6 +140,7 @@ Public Module LocalResourceLoaders
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
If ModrinthVersion.Count = 0 Then Return
|
||||
Dim ModrinthMapping As New Dictionary(Of String, List(Of LocalResourceFile))
|
||||
Dim ModrinthLoaders As New Dictionary(Of LocalResourceFile, List(Of String))
|
||||
For Each Entry In Mods
|
||||
If Not ModrinthVersion.ContainsKey(Entry.ModrinthHash) Then Continue For
|
||||
If ModrinthVersion(Entry.ModrinthHash)("files")(0)("hashes")("sha1") <> Entry.ModrinthHash Then Continue For
|
||||
@@ -149,6 +150,8 @@ Public Module LocalResourceLoaders
|
||||
ModrinthMapping(ProjectId).Add(Entry)
|
||||
'记录对应的 ProjectVersion
|
||||
Dim File = ResourceVersion.FromPlatformJson(ModrinthVersion(Entry.ModrinthHash), ResourceTypes.Mod)
|
||||
ModrinthLoaders(Entry) = ModrinthVersion(Entry.ModrinthHash)("loaders").
|
||||
Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
|
||||
If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then
|
||||
Entry.ProjectVersion = File
|
||||
Else
|
||||
@@ -169,17 +172,20 @@ Public Module LocalResourceLoaders
|
||||
Next
|
||||
Logger.Info($"已从 Modrinth 获取本地 Mod 信息,继续获取更新信息")
|
||||
'步骤 4:获取更新信息
|
||||
If ModLoaders = ModLoaders.None Then
|
||||
Logger.Warn("该 Minecraft 版本没有可用的 Mod 加载器,不获取 Mod 更新信息")
|
||||
Return
|
||||
End If
|
||||
Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update", HttpMethod.Post,
|
||||
$"{{""hashes"": [""{ModrinthMapping.SelectMany(Function(l) l.Value.Select(Function(m) m.ModrinthHash)).Join(""",""")}""], ""algorithm"": ""sha1"",
|
||||
""loaders"": [""{ModLoaders.Flags.Join(""",""").Lower}""],""game_versions"": [""{VanillaVersion}""]}}", "application/json")
|
||||
Dim ModsToUpdate = ModrinthMapping.SelectMany(Function(l) l.Value).ToList
|
||||
If Not ModsToUpdate.Any Then Return
|
||||
Dim UpdateData As String = ModsToUpdate.
|
||||
Select(Function(m) $"{{""hash"": ""{m.ModrinthHash}"", ""loaders"": [""{ModrinthLoaders(m).Join(""",""")}""], ""game_versions"": [""{VanillaVersion}""]}}").
|
||||
Join(",")
|
||||
Dim ModrinthUpdate As JObject = DlModRequest("https://api.modrinth.com/v2/version_files/update_individual", HttpMethod.Post,
|
||||
$"{{""algorithm"": ""sha1"", ""hashes"": [{UpdateData}]}}", "application/json")
|
||||
For Each Entry In Mods
|
||||
If Not ModrinthUpdate.ContainsKey(Entry.ModrinthHash) OrElse Entry.ProjectVersion Is Nothing Then Continue For
|
||||
Dim UpdateFile = ResourceVersion.FromPlatformJson(ModrinthUpdate(Entry.ModrinthHash), ResourceTypes.Mod)
|
||||
If Not UpdateFile.DownloadAvailable Then Continue For
|
||||
Dim UpdateLoaders = ModrinthUpdate(Entry.ModrinthHash)("loaders").
|
||||
Select(Function(l) l.ToString.Lower).Distinct.OrderBy(Function(l) l).ToList
|
||||
If Not UpdateLoaders.SequenceEqual(ModrinthLoaders(Entry)) Then Continue For
|
||||
Logger.Trace(Function() $"本地文件 {Entry.ProjectVersion.FileName} 在 Modrinth 上的最新版为 {UpdateFile.FileName}")
|
||||
If Entry.ProjectVersion.ReleaseDate >= UpdateFile.ReleaseDate OrElse Entry.ProjectVersion.Hash = UpdateFile.Hash Then Continue For
|
||||
'设置更新日志与更新文件
|
||||
@@ -217,6 +223,7 @@ Public Module LocalResourceLoaders
|
||||
|
||||
'步骤 2:尝试读取工程信息缓存,构建其他 Mod 的对应关系
|
||||
Dim ProjectIdToLocalFiles As New Dictionary(Of Integer, List(Of LocalResourceFile))
|
||||
Dim CurseForgeLoaders As New Dictionary(Of LocalResourceFile, ModLoaders)
|
||||
For Each Project In CurseForgeRaw
|
||||
Dim ProjectId = Project("id").ToString
|
||||
Dim Hash As UInteger = Project("file")("fileFingerprint")
|
||||
@@ -226,6 +233,7 @@ Public Module LocalResourceLoaders
|
||||
ProjectIdToLocalFiles.AddIntoValueCollection(ProjectId, Entry)
|
||||
'记录对应的 ProjectVersion
|
||||
Dim File = ResourceVersion.FromPlatformJson(Project("file"), ResourceTypes.Mod)
|
||||
CurseForgeLoaders(Entry) = File.ModLoaders
|
||||
If Entry.ProjectVersion Is Nothing OrElse Entry.ProjectVersion.ReleaseDate < File.ReleaseDate Then Entry.ProjectVersion = File
|
||||
Next
|
||||
Next
|
||||
@@ -242,34 +250,34 @@ Public Module LocalResourceLoaders
|
||||
If ProjectJson("isAvailable") IsNot Nothing AndAlso Not ProjectJson("isAvailable").ToObject(Of Boolean) Then Continue For
|
||||
'设置 Entry 中的工程信息
|
||||
Dim Project As New ResourceProject(ProjectJson)
|
||||
Dim TargetLoaders As ModLoaders = ModLoaders.None
|
||||
For Each LocalFile In ProjectIdToLocalFiles(Project.Id) '倒查防止 CurseForge 返回的内容有漏
|
||||
If LocalFile.Project?.Platform = ResourcePlatforms.Modrinth Then
|
||||
LocalFile.Project = LocalFile.Project '再次触发修改事件
|
||||
Else
|
||||
LocalFile.Project = Project
|
||||
TargetLoaders = TargetLoaders Or LocalFile.Project.ModLoaders
|
||||
End If
|
||||
Next
|
||||
TargetLoaders = TargetLoaders And ModLoaders '与目标 MC 版本的加载器取交集
|
||||
'查找或许版本更新的文件列表
|
||||
If ModLoaders = ModLoaders.None Then Continue For '无法判断 MC 版本使用的加载器,跳过更新检查
|
||||
Dim LatestFiles = ProjectJson("latestFilesIndexes").Where(
|
||||
Function(Entry)
|
||||
'Mod Loader 匹配
|
||||
If Entry("modLoader") Is Nothing Then Return False
|
||||
Dim EntryLoader As ModLoaders = Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer))
|
||||
If Not TargetLoaders.HasFlag(EntryLoader) Then Return False
|
||||
'MC 版本匹配
|
||||
Dim EntryVersion As String = Entry("gameVersion")
|
||||
Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
|
||||
End Function).
|
||||
MaxByAll(Function(Entry) Entry("gameVersion").ToString, New VersionComparer) '只保留最新的 MC 版本
|
||||
For Each Entry In LatestFiles
|
||||
Dim FileId = Entry("fileId").ToObject(Of Integer)
|
||||
If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
|
||||
UpdateFileIds(FileId).AddRange(ProjectIdToLocalFiles(Project.Id))
|
||||
FileIdToProjectSlug(FileId) = Project.Slug
|
||||
'查找可能有版本更新的文件列表
|
||||
Dim LatestFiles = ProjectJson("latestFilesIndexes").Where(
|
||||
Function(Entry)
|
||||
'Mod Loader 匹配
|
||||
Dim EntryLoader As ModLoaders = If(Entry("modLoader") Is Nothing, ModLoaders.None,
|
||||
Resource.FromCurseForgeModLoaderType(Entry("modLoader").ToObject(Of Integer)))
|
||||
If CurseForgeLoaders(LocalFile) = ModLoaders.None Then
|
||||
If EntryLoader <> ModLoaders.None Then Return False
|
||||
ElseIf Not CurseForgeLoaders(LocalFile).HasFlagF(EntryLoader) Then
|
||||
Return False
|
||||
End If
|
||||
'MC 版本匹配
|
||||
Dim EntryVersion As String = Entry("gameVersion")
|
||||
Return EntryVersion IsNot Nothing AndAlso EntryVersion = VanillaVersion
|
||||
End Function).
|
||||
MaxByAll(Function(Entry) Entry("gameVersion").ToString, New VersionComparer) '只保留最新的 MC 版本
|
||||
For Each Entry In LatestFiles
|
||||
Dim FileId = Entry("fileId").ToObject(Of Integer)
|
||||
If Not UpdateFileIds.ContainsKey(FileId) Then UpdateFileIds(FileId) = New List(Of LocalResourceFile)
|
||||
UpdateFileIds(FileId).Add(LocalFile)
|
||||
FileIdToProjectSlug(FileId) = Project.Slug
|
||||
Next
|
||||
Next
|
||||
Next
|
||||
Logger.Info($"已从 CurseForge 获取本地 Mod 信息,需要获取 {UpdateFileIds.Count} 个用于检查更新的文件信息")
|
||||
@@ -283,6 +291,10 @@ Public Module LocalResourceLoaders
|
||||
Dim File = ResourceVersion.FromPlatformJson(FileJson, ResourceTypes.Mod)
|
||||
If Not File.DownloadAvailable Then Continue For
|
||||
For Each Entry As LocalResourceFile In UpdateFileIds(File.Id)
|
||||
If File.ModLoaders <> CurseForgeLoaders(Entry) Then
|
||||
Logger.Warn($"从 CurseForge 获取到的更新文件 {File.FileName} 加载器为 {File.ModLoaders},与本地文件 {Entry.File.Name} 的加载器 {CurseForgeLoaders(Entry)} 不一致,已忽略", LogBehavior.None)
|
||||
Continue For
|
||||
End If
|
||||
If UpdateFiles.ContainsKey(Entry) AndAlso UpdateFiles(Entry).ReleaseDate >= File.ReleaseDate Then Continue For
|
||||
UpdateFiles(Entry) = File
|
||||
Next
|
||||
|
||||
@@ -87,7 +87,7 @@ Public Class ResourceProject
|
||||
Get
|
||||
If Not LoadedDatabase Then
|
||||
LoadedDatabase = True
|
||||
If Types.HasFlag(ResourceTypes.Mod) OrElse Types.HasFlag(ResourceTypes.DataPack) Then
|
||||
If Types.HasFlagF(ResourceTypes.Mod) OrElse Types.HasFlagF(ResourceTypes.DataPack) Then
|
||||
_WikiEntry = WikiEntry.All.Value.FirstOrDefault(Function(c) c.Slugs.GetOrDefault(Platform) = Slug)
|
||||
End If
|
||||
End If
|
||||
@@ -415,7 +415,7 @@ Public Class ResourceProject
|
||||
If Not MissingProjectIds.Any() Then Return
|
||||
'联网获取剩余的工程对象
|
||||
Dim Projects As JArray
|
||||
If Platform.HasFlag(ResourcePlatforms.CurseForge) Then
|
||||
If Platform.HasFlagF(ResourcePlatforms.CurseForge) Then
|
||||
Projects = DlModRequest("https://api.curseforge.com/v1/mods",
|
||||
HttpMethod.Post, "{""modIds"": [" & MissingProjectIds.Join(","c) & "]}", "application/json")("data")
|
||||
Else
|
||||
|
||||
@@ -21,8 +21,8 @@ Public Class ResourceSearcher
|
||||
''' </summary>
|
||||
Public ReadOnly Property CanContinue As Boolean
|
||||
Get
|
||||
If Tag.StartsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
|
||||
If Tag.EndsWithF("/") OrElse Not Sources.HasFlag(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
|
||||
If Tag.StartsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.CurseForge) Then Storage.CurseForgeTotal = 0
|
||||
If Tag.EndsWithF("/") OrElse Not Sources.HasFlagF(ResourcePlatforms.Modrinth) Then Storage.ModrinthTotal = 0
|
||||
If Storage.CurseForgeTotal = -1 OrElse Storage.ModrinthTotal = -1 Then Return True
|
||||
Return Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
|
||||
End Get
|
||||
@@ -241,7 +241,7 @@ Public Class ResourceSearcher
|
||||
Next
|
||||
End Function
|
||||
'CurseForge
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) Then
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) Then
|
||||
'数据库搜索
|
||||
Static CurseForgeSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.CurseForge).ToList()
|
||||
Dim CurseForgeSearchResults = Search(CurseForgeSearchEntries, RawSearchText, 100, 0.25)
|
||||
@@ -258,7 +258,7 @@ Public Class ResourceSearcher
|
||||
End If
|
||||
End If
|
||||
'Modrinth
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) Then
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) Then
|
||||
'数据库搜索
|
||||
Static ModrinthSearchEntries As List(Of SearchEntry(Of WikiEntry)) = GetSearchEntries(ResourcePlatforms.Modrinth).ToList()
|
||||
Dim ModrinthSearchResults = Search(ModrinthSearchEntries, RawSearchText, 100, 0.25)
|
||||
@@ -303,7 +303,7 @@ NextPage:
|
||||
Try
|
||||
|
||||
'CurseForge 搜索
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.CurseForge) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.CurseForge) AndAlso
|
||||
Not (Storage.CurseForgeTotal > -1 AndAlso Storage.CurseForgeTotal <= Storage.CurseForgeOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
(Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(CurseForgeAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -317,7 +317,7 @@ NextPage:
|
||||
For Each JsonEntry As JObject In RequestResult("data")
|
||||
Dim Project As New ResourceProject(JsonEntry)
|
||||
If Request.Type = ResourceTypes.ResourcePack AndAlso Project.Tags.Contains("数据包") Then Continue For 'CurseForge 将一些数据包分类成了资源包
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For '过滤分区不匹配的搜索结果(#8265)
|
||||
ProjectList.Add(Project)
|
||||
Next
|
||||
'更新结果
|
||||
@@ -335,7 +335,7 @@ NextPage:
|
||||
End If
|
||||
|
||||
'Modrinth 搜索
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
|
||||
Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
(Not IsChineseSearch OrElse (IsChineseSearch AndAlso Not String.IsNullOrEmpty(ModrinthAltSearchText))) Then '如果是中文搜索,就只在有对应搜索关键词的时候才继续
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -363,7 +363,7 @@ NextPage:
|
||||
End If
|
||||
|
||||
'Modrinth 直接获取工程
|
||||
If Request.Sources.HasFlag(ResourcePlatforms.Modrinth) AndAlso
|
||||
If Request.Sources.HasFlagF(ResourcePlatforms.Modrinth) AndAlso
|
||||
Not (Storage.ModrinthTotal > -1 AndAlso Storage.ModrinthTotal <= Storage.ModrinthOffset) AndAlso '剩余的未显示的搜索结果不足
|
||||
ModrinthSlugs.Any Then '有直接获取的 Slug
|
||||
WorkThreads.Add(RunInNewThread(
|
||||
@@ -375,7 +375,7 @@ NextPage:
|
||||
For Each JsonEntry As JObject In DlModRequest(ModrinthUrl)
|
||||
Dim Project As New ResourceProject(JsonEntry)
|
||||
'应用筛选
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlag(Request.Type) Then Continue For
|
||||
If Request.Type <> ResourceTypes.Any AndAlso Not Project.Types.HasFlagF(Request.Type) Then Continue For
|
||||
If Not String.IsNullOrEmpty(Request.Tag) AndAlso
|
||||
Not JsonEntry("categories").Any(Function(c) c.ToString = Request.Tag.AfterLast("/")) Then Continue For 'Project.Tags 已经转换成中文了,只能从 json 判
|
||||
If Request.ModLoaders <> ModLoaders.None AndAlso Not IgnoreModLoaderFilter AndAlso
|
||||
@@ -405,7 +405,7 @@ NextPage:
|
||||
|
||||
'仅保留兼容 Forge 的 Mod,或老版本中没有标注任何 Mod Loader 的 Mod
|
||||
If IgnoreModLoaderFilter Then
|
||||
RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlag(ModLoaders.Forge))
|
||||
RawResults.KeepIf(Function(p) p.ModLoaders = ModLoaders.None OrElse p.ModLoaders.HasFlagF(ModLoaders.Forge))
|
||||
End If
|
||||
|
||||
'确保存在结果
|
||||
|
||||
@@ -185,7 +185,7 @@ Public Class ResourceVersion
|
||||
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 .ResourceType.HasFlagF(ResourceTypes.Mod) OrElse .ResourceType.HasFlagF(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
|
||||
|
||||
@@ -51,6 +51,6 @@ Imports System.Runtime.InteropServices
|
||||
' 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
' 方法是按如下所示使用“*”
|
||||
|
||||
<Assembly: AssemblyVersion("2.13.1.0")>
|
||||
<Assembly: AssemblyFileVersion("2.13.1.0")>
|
||||
<Assembly: AssemblyVersion("2.13.1.1")>
|
||||
<Assembly: AssemblyFileVersion("2.13.1.1")>
|
||||
<Assembly: NeutralResourcesLanguage("")>
|
||||
|
||||
@@ -11,6 +11,7 @@ Public Module ModDownloadLib
|
||||
Dim VersionFolder As String = McFolderSelected & "versions\" & InstanceName & "\"
|
||||
|
||||
Dim Loaders As New List(Of LoaderBase)
|
||||
Dim NewInstance As McInstance = Nothing
|
||||
|
||||
'下载版本 Json 文件
|
||||
If JsonUrl Is Nothing Then
|
||||
@@ -23,14 +24,19 @@ Public Module ModDownloadLib
|
||||
Loaders.Add(New LoaderDownload(McDownloadClientJsonName, New List(Of NetFile) From {
|
||||
New NetFile(DlSourceLauncherOrMetaGet(If(JsonUrl, "")), VersionFolder & InstanceName & ".json", New FileChecker With {.IsJson = True})
|
||||
}) With {.ProgressWeight = 3})
|
||||
'读取 Json 文件并创建实例
|
||||
Loaders.Add(New LoaderTask(Of String, String)("读取 json 文件",
|
||||
Sub(Task As LoaderTask(Of String, String))
|
||||
NewInstance = New McInstance(VersionFolder)
|
||||
NewInstance.Load()
|
||||
End Sub) With {.ProgressWeight = 0, .Show = False})
|
||||
|
||||
'下载支持库文件
|
||||
Dim LoadersLib As New List(Of LoaderBase)
|
||||
LoadersLib.Add(New LoaderTask(Of String, List(Of NetFile))("分析原版支持库文件(副加载器)",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Thread.Sleep(50) '等待 JSON 文件实际写入硬盘(#3710)
|
||||
Logger.Info($"开始分析原版支持库文件:{VersionFolder}")
|
||||
Task.Output = McLibNetFilesFromInstance(New McInstance(VersionFolder))
|
||||
Task.Output = McLibNetFilesFromInstance(NewInstance)
|
||||
End Sub) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersLib.Add(New LoaderDownload("下载原版支持库文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 13, .Show = False})
|
||||
Loaders.Add(New LoaderCombo(Of String)(McDownloadClientLibName, LoadersLib) With {.Block = False, .ProgressWeight = 14})
|
||||
@@ -40,7 +46,7 @@ Public Module ModDownloadLib
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析资源文件索引地址(副加载器)",
|
||||
Sub(Task As LoaderTask(Of String, List(Of NetFile)))
|
||||
Try
|
||||
Dim AssetIndex = DlClientAssetIndexGet(New McInstance(VersionFolder))
|
||||
Dim AssetIndex = DlClientAssetIndexGet(NewInstance)
|
||||
Task.Output = If(AssetIndex Is Nothing, New List(Of NetFile), New List(Of NetFile) From {AssetIndex})
|
||||
Catch ex As Exception
|
||||
Throw New Exception("分析资源文件索引地址失败", ex)
|
||||
@@ -56,7 +62,7 @@ Public Module ModDownloadLib
|
||||
End Sub) With {.ProgressWeight = 1, .Show = False})
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件索引(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 3, .Show = False})
|
||||
LoadersAssets.Add(New LoaderTask(Of String, List(Of NetFile))("分析所需资源文件(副加载器)",
|
||||
Sub(Task) Task.Output = McAssetsFixList(New McInstance(VersionFolder), True, Task)) With {.ProgressWeight = 0.01, .Show = False})
|
||||
Sub(Task) Task.Output = McAssetsFixList(NewInstance, True, Task)) With {.ProgressWeight = 0.01, .Show = False})
|
||||
LoadersAssets.Add(New LoaderDownload("下载资源文件(副加载器)", New List(Of NetFile)) With {.ProgressWeight = 14, .Show = False})
|
||||
Loaders.Add(New LoaderCombo(Of String)("下载原版资源文件", LoadersAssets) With {.Block = False, .ProgressWeight = 18})
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ Public Class PageInstanceOverall
|
||||
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
|
||||
Dim IsHintIndie As Boolean = PageInstanceLeft.Instance.State <> McInstanceState.Error AndAlso PageInstanceLeft.Instance.PathIndie <> McFolderSelected
|
||||
Dim SavesFolder = DirectoryUtils.GetInfo(PageInstanceLeft.Instance.PathIndie & "saves\")
|
||||
Dim SaveEntries = If(SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
|
||||
Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
|
||||
Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {PageInstanceLeft.Instance.Name} 吗?" &
|
||||
If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!" &
|
||||
If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""), ""),
|
||||
|
||||
@@ -134,8 +134,10 @@
|
||||
<WrapPanel Margin="0,0,0,10">
<TextBlock Margin="0,1.5,6,0" Text="L" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Z" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="N" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="P." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="...:" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="妖" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="FL" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="萧" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="乔" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="cc" />
|
||||
@@ -150,6 +152,7 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="33" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="kk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="jjb" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="狮." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="+1" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Iirs" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Lio" />
|
||||
@@ -158,16 +161,22 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Sht" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Nul" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="250" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Life" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="kile" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="pbc" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Aya" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mjp" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="azzt" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小小" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="冒子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="王道" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="鸡人" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="喵根" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="折花" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="归墟" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="墨渊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="键来" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="睚眦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="而后" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="南安" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="曰久" />
|
||||
@@ -213,7 +222,21 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="哈哈" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小洛" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="璃辰" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="茉莉" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="旅人" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="航卡" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="班总" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小圆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="梨子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="风?" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="瓜瓜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="阳光" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="菜菜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="副连" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="如舟" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="bow" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Byet" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Kian" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ZDD" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Um1" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="2514" />
|
||||
@@ -224,21 +247,26 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="joker" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="RXZY" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="白成y" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mww" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Rofni" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mzqc" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="jason" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="youzi" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="xxlzta" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="end_x" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="QWYJ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="qwqw" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Angel" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="15041" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="晓空er" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="kaoyu" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="肥富帅" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小王子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="狐狸涩" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="大涡流" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无名氏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="紫风狐" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="杨巨焕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="范桂圆" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="芸芸云" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="快夏波" />
|
||||
@@ -255,6 +283,7 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="乐永昶" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="lingke" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="东雪莲" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="殷疯子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="伊文斯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="丁乙恒" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="朔望月" />
|
||||
@@ -273,12 +302,22 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="浮千傀" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="恰啦啦" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="梦痕丿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="三叶铭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="卿姬昉" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="疯一回" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="李困困" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="厄尔萨" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="伏拉夫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="α-蒎烯" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="你的JK" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="jiujinh" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="慕qwq" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Twicat" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="stenac" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="JOKER" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="半日谣." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="EX星辞" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Tefrish" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Triple9" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="gediao" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Zen1th" />
|
||||
@@ -292,12 +331,18 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yi_xuan" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MC芬达" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="shallow" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ETL蓝夢" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="𝕳轩辕氏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="谜茶360" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="颜沐Jun" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="CS2七松" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Eventful" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Molioos" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="logicum" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Mr_loba" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="一叶知秋" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="我妻狗卷" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="御神凌霜" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="狼烟居士" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="千风伶羽" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="mumiao" />
|
||||
@@ -332,11 +377,16 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="春山如笑" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="幻天阿绝" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="忘道蜂枫" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="深海无渊" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="心软的神" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="砂锅豆腐" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="好人好逝" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="登峰破晓" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="东隅未殁" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="星花银影" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="往事如昔" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="林克之空" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="0X00001" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MuMAA" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="溯枫qwq" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Tacenda" />
|
||||
@@ -344,6 +394,7 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="👁👄👃" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秋慕owo" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="船长Yuki" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="HIMEKO" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="是小61呀" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="47号虾球" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Alancnel" />
|
||||
@@ -360,6 +411,7 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="MselQAQ" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="UZQueen" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="hihospital" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="cutegirl3k" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Prifo_lone" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="有....暗星!" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Hitsser 1.0" />
|
||||
@@ -382,11 +434,13 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="离人如飘萍" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="秋葉逐殘夏" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="水星有海吗" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="吃饱爱睡觉" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="ac纯萌新cy" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="岁月suiyue" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="陌然MajorC" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="-Pareidolia." />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="蛙蛙大王Kzr" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Aurorawalk" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Yhcmdbfwz" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Ayers赜蠹嬭" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="We1J1uSh1" />
|
||||
@@ -405,6 +459,7 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小田love白子" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Sasha_Xiong" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="赤狸_crisnow" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="dorod月gour" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Name0emaN" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="happypickles" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="无中生有为dd" />
|
||||
@@ -439,13 +494,17 @@
|
||||
<TextBlock Margin="0,1.5,6,0" Text="没什么名字好取" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Wanderer5147" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="取个名字耗阳寿" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="北熊何时与狼行" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="向着有风的地方" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="AAA神秘人批发" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Bangbang2025" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="贝吉蒙Begimon" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="取个名字真难0.0" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="NoTuring_Steve" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="VividerCafe2379" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="冰冰watermelon" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="DOODOODOOD" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="怕做苦力的苦力怕" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="小句号就是小句号" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="弹跳爆炸小鱼珠🐟" />
|
||||
<TextBlock Margin="0,1.5,6,0" Text="Xanadu Silk Fable" />
|
||||
|
||||
@@ -186,7 +186,7 @@ Public Class PageSelectRight
|
||||
Dim IsShiftPressed As Boolean = My.Computer.Keyboard.ShiftKeyDown
|
||||
Dim IsHintIndie As Boolean = Instance.State <> McInstanceState.Error AndAlso Instance.PathIndie <> McFolderSelected
|
||||
Dim SavesFolder = DirectoryUtils.GetInfo(Instance.PathIndie & "saves\")
|
||||
Dim SaveEntries = If(SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
|
||||
Dim SaveEntries = If(IsHintIndie AndAlso SavesFolder.Exists, SavesFolder.EnumerateDirectories.OrderByDescending(Function(Save) Save.LastWriteTime).Select(Function(Save) $"{Save.Name}(上次修改:{StringUtils.FormatTimeSpan(Save.LastWriteTime - Date.Now, False)})").ToList, New List(Of String))
|
||||
Select Case MyMsgBox($"你确定要{If(IsShiftPressed, "永久", "")}删除版本 {Instance.Name} 吗?" &
|
||||
If(IsHintIndie, vbCrLf & "该版本对应的存档、资源包、Mod 等文件也将被一并删除!", "") &
|
||||
If(SaveEntries.Any, vbCrLf & vbCrLf & "这会删除以下存档:" & vbCrLf & "· " & SaveEntries.Join(vbCrLf & "· "), ""),
|
||||
|
||||
Reference in New Issue
Block a user