diff --git a/ProjBobcat/.editorconfig b/ProjBobcat/.editorconfig index 2050d64e..1811c565 100644 --- a/ProjBobcat/.editorconfig +++ b/ProjBobcat/.editorconfig @@ -1,8 +1,5 @@ [*.cs] -# CA1063: 正确实现 IDisposable -dotnet_diagnostic.CA1063.severity = silent - dotnet_diagnostic.CS1591.severity = suggestion dotnet_diagnostic.CA2007.severity = none diff --git a/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs b/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs index b8365bf1..7f14846f 100644 --- a/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs +++ b/ProjBobcat/ProjBobcat/Class/Helper/DownloadHelper.cs @@ -54,12 +54,10 @@ public static async Task AdvancedDownloadListFile(IEnumerable file var filesBlock = new TransformManyBlock, DownloadFile>(d => { - var dl = d.ToList(); - - foreach (var df in dl.Where(df => !Directory.Exists(df.DownloadPath))) + foreach (var df in d.Where(df => !Directory.Exists(df.DownloadPath))) Directory.CreateDirectory(df.DownloadPath); - return dl; + return d; }); var actionBlock = new ActionBlock(async d => @@ -143,14 +141,11 @@ public static async Task DownloadData(DownloadFile downloadProperty, Cancellatio tSpeed += speed; cSpeed++; - downloadProperty.Changed?.Invoke(null, - new DownloadFileChangedEventArgs - { - ProgressPercentage = (double)downloadedBytesCount / responseLength, - BytesReceived = downloadedBytesCount, - TotalBytes = responseLength, - Speed = speed - }); + downloadProperty.OnChanged( + speed, + (double)downloadedBytesCount / responseLength, + downloadedBytesCount, + responseLength); } sw.Stop(); @@ -158,13 +153,11 @@ public static async Task DownloadData(DownloadFile downloadProperty, Cancellatio stream.Close(); var aSpeed = tSpeed / cSpeed; - downloadProperty.Completed?.Invoke(null, - new DownloadFileCompletedEventArgs(true, null, downloadProperty, aSpeed)); + downloadProperty.OnCompleted(true, null, aSpeed); } catch (Exception e) { - downloadProperty.Completed?.Invoke(null, - new DownloadFileCompletedEventArgs(false, e, downloadProperty, 0)); + downloadProperty.OnCompleted(false, e, 0); } } @@ -356,14 +349,11 @@ public static async Task MultiPartDownloadTaskAsync(DownloadFile downloadFile, i tSpeed += speed; cSpeed++; - downloadFile.Changed?.Invoke(t, - new DownloadFileChangedEventArgs - { - ProgressPercentage = (double)downloadedBytesCount / responseLength, - BytesReceived = downloadedBytesCount, - TotalBytes = responseLength, - Speed = speed - }); + downloadFile.OnChanged( + speed, + (double)downloadedBytesCount / responseLength, + downloadedBytesCount, + responseLength); } sw.Stop(); @@ -403,8 +393,7 @@ await writeActionBlock.Completion.ContinueWith(async task => { var ex = task.Exception ?? new AggregateException(new Exception("没有完全下载所有的分片")); - downloadFile.Completed?.Invoke(task, - new DownloadFileCompletedEventArgs(false, ex, downloadFile, aSpeed)); + downloadFile.OnCompleted(false, ex, aSpeed); if (File.Exists(filePath)) File.Delete(filePath); @@ -426,8 +415,7 @@ await writeActionBlock.Completion.ContinueWith(async task => } outputStream.Close(); - downloadFile.Completed?.Invoke(null, - new DownloadFileCompletedEventArgs(true, null, downloadFile, aSpeed)); + downloadFile.OnCompleted(true, null, aSpeed); }, cts.Token); streamBlock.Complete(); @@ -440,9 +428,7 @@ await writeActionBlock.Completion.ContinueWith(async task => foreach (var piece in readRanges.Where(piece => File.Exists(piece.TempFileName))) File.Delete(piece.TempFileName); - downloadFile.Completed?.Invoke(null, - new DownloadFileCompletedEventArgs(false, ex, downloadFile, 0)); - + downloadFile.OnCompleted(false, ex, 0); } } diff --git a/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs b/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs index 87cb41f0..430b7b5c 100644 --- a/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs +++ b/ProjBobcat/ProjBobcat/Class/LaunchWrapper.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using ProjBobcat.Class.Model; using ProjBobcat.Class.Model.Auth; +using ProjBobcat.DefaultComponent.Launch.GameCore; using ProjBobcat.Event; using ProjBobcat.Interface; @@ -12,6 +13,8 @@ namespace ProjBobcat.Class /// public class LaunchWrapper : IDisposable { + bool disposedValue; + /// /// 构造函数 /// @@ -41,10 +44,6 @@ public LaunchWrapper(AuthResultBase authResult) /// public Process Process { get; init; } - public void Dispose() - { - } - /// /// 执行过程 /// @@ -73,11 +72,14 @@ void ProcessOnErrorDataReceived(object sender, DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) return; - GameCore.LogGameData(sender, new GameLogEventArgs + if (GameCore is GameCoreBase coreBase) { - LogType = GameLogType.Unknown, - RawContent = e.Data - }); + coreBase.OnLogGameData(sender, new GameLogEventArgs + { + LogType = GameLogType.Unknown, + RawContent = e.Data + }); + } } void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) @@ -94,13 +96,16 @@ void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) var exceptionMsg = GameCore.GameLogResolver.ResolveExceptionMsg(e.Data); var stackTrace = GameCore.GameLogResolver.ResolveStackTrace(e.Data); - GameCore.LogGameData(sender, new GameLogEventArgs + if(GameCore is GameCoreBase gameCoreBase) { - LogType = type, - RawContent = e.Data, - StackTrace = stackTrace, - ExceptionMsg = exceptionMsg - }); + gameCoreBase.OnLogGameData(sender, new GameLogEventArgs + { + LogType = type, + RawContent = e.Data, + StackTrace = stackTrace, + ExceptionMsg = exceptionMsg + }); + } return; } @@ -109,14 +114,46 @@ void ProcessOnOutputDataReceived(object sender, DataReceivedEventArgs e) var source = GameCore.GameLogResolver.ResolveSource(totalPrefix); - GameCore.LogGameData(sender, new GameLogEventArgs + if (GameCore is GameCoreBase coreBase) + { + coreBase.OnLogGameData(sender, new GameLogEventArgs + { + LogType = type, + RawContent = e.Data, + Content = e.Data[(totalPrefix?.Length ?? 0)..], + Source = source, + Time = time + }); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - LogType = type, - RawContent = e.Data, - Content = e.Data[(totalPrefix?.Length ?? 0)..], - Source = source, - Time = time - }); + if (disposing) + { + Process?.Dispose(); + } + + // TODO: 释放未托管的资源(未托管的对象)并重写终结器 + // TODO: 将大型字段设置为 null + disposedValue = true; + } + } + + // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + // ~LaunchWrapper() + // { + // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + // Dispose(disposing: false); + // } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs b/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs index 3018c24b..31e05ff8 100644 --- a/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs +++ b/ProjBobcat/ProjBobcat/Class/Model/DownloadFile.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using ProjBobcat.Event; namespace ProjBobcat.Class.Model @@ -6,7 +7,7 @@ namespace ProjBobcat.Class.Model /// /// 下载文件信息类 /// - public class DownloadFile : ICloneable + public class DownloadFile : ICloneable, IDisposable { /// /// 下载Uri @@ -56,12 +57,59 @@ public class DownloadFile : ICloneable /// /// 下载完成事件 /// - public EventHandler Completed { get; set; } + public event EventHandler Completed + { + add + { + listEventDelegates.AddHandler(CompletedEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(CompletedEventKey, value); + } + } /// /// 下载改变事件 /// - public EventHandler Changed { get; set; } + public event EventHandler Changed + { + add + { + listEventDelegates.AddHandler(ChangedEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(ChangedEventKey, value); + } + } + + protected EventHandlerList listEventDelegates = new (); + + bool disposedValue; + static readonly object CompletedEventKey = new (); + static readonly object ChangedEventKey = new(); + + public void OnChanged(double speed, double progress, long bytesReceived, long totalBytes) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[ChangedEventKey]; + @event?.Invoke(this, new DownloadFileChangedEventArgs + { + Speed = speed, + ProgressPercentage = progress, + BytesReceived = bytesReceived, + TotalBytes = totalBytes + }); + } + + public void OnCompleted(bool? success, Exception ex, double averageSpeed) + { + var eventList = listEventDelegates; + var test = eventList[CompletedEventKey]; + var @event = (EventHandler)eventList[CompletedEventKey]; + @event?.Invoke(this, new DownloadFileCompletedEventArgs(success, ex, averageSpeed)); + } public object Clone() { @@ -78,5 +126,35 @@ public object Clone() TimeOut = TimeOut }; } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: 释放托管状态(托管对象) + listEventDelegates.Dispose(); + } + + // TODO: 释放未托管的资源(未托管的对象)并重写终结器 + // TODO: 将大型字段设置为 null + disposedValue = true; + } + } + + // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + // ~DownloadFile() + // { + // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + // Dispose(disposing: false); + // } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs index e7f32339..1be29396 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Authenticator/YggdrasilAuthenticator.cs @@ -126,8 +126,8 @@ public async Task AuthTaskAsync(bool userField = false) } } - using var resultJson = await HttpHelper.Post(LoginAddress, requestJson).ConfigureAwait(true); - var content = await resultJson.Content.ReadAsStringAsync().ConfigureAwait(true); + using var resultJson = await HttpHelper.Post(LoginAddress, requestJson); + var content = await resultJson.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject(content); if (result == default || string.IsNullOrEmpty(result.AccessToken)) @@ -305,8 +305,8 @@ public async Task AuthRefreshTaskAsync(AuthResponseModel respons }; var requestJson = JsonConvert.SerializeObject(requestModel, JsonHelper.CamelCasePropertyNamesSettings); - using var resultJson = await HttpHelper.Post(RefreshAddress, requestJson).ConfigureAwait(true); - var content = await resultJson.Content.ReadAsStringAsync().ConfigureAwait(true); + using var resultJson = await HttpHelper.Post(RefreshAddress, requestJson); + var content = await resultJson.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject(content); switch (result) @@ -388,7 +388,7 @@ public async Task ValidateTokenTaskAsync(string accessToken) }; var requestJson = JsonConvert.SerializeObject(requestModel, JsonHelper.CamelCasePropertyNamesSettings); - using var result = await HttpHelper.Post(ValidateAddress, requestJson).ConfigureAwait(true); + using var result = await HttpHelper.Post(ValidateAddress, requestJson); return result.StatusCode.Equals(HttpStatusCode.NoContent); } @@ -401,7 +401,7 @@ public async Task TokenRevokeTaskAsync(string accessToken) }; var requestJson = JsonConvert.SerializeObject(requestModel, JsonHelper.CamelCasePropertyNamesSettings); - using var x = await HttpHelper.Post(RevokeAddress, requestJson).ConfigureAwait(true); + using var x = await HttpHelper.Post(RevokeAddress, requestJson); } /// @@ -418,7 +418,7 @@ public async Task SignOutTaskAsync() }; var requestJson = JsonConvert.SerializeObject(requestModel, JsonHelper.CamelCasePropertyNamesSettings); - using var result = await HttpHelper.Post(SignOutAddress, requestJson).ConfigureAwait(true); + using var result = await HttpHelper.Post(SignOutAddress, requestJson); return result.StatusCode.Equals(HttpStatusCode.NoContent); } } diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs b/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs index 30abbc2c..107a02a5 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/DefaultResourceCompleter.cs @@ -1,14 +1,15 @@ -using System; +using ProjBobcat.Class.Helper; +using ProjBobcat.Class.Model; +using ProjBobcat.Event; +using ProjBobcat.Interface; +using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; -using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; -using ProjBobcat.Event; -using ProjBobcat.Interface; namespace ProjBobcat.DefaultComponent { @@ -33,10 +34,49 @@ public DefaultResourceCompleter() public bool CheckFile { get; set; } public IEnumerable ResourceInfoResolvers { get; set; } + protected EventHandlerList listEventDelegates = new(); + + bool disposedValue; - public event EventHandler GameResourceInfoResolveStatus; - public event EventHandler DownloadFileChangedEvent; - public event EventHandler DownloadFileCompletedEvent; + static readonly object ResolveEventKey = new(); + static readonly object ChangedEventKey = new(); + static readonly object CompletedEventKey = new(); + + public event EventHandler GameResourceInfoResolveStatus + { + add + { + listEventDelegates.AddHandler(ResolveEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(ResolveEventKey, value); + } + } + + public event EventHandler DownloadFileChangedEvent + { + add + { + listEventDelegates.AddHandler(ChangedEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(ChangedEventKey, value); + } + } + + public event EventHandler DownloadFileCompletedEvent + { + add + { + listEventDelegates.AddHandler(CompletedEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(CompletedEventKey, value); + } + } public TaskResult CheckAndDownload() { @@ -53,7 +93,9 @@ public DefaultResourceCompleter() var totalLostFiles = new List(); foreach (var resolver in ResourceInfoResolvers) { - resolver.GameResourceInfoResolveEvent += GameResourceInfoResolveStatus; + var handler = (EventHandler)listEventDelegates[ResolveEventKey]!; + if(handler != null) + resolver.GameResourceInfoResolveEvent += handler; var lostFiles = await resolver.ResolveResourceAsync(); totalLostFiles.AddRange(lostFiles); @@ -65,12 +107,11 @@ public DefaultResourceCompleter() totalLostFiles.Shuffle(); NeedToDownload = totalLostFiles.Count; - var downloadList = - ( - from f in totalLostFiles - select new DownloadFile + var downloadList = new List(); + foreach (var f in totalLostFiles) + { + var dF = new DownloadFile { - Completed = WhenCompleted, DownloadPath = f.Path, DownloadUri = f.Uri, FileName = f.FileName, @@ -78,13 +119,16 @@ from f in totalLostFiles CheckSum = f.CheckSum, FileType = f.Type, TimeOut = 10000 - }).ToList(); + }; + dF.Completed += WhenCompleted; + + downloadList.Add(dF); + } if (downloadList.First().FileType.Equals("GameJar", StringComparison.OrdinalIgnoreCase)) - downloadList.First().Changed = (_, args) => + downloadList.First().Changed += (_, args) => { - DownloadFileCompletedEvent?.Invoke(this, - new DownloadFileCompletedEventArgs(null, null, downloadList.First(), args.Speed)); + OnCompleted(downloadList.First(), new DownloadFileCompletedEventArgs(null, null, args.Speed)); }; var (item1, item2) = await DownloadFiles(downloadList); @@ -92,28 +136,42 @@ from f in totalLostFiles return new TaskResult(item1, value: item2); } - /// - /// IDisposable接口保留字段 - /// - public void Dispose() + void OnCompleted(object? sender, DownloadFileCompletedEventArgs e) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[CompletedEventKey]!; + @event?.Invoke(sender, e); + } + + void OnChanged(double progress, double speed) { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[ChangedEventKey]!; + + @event?.Invoke(this, new DownloadFileChangedEventArgs + { + ProgressPercentage = progress, + Speed = speed + }); } void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { + if (sender is not DownloadFile file) return; + TotalDownloaded++; - InvokeDownloadProgressChangedEvent((double)TotalDownloaded / NeedToDownload, e.AverageSpeed); - DownloadFileCompletedEvent?.Invoke(this, e); + OnChanged((double)TotalDownloaded / NeedToDownload, e.AverageSpeed); + OnCompleted(sender, e); if (!(e.Success ?? false)) { - _retryFiles.Add(e.File); + _retryFiles.Add(file); return; } if (!CheckFile) return; - Check(e.File, ref _retryFiles); + Check(file, ref _retryFiles); } static void Check(DownloadFile file, ref ConcurrentBag bag) @@ -160,7 +218,7 @@ static void Check(DownloadFile file, ref ConcurrentBag bag) foreach (var file in files) { file.RetryCount++; - file.Completed = WhenCompleted; + file.Completed += WhenCompleted; } await DownloadHelper.AdvancedDownloadListFile(files); @@ -177,13 +235,33 @@ static void Check(DownloadFile file, ref ConcurrentBag bag) return (resultType, new ResourceCompleterCheckResult { IsLibDownloadFailed = isLibraryFailed }); } - void InvokeDownloadProgressChangedEvent(double progress, double speed) + protected virtual void Dispose(bool disposing) { - DownloadFileChangedEvent?.Invoke(this, new DownloadFileChangedEventArgs + if (!disposedValue) { - ProgressPercentage = progress, - Speed = speed - }); + if (disposing) + { + listEventDelegates.Dispose(); + } + + // TODO: 释放未托管的资源(未托管的对象)并重写终结器 + // TODO: 将大型字段设置为 null + disposedValue = true; + } + } + + // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + // ~DefaultResourceCompleter() + // { + // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + // Dispose(disposing: false); + // } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/CurseForgeInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/CurseForgeInstaller.cs index 6061a32e..e24fc2a1 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/CurseForgeInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/CurseForgeInstaller.cs @@ -61,13 +61,15 @@ public async Task InstallTaskAsync() var d = downloadUrlRes.Trim('"'); var fn = Path.GetFileName(d); - urlBags.Add(new DownloadFile + var downloadFile = new DownloadFile { - Completed = WhenCompleted, DownloadPath = di.FullName, DownloadUri = d, FileName = fn - }); + }; + downloadFile.Completed += WhenCompleted; + + urlBags.Add(downloadFile); _totalDownloaded++; @@ -151,7 +153,7 @@ async Task DownloadFiles(IEnumerable downloadList) foreach (var file in files) { file.RetryCount++; - file.Completed = WhenCompleted; + file.Completed += WhenCompleted; } await DownloadHelper.AdvancedDownloadListFile(files); @@ -165,24 +167,26 @@ async Task DownloadFiles(IEnumerable downloadList) private void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { + if (sender is not DownloadFile file) return; + _totalDownloaded++; var progress = (double)_totalDownloaded / _needToDownload * 100; - var retryStr = e.File.RetryCount > 0 ? $"[重试 - {e.File.RetryCount}] " : string.Empty; - var fileName = e.File.FileName.Length > 20 - ? $"{e.File.FileName[..20]}..." - : e.File.FileName; + var retryStr = file.RetryCount > 0 ? $"[重试 - {file.RetryCount}] " : string.Empty; + var fileName = file.FileName.Length > 20 + ? $"{file.FileName[..20]}..." + : file.FileName; InvokeStatusChangedEvent($"{retryStr}下载整合包中的 Mods - {fileName} ({_totalDownloaded} / {_needToDownload})", progress); if (!(e.Success ?? false)) { - _retryFiles.Add(e.File); + _retryFiles.Add(file); return; } - Check(e.File, ref _retryFiles); + Check(file, ref _retryFiles); } private static void Check(DownloadFile file, ref ConcurrentBag bag) diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs index f4e50bab..ab12c26f 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Installer/ForgeInstaller/HighVersionForgeInstaller.cs @@ -368,13 +368,13 @@ string ResolveVariableRegex(string val) var df = new DownloadFile { - Completed = WhenCompleted, CheckSum = lib.Sha1, DownloadPath = path, FileName = fileName, DownloadUri = lib.Url, FileSize = lib.Size }; + df.Completed += WhenCompleted; libDownloadInfo.Add(df); } @@ -537,7 +537,7 @@ async Task DownloadFiles(IEnumerable downloadList) foreach (var file in files) { file.RetryCount++; - file.Completed = WhenCompleted; + file.Completed += WhenCompleted; } await DownloadHelper.AdvancedDownloadListFile(files); @@ -551,22 +551,24 @@ async Task DownloadFiles(IEnumerable downloadList) private void WhenCompleted(object? sender, DownloadFileCompletedEventArgs e) { + if (sender is not DownloadFile file) return; + _totalDownloaded++; var progress = (double)_totalDownloaded / _needToDownload; - var retryStr = e.File.RetryCount > 0 ? $"[重试 - {e.File.RetryCount}] " : string.Empty; + var retryStr = file.RetryCount > 0 ? $"[重试 - {file.RetryCount}] " : string.Empty; InvokeStatusChangedEvent( - $"{retryStr}下载模组 - {e.File.FileName} ( {_totalDownloaded} / {_needToDownload} )", + $"{retryStr}下载模组 - {file.FileName} ( {_totalDownloaded} / {_needToDownload} )", progress); if (!(e.Success ?? false)) { - _retryFiles.Add(e.File); + _retryFiles.Add(file); return; } - Check(e.File, ref _retryFiles); + Check(file, ref _retryFiles); } private static void Check(DownloadFile file, ref ConcurrentBag bag) diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultMinecraftUWPCore.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultMinecraftUWPCore.cs deleted file mode 100644 index 634153d9..00000000 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultMinecraftUWPCore.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using ProjBobcat.Class; -using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; -using ProjBobcat.Event; -using ProjBobcat.Interface; - -namespace ProjBobcat.DefaultComponent.Launch -{ - /// - /// 提供了UWP版本MineCraft的启动核心 - /// - public class DefaultMineCraftUWPCore : IGameCore - { - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public string RootPath - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public Guid ClientToken - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public VersionLocatorBase VersionLocator - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public IGameLogResolver GameLogResolver - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public event EventHandler GameExitEventDelegate; - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public event EventHandler GameLogEventDelegate; - - /// - /// 无用字段 - /// - [Obsolete("UWP 版本的Minecraft不需要该字段。")] - public event EventHandler LaunchLogEventDelegate; - - public LaunchResult Launch(LaunchSettings launchSettings) - { - if (!SystemInfoHelper.IsMinecraftUWPInstalled()) throw new InvalidOperationException(); - - using var process = new Process - { StartInfo = new ProcessStartInfo { UseShellExecute = true, FileName = "minecraft:" } }; - process.Start(); - - return default; - } - - [Obsolete("UWP启动核心并不支持异步启动")] - public Task LaunchTaskAsync(LaunchSettings settings) - { - throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - public void LogGameData(object sender, GameLogEventArgs e) - { - throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - public void LogLaunchData(object sender, LaunchLogEventArgs e) - { - throw new NotImplementedException(); - } - - /// - /// 无用字段 - /// - public void GameExit(object sender, GameExitEventArgs e) - { - throw new NotImplementedException(); - } - - #region IDisposable Support - - /// - /// IDisposable保留字段 - /// - public void Dispose() - { - } - - #endregion - - static void GameExit(object sender, EventArgs e) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultGameCore.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs similarity index 82% rename from ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultGameCore.cs rename to ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs index d45e8041..6f0cd7c7 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/DefaultGameCore.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultGameCore.cs @@ -13,12 +13,12 @@ using SharpCompress.Archives; using FileInfo = System.IO.FileInfo; -namespace ProjBobcat.DefaultComponent.Launch +namespace ProjBobcat.DefaultComponent.Launch.GameCore { /// /// 表示一个默认的游戏核心。 /// - public class DefaultGameCore : IGameCore + public class DefaultGameCore : GameCoreBase { string _rootPath; @@ -30,7 +30,7 @@ public class DefaultGameCore : IGameCore /// /// .minecraft 目录 /// - public string RootPath + public override string RootPath { get => _rootPath; set @@ -42,49 +42,7 @@ public string RootPath } } - /// - /// 游戏版本查找器 - /// - public VersionLocatorBase VersionLocator { get; set; } - - /// - /// 客户端识别码 - /// - public Guid ClientToken { get; set; } - - public IGameLogResolver GameLogResolver { get; set; } - - /// - /// 游戏退出事件 - /// - public event EventHandler GameExitEventDelegate; - - /// - /// 游戏日志输出事件 - /// - public event EventHandler GameLogEventDelegate; - - /// - /// 启动日志输出事件 - /// - public event EventHandler LaunchLogEventDelegate; - - /// - /// 启动 (同步方法) - /// - /// - /// - public LaunchResult Launch(LaunchSettings settings) - { - return LaunchTaskAsync(settings).Result; - } - - /// - /// 启动 (异步方法) - /// - /// - /// - public async Task LaunchTaskAsync(LaunchSettings settings) + public override async Task LaunchTaskAsync(LaunchSettings settings) { try { @@ -329,7 +287,7 @@ public async Task LaunchTaskAsync(LaunchSettings settings) Task.Run(launchWrapper.Process.WaitForExit) .ContinueWith(task => { - GameExit(launchWrapper, new GameExitEventArgs + OnGameExit(launchWrapper, new GameExitEventArgs { Exception = task.Exception, ExitCode = launchWrapper.ExitCode @@ -369,17 +327,6 @@ public async Task LaunchTaskAsync(LaunchSettings settings) } } - #region IDisposable Support - - /// - /// 释放资源。 - /// - public void Dispose() - { - } - - #endregion - #region 内部方法 Internal Methods /// @@ -391,7 +338,7 @@ public void Dispose() /// void InvokeLaunchLogThenStart(string item, ref TimeSpan time, ref Stopwatch sw) { - LogLaunchData(this, new LaunchLogEventArgs + OnLogLaunchData(this, new LaunchLogEventArgs { Item = item, ItemRunTime = sw.Elapsed - time @@ -400,39 +347,6 @@ void InvokeLaunchLogThenStart(string item, ref TimeSpan time, ref Stopwatch sw) sw.Start(); } - /// - /// 指示需要记录游戏日志。 - /// 此方法将引发事件 。 - /// - /// sender - /// e - public void LogGameData(object sender, GameLogEventArgs e) - { - GameLogEventDelegate?.Invoke(sender, e); - } - - /// - /// 指示游戏已经结束。 - /// 此方法将引发事件 。 - /// - /// sender - /// e - public void GameExit(object sender, GameExitEventArgs e) - { - GameExitEventDelegate?.Invoke(sender, e); - } - - /// - /// 指示需要记录启动日志。 - /// 此方法将引发事件 。 - /// - /// sender - /// e - public void LogLaunchData(object sender, LaunchLogEventArgs e) - { - LaunchLogEventDelegate?.Invoke(sender, e); - } - #endregion } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultMinecraftUWPCore.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultMinecraftUWPCore.cs new file mode 100644 index 00000000..06a36863 --- /dev/null +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/DefaultMinecraftUWPCore.cs @@ -0,0 +1,33 @@ +using ProjBobcat.Class.Helper; +using ProjBobcat.Class.Model; +using System; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace ProjBobcat.DefaultComponent.Launch.GameCore +{ + /// + /// 提供了UWP版本MineCraft的启动核心 + /// + public class DefaultMineCraftUWPCore : GameCoreBase + { + public override LaunchResult Launch(LaunchSettings launchSettings) + { + if (!SystemInfoHelper.IsMinecraftUWPInstalled()) throw new InvalidOperationException(); + + using var process = new Process + { StartInfo = new ProcessStartInfo { UseShellExecute = true, FileName = "minecraft:" } }; + process.Start(); + + return default; + } + + [Obsolete("UWP启动核心并不支持异步启动")] +#pragma warning disable CS0809 // 过时成员重写未过时成员 + public override Task LaunchTaskAsync(LaunchSettings settings) +#pragma warning restore CS0809 // 过时成员重写未过时成员 + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs new file mode 100644 index 00000000..283fe5e7 --- /dev/null +++ b/ProjBobcat/ProjBobcat/DefaultComponent/Launch/GameCore/GameCoreBase.cs @@ -0,0 +1,128 @@ +using ProjBobcat.Class; +using ProjBobcat.Class.Model; +using ProjBobcat.Event; +using ProjBobcat.Interface; +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace ProjBobcat.DefaultComponent.Launch.GameCore +{ + public abstract class GameCoreBase : IGameCore + { + public virtual string RootPath { get; set; } + public virtual Guid ClientToken { get; set; } + public virtual VersionLocatorBase VersionLocator { get; set; } + public virtual IGameLogResolver GameLogResolver { get; set; } + + static readonly object GameExitEventKey = new(); + static readonly object GameLogEventKey = new(); + static readonly object LaunchLogEventKey = new(); + + protected EventHandlerList listEventDelegates = new(); + bool disposedValue; + + public event EventHandler GameExitEventDelegate + { + add + { + listEventDelegates.AddHandler(GameExitEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(GameExitEventKey, value); + } + } + + public event EventHandler GameLogEventDelegate + { + add + { + listEventDelegates.AddHandler(GameLogEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(GameLogEventKey, value); + } + } + + public event EventHandler LaunchLogEventDelegate + { + add + { + listEventDelegates.AddHandler(LaunchLogEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(LaunchLogEventKey, value); + } + } + + /// + /// 启动 (同步方法) + /// + /// + /// + public virtual LaunchResult Launch(LaunchSettings settings) + { + return LaunchTaskAsync(settings).Result; + } + + /// + /// 启动 (异步方法) + /// + /// + /// + public abstract Task LaunchTaskAsync(LaunchSettings settings); + + public virtual void OnGameExit(object sender, GameExitEventArgs e) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[GameExitEventKey]!; + @event.Invoke(sender, e); + } + + public virtual void OnLogGameData(object sender, GameLogEventArgs e) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[GameLogEventKey]!; + @event.Invoke(sender, e); + } + + public virtual void OnLogLaunchData(object sender, LaunchLogEventArgs e) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[LaunchLogEventKey]!; + @event.Invoke(sender, e); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + listEventDelegates.Dispose(); + } + + // TODO: 释放未托管的资源(未托管的对象)并重写终结器 + // TODO: 将大型字段设置为 null + disposedValue = true; + } + } + + // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + // ~GameCoreBase() + // { + // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + // Dispose(disposing: false); + // } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs index 7ff122fe..363a4a08 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/AssetInfoResolver.cs @@ -1,4 +1,10 @@ -using System; +using Newtonsoft.Json; +using ProjBobcat.Class.Helper; +using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.GameResource; +using ProjBobcat.Class.Model.Mojang; +using ProjBobcat.Interface; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -6,21 +12,17 @@ using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; -using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; -using ProjBobcat.Class.Model.GameResource; -using ProjBobcat.Class.Model.Mojang; -using ProjBobcat.Event; -using ProjBobcat.Interface; namespace ProjBobcat.DefaultComponent.ResourceInfoResolver { - public class AssetInfoResolver : IResourceInfoResolver + public class AssetInfoResolver : ResolverBase { - readonly string _assetIndexUrlRoot; + public AssetInfoResolver() + { + MaxDegreeOfParallelism = 2; + } - string _basePath; + readonly string _assetIndexUrlRoot; public string AssetIndexUriRoot { @@ -30,29 +32,11 @@ public string AssetIndexUriRoot public string AssetUriRoot { get; init; } = "https://resources.download.minecraft.net/"; - public bool CheckLocalFiles { get; set; } - - public string BasePath - { - get => _basePath.TrimEnd('\\'); - set => _basePath = value; - } - - public VersionInfo VersionInfo { get; set; } public List Versions { get; set; } - public int MaxDegreeOfParallelism { get; init; } = 2; - - public event EventHandler GameResourceInfoResolveEvent; - - public IEnumerable ResolveResource() - { - var result = ResolveResourceAsync().Result; - return result; - } - public async Task> ResolveResourceAsync() + public override async Task> ResolveResourceAsync() { - LogGameResourceInfoResolveStatus("开始进行游戏资源(Asset)检查"); + OnResolve("开始进行游戏资源(Asset)检查"); if (!(Versions?.Any() ?? false) && VersionInfo?.AssetInfo == null) return Enumerable.Empty(); @@ -75,7 +59,7 @@ public async Task> ResolveResourceAsync() var assetIndexesPath = Path.Combine(assetIndexesDi.FullName, $"{id}.json"); if (!File.Exists(assetIndexesPath)) { - LogGameResourceInfoResolveStatus("没有发现Asset Indexes 文件, 开始下载"); + OnResolve("没有发现Asset Indexes 文件, 开始下载"); var assetIndexDownloadUri = VersionInfo?.AssetInfo?.Url; @@ -115,14 +99,14 @@ public async Task> ResolveResourceAsync() } catch (Exception e) { - LogGameResourceInfoResolveStatus($"解析Asset Indexes 文件失败!原因:{e.Message}", logType: LogType.Error); + OnResolve($"解析Asset Indexes 文件失败!原因:{e.Message}"); return Enumerable.Empty(); } - LogGameResourceInfoResolveStatus("Asset Indexes 文件下载完成", 100, LogType.Success); + OnResolve("Asset Indexes 文件下载完成", 100); } - LogGameResourceInfoResolveStatus("开始解析Asset Indexes 文件..."); + OnResolve("开始解析Asset Indexes 文件..."); AssetObjectModel assetObject; try @@ -132,14 +116,14 @@ public async Task> ResolveResourceAsync() } catch (Exception ex) { - LogGameResourceInfoResolveStatus($"解析Asset Indexes 文件失败!原因:{ex.Message}", logType: LogType.Error); + OnResolve($"解析Asset Indexes 文件失败!原因:{ex.Message}"); File.Delete(assetIndexesPath); return Enumerable.Empty(); } if (assetObject == null) { - LogGameResourceInfoResolveStatus("解析Asset Indexes 文件失败!原因:文件可能损坏或为空", logType: LogType.Error); + OnResolve("解析Asset Indexes 文件失败!原因:文件可能损坏或为空"); File.Delete(assetIndexesPath); return Enumerable.Empty(); } @@ -152,7 +136,7 @@ public async Task> ResolveResourceAsync() var objectCount = assetObject.Objects.Count; var result = new ConcurrentBag(); - LogGameResourceInfoResolveStatus("检索并验证 Asset 资源", 0); + OnResolve("检索并验证 Asset 资源", 0); Parallel.ForEach(assetObject.Objects, new ParallelOptions { @@ -167,7 +151,7 @@ public async Task> ResolveResourceAsync() Interlocked.Increment(ref checkedObject); var progress = (double) checkedObject / objectCount * 100; - LogGameResourceInfoResolveStatus(string.Empty, progress); + OnResolve(string.Empty, progress); if (File.Exists(filePath)) { @@ -196,28 +180,9 @@ public async Task> ResolveResourceAsync() }); }); - LogGameResourceInfoResolveStatus("Assets 解析完成", 100, logType: LogType.Success); + OnResolve("Assets 解析完成", 100); return result; } - - void LogGameResourceInfoResolveStatus(string currentStatus, double progress = 0, LogType logType = LogType.Normal) - { - if(string.IsNullOrEmpty(currentStatus)) - { - GameResourceInfoResolveEvent?.Invoke(this, new GameResourceInfoResolveEventArgs - { - Progress = progress, - LogType = logType - }); - } - - GameResourceInfoResolveEvent?.Invoke(this, new GameResourceInfoResolveEventArgs - { - Status = currentStatus, - Progress = progress, - LogType = logType - }); - } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs index d88c7a47..fb3895e2 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/LibraryInfoResolver.cs @@ -1,4 +1,8 @@ -using System; +using ProjBobcat.Class.Helper; +using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.GameResource; +using ProjBobcat.Interface; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -6,35 +10,23 @@ using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; -using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; -using ProjBobcat.Class.Model.GameResource; -using ProjBobcat.Event; -using ProjBobcat.Interface; using FileInfo = ProjBobcat.Class.Model.FileInfo; namespace ProjBobcat.DefaultComponent.ResourceInfoResolver { - public class LibraryInfoResolver : IResourceInfoResolver + public class LibraryInfoResolver : ResolverBase { - public string LibraryUriRoot { get; init; } = "https://libraries.minecraft.net/"; - public string ForgeUriRoot { get; init; } = "https://files.minecraftforge.net/"; - public string BasePath { get; set; } - public VersionInfo VersionInfo { get; set; } - public bool CheckLocalFiles { get; set; } - public int MaxDegreeOfParallelism { get; init; } = 2; - - public event EventHandler GameResourceInfoResolveEvent; - - public IEnumerable ResolveResource() + public LibraryInfoResolver() { - var result = ResolveResourceAsync().Result; - return result; + MaxDegreeOfParallelism = 2; } - public async Task> ResolveResourceAsync() + public string LibraryUriRoot { get; init; } = "https://libraries.minecraft.net/"; + public string ForgeUriRoot { get; init; } = "https://files.minecraftforge.net/"; + + public override async Task> ResolveResourceAsync() { - LogGameResourceInfoResolveStatus("开始进行游戏资源(Library)检查"); + OnResolve("开始进行游戏资源(Library)检查"); if (!(VersionInfo?.Natives?.Any() ?? false) && !(VersionInfo?.Libraries?.Any() ?? false)) return Enumerable.Empty(); @@ -64,7 +56,7 @@ public async Task> ResolveResourceAsync() Interlocked.Increment(ref checkedLib); var progress = (double)checkedLib / libCount * 100; - LogGameResourceInfoResolveStatus("检索并验证 Library", progress); + OnResolve("检索并验证 Library", progress); if (File.Exists(filePath)) { @@ -106,7 +98,7 @@ public async Task> ResolveResourceAsync() Interlocked.Increment(ref checkedLib); var progress = (double)checkedLib / libCount * 100; - LogGameResourceInfoResolveStatus("检索并验证 Native", progress); + OnResolve("检索并验证 Native", progress); try { @@ -153,19 +145,9 @@ public async Task> ResolveResourceAsync() await Task.Delay(1); - LogGameResourceInfoResolveStatus("检查Library完成", 100); + OnResolve("检查Library完成", 100); return result; } - - void LogGameResourceInfoResolveStatus(string currentStatus, double progress = 0, LogType logType = LogType.Normal) - { - GameResourceInfoResolveEvent?.Invoke(this, new GameResourceInfoResolveEventArgs - { - Status = currentStatus, - Progress = progress, - LogType = logType - }); - } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs new file mode 100644 index 00000000..a14eb548 --- /dev/null +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/ResolverBase.cs @@ -0,0 +1,91 @@ +using ProjBobcat.Class.Model; +using ProjBobcat.Event; +using ProjBobcat.Interface; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace ProjBobcat.DefaultComponent.ResourceInfoResolver +{ + public abstract class ResolverBase : IResourceInfoResolver + { + protected EventHandlerList listEventDelegates = new(); + bool disposedValue; + static readonly object ResolveEventKey = new(); + + public event EventHandler GameResourceInfoResolveEvent + { + add + { + listEventDelegates.AddHandler(ResolveEventKey, value); + } + remove + { + listEventDelegates.RemoveHandler(ResolveEventKey, value); + } + } + + public string BasePath { get; set; } + public bool CheckLocalFiles { get; set; } + public VersionInfo VersionInfo { get; set; } + + public int MaxDegreeOfParallelism { get; init; } + + public virtual void OnResolve(string currentStatus, double progress = 0) + { + var eventList = listEventDelegates; + var @event = (EventHandler)eventList[ResolveEventKey]!; + + if (string.IsNullOrEmpty(currentStatus)) + { + @event?.Invoke(this, new GameResourceInfoResolveEventArgs + { + Progress = progress + }); + } + + @event?.Invoke(this, new GameResourceInfoResolveEventArgs + { + Status = currentStatus, + Progress = progress + }); + } + + public abstract Task> ResolveResourceAsync(); + + public virtual IEnumerable ResolveResource() + { + return ResolveResourceAsync().Result; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + listEventDelegates.Dispose(); + } + + // TODO: 释放未托管的资源(未托管的对象)并重写终结器 + // TODO: 将大型字段设置为 null + disposedValue = true; + } + } + + // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 + // ~ResolverBase() + // { + // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + // Dispose(disposing: false); + // } + + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs index af5f89b2..a7acbb4e 100644 --- a/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/DefaultComponent/ResourceInfoResolver/VersionInfoResolver.cs @@ -1,34 +1,20 @@ -using System; +using Newtonsoft.Json; +using ProjBobcat.Class.Helper; +using ProjBobcat.Class.Model; +using ProjBobcat.Class.Model.GameResource; +using ProjBobcat.Interface; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; -using Newtonsoft.Json; -using ProjBobcat.Class.Helper; -using ProjBobcat.Class.Model; -using ProjBobcat.Class.Model.GameResource; -using ProjBobcat.Event; -using ProjBobcat.Interface; namespace ProjBobcat.DefaultComponent.ResourceInfoResolver { - public class VersionInfoResolver : IResourceInfoResolver + public class VersionInfoResolver : ResolverBase { - public string BasePath { get; set; } - public bool CheckLocalFiles { get; set; } - public VersionInfo VersionInfo { get; set; } - [Obsolete("不需要此字段")] - public int MaxDegreeOfParallelism { get; init; } - public event EventHandler GameResourceInfoResolveEvent; - - public IEnumerable ResolveResource() - { - var result = ResolveResourceAsync().Result; - return result; - } - - public async Task> ResolveResourceAsync() + public override async Task> ResolveResourceAsync() { if (!CheckLocalFiles) return Enumerable.Empty(); diff --git a/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs b/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs index c61e1e57..b2ebf891 100644 --- a/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs +++ b/ProjBobcat/ProjBobcat/Event/DownloadFileChangedEventArgs.cs @@ -8,7 +8,5 @@ public class DownloadFileChangedEventArgs : EventArgs public double ProgressPercentage { get; set; } public long BytesReceived { get; set; } public long? TotalBytes { get; set; } - public int NeedToDownload { get; set; } - public int TotalDownloaded { get; set; } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Event/DownloadFileCompletedEventArgs.cs b/ProjBobcat/ProjBobcat/Event/DownloadFileCompletedEventArgs.cs index a8fe0cf3..10f650a7 100644 --- a/ProjBobcat/ProjBobcat/Event/DownloadFileCompletedEventArgs.cs +++ b/ProjBobcat/ProjBobcat/Event/DownloadFileCompletedEventArgs.cs @@ -5,16 +5,14 @@ namespace ProjBobcat.Event { public class DownloadFileCompletedEventArgs : EventArgs { - public DownloadFileCompletedEventArgs(bool? success, Exception ex, DownloadFile file, double averageSpeed) + public DownloadFileCompletedEventArgs(bool? success, Exception ex, double averageSpeed) { Success = success; Error = ex; - File = file; AverageSpeed = averageSpeed; } public double AverageSpeed { get; set; } - public DownloadFile File { get; } #nullable enable public bool? Success { get; } #nullable restore diff --git a/ProjBobcat/ProjBobcat/Event/GameResourceInfoResolveEventArgs.cs b/ProjBobcat/ProjBobcat/Event/GameResourceInfoResolveEventArgs.cs index ba438858..ab169d01 100644 --- a/ProjBobcat/ProjBobcat/Event/GameResourceInfoResolveEventArgs.cs +++ b/ProjBobcat/ProjBobcat/Event/GameResourceInfoResolveEventArgs.cs @@ -7,6 +7,5 @@ public class GameResourceInfoResolveEventArgs : EventArgs { public double Progress { get; init; } public string Status { get; init; } - public LogType LogType { get; init; } } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Interface/IGameCore.cs b/ProjBobcat/ProjBobcat/Interface/IGameCore.cs index 6ef4b8f5..e18cf686 100644 --- a/ProjBobcat/ProjBobcat/Interface/IGameCore.cs +++ b/ProjBobcat/ProjBobcat/Interface/IGameCore.cs @@ -58,26 +58,5 @@ public interface IGameCore : IDisposable /// 启动日志输出事件 /// event EventHandler LaunchLogEventDelegate; - - /// - /// 记录游戏日志调用方法 - /// - /// - /// - void LogGameData(object sender, GameLogEventArgs e); - - /// - /// 记录启动器日志调用方法 - /// - /// - /// - void LogLaunchData(object sender, LaunchLogEventArgs e); - - /// - /// 游戏退出调用方法 - /// - /// - /// - void GameExit(object sender, GameExitEventArgs e); } } \ No newline at end of file diff --git a/ProjBobcat/ProjBobcat/Interface/IResourceInfoResolver.cs b/ProjBobcat/ProjBobcat/Interface/IResourceInfoResolver.cs index 2a4ebc7a..e0f0a2d8 100644 --- a/ProjBobcat/ProjBobcat/Interface/IResourceInfoResolver.cs +++ b/ProjBobcat/ProjBobcat/Interface/IResourceInfoResolver.cs @@ -6,7 +6,7 @@ namespace ProjBobcat.Interface { - public interface IResourceInfoResolver + public interface IResourceInfoResolver : IDisposable { string BasePath { get; set; } bool CheckLocalFiles { get; set; } diff --git a/ProjBobcat/ProjBobcat/ProjBobcat.csproj b/ProjBobcat/ProjBobcat/ProjBobcat.csproj index 318855c0..c3c81a08 100644 --- a/ProjBobcat/ProjBobcat/ProjBobcat.csproj +++ b/ProjBobcat/ProjBobcat/ProjBobcat.csproj @@ -47,19 +47,6 @@ - - 3.3.1 - all - - - 6.6.0 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - 1.9.0 - all - all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -72,7 +59,7 @@ 13.0.1 - 0.30.0 + 0.30.1