diff --git a/Directory.Build.props b/Directory.Build.props index 7d6d9de5..062ae00b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ Vladimir Khil Khil-soft - 7.11.3 - 6.0.0 + 7.11.11 + 6.0.2 2.15.0 diff --git a/SIGame.sln b/SIGame.sln index 7ba93dc1..20fd3b0f 100644 --- a/SIGame.sln +++ b/SIGame.sln @@ -59,8 +59,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SI.GameServer.Client", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "src\Common\Utils\Utils.csproj", "{49D06BD3-C088-4E5A-888E-697D78417ABF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppService.Client", "src\Common\AppService.Client\AppService.Client.csproj", "{26C59ADA-8AE7-467B-8D03-404725CCFC53}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIStorageService.ViewModel", "src\Common\SIStorageService.ViewModel\SIStorageService.ViewModel.csproj", "{AFF2B73A-0BA3-4152-82D0-9573C05FDC98}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{0D6E84B6-C90C-4FA8-A932-1DA7179E9F1D}" @@ -530,26 +528,6 @@ Global {49D06BD3-C088-4E5A-888E-697D78417ABF}.Release|x64.Build.0 = Release|Any CPU {49D06BD3-C088-4E5A-888E-697D78417ABF}.Release|x86.ActiveCfg = Release|Any CPU {49D06BD3-C088-4E5A-888E-697D78417ABF}.Release|x86.Build.0 = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|ARM.ActiveCfg = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|ARM.Build.0 = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|ARM64.Build.0 = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|x64.ActiveCfg = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|x64.Build.0 = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|x86.ActiveCfg = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Debug|x86.Build.0 = Debug|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|Any CPU.ActiveCfg = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|Any CPU.Build.0 = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|ARM.ActiveCfg = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|ARM.Build.0 = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|ARM64.ActiveCfg = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|ARM64.Build.0 = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|x64.ActiveCfg = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|x64.Build.0 = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|x86.ActiveCfg = Release|Any CPU - {26C59ADA-8AE7-467B-8D03-404725CCFC53}.Release|x86.Build.0 = Release|Any CPU {AFF2B73A-0BA3-4152-82D0-9573C05FDC98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AFF2B73A-0BA3-4152-82D0-9573C05FDC98}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFF2B73A-0BA3-4152-82D0-9573C05FDC98}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -700,7 +678,6 @@ Global {8D528C21-F673-4CE0-A430-3F23F5B6A346} = {3E07412F-2F9C-458E-B897-E07E86BF682A} {35AD14FA-7BDC-42CB-8501-1EC446805C99} = {98AD9562-11D8-4547-B71E-56BDAC94A39C} {49D06BD3-C088-4E5A-888E-697D78417ABF} = {98AD9562-11D8-4547-B71E-56BDAC94A39C} - {26C59ADA-8AE7-467B-8D03-404725CCFC53} = {98AD9562-11D8-4547-B71E-56BDAC94A39C} {AFF2B73A-0BA3-4152-82D0-9573C05FDC98} = {98AD9562-11D8-4547-B71E-56BDAC94A39C} {9D7B6E42-82F0-491E-9E7C-F36086715D50} = {98AD9562-11D8-4547-B71E-56BDAC94A39C} {8C805CF1-39A5-461D-BB30-09E520FF3C78} = {3E07412F-2F9C-458E-B897-E07E86BF682A} diff --git a/SIQuester.sln b/SIQuester.sln index cce552f6..2803f86a 100644 --- a/SIQuester.sln +++ b/SIQuester.sln @@ -31,8 +31,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{29A5EB8A-6B8 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "src\Common\Utils\Utils.csproj", "{DB37734A-E865-4E1E-8CB9-3A9EB3D6BDF4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppService.Client", "src\Common\AppService.Client\AppService.Client.csproj", "{85ABCE10-858E-44F8-A1C1-5CAA9E84F484}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIPackages.Tests", "test\Common\SIPackages.Tests\SIPackages.Tests.csproj", "{3B650F1B-FA32-4C3C-A79E-6C386DF9977E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIStorageService.ViewModel", "src\Common\SIStorageService.ViewModel\SIStorageService.ViewModel.csproj", "{C3B297B7-6782-473B-B908-42458A636100}" @@ -43,6 +41,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIEngine.Core", "src\Common EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils.Wpf", "src\Common\Utils.Wpf\Utils.Wpf.csproj", "{F7AF5912-E7B2-43A2-8E3E-DE5CC75243A6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{1293FEFC-100A-4F00-8B73-1660DBF26F3F}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,14 +122,6 @@ Global {DB37734A-E865-4E1E-8CB9-3A9EB3D6BDF4}.Release|Any CPU.Build.0 = Release|Any CPU {DB37734A-E865-4E1E-8CB9-3A9EB3D6BDF4}.Release|x86.ActiveCfg = Release|Any CPU {DB37734A-E865-4E1E-8CB9-3A9EB3D6BDF4}.Release|x86.Build.0 = Release|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Debug|x86.ActiveCfg = Debug|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Debug|x86.Build.0 = Debug|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Release|Any CPU.Build.0 = Release|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Release|x86.ActiveCfg = Release|Any CPU - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484}.Release|x86.Build.0 = Release|Any CPU {3B650F1B-FA32-4C3C-A79E-6C386DF9977E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3B650F1B-FA32-4C3C-A79E-6C386DF9977E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3B650F1B-FA32-4C3C-A79E-6C386DF9977E}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -183,7 +178,6 @@ Global {191BE083-3920-4C18-8130-4D6B2BF0AB59} = {9BD30589-9C99-4E58-8447-2DDB33A7F0FC} {80E6576A-4906-4D07-B473-A994EDDD13AC} = {9BD30589-9C99-4E58-8447-2DDB33A7F0FC} {DB37734A-E865-4E1E-8CB9-3A9EB3D6BDF4} = {D8DC2EF3-36A3-475B-A88F-861AB1E112F6} - {85ABCE10-858E-44F8-A1C1-5CAA9E84F484} = {D8DC2EF3-36A3-475B-A88F-861AB1E112F6} {3B650F1B-FA32-4C3C-A79E-6C386DF9977E} = {03BB87B1-7EA0-49F8-B8A5-46A64E7A6E0C} {C3B297B7-6782-473B-B908-42458A636100} = {D8DC2EF3-36A3-475B-A88F-861AB1E112F6} {64E4E20D-318F-404F-B7D4-1039FD67F84B} = {03BB87B1-7EA0-49F8-B8A5-46A64E7A6E0C} diff --git a/SImulator.sln b/SImulator.sln index 7ce3ecda..fb0cd6c1 100644 --- a/SImulator.sln +++ b/SImulator.sln @@ -27,8 +27,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SImulator", "src\SImulator\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "src\Common\Utils\Utils.csproj", "{349CBBC7-36F8-4ED6-B45B-7B34D5B970DD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppService.Client", "src\Common\AppService.Client\AppService.Client.csproj", "{56112D6B-FD5A-4918-982D-4D3724375CF9}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EDF0B6AF-1972-47A9-B812-533BD079C701}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIStorageService.ViewModel", "src\Common\SIStorageService.ViewModel\SIStorageService.ViewModel.csproj", "{9CE7BB4E-0C76-4B89-9249-49369580C25B}" @@ -41,6 +39,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SIEngine.Tests", "test\Comm EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils.Wpf", "src\Common\Utils.Wpf\Utils.Wpf.csproj", "{4C678CED-354B-432E-BB67-700E669EE0E7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{B368E16F-622F-4C82-97CE-DFF0F750A473}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -235,26 +238,6 @@ Global {349CBBC7-36F8-4ED6-B45B-7B34D5B970DD}.Release|x64.Build.0 = Release|Any CPU {349CBBC7-36F8-4ED6-B45B-7B34D5B970DD}.Release|x86.ActiveCfg = Release|Any CPU {349CBBC7-36F8-4ED6-B45B-7B34D5B970DD}.Release|x86.Build.0 = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|ARM.ActiveCfg = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|ARM.Build.0 = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|ARM64.Build.0 = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|x64.ActiveCfg = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|x64.Build.0 = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|x86.ActiveCfg = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Debug|x86.Build.0 = Debug|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|Any CPU.Build.0 = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|ARM.ActiveCfg = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|ARM.Build.0 = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|ARM64.ActiveCfg = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|ARM64.Build.0 = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|x64.ActiveCfg = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|x64.Build.0 = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|x86.ActiveCfg = Release|Any CPU - {56112D6B-FD5A-4918-982D-4D3724375CF9}.Release|x86.Build.0 = Release|Any CPU {9CE7BB4E-0C76-4B89-9249-49369580C25B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CE7BB4E-0C76-4B89-9249-49369580C25B}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CE7BB4E-0C76-4B89-9249-49369580C25B}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -371,7 +354,6 @@ Global {CB2B8CE7-876C-4D16-BFA4-E1F67F949280} = {F7CE4B38-4286-47BA-B5BA-0E4FA77E594E} {7BF7F1CE-2187-4372-9A31-CC0048031E09} = {117739F8-28CD-4CC4-950C-E3513265A87D} {349CBBC7-36F8-4ED6-B45B-7B34D5B970DD} = {069B16F1-8F70-4CFC-8651-E4763D225B60} - {56112D6B-FD5A-4918-982D-4D3724375CF9} = {069B16F1-8F70-4CFC-8651-E4763D225B60} {9CE7BB4E-0C76-4B89-9249-49369580C25B} = {069B16F1-8F70-4CFC-8651-E4763D225B60} {D960F7D2-7575-46DA-A2E4-A315A50B9863} = {069B16F1-8F70-4CFC-8651-E4763D225B60} {72F8EAE3-CBAD-45A5-A1BC-0FDA0C363DD2} = {F7CE4B38-4286-47BA-B5BA-0E4FA77E594E} diff --git a/deploy/SIGame.Setup/Product.wxs b/deploy/SIGame.Setup/Product.wxs index 0c458977..d06ae9b7 100644 --- a/deploy/SIGame.Setup/Product.wxs +++ b/deploy/SIGame.Setup/Product.wxs @@ -119,9 +119,6 @@ - - - @@ -345,9 +342,6 @@ - - - diff --git a/deploy/SIQuester.Setup/Product.wxs b/deploy/SIQuester.Setup/Product.wxs index d3442e2c..26ba6061 100644 --- a/deploy/SIQuester.Setup/Product.wxs +++ b/deploy/SIQuester.Setup/Product.wxs @@ -161,9 +161,6 @@ - - - diff --git a/src/Common/AppService.Client/AppService.Client.csproj b/src/Common/AppService.Client/AppService.Client.csproj deleted file mode 100644 index b66e6ec9..00000000 --- a/src/Common/AppService.Client/AppService.Client.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - net6.0 - enable - enable - True - key.snk - Khil-soft - Copyright © Khil-soft 2022 - 2024 - - - none - false - - - - - - - - - - diff --git a/src/Common/AppService.Client/AppServiceClient.cs b/src/Common/AppService.Client/AppServiceClient.cs deleted file mode 100644 index 15bc5538..00000000 --- a/src/Common/AppService.Client/AppServiceClient.cs +++ /dev/null @@ -1,81 +0,0 @@ -using AppService.Client.Models; -using Newtonsoft.Json; -using System.Reflection; -using System.Text; - -namespace AppService.Client -{ - /// - public sealed class AppServiceClient : IAppServiceClient - { - private static readonly JsonSerializer Serializer = new(); - - private readonly HttpClient _client; - - public AppServiceClient(HttpClient client) - { - _client = client; - } - - public async Task GetProductAsync(string name, CancellationToken cancellationToken = default) - { - var appInfo = await CallAsync("Product?name=" + name + "&osVersion=" + Environment.OSVersion.Version, cancellationToken); - - if (appInfo == null) - { - throw new Exception($"Product {name} not found"); - } - - if (!appInfo.Uri.IsAbsoluteUri) - { - appInfo.Uri = new Uri("https://vladimirkhil.com/" + appInfo.Uri.OriginalString); - } - - return appInfo; - } - - public async Task SendErrorReportAsync( - string application, - string errorMessage, - Version? appVersion = null, - DateTime? time = null, - CancellationToken cancellationToken = default) - { - var version = appVersion ?? Assembly.GetEntryAssembly()?.GetName().Version; - - var errorInfo = new ErrorInfo - { - Application = application, - Error = errorMessage, - Version = version?.ToString() ?? "", - Time = time ?? DateTime.UtcNow, - OSVersion = Environment.OSVersion.Version.ToString() - }; - - var sb = new StringBuilder(); - - using (var writer = new StringWriter(sb)) - { - Serializer.Serialize(writer, errorInfo); - } - - var content = new StringContent(sb.ToString(), Encoding.UTF8, "application/json"); - - using var response = await _client.PostAsync("SendErrorReport2", content, cancellationToken); - using var stream = await response.Content.ReadAsStreamAsync(cancellationToken); - using var reader = new StreamReader(stream); - - return (ErrorStatus?)Serializer.Deserialize(reader, typeof(ErrorStatus)); - } - - private async Task CallAsync(string request, CancellationToken cancellationToken = default) - { - using var stream = await _client.GetStreamAsync(request, cancellationToken); - using var reader = new StreamReader(stream); - - return (T?)Serializer.Deserialize(reader, typeof(T)); - } - - public void Dispose() => _client.Dispose(); - } -} diff --git a/src/Common/AppService.Client/AppServiceClientOptions.cs b/src/Common/AppService.Client/AppServiceClientOptions.cs deleted file mode 100644 index 0ad8ec20..00000000 --- a/src/Common/AppService.Client/AppServiceClientOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace AppService.Client -{ - /// - /// Provides options for . - /// - public sealed class AppServiceClientOptions - { - public const string ConfigurationSectionName = "AppServiceClient"; - - /// - /// AppService address. - /// - public Uri? ServiceUri { get; set; } - } -} diff --git a/src/Common/AppService.Client/IAppServiceClient.cs b/src/Common/AppService.Client/IAppServiceClient.cs deleted file mode 100644 index ea8d4ccf..00000000 --- a/src/Common/AppService.Client/IAppServiceClient.cs +++ /dev/null @@ -1,34 +0,0 @@ -using AppService.Client.Models; - -namespace AppService.Client -{ - /// - /// Provides API for working with published products. - /// - public interface IAppServiceClient : IDisposable - { - /// - /// Gets product info. - /// - /// Product name. - /// Cancellation token. - /// Product info. - Task GetProductAsync(string name, CancellationToken cancellationToken = default); - - /// - /// Sends product execution error information. - /// - /// Application name. - /// Error message. - /// Application version. - /// Error time. - /// Cancellation token. - /// Current status of this error. - Task SendErrorReportAsync( - string application, - string errorMessage, - Version? appVersion = null, - DateTime? time = null, - CancellationToken cancellationToken = default); - } -} diff --git a/src/Common/AppService.Client/Models/AppInfo.cs b/src/Common/AppService.Client/Models/AppInfo.cs deleted file mode 100644 index f4d3d7a0..00000000 --- a/src/Common/AppService.Client/Models/AppInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace AppService.Client.Models -{ - /// - /// Defines an application info. - /// - public sealed class AppInfo - { - /// - /// Current application version. - /// - public Version Version { get; set; } - - /// - /// Application setup uri. - /// - public Uri Uri { get; set; } - } -} diff --git a/src/Common/AppService.Client/Models/ErrorInfo.cs b/src/Common/AppService.Client/Models/ErrorInfo.cs deleted file mode 100644 index 5977314f..00000000 --- a/src/Common/AppService.Client/Models/ErrorInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace AppService.Client.Models -{ - /// - /// Contains information about application error. - /// - public sealed class ErrorInfo - { - /// - /// Application name. - /// - public string Application { get; set; } - - /// - /// Application version. - /// - public string Version { get; set; } - - /// - /// Error time. - /// - public DateTime Time { get; set; } - - /// - /// Error text. - /// - public string Error { get; set; } - - /// - /// Operation system version. - /// - public string OSVersion { get; set; } - } -} diff --git a/src/Common/AppService.Client/Models/ErrorStatus.cs b/src/Common/AppService.Client/Models/ErrorStatus.cs deleted file mode 100644 index 200349ec..00000000 --- a/src/Common/AppService.Client/Models/ErrorStatus.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AppService.Client.Models -{ - /// - /// Defines an application error current status. - /// - public enum ErrorStatus - { - /// - /// The error has not been fixed yet. - /// - NotFixed, - /// - /// The error has been fixed in current application version. - /// - Fixed, - /// - /// The error can not be reproduced by the application author. - /// - CannotReproduce - } -} \ No newline at end of file diff --git a/src/Common/AppService.Client/NoOpAppServiceClient.cs b/src/Common/AppService.Client/NoOpAppServiceClient.cs deleted file mode 100644 index 5684bd13..00000000 --- a/src/Common/AppService.Client/NoOpAppServiceClient.cs +++ /dev/null @@ -1,24 +0,0 @@ -using AppService.Client.Models; - -namespace AppService.Client -{ - /// - /// Provides a no-op implementation for . - /// It is used when AppSerive Uri has not been specified. - /// - /// - internal sealed class NoOpAppServiceClient : IAppServiceClient - { - public Task GetProductAsync(string name, CancellationToken cancellationToken = default) => Task.FromResult(null); - - public Task SendErrorReportAsync( - string application, - string errorMessage, - Version? appVersion = null, - DateTime? time = null, - CancellationToken cancellationToken = default) => - Task.FromResult(ErrorStatus.NotFixed); - - public void Dispose() { } - } -} diff --git a/src/Common/AppService.Client/ServiceCollectionExtensions.cs b/src/Common/AppService.Client/ServiceCollectionExtensions.cs deleted file mode 100644 index 854fcb8f..00000000 --- a/src/Common/AppService.Client/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System.Net; - -namespace AppService.Client -{ - /// - /// Provides an extension method for adding implementation to service collection. - /// - public static class ServiceCollectionExtensions - { - /// - /// Adds implementation to service collection. - /// - /// - /// When no gameresult service Uri has been provided, ads no-op implementation. - /// - /// Service collection. - /// App configuration. - public static IServiceCollection AddAppServiceClient(this IServiceCollection services, IConfiguration configuration) - { - var optionsSection = configuration.GetSection(AppServiceClientOptions.ConfigurationSectionName); - services.Configure(optionsSection); - - var options = optionsSection.Get(); - - if (options?.ServiceUri != null) - { - services.AddHttpClient( - client => - { - client.BaseAddress = options?.ServiceUri; - client.DefaultRequestVersion = HttpVersion.Version20; - }); - } - else - { - services.AddSingleton(); - } - - return services; - } - } -} diff --git a/src/Common/AppService.Client/key.snk b/src/Common/AppService.Client/key.snk deleted file mode 100644 index 53c34449..00000000 Binary files a/src/Common/AppService.Client/key.snk and /dev/null differ diff --git a/src/Common/SI.GameServer.Client/GameServerClient.cs b/src/Common/SI.GameServer.Client/GameServerClient.cs index f86d548e..d6ecf90c 100644 --- a/src/Common/SI.GameServer.Client/GameServerClient.cs +++ b/src/Common/SI.GameServer.Client/GameServerClient.cs @@ -6,11 +6,8 @@ using SIData; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Handlers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -22,8 +19,6 @@ namespace SI.GameServer.Client; /// public sealed class GameServerClient : IGameServerClient { - private const int _BufferSize = 80 * 1024; - private bool _isOpened; private readonly GameServerClientOptions _options; @@ -31,7 +26,6 @@ public sealed class GameServerClient : IGameServerClient private readonly CookieContainer _cookieContainer; private readonly HttpClientHandler _httpClientHandler; private readonly HttpClient _client; - private readonly ProgressMessageHandler _progressMessageHandler; private HubConnection? _connection; @@ -56,7 +50,6 @@ private HubConnection Connection public event Action? Joined; public event Action? Leaved; public event Action? Receieve; - public event Action? UploadProgress; public event Action? IncomingMessage; public event Func? Closed; @@ -73,10 +66,7 @@ public GameServerClient(IOptions options, IUIThreadExec _cookieContainer = new CookieContainer(); _httpClientHandler = new HttpClientHandler { CookieContainer = _cookieContainer }; - _progressMessageHandler = new ProgressMessageHandler(_httpClientHandler); - _progressMessageHandler.HttpSendProgress += MessageHandler_HttpSendProgress; - - _client = new HttpClient(_progressMessageHandler) + _client = new HttpClient(_httpClientHandler) { BaseAddress = new Uri(ServiceUri), Timeout = _options.Timeout, @@ -87,74 +77,6 @@ public GameServerClient(IOptions options, IUIThreadExec public Task RunGameAsync(RunGameRequest runGameRequest, CancellationToken cancellationToken = default) => Connection.InvokeAsync("RunGame", runGameRequest, cancellationToken); - public Task CreateGameAsync( - GameSettingsCore gameSettings, - PackageKey packageKey, - ComputerAccountInfo[] computerAccounts, - CancellationToken cancellationToken = default) - { - gameSettings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - - return _connection.InvokeAsync( - "CreateGame", - gameSettings, - packageKey, - computerAccounts.Select(ca => ca.Account).ToArray(), - cancellationToken); - } - - public Task CreateGame2Async( - GameSettingsCore gameSettings, - PackageInfo packageInfo, - ComputerAccountInfo[] computerAccounts, - CancellationToken cancellationToken = default) - { - gameSettings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - - return Connection.InvokeAsync( - "CreateGame2", - gameSettings, - packageInfo, - computerAccounts.Select(ca => ca.Account).ToArray(), - cancellationToken); - } - - public Task CreateAndJoinGameAsync( - GameSettingsCore gameSettings, - PackageKey packageKey, - ComputerAccountInfo[] computerAccounts, - bool isMale, - CancellationToken cancellationToken = default) - { - gameSettings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - - return _connection.InvokeAsync( - "CreateAndJoinGameNew", - gameSettings, - packageKey, - computerAccounts.Select(ca => ca.Account).ToArray(), - isMale, - cancellationToken); - } - - public Task CreateAndJoinGame2Async( - GameSettingsCore gameSettings, - PackageInfo packageInfo, - ComputerAccountInfo[] computerAccounts, - bool isMale, - CancellationToken cancellationToken = default) - { - gameSettings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - - return Connection.InvokeAsync( - "CreateAndJoinGame2", - gameSettings, - packageInfo, - computerAccounts.Select(ca => ca.Account).ToArray(), - isMale, - cancellationToken); - } - public async ValueTask DisposeAsync() { if (_connection != null) @@ -179,7 +101,7 @@ public Task> GetGamesAsync(int fromId, CancellationToken cancell _connection.InvokeAsync>("GetGamesSlice", fromId, cancellationToken); public Task GetGamesHostInfoAsync(CancellationToken cancellationToken = default) => - _connection.InvokeAsync("GetGamesHostInfo", cancellationToken); + _connection.InvokeAsync("GetGamesHostInfoNew", Thread.CurrentThread.CurrentUICulture.Name, cancellationToken); public Task GetNewsAsync(CancellationToken cancellationToken = default) => _connection.InvokeAsync("GetNews", cancellationToken); @@ -190,13 +112,7 @@ public Task GetLatestChatMessagesAsync(CancellationToken cancella public Task GetUsersAsync(CancellationToken cancellationToken = default) => _connection.InvokeAsync("GetUsers", cancellationToken); - public Task HasPackageAsync(PackageKey packageKey, CancellationToken cancellationToken = default) => - _connection.InvokeAsync("HasPackage", packageKey, cancellationToken); - - public Task HasImageAsync(FileKey imageKey, CancellationToken cancellationToken = default) => - _connection.InvokeAsync("HasPicture", imageKey, cancellationToken); - - private async Task AuthenticateUserAsync( + private async Task AuthenticateUserAsync( string user, string password, CancellationToken cancellationToken = default) @@ -214,7 +130,7 @@ private async Task AuthenticateUserAsync( if (response.IsSuccessStatusCode) { - return await response.Content.ReadAsStringAsync(cancellationToken); + return; } throw response.StatusCode switch @@ -232,14 +148,15 @@ public async Task OpenAsync(string userName, CancellationToken cancellationToken throw new InvalidOperationException("Client has been already opened"); } - var token = await AuthenticateUserAsync(userName, "", cancellationToken); + await AuthenticateUserAsync(userName, "", cancellationToken); _connection = new HubConnectionBuilder() .WithUrl( - $"{ServiceUri}sionline?token={token}", + $"{ServiceUri}sionline", options => { options.AccessTokenProvider = () => Task.FromResult(Convert.ToBase64String(Encoding.UTF8.GetBytes(userName))); + options.Cookies = _cookieContainer; }) .WithAutomaticReconnect(new ReconnectPolicy()) .AddMessagePackProtocol() @@ -287,98 +204,6 @@ public async Task OpenAsync(string userName, CancellationToken cancellationToken public Task SayAsync(string message) => _connection.InvokeAsync("Say", message); - public async Task UploadPackageAsync(FileKey packageKey, Stream stream, CancellationToken cancellationToken = default) - { - var url = "api/upload/package"; - using var content = new StreamContent(stream, _BufferSize); - - try - { - using var formData = new MultipartFormDataContent - { - { content, "file", packageKey.Name } - }; - - formData.Headers.ContentMD5 = packageKey.Hash; - using var response = await _client.PostAsync(url, formData, cancellationToken); - - if (!response.IsSuccessStatusCode) - { - var errorMessage = await GetErrorMessageAsync(response, cancellationToken); - throw new Exception(errorMessage); - } - } - catch (HttpRequestException exc) - { - throw new Exception(Resources.UploadPackageConnectionError, exc.InnerException ?? exc); - } - catch (TaskCanceledException exc) - { - if (!exc.CancellationToken.IsCancellationRequested) - { - throw new Exception(Resources.UploadPackageTimeout, exc); - } - - throw exc; - } - } - - public async Task UploadImageAsync(FileKey imageKey, Stream data, CancellationToken cancellationToken = default) - { - var uri = "api/upload/image"; - - var bytesContent = new StreamContent(data, _BufferSize); - - using var formData = new MultipartFormDataContent - { - { bytesContent, Convert.ToBase64String(imageKey.Hash), imageKey.Name } - }; - - formData.Headers.ContentMD5 = imageKey.Hash; - - var response = await _client.PostAsync(uri, formData, cancellationToken); - - if (!response.IsSuccessStatusCode) - { - var errorString = await response.Content.ReadAsStringAsync(cancellationToken); - throw new Exception($"{response.StatusCode}: {errorString}"); - } - - return await response.Content.ReadAsStringAsync(cancellationToken); - } - - public Task JoinGameAsync( - int gameId, - GameRole role, - bool isMale, - string password, - CancellationToken cancellationToken = default) => - _connection.InvokeAsync("JoinGameNew", gameId, (int)role, isMale, password, cancellationToken); - - public Task SendMessageAsync(Message message, CancellationToken cancellationToken = default) => - _connection.InvokeAsync("SendMessage", message, cancellationToken); - - public Task LeaveGameAsync(CancellationToken cancellationToken = default) => - _connection.InvokeAsync("LeaveGame", cancellationToken); - - private static async Task GetErrorMessageAsync(HttpResponseMessage response, CancellationToken cancellationToken) - { - var serverError = await response.Content.ReadAsStringAsync(cancellationToken); - - if (response.StatusCode == HttpStatusCode.RequestEntityTooLarge || - response.StatusCode == HttpStatusCode.BadRequest && serverError == "Request body too large.") - { - return Resources.FileTooLarge; - } - - if (response.StatusCode == HttpStatusCode.BadGateway) - { - return $"{response.StatusCode}: Bad Gateway"; - } - - return $"{response.StatusCode}: {serverError}"; - } - private Task OnConnectionClosedAsync(Exception? exc) => Closed != null ? Closed(exc) : Task.CompletedTask; private void OnUI(Action action) @@ -392,8 +217,6 @@ private void OnUI(Action action) action(); } - private void MessageHandler_HttpSendProgress(object? sender, HttpProgressEventArgs e) => UploadProgress?.Invoke(e.ProgressPercentage); - private sealed class ReconnectPolicy : IRetryPolicy { public TimeSpan? NextRetryDelay(RetryContext retryContext) => TimeSpan.FromSeconds(5); diff --git a/src/Common/SI.GameServer.Client/IGameServerClient.cs b/src/Common/SI.GameServer.Client/IGameServerClient.cs index 46b00066..fbf24cd6 100644 --- a/src/Common/SI.GameServer.Client/IGameServerClient.cs +++ b/src/Common/SI.GameServer.Client/IGameServerClient.cs @@ -1,7 +1,5 @@ using SI.GameServer.Contract; -using SIData; using System; -using System.IO; using System.Threading; using System.Threading.Tasks; @@ -10,10 +8,25 @@ namespace SI.GameServer.Client; /// /// Provides a client to SIGame server. /// -public interface IGameServerClient : IGameClient +public interface IGameServerClient : IAsyncDisposable { string ServiceUri { get; } + /// + /// Reconnecting event. + /// + event Func Reconnecting; + + /// + /// Reconnected event. + /// + event Func Reconnected; + + /// + /// Connection closed event. + /// + event Func Closed; + event Action GameCreated; event Action GameDeleted; event Action GameChanged; @@ -22,8 +35,6 @@ public interface IGameServerClient : IGameClient event Action Leaved; event Action Receieve; - event Action UploadProgress; - Task OpenAsync(string userName, CancellationToken token = default); /// @@ -50,62 +61,7 @@ public interface IGameServerClient : IGameClient Task> GetGamesAsync(int fromId, CancellationToken cancellationToken = default); - Task HasPackageAsync(PackageKey packageKey, CancellationToken cancellationToken = default); - - Task UploadPackageAsync(FileKey packageHash, Stream stream, CancellationToken cancellationToken = default); - - Task RunGameAsync( - RunGameRequest runGameRequest, - CancellationToken cancellationToken = default); - - Task CreateGameAsync( - GameSettingsCore gameSettings, - PackageKey packageKey, - ComputerAccountInfo[] computerAccounts, - CancellationToken cancellationToken = default); - - Task CreateGame2Async( - GameSettingsCore gameSettings, - PackageInfo packageInfo, - ComputerAccountInfo[] computerAccounts, - CancellationToken cancellationToken = default); - - Task HasImageAsync(FileKey imageKey, CancellationToken cancellationToken = default); - - Task UploadImageAsync(FileKey imageKey, Stream data, CancellationToken cancellationToken = default); + Task RunGameAsync(RunGameRequest runGameRequest, CancellationToken cancellationToken = default); Task SayAsync(string message); - - Task JoinGameAsync( - int gameId, - GameRole role, - bool isMale, - string password, - CancellationToken cancellationToken = default); - - Task SendMessageAsync(Message message, CancellationToken cancellationToken = default); - - Task CreateAndJoinGameAsync( - GameSettingsCore gameSettings, - PackageKey packageKey, - ComputerAccountInfo[] computerAccounts, - bool isMale, - CancellationToken cancellationToken = default); - - /// - /// Creates a new game and joins its. - /// - /// Game settings. - /// Package info. - /// Custom computer accounts info. - /// Male flag. - /// Cancellation token. - Task CreateAndJoinGame2Async( - GameSettingsCore gameSettings, - PackageInfo packageInfo, - ComputerAccountInfo[] computerAccounts, - bool isMale, - CancellationToken cancellationToken = default); - - Task LeaveGameAsync(CancellationToken cancellationToken = default); } diff --git a/src/Common/SIPackages.Providers/SIPackages.Providers.csproj b/src/Common/SIPackages.Providers/SIPackages.Providers.csproj index 7e733096..56dee7fd 100644 --- a/src/Common/SIPackages.Providers/SIPackages.Providers.csproj +++ b/src/Common/SIPackages.Providers/SIPackages.Providers.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Common/SIPackages/GlobalData.cs b/src/Common/SIPackages/GlobalData.cs index 5b37e8d4..3fc9291c 100644 --- a/src/Common/SIPackages/GlobalData.cs +++ b/src/Common/SIPackages/GlobalData.cs @@ -1,6 +1,5 @@ using SIPackages.Models; using System.Xml; -using System.Xml.Schema; namespace SIPackages; diff --git a/src/Common/SIStorageService.ViewModel/SIStorageService.ViewModel.csproj b/src/Common/SIStorageService.ViewModel/SIStorageService.ViewModel.csproj index 9f79d943..317f1401 100644 --- a/src/Common/SIStorageService.ViewModel/SIStorageService.ViewModel.csproj +++ b/src/Common/SIStorageService.ViewModel/SIStorageService.ViewModel.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Common/SIUI.ViewModel/TableInfoViewModel.cs b/src/Common/SIUI.ViewModel/TableInfoViewModel.cs index e3dd7c65..bc05a9f6 100644 --- a/src/Common/SIUI.ViewModel/TableInfoViewModel.cs +++ b/src/Common/SIUI.ViewModel/TableInfoViewModel.cs @@ -373,7 +373,7 @@ public double Volume get => _volume; set { - if (_volume != value) + if (_volume != value && _volume >= 0.0 && _volume <= 1.0) { var oldValue = _volume; _volume = value; diff --git a/src/Common/SIUI/Behaviors/MediaController.cs b/src/Common/SIUI/Behaviors/MediaController.cs index bf8dbad8..ae6505d4 100644 --- a/src/Common/SIUI/Behaviors/MediaController.cs +++ b/src/Common/SIUI/Behaviors/MediaController.cs @@ -24,7 +24,7 @@ static MediaController() public static void UpdateVolume(double factor) { - Volume *= factor; + Volume = Math.Min(1.0, Math.Max(0.0, Volume * factor)); _ = waveOutSetVolume(IntPtr.Zero, (uint)(0xFFFF * 2 * Volume)); } @@ -125,9 +125,9 @@ void resumeHandler() mediaElement.Dispatcher.BeginInvoke(mediaElement.Play); } - void volumeChangedHandler(double volume) + void volumeChangedHandler(double factor) { - mediaElement.Volume *= volume; + mediaElement.Volume = Math.Min(1.0, Math.Max(0.0, mediaElement.Volume * factor)); } tableInfo.MediaSeek += seekHandler; diff --git a/src/Common/Utils/UI.cs b/src/Common/Utils/UI.cs index 0dbe4920..e11619ac 100644 --- a/src/Common/Utils/UI.cs +++ b/src/Common/Utils/UI.cs @@ -7,6 +7,9 @@ public static class UI { public static TaskScheduler? Scheduler { get; private set; } // TODO: -> private + /// + /// Captures UI thread task scheduler. + /// public static void Initialize() { Scheduler = TaskScheduler.FromCurrentSynchronizationContext(); diff --git a/src/SICore/SICore/Clients/IConnector.cs b/src/SICore/SICore/Clients/IConnector.cs index 6d5bc664..5f0fba25 100644 --- a/src/SICore/SICore/Clients/IConnector.cs +++ b/src/SICore/SICore/Clients/IConnector.cs @@ -1,4 +1,5 @@ -namespace SICore; + +namespace SICore; public interface IConnector { @@ -12,6 +13,8 @@ public interface IConnector int GameId { get; } + Uri? HostUri { get; } + Task ReconnectToServer(); Task RejoinGame(); @@ -19,4 +22,6 @@ public interface IConnector void SetHost(IViewerClient newHost); void SetGameID(int gameID); + + void SetHostUri(Uri? hostUri); } diff --git a/src/SICore/SICore/Clients/Other/SIReport.cs b/src/SICore/SICore/Clients/Other/SIReport.cs index 43177e9a..6adebaea 100644 --- a/src/SICore/SICore/Clients/Other/SIReport.cs +++ b/src/SICore/SICore/Clients/Other/SIReport.cs @@ -2,38 +2,41 @@ using System.Runtime.CompilerServices; using System.Windows.Input; -namespace SICore +namespace SICore; + +/// +/// Defines a user provided report. +/// +public sealed class SIReport : INotifyPropertyChanged { - public sealed class SIReport : INotifyPropertyChanged - { - private string _report = ""; + private string _report = ""; - public string Report - { - get { return _report; } - set { _report = value; OnPropertyChanged(); } - } + public string Report + { + get => _report; + set { _report = value; OnPropertyChanged(); } + } - private string _comment = ""; + private string _comment = ""; - public string Comment - { - get { return _comment; } - set { _comment = value; OnPropertyChanged(); } - } + public string Comment + { + get => _comment; + set { _comment = value; OnPropertyChanged(); } + } - public string Title { get; set; } + public string Title { get; set; } - public string Subtitle { get; set; } + public string Subtitle { get; set; } - public ICommand SendReport { get; set; } - public ICommand SendNoReport { get; set; } + public ICommand SendReport { get; set; } - private void OnPropertyChanged([CallerMemberName] string name = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); - } + public ICommand SendNoReport { get; set; } - public event PropertyChangedEventHandler PropertyChanged; + private void OnPropertyChanged([CallerMemberName] string? name = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } + + public event PropertyChangedEventHandler? PropertyChanged; } diff --git a/src/SICore/SICore/Clients/Viewer/Viewer.cs b/src/SICore/SICore/Clients/Viewer/Viewer.cs index 8acc8443..4d9317e7 100644 --- a/src/SICore/SICore/Clients/Viewer/Viewer.cs +++ b/src/SICore/SICore/Clients/Viewer/Viewer.cs @@ -2138,6 +2138,7 @@ await _client.Node.ConnectionsLock.WithLockAsync(() => UpdateDeleteTableCommand(); SendPicture(); + _viewerActions.SendMessage(Messages.Moveable); } private void UpdateDeleteTableCommand() diff --git a/src/SICore/SICore/Messages.cs b/src/SICore/SICore/Messages.cs index 3eeccfa7..e05695cb 100644 --- a/src/SICore/SICore/Messages.cs +++ b/src/SICore/SICore/Messages.cs @@ -275,6 +275,11 @@ public static class Messages /// public const string Move = "MOVE"; + /// + /// Denotes that the person could be moved during the game. + /// + public const string Moveable = "MOVEABLE"; + /// /// Выбрать следующего игрока /// diff --git a/src/SIGame/SIGame.ViewModel/Helpers/GameResultExtensions.cs b/src/SIGame/SIGame.ViewModel/Helpers/GameResultExtensions.cs index 3ec54c41..624d9c51 100644 --- a/src/SIGame/SIGame.ViewModel/Helpers/GameResultExtensions.cs +++ b/src/SIGame/SIGame.ViewModel/Helpers/GameResultExtensions.cs @@ -35,17 +35,13 @@ public static GameReport ToGameReport(this GameResult gameResult) return new GameReport { - Info = new GameResultInfo + Info = new GameResultInfo( + new PackageInfo(gameResult.PackageName, gameResult.PackageHash, gameResult.PackageAuthors, gameResult.PackageAuthorsContacts), + gameResult.Language) { FinishTime = DateTimeOffset.UtcNow, Duration = gameResult.Duration, Name = gameResult.Name, - Package = new PackageInfo - { - Authors = gameResult.PackageAuthors, - Hash = gameResult.PackageHash, - Name = gameResult.PackageName - }, Platform = GamePlatforms.Local, Results = gameResult.Results, Reviews = gameResult.Reviews diff --git a/src/SIGame/SIGame.ViewModel/PackageSources/RandomServerPackageSource.cs b/src/SIGame/SIGame.ViewModel/PackageSources/RandomServerPackageSource.cs deleted file mode 100644 index 1fec626d..00000000 --- a/src/SIGame/SIGame.ViewModel/PackageSources/RandomServerPackageSource.cs +++ /dev/null @@ -1,23 +0,0 @@ -using SIGame.ViewModel.Properties; - -namespace SIGame.ViewModel.PackageSources; - -/// -/// Случайный пакет сервера -/// -public sealed class RandomServerPackageSource : PackageSource -{ - public override PackageSourceKey Key => new PackageSourceKey { Type = PackageSourceTypes.RandomServer }; - - public override string Source => Resources.RandomServerThemes; - - public override bool RandomSpecials => true; - - public override Task<(string, bool)> GetPackageFileAsync(CancellationToken cancellationToken = default) => - throw new NotImplementedException(); - - public override Task GetPackageHashAsync(CancellationToken cancellationToken = default) => - Task.FromResult(Array.Empty()); - - public override string GetPackageName() => ""; -} diff --git a/src/SIGame/SIGame.ViewModel/PackageSources/RandomStoragePackageSource.cs b/src/SIGame/SIGame.ViewModel/PackageSources/RandomStoragePackageSource.cs new file mode 100644 index 00000000..cecebef0 --- /dev/null +++ b/src/SIGame/SIGame.ViewModel/PackageSources/RandomStoragePackageSource.cs @@ -0,0 +1,64 @@ +using SIGame.ViewModel.Properties; +using SIStorage.Service.Contract; +using SIStorage.Service.Contract.Requests; +using System.Net; + +namespace SIGame.ViewModel.PackageSources; + +/// +/// Represents random storage package source. +/// +public sealed class RandomStoragePackageSource : PackageSource +{ + private readonly ISIStorageServiceClient _storageServiceClient; + private Uri? _packageUri; + + public override PackageSourceKey Key => new() { Type = PackageSourceTypes.RandomServer }; + + public override string Source => Resources.RandomServerThemes; + + public override bool RandomSpecials => true; + + public RandomStoragePackageSource(ISIStorageServiceClient storageServiceClient) => _storageServiceClient = storageServiceClient; + + public override Task<(string, bool)> GetPackageFileAsync(CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public override async Task GetPackageHashAsync(CancellationToken cancellationToken = default) + { + if (_packageUri == null) + { + _packageUri = await CreateRandomPackageAsync(cancellationToken); + } + + return Array.Empty(); + } + + public override string GetPackageName() => ""; + + public override Uri? GetPackageUri() => _packageUri; + + private async Task CreateRandomPackageAsync(CancellationToken cancellationToken = default) + { + try + { + var package = await _storageServiceClient.Packages.GetRandomPackageAsync( + new RandomPackageParameters + { + RestrictionIds = new int[] { -1 } + }, + cancellationToken); + + if (package.DirectContentUri == null) + { + throw new Exception("Random package generation failed"); + } + + return package.DirectContentUri; + } + catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests) + { + throw new Exception(Resources.TooMuchRandomPackages, ex); + } + } +} diff --git a/src/SIGame/SIGame.ViewModel/Properties/Resources.Designer.cs b/src/SIGame/SIGame.ViewModel/Properties/Resources.Designer.cs index 9888e3ec..d74764b1 100644 --- a/src/SIGame/SIGame.ViewModel/Properties/Resources.Designer.cs +++ b/src/SIGame/SIGame.ViewModel/Properties/Resources.Designer.cs @@ -1230,6 +1230,15 @@ public static string TooManyGames { } } + /// + /// Ищет локализованную строку, похожую на You created too much random pakages. Please try again later. + /// + public static string TooMuchRandomPackages { + get { + return ResourceManager.GetString("TooMuchRandomPackages", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Unknown error. /// diff --git a/src/SIGame/SIGame.ViewModel/Properties/Resources.resx b/src/SIGame/SIGame.ViewModel/Properties/Resources.resx index 0b06938f..1f5149eb 100644 --- a/src/SIGame/SIGame.ViewModel/Properties/Resources.resx +++ b/src/SIGame/SIGame.ViewModel/Properties/Resources.resx @@ -525,4 +525,7 @@ Unknown error + + You created too much random pakages. Please try again later + \ No newline at end of file diff --git a/src/SIGame/SIGame.ViewModel/Properties/Resources.ru-RU.resx b/src/SIGame/SIGame.ViewModel/Properties/Resources.ru-RU.resx index 91d44f69..a4c58ef1 100644 --- a/src/SIGame/SIGame.ViewModel/Properties/Resources.ru-RU.resx +++ b/src/SIGame/SIGame.ViewModel/Properties/Resources.ru-RU.resx @@ -522,4 +522,7 @@ Неизвестная ошибка + + Вы создали слишком много случайных пакетов. Попробуйте ещё раз позже + \ No newline at end of file diff --git a/src/SIGame/SIGame.ViewModel/SIGame.ViewModel.csproj b/src/SIGame/SIGame.ViewModel/SIGame.ViewModel.csproj index 08226b49..636f33b5 100644 --- a/src/SIGame/SIGame.ViewModel/SIGame.ViewModel.csproj +++ b/src/SIGame/SIGame.ViewModel/SIGame.ViewModel.csproj @@ -12,8 +12,8 @@ enable - - + + diff --git a/src/SIGame/SIGame.ViewModel/Settings/ErrorInfo.cs b/src/SIGame/SIGame.ViewModel/Settings/ErrorInfo.cs index 4e857f62..7dbee15a 100644 --- a/src/SIGame/SIGame.ViewModel/Settings/ErrorInfo.cs +++ b/src/SIGame/SIGame.ViewModel/Settings/ErrorInfo.cs @@ -1,10 +1,17 @@ namespace SIGame; +/// +/// Defines an error saved for sending to AppRegistry service. +/// public sealed class ErrorInfo { - public string Version { get; set; } + public string? Version { get; set; } + public DateTime Time { get; set; } - public string Error { get; set; } + + public string? Error { get; set; } + + public string? UserNotes { get; set; } } public sealed class ErrorInfoList : List { } diff --git a/src/SIGame/SIGame.ViewModel/Settings/UserSettings.cs b/src/SIGame/SIGame.ViewModel/Settings/UserSettings.cs index afb7e73d..e6db95fb 100644 --- a/src/SIGame/SIGame.ViewModel/Settings/UserSettings.cs +++ b/src/SIGame/SIGame.ViewModel/Settings/UserSettings.cs @@ -67,7 +67,7 @@ public bool MainMenuSound private double _volume = 25; /// - /// Громкость звука + /// Sound volume level. /// public double Volume { diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/GameSettingsViewModel.cs b/src/SIGame/SIGame.ViewModel/ViewModel/GameSettingsViewModel.cs index 3e5eff5d..6103d3dc 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/GameSettingsViewModel.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/GameSettingsViewModel.cs @@ -1,4 +1,5 @@ -using SICore; +using Microsoft.Extensions.DependencyInjection; +using SICore; using SICore.BusinessLogic; using SICore.Clients; using SICore.Contracts; @@ -14,6 +15,7 @@ using SIGame.ViewModel.Properties; using SIGame.ViewModel.Web; using SIPackages; +using SIStorage.Service.Contract; using SIStorageService.ViewModel; using SIUI.ViewModel; using System.Collections.ObjectModel; @@ -440,7 +442,7 @@ public GameSettingsViewModel( break; case PackageSourceTypes.RandomServer: - Package = new RandomServerPackageSource(); + Package = new RandomStoragePackageSource(PlatformManager.Instance.ServiceProvider!.GetRequiredService()); break; case PackageSourceTypes.Local: @@ -524,7 +526,7 @@ private async Task SelectPackage_Executed(object? arg) break; case PackageSourceTypes.RandomServer: - Package = new RandomServerPackageSource(); + Package = new RandomStoragePackageSource(PlatformManager.Instance.ServiceProvider!.GetRequiredService()); break; case PackageSourceTypes.Local: diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/GameViewModel.cs b/src/SIGame/SIGame.ViewModel/ViewModel/GameViewModel.cs index f7c124b3..7221baa2 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/GameViewModel.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/GameViewModel.cs @@ -365,7 +365,9 @@ private async void PrintOnlineInformation() try { - Host.AddLog($"{Resources.OnlineGameAddress}: {CommonSettings.NewOnlineGameUrl}{Host.Connector?.GameId}&invite=true"); + var hostUri = Host.Connector?.HostUri; + var hostInfo = hostUri != null ? "&host=" + Uri.EscapeDataString(hostUri.ToString()) : ""; + Host.AddLog($"{Resources.OnlineGameAddress}: {CommonSettings.NewOnlineGameUrl}{Host.Connector?.GameId}{hostInfo}&invite=true"); } catch (Exception exc) { diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/Logic/ViewerHumanLogic.cs b/src/SIGame/SIGame.ViewModel/ViewModel/Logic/ViewerHumanLogic.cs index 13b4f9ea..9f7b2e5e 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/Logic/ViewerHumanLogic.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/Logic/ViewerHumanLogic.cs @@ -699,7 +699,6 @@ public void OnContent(string[] mparams) if (content.Count == 0) { - OnSpecialReplic("No content found"); return; } diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/MainViewModel.cs b/src/SIGame/SIGame.ViewModel/ViewModel/MainViewModel.cs index c461df75..d1ab2f6a 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/MainViewModel.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/MainViewModel.cs @@ -100,7 +100,7 @@ public bool IsSlideMenuOpen public ICommand CloseSlideMenu { get; private set; } - private string _startMenuPage; + private string _startMenuPage = "MainMenuView.xaml"; public string StartMenuPage { diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/ReconnectManager.cs b/src/SIGame/SIGame.ViewModel/ViewModel/ReconnectManager.cs index f0091245..9d2594b6 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/ReconnectManager.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/ReconnectManager.cs @@ -30,6 +30,8 @@ internal sealed class ReconnectManager : IConnector public int GameId { get; set; } = -1; + public Uri? HostUri { get; set; } + public ReconnectManager( SecondaryNode server, Client client, @@ -147,4 +149,6 @@ private async Task JoinGameAsync() } public void SetHost(IViewerClient newHost) => _host = newHost; + + public void SetHostUri(Uri? hostUri) => HostUri = hostUri; } diff --git a/src/SIGame/SIGame.ViewModel/ViewModel/SIOnlineViewModel.cs b/src/SIGame/SIGame.ViewModel/ViewModel/SIOnlineViewModel.cs index eeee1bd7..d9a12c82 100644 --- a/src/SIGame/SIGame.ViewModel/ViewModel/SIOnlineViewModel.cs +++ b/src/SIGame/SIGame.ViewModel/ViewModel/SIOnlineViewModel.cs @@ -25,7 +25,12 @@ namespace SIGame.ViewModel; /// public sealed class SIOnlineViewModel : ConnectionDataViewModel { - private const int SupportedProtocolVersion = 0; + /// + /// Random package marker. + /// + private const string RandomIndicator = "@{random}"; + + private const int SupportedProtocolVersion = 1; public const int MaxAvatarSizeMb = 2; @@ -365,8 +370,6 @@ public SIOnlineViewModel( _gameServerClient.Reconnected += GameServerClient_Reconnected; _gameServerClient.Closed += GameServerClient_Closed; - _gameServerClient.UploadProgress += GameServerClient_UploadProgress; - ServerAddress = _gameServerClient.ServiceUri; AddEmoji = new CustomCommand(AddEmoji_Executed); @@ -416,8 +419,6 @@ private Task GameServerClient_Reconnected(string? message) private void AddEmoji_Executed(object? arg) => ChatText += arg.ToString(); - private void GameServerClient_UploadProgress(int progress) => UploadProgress = progress; - private Task GameServerClient_Closed(Exception? exception) { PlatformSpecific.PlatformManager.Instance.ShowMessage( @@ -694,8 +695,6 @@ protected override async Task ClearConnectionAsync() _gameServerClient.Reconnecting -= GameServerClient_Reconnecting; _gameServerClient.Reconnected -= GameServerClient_Reconnected; _gameServerClient.Closed -= GameServerClient_Closed; - - _gameServerClient.UploadProgress -= GameServerClient_UploadProgress; } await base.ClearConnectionAsync(); @@ -807,77 +806,7 @@ protected override void Prepare(GameSettingsViewModel gameSettings) Uri = packageSource.GetPackageUri() }; - if (!string.IsNullOrEmpty(packageKey.Name) && _gamesHostInfo?.ContentInfos?.Length > 0) - { - return await CreateGameWithContentServiceAsync(settings, packageKey, packageSource, cancellationTokenSource.Token); - } - - // This execution branch will sooner or later be deleted - var hasPackage = await _gameServerClient.HasPackageAsync(packageKey, cancellationTokenSource.Token); - - if (!hasPackage) - { - if (!await UploadPackageAsync(packageSource, packageKey, cancellationTokenSource.Token)) - { - return null; - } - } - - GameSettings.Message = Resources.Preparing; - - var computerAccounts = await ProcessCustomPersonsAsync(null, settings, cancellationTokenSource.Token); - - GameSettings.Message = Resources.Creating; - - if (_userSettings.UseSignalRConnection) - { - var gameCreatingResult2 = await _gameServerClient.CreateAndJoinGameAsync( - (GameSettingsCore)settings, - packageKey, - computerAccounts.ToArray(), - Human.IsMale, - cancellationTokenSource.Token); - - if (gameCreatingResult2.Code != SI.GameServer.Contract.GameCreationResultCode.Ok) - { - var errorDetail = gameCreatingResult2.ErrorMessage != null ? $": {gameCreatingResult2.ErrorMessage}" : null; - throw new Exception(GetMessage(gameCreatingResult2.Code) + errorDetail); - } - - await InitNodeAndClientNewAsync(_gameServerClient, cancellationTokenSource.Token); - await JoinGameCompletedAsync(settings.Role, true, cancellationTokenSource.Token); - - if (_host == null) - { - return null; - } - - _host.Connector.SetGameID(gameCreatingResult2.GameId); - - return (_node, _host); - } - - var gameCreatingResult = await _gameServerClient.CreateGameAsync( - (GameSettingsCore)settings, - packageKey, - computerAccounts.ToArray(), - cancellationTokenSource.Token); - - if (gameCreatingResult.Code != SI.GameServer.Contract.GameCreationResultCode.Ok) - { - throw new Exception(GetMessage(gameCreatingResult.Code)); - } - - GameSettings.Message = Resources.GameEntering; - - await ConnectToServerAsHostAsync(gameCreatingResult.GameId, settings, cancellationTokenSource.Token); - - if (_host == null) - { - return null; - } - - return (_node, _host); + return await CreateGameWithContentServiceAsync(settings, packageKey, packageSource, cancellationTokenSource.Token); } private async Task<(SecondaryNode, IViewerClient)?> CreateGameWithContentServiceAsync( @@ -903,7 +832,7 @@ protected override void Prepare(GameSettingsViewModel gameSettings) } else // Library package { - if (packageKey.Uri == null) + if (packageKey.Uri == null) // Random package { return null; } @@ -921,94 +850,42 @@ protected override void Prepare(GameSettingsViewModel gameSettings) GameSettings.Message = Resources.Creating; + settings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - // TODO: enable after server update - //settings.AppSettings.Culture = Thread.CurrentThread.CurrentUICulture.Name; - - //var runGameResponse = await _gameServerClient.RunGameAsync( - // new SI.GameServer.Contract.RunGameRequest(( - // GameSettingsCore)settings, - // packageInfo, - // computerAccounts.Select(ca => ca.Account).ToArray()), - // cancellationToken); - - //if (!runGameResponse.IsSuccess) - //{ - // throw new Exception(GetMessage(runGameResponse.ErrorType)); - //} - - // GameSettings.Message = Resources.GameEntering; - - //var name = Human.Name; - - //_password = settings.NetworkGamePassword; - - //var game = new GameInfo - //{ - // HostUri = runGameResponse.HostUri, - // GameID = runGameResponse.GameId, - // Owner = name - //}; - - //await JoinGameAsync(game, settings.Role, runGameResponse.IsHost, cancellationToken); - - //if (_host == null) - //{ - // return null; - //} - - //_host.Connector?.SetGameID(runGameResponse.GameId); - - //return (_node, _host); - - if (_userSettings.UseSignalRConnection) - { - var gameCreatingResult2 = await _gameServerClient.CreateAndJoinGame2Async( - (GameSettingsCore)settings, + var runGameResponse = await _gameServerClient.RunGameAsync( + new SI.GameServer.Contract.RunGameRequest(( + GameSettingsCore)settings, packageInfo, - computerAccounts.ToArray(), - Human.IsMale, - cancellationToken); - - if (gameCreatingResult2.Code != SI.GameServer.Contract.GameCreationResultCode.Ok) - { - var errorDetail = gameCreatingResult2.ErrorMessage != null ? $": {gameCreatingResult2.ErrorMessage}" : null; - throw new Exception(GetMessage(gameCreatingResult2.Code) + errorDetail); - } - - await InitNodeAndClientNewAsync(_gameServerClient, cancellationToken); - await JoinGameCompletedAsync(settings.Role, true, cancellationToken); - - if (_host == null) - { - return null; - } - - _host.Connector.SetGameID(gameCreatingResult2.GameId); - - return (_node, _host); - } - - var gameCreatingResult = await _gameServerClient.CreateGame2Async( - (GameSettingsCore)settings, - packageInfo, - computerAccounts.ToArray(), + computerAccounts.Select(ca => ca.Account).ToArray()), cancellationToken); - if (gameCreatingResult.Code != SI.GameServer.Contract.GameCreationResultCode.Ok) + if (!runGameResponse.IsSuccess) { - throw new Exception(GetMessage(gameCreatingResult.Code)); + throw new Exception(GetMessage(runGameResponse.ErrorType)); } GameSettings.Message = Resources.GameEntering; - await ConnectToServerAsHostAsync(gameCreatingResult.GameId, settings, cancellationToken); + var name = Human.Name; + + _password = settings.NetworkGamePassword; + + var game = new GameInfo + { + HostUri = runGameResponse.HostUri, + GameID = runGameResponse.GameId, + Owner = name + }; + + await JoinGameAsync(game, settings.Role, runGameResponse.IsHost, cancellationToken); if (_host == null) { return null; } + _host.Connector?.SetGameID(runGameResponse.GameId); + return (_node, _host); } @@ -1082,46 +959,6 @@ private SI.GameServer.Contract.SIContentInfo GetContentInfo() return _gamesHostInfo.ContentInfos[randomIndex]; } - private async Task UploadPackageAsync( - PackageSource packageSource, - PackageKey packageKey, - CancellationToken cancellationToken) - { - var data = await packageSource.GetPackageDataAsync(cancellationToken); - - if (data == null) - { - throw new Exception(Resources.BadPackage); - } - - using (data) - { - if (cancellationToken.IsCancellationRequested) - { - return false; - } - - if (data.Length > _gamesHostInfo.MaxPackageSizeMb * 1024 * 1024) - { - throw new Exception($"{Resources.FileTooLarge}. {string.Format(Resources.MaximumFileSize, _gamesHostInfo.MaxPackageSizeMb)}"); - } - - GameSettings.Message = Resources.SendingPackageToServer; - ShowProgress = true; - - try - { - await _gameServerClient.UploadPackageAsync(packageKey, data, cancellationToken); - } - finally - { - ShowProgress = false; - } - } - - return true; - } - private async Task InitNodeAndClientNewAsync(IGameClient gameClient, CancellationToken cancellationToken = default) { _node = new GameServerSlave( @@ -1216,48 +1053,32 @@ private async Task ProcessCustomPersonAsync( var avatarKey = new FileKey { Name = Path.GetFileName(avatarUri), Hash = fileHash }; - if (contentServiceClient?.ServiceUri != null) + if (contentServiceClient?.ServiceUri == null) { - var key = new SIContentService.Contract.Models.FileKey - { - Name = Path.GetFileName(avatarUri), - Hash = fileHash, - }; - - var avatarServerUri = await contentServiceClient.TryGetAvatarUriAsync(key, cancellationToken); - - if (avatarServerUri == null) - { - using var stream = File.OpenRead(avatarUri); - avatarServerUri = await contentServiceClient.UploadAvatarAsync(key, stream, cancellationToken); - } - - if (avatarServerUri != null && !avatarServerUri.IsAbsoluteUri) - { - // Prepend avatarServerUri with service content root uri - avatarServerUri = new Uri(contentServiceClient.ServiceUri, avatarServerUri.ToString().TrimStart('/')); - } - - return (avatarServerUri?.ToString(), avatarKey); + throw new InvalidOperationException("contentServiceClient?.ServiceUri == null"); } - // Upload file if it does not exist on server - var avatarPath = await _gameServerClient.HasImageAsync(avatarKey, cancellationToken); + var key = new SIContentService.Contract.Models.FileKey + { + Name = Path.GetFileName(avatarUri), + Hash = fileHash, + }; - if (avatarPath == null) + var avatarServerUri = await contentServiceClient.TryGetAvatarUriAsync(key, cancellationToken); + + if (avatarServerUri == null) { using var stream = File.OpenRead(avatarUri); - avatarPath = await _gameServerClient.UploadImageAsync(avatarKey, stream, cancellationToken); + avatarServerUri = await contentServiceClient.UploadAvatarAsync(key, stream, cancellationToken); } - if (avatarPath != null && !Uri.IsWellFormedUriString(avatarPath, UriKind.Absolute)) + if (avatarServerUri != null && !avatarServerUri.IsAbsoluteUri) { - // Prepend avatarPath with service content root uri - var rootAddress = _gamesHostInfo?.ContentPublicBaseUrls.FirstOrDefault() ?? _gameServerClient.ServiceUri; - avatarPath = rootAddress + avatarPath; + // Prepend avatarServerUri with service content root uri + avatarServerUri = new Uri(contentServiceClient.ServiceUri, avatarServerUri.ToString().TrimStart('/')); } - return (avatarPath, avatarKey); + return (avatarServerUri?.ToString(), avatarKey); } protected override string GetExtraCredentials() => !string.IsNullOrEmpty(_password) ? $"\n{_password}" : ""; @@ -1353,6 +1174,7 @@ public override async Task JoinGameCoreAsync( } _host.Connector.SetGameID(gameInfo.GameID); + _host.Connector.SetHostUri(gameInfo.HostUri); } catch (TaskCanceledException exc) { @@ -1366,16 +1188,6 @@ public override async Task JoinGameCoreAsync( } } - internal async Task ConnectToServerAsHostAsync(int gameID, GameSettings gameSettings, CancellationToken cancellationToken = default) - { - var name = Human.Name; - - _password = gameSettings.NetworkGamePassword; - var game = new GameInfo { GameID = gameID, Owner = name }; - - await JoinGameAsync(game, gameSettings.Role, true, cancellationToken); - } - public void Say(string message, bool system = false) { if (!system) @@ -1397,7 +1209,7 @@ private static GameInfo ToSICoreGame(SI.GameServer.Contract.GameInfo gameInfo) = GameName = gameInfo.GameName, Mode = gameInfo.Mode, Owner = gameInfo.Owner, - PackageName = gameInfo.PackageName, + PackageName = gameInfo.PackageName == RandomIndicator ? Resources.RandomServerThemes : gameInfo.PackageName, PasswordRequired = gameInfo.PasswordRequired, Persons = gameInfo.Persons, RealStartTime = gameInfo.RealStartTime == DateTime.MinValue ? DateTime.MinValue : gameInfo.RealStartTime.ToLocalTime(), diff --git a/src/SIGame/SIGame/App.xaml.cs b/src/SIGame/SIGame/App.xaml.cs index 0a700a87..e28eb677 100644 --- a/src/SIGame/SIGame/App.xaml.cs +++ b/src/SIGame/SIGame/App.xaml.cs @@ -1,4 +1,4 @@ -using AppService.Client; +using AppRegistryService.Client; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -17,17 +17,21 @@ using SIStorage.Service.Client; using System; using System.ComponentModel; -using System.Diagnostics; using System.Net; using System.Threading; using System.Windows; using System.Windows.Threading; using Utils; + #if UPDATE -using AppService.Client.Models; +using AppRegistryService.Contract; +using AppRegistryService.Contract.Responses; +using AppRegistryService.Contract.Requests; using SICore; +using System.Diagnostics; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using System.Net.Http; using System.Threading.Tasks; #endif @@ -39,6 +43,11 @@ namespace SIGame; /// public partial class App : Application { + /// + /// Unique application identifier. + /// + internal static readonly Guid AppId = Guid.Parse("4394ecaf-e377-4fee-aba8-303adc8cdc36"); + private IHost? _host; private ILogger? _logger; @@ -90,7 +99,7 @@ private void ConfigureServices(HostBuilderContext ctx, IServiceCollection servic { var configuration = ctx.Configuration; - services.AddAppServiceClient(configuration); + services.AddAppRegistryServiceClient(configuration); services.AddSIGameServerClient(configuration); services.AddSIStatisticsServiceClient(configuration); services.AddSIStorageServiceClient(configuration); @@ -163,8 +172,6 @@ protected override void OnStartup(StartupEventArgs e) } } - Trace.TraceInformation("Game launched"); - UserSettings.Default.UseSignalRConnection = UseSignalRConnection; UserSettings.Default.PropertyChanged += Default_PropertyChanged; @@ -218,7 +225,7 @@ protected override void OnStartup(StartupEventArgs e) } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) => - _logger.LogError("Common game error: {error}", e.ExceptionObject); + _logger?.LogError("Common game error: {error}", e.ExceptionObject); private void Default_PropertyChanged(object? sender, PropertyChangedEventArgs e) { @@ -236,7 +243,7 @@ private async void CheckUpdate() { try { - var updateInfo = await SearchForUpdatesAsync(); + var updateInfo = await TryFindUpdateAsync(); if (updateInfo != null) { @@ -245,7 +252,7 @@ private async void CheckUpdate() } catch (Exception exc) { - _logger.LogError(exc, "Update error: {error}", exc.Message); + _logger?.LogError(exc, "Update error: {error}", exc.Message); MessageBox.Show( string.Format(SIGame.Properties.Resources.UpdateException, exc.Message), @@ -256,39 +263,45 @@ private async void CheckUpdate() } /// - /// Произвести поиск обновлений + /// Tries to find a product update. /// - /// Нужно ли завершить приложение для выполнения обновления - private async Task SearchForUpdatesAsync() + private async Task TryFindUpdateAsync(CancellationToken cancellationToken = default) { - using var appService = _host.Services.GetRequiredService(); + EnsureThat.EnsureArg.IsNotNull(_host); - var assembly = Assembly.GetAssembly(typeof(MainViewModel)); + var assembly = Assembly.GetAssembly(typeof(MainViewModel)) ?? throw new Exception("Main assembly not found"); + var currentVersion = assembly.GetName().Version ?? throw new Exception("No app version found"); - if (assembly == null) - { - throw new Exception("assembly == null"); - } + var appRegistryClient = _host.Services.GetRequiredService(); - var currentVersion = assembly.GetName().Version; - var product = await appService.GetProductAsync("SI"); + var productInfo = await appRegistryClient.Apps.PostAppUsageAsync( + AppId, + new AppUsageInfo(currentVersion, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture), + cancellationToken); - if (product?.Version > currentVersion) + if (productInfo?.Release?.Version > currentVersion) { - return product; + return productInfo; } return null; } - private void SearchForUpdatesFinished(AppInfo updateInfo) + private void SearchForUpdatesFinished(AppInstallerReleaseInfoResponse updateInfo) { - var updateUri = updateInfo.Uri; + var updateUri = updateInfo.Installer?.Uri!; - var mainViewModel = (MainViewModel)MainWindow.DataContext; + if (updateInfo.Release != null && updateInfo.Release.IsMandatory) + { + Update_Executed(updateUri); + } + else + { + var mainViewModel = (MainViewModel)MainWindow.DataContext; - mainViewModel.StartMenu.UpdateVersion = updateInfo.Version; - mainViewModel.StartMenu.Update = new CustomCommand(obj => Update_Executed(updateUri)); + mainViewModel.StartMenu.UpdateVersion = updateInfo.Release?.Version!; + mainViewModel.StartMenu.Update = new CustomCommand(obj => Update_Executed(updateUri)); + } } private bool _isUpdating = false; @@ -351,6 +364,4 @@ protected override void OnExit(ExitEventArgs e) private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => e.Handled = new ExceptionHandler(_host.Services.GetRequiredService()).Handle(e.Exception); - - } diff --git a/src/SIGame/SIGame/Implementation/ErrorManager.cs b/src/SIGame/SIGame/Implementation/ErrorManager.cs index 0822d7c8..8dc07fd1 100644 --- a/src/SIGame/SIGame/Implementation/ErrorManager.cs +++ b/src/SIGame/SIGame/Implementation/ErrorManager.cs @@ -1,6 +1,6 @@ -using AppService.Client; -using AppService.Client.Models; -using Microsoft.Extensions.Options; +using AppRegistryService.Contract; +using AppRegistryService.Contract.Models; +using AppRegistryService.Contract.Requests; using SICore; using SIGame.Contracts; using SIGame.Properties; @@ -9,6 +9,7 @@ using System; using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows; @@ -19,29 +20,23 @@ namespace SIGame.Implementation; /// internal sealed class ErrorManager : IErrorManager { - private const string AppCode = "SI"; private static bool ErrorHappened = false; private static readonly object ErrorSync = new(); - private readonly IAppServiceClient _appServiceClient; + private readonly IAppRegistryServiceClient _appRegistryServiceClient; private readonly ISIStatisticsServiceClient _siStatisticsServiceClient; private readonly AppState _appState; - private readonly bool _useAppService; - public ErrorManager( - IAppServiceClient appServiceClient, + IAppRegistryServiceClient appRegistryServiceClient, ISIStatisticsServiceClient gameResultServiceClient, - AppState appState, - IOptions appServiceClientOptions) + AppState appState) { - _appServiceClient = appServiceClient; + _appRegistryServiceClient = appRegistryServiceClient; _siStatisticsServiceClient = gameResultServiceClient; _appState = appState; - - _useAppService = appServiceClientOptions.Value.ServiceUri != null; } public bool SendErrorReport(Exception e) @@ -96,12 +91,6 @@ public bool SendErrorReport(Exception e) private async void SendErrorMessage(string errorMessage, object sync) { - if (!_useAppService) - { - MessageBox.Show(errorMessage); - return; - } - try { var errorReport = new SIReport @@ -112,18 +101,24 @@ private async void SendErrorMessage(string errorMessage, object sync) Subtitle = Resources.ErrorDescribe }; - var reportView = new GameReportView { DataContext = errorReport }; + var reportView = new GameReportView { DataContext = errorReport, Foreground = Brushes.White, VerticalAlignment = VerticalAlignment.Center }; var mainWindow = Application.Current.MainWindow; errorReport.SendReport = new CustomCommand(async arg => { - var version = Assembly.GetExecutingAssembly().GetName().Version; - var message = errorMessage + Environment.NewLine + errorReport.Comment; + var version = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(); try { - var result = await _appServiceClient.SendErrorReportAsync(AppCode, message, version, DateTime.Now); + var result = await _appRegistryServiceClient.Apps.SendAppErrorReportAsync( + App.AppId, + new AppErrorRequest(version, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = errorMessage, + ErrorTime = DateTimeOffset.UtcNow, + UserNotes = errorReport.Comment + }); switch (result) { @@ -145,17 +140,15 @@ private async void SendErrorMessage(string errorMessage, object sync) CommonSettings.Default.DelayedErrorsNew.Add( new ErrorInfo { - Error = message, + Error = errorMessage, Time = DateTime.Now, - Version = version.ToString() + Version = version.ToString(), + UserNotes = errorReport.Comment }); } } - if (mainWindow != null) - { - mainWindow.Close(); - } + mainWindow?.Close(); Application.Current.Dispatcher.Invoke(Application.Current.Shutdown); }); @@ -184,10 +177,17 @@ private async void SendErrorMessage(string errorMessage, object sync) { if (MessageBox.Show(Resources.FatalErrorMessage, CommonSettings.AppName, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - var version = Assembly.GetExecutingAssembly().GetName().Version; + var version = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(); + try { - await _appServiceClient.SendErrorReportAsync(AppCode, errorMessage, version, DateTime.Now); + await _appRegistryServiceClient.Apps.SendAppErrorReportAsync( + App.AppId, + new AppErrorRequest(version, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = errorMessage, + ErrorTime = DateTimeOffset.UtcNow, + }); } catch (Exception) { @@ -219,15 +219,23 @@ public async void SendDelayedReports() return; } - if (_useAppService) + while (CommonSettings.Default.DelayedErrorsNew.Count > 0) { - while (CommonSettings.Default.DelayedErrorsNew.Count > 0) - { - var report = CommonSettings.Default.DelayedErrorsNew[0]; + var report = CommonSettings.Default.DelayedErrorsNew[0]; - await _appServiceClient.SendErrorReportAsync(AppCode, report.Error, Version.Parse(report.Version), report.Time); - CommonSettings.Default.DelayedErrorsNew.RemoveAt(0); + if (report.Version != null && report.Error != null) + { + await _appRegistryServiceClient.Apps.SendAppErrorReportAsync( + App.AppId, + new AppErrorRequest(Version.Parse(report.Version), Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = report.Error, + ErrorTime = report.Time, + UserNotes = report.UserNotes + }); } + + CommonSettings.Default.DelayedErrorsNew.RemoveAt(0); } while (_appState.DelayedReports.Count > 0) diff --git a/src/SIGame/SIGame/SIGame.csproj b/src/SIGame/SIGame/SIGame.csproj index 3da86874..61690ce4 100644 --- a/src/SIGame/SIGame/SIGame.csproj +++ b/src/SIGame/SIGame/SIGame.csproj @@ -6,7 +6,7 @@ Khil-soft SIGame Quizz game - Copyright © Khil-soft 2002-2023 + Copyright © Khil-soft 2002-2024 $(SIGameVersion) true true @@ -42,6 +42,7 @@ + @@ -51,9 +52,6 @@ - - PreserveNewest - @@ -124,7 +122,6 @@ - @@ -256,9 +253,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/src/SIGame/SIGame/appsettings.Production.json b/src/SIGame/SIGame/appsettings.Production.json deleted file mode 100644 index d30d08cc..00000000 --- a/src/SIGame/SIGame/appsettings.Production.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AppServiceClient": { - "ServiceUri": "https://vladimirkhil.com/api/app/" - } -} \ No newline at end of file diff --git a/src/SIGame/SIGame/appsettings.json b/src/SIGame/SIGame/appsettings.json index 0e5f6e09..2c475897 100644 --- a/src/SIGame/SIGame/appsettings.json +++ b/src/SIGame/SIGame/appsettings.json @@ -1,6 +1,6 @@ { - "AppServiceClient": { - "ServiceUri": null + "AppRegistryServiceClient": { + "ServiceUri": "https://vladimirkhil.com/appregistry/" }, "SIStatisticsServiceClient": { "ServiceUri": "https://vladimirkhil.com/sistatistics/" diff --git a/src/SIGame/SIGame/licenses/Newtonsoft.Json.LICENSE.md b/src/SIGame/SIGame/licenses/Newtonsoft.Json.LICENSE.md deleted file mode 100644 index dfaadbe4..00000000 --- a/src/SIGame/SIGame/licenses/Newtonsoft.Json.LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2007 James Newton-King - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs b/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs index e12af296..0f871e18 100644 --- a/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs +++ b/src/SIQuester/SIQuester.ViewModel/Workspaces/QDocument.cs @@ -3179,7 +3179,10 @@ private void ConvertToCompTvSI_Executed(object? arg) atom.Model.Text = atom.Model.Text.ClearPoints(); } - questionViewModel.Right[0] = questionViewModel.Right[0].ClearPoints().GrowFirstLetter(); + if (questionViewModel.Right.Count > 0) + { + questionViewModel.Right[0] = questionViewModel.Right[0].ClearPoints().GrowFirstLetter(); + } } if (ind < allthemes.Count) diff --git a/src/SIQuester/SIQuester/App.xaml.cs b/src/SIQuester/SIQuester/App.xaml.cs index 7b3da2e9..ee1fa580 100644 --- a/src/SIQuester/SIQuester/App.xaml.cs +++ b/src/SIQuester/SIQuester/App.xaml.cs @@ -1,6 +1,4 @@ -using AppService.Client; -using AppService.Client.Models; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -26,8 +24,12 @@ using System.Windows; using System.Windows.Threading; using System.Xaml; +using AppRegistryService.Client; +using AppRegistryService.Contract; +using AppRegistryService.Contract.Requests; +using AppRegistryService.Contract.Models; -#if !DEBUG +#if UPDATE using Microsoft.WindowsAPICodePack.Dialogs; using System.Net; using System.Net.Http; @@ -40,9 +42,13 @@ namespace SIQuester; /// public partial class App : Application { + /// + /// Unique application identifier. + /// + internal static readonly Guid AppId = Guid.Parse("42813995-fe5a-4de1-827e-af543293884e"); + private IHost? _host; - private AppSettings _settings = SettingsHelper.LoadUserSettings() ?? AppSettings.Create(); - private bool _useAppService; + private readonly AppSettings _settings = SettingsHelper.LoadUserSettings() ?? AppSettings.Create(); private readonly Implementation.DesktopManager _manager = new(); @@ -99,10 +105,7 @@ private async void Application_Startup(object sender, StartupEventArgs e) _manager.ServiceProvider = _host.Services; - var appServiceClientOptions = _host.Services.GetRequiredService>().Value; - _useAppService = appServiceClientOptions.ServiceUri != null; - -#if !DEBUG +#if UPDATE if (_settings.SearchForUpdates) { SearchForUpdatesAsync(); @@ -167,7 +170,7 @@ protected override void OnStartup(StartupEventArgs e) private void ConfigureServices(HostBuilderContext ctx, IServiceCollection services) { - services.AddAppServiceClient(ctx.Configuration); + services.AddAppRegistryServiceClient(ctx.Configuration); services.AddSIStorageServiceClient(ctx.Configuration); services.AddChgkServiceClient(ctx.Configuration); @@ -179,24 +182,28 @@ private void ConfigureServices(HostBuilderContext ctx, IServiceCollection servic services.AddSIQuester(); } -#if !DEBUG +#if UPDATE private async void SendDelayedReports() { - if (!_useAppService || _host == null) - { - return; - } + EnsureThat.EnsureArg.IsNotNull(_host); - using var appService = _host.Services.GetRequiredService(); + var appRegistryClient = _host.Services.GetRequiredService(); try { while (_settings.DelayedErrors.Count > 0) { var error = _settings.DelayedErrors[0]; - var errorInfo = (Model.ErrorInfo)XamlServices.Load(error); + var report = (ErrorInfo)XamlServices.Load(error); + + await appRegistryClient.Apps.SendAppErrorReportAsync( + AppId, + new AppErrorRequest(report.Version, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = report.Error, + ErrorTime = report.Time + }); - await appService.SendErrorReportAsync("SIQuester", errorInfo.Error, errorInfo.Version, errorInfo.Time); _settings.DelayedErrors.RemoveAt(0); } } @@ -221,27 +228,28 @@ private async void SearchForUpdatesAsync() /// Should the app be closed for update installation. private async Task SearchForUpdatesNewAsync(CancellationToken cancellationToken = default) { - if (!_useAppService || _host == null) - { - return false; - } + EnsureThat.EnsureArg.IsNotNull(_host); - using var appService = _host.Services.GetRequiredService(); + var appRegistryClient = _host.Services.GetRequiredService(); try { - var currentVersion = Assembly.GetExecutingAssembly().GetName().Version; - var product = await appService.GetProductAsync("SIQuester", cancellationToken); + var currentVersion = Assembly.GetExecutingAssembly().GetName().Version ?? throw new Exception("No app version found"); + + var productInfo = await appRegistryClient.Apps.PostAppUsageAsync( + AppId, + new AppUsageInfo(currentVersion, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture), + cancellationToken); - if (product != null && product.Version > currentVersion) + if (productInfo != null && productInfo.Release?.Version > currentVersion) { + var updateUri = productInfo.Installer?.Uri!; + _logger?.LogInformation( "Update detected. Current version: {currentVersion}. Product version: {productVersion}. Product uri: {productUri}", currentVersion, - product.Version, - product.Uri); - - var updateUri = product.Uri; + productInfo.Release.Version, + updateUri); var localFile = Path.Combine(Path.GetTempPath(), "setup.exe"); @@ -287,6 +295,8 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa return; } + e.Handled = true; + _hasError = true; _logger?.LogError(e.Exception, "Application error: {message}", e.Exception.Message); @@ -374,7 +384,7 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa var exception = e.Exception; var message = new StringBuilder(); var systemMessage = new StringBuilder(); - var version = Assembly.GetExecutingAssembly().GetName().Version; + var version = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(); while (exception != null) { @@ -388,7 +398,7 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa exception = exception.InnerException; } - var errorInfo = new Model.ErrorInfo { Time = DateTime.Now, Version = version, Error = systemMessage.ToString() }; + var errorInfo = new ErrorInfo { Time = DateTime.Now, Version = version, Error = systemMessage.ToString() }; #if !DEBUG var dialog = new TaskDialog { @@ -415,22 +425,24 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa #endif } - e.Handled = true; Shutdown(); } - private async Task SendMessageAsync(Model.ErrorInfo errorInfo) + private async Task SendMessageAsync(ErrorInfo errorInfo) { - if (!_useAppService || _host == null) - { - return; - } + EnsureThat.EnsureArg.IsNotNull(_host); - using var appService = _host.Services.GetRequiredService(); + var appRegistryServiceClient = _host.Services.GetRequiredService(); try { - var result = await appService.SendErrorReportAsync("SIQuester", errorInfo.Error, errorInfo.Version, errorInfo.Time); + var result = await appRegistryServiceClient.Apps.SendAppErrorReportAsync( + AppId, + new AppErrorRequest(errorInfo.Version, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = errorInfo.Error, + ErrorTime = errorInfo.Time, + }); switch (result) { diff --git a/src/SIQuester/SIQuester/SIQuester.csproj b/src/SIQuester/SIQuester/SIQuester.csproj index 089e1602..39b47b2a 100644 --- a/src/SIQuester/SIQuester/SIQuester.csproj +++ b/src/SIQuester/SIQuester/SIQuester.csproj @@ -20,6 +20,7 @@ none false + $(DefineConstants);UPDATE true @@ -32,7 +33,6 @@ - @@ -55,6 +55,7 @@ + @@ -65,15 +66,9 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -199,7 +194,6 @@ - diff --git a/src/SIQuester/SIQuester/appsettings.Production.json b/src/SIQuester/SIQuester/appsettings.Production.json deleted file mode 100644 index d30d08cc..00000000 --- a/src/SIQuester/SIQuester/appsettings.Production.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AppServiceClient": { - "ServiceUri": "https://vladimirkhil.com/api/app/" - } -} \ No newline at end of file diff --git a/src/SIQuester/SIQuester/appsettings.json b/src/SIQuester/SIQuester/appsettings.json index 15dfc33d..d4972bf8 100644 --- a/src/SIQuester/SIQuester/appsettings.json +++ b/src/SIQuester/SIQuester/appsettings.json @@ -1,6 +1,6 @@ { - "AppServiceClient": { - "ServiceUri": null + "AppRegistryServiceClient": { + "ServiceUri": "https://vladimirkhil.com/appregistry/" }, "SIStorageServiceClient": { "ServiceUri": "https://vladimirkhil.com/sistorage/" diff --git a/src/SIQuester/SIQuester/licenses/Newtonsoft.Json.LICENSE.md b/src/SIQuester/SIQuester/licenses/Newtonsoft.Json.LICENSE.md deleted file mode 100644 index dfaadbe4..00000000 --- a/src/SIQuester/SIQuester/licenses/Newtonsoft.Json.LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2007 James Newton-King - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/SImulator/SImulator.ViewModel/Properties/Resources.en-US.resx b/src/SImulator/SImulator.ViewModel/Properties/Resources.en-US.resx new file mode 100644 index 00000000..1372e668 --- /dev/null +++ b/src/SImulator/SImulator.ViewModel/Properties/Resources.en-US.resx @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Add + + + Connection error: {0} + + + Error + + + Game end error: {0} + + + Connection to the demonstration computer has been terminated. The game is stopped. + + + Error loading game package + + + Game start error: {0} + + + [Your IP address] + + + Log file creation error: {0}. Logging will not be performed. + + + Error starting log recording + + + Log directory is not specified. Logs will not be recorded. + + + Next + + + License folder not found + + + No Risk question + + + Error opening license folder: {0} + + + Package preparation error + + + Pause + + + Press the button + + + Round + + + Round themes + + + Round time has expired. + + + Start + + + Score + + + Secret question + + + Select audio file + + + Select background image + + + Select background video file + + + Select splash video file + + + Select splash image + + + Stake question + + + Start game + + + Theme + + \ No newline at end of file diff --git a/src/SImulator/SImulator/App.xaml b/src/SImulator/SImulator/App.xaml index 8c6ae236..bfaa9ec3 100644 --- a/src/SImulator/SImulator/App.xaml +++ b/src/SImulator/SImulator/App.xaml @@ -322,7 +322,7 @@ - + diff --git a/src/SImulator/SImulator/App.xaml.cs b/src/SImulator/SImulator/App.xaml.cs index 2e2b7d2b..94c6c685 100644 --- a/src/SImulator/SImulator/App.xaml.cs +++ b/src/SImulator/SImulator/App.xaml.cs @@ -1,5 +1,7 @@ -using AppService.Client; -using AppService.Client.Models; +using AppRegistryService.Client; +using AppRegistryService.Contract; +using AppRegistryService.Contract.Models; +using AppRegistryService.Contract.Requests; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -7,8 +9,8 @@ using NLog.Web; using SImulator.Implementation; using SImulator.ViewModel; +using SImulator.ViewModel.Core; using SIStorage.Service.Client; -using SIStorage.Service.Contract.Models; using SIStorageService.ViewModel; using System; using System.ComponentModel; @@ -23,6 +25,10 @@ using Utils; using Settings = SImulator.ViewModel.Model.AppSettings; +#if DEBUG +using SIStorage.Service.Contract.Models; +#endif + namespace SImulator; /// @@ -30,6 +36,11 @@ namespace SImulator; /// public partial class App : Application { + /// + /// Unique application identifier. + /// + internal static readonly Guid AppId = Guid.Parse("ea8635a0-183e-47d6-944c-50643e32d689"); + private IHost? _host; #pragma warning disable IDE0052 @@ -43,8 +54,6 @@ public partial class App : Application internal Settings Settings { get; } = LoadSettings(); - private bool _useAppService; - private async void Application_Startup(object sender, StartupEventArgs e) { _host = new HostBuilder() @@ -77,17 +86,12 @@ private void ConfigureServices(HostBuilderContext ctx, IServiceCollection servic { var configuration = ctx.Configuration; - services.AddAppServiceClient(configuration); + services.AddAppRegistryServiceClient(configuration); services.AddSIStorageServiceClient(configuration); services.AddSingleton(typeof(StorageViewModel)); services.AddTransient(); - - var options = configuration.GetSection(AppServiceClientOptions.ConfigurationSectionName); - var appServiceClientOptions = options.Get(); - - _useAppService = appServiceClientOptions?.ServiceUri != null; } protected override void OnStartup(StartupEventArgs e) @@ -96,15 +100,15 @@ protected override void OnStartup(StartupEventArgs e) UI.Initialize(); - //if (Settings.Language != null) - //{ - // Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(Settings.Language); - //} - //else - //{ - // var currentLanguage = Thread.CurrentThread.CurrentUICulture.Name; - // Settings.Language = currentLanguage == "ru-RU" ? currentLanguage : "en-US"; - //} + if (Settings.Language != null) + { + Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(Settings.Language); + } + else + { + var currentLanguage = Thread.CurrentThread.CurrentUICulture.Name; + Settings.Language = currentLanguage == "ru-RU" ? currentLanguage : "en-US"; + } var main = new MainViewModel(Settings); @@ -200,24 +204,37 @@ public static Settings LoadSettings() } #if !DEBUG - private async void ProcessAsync() + private async void ProcessAsync(CancellationToken cancellationToken = default) { - if (!_useAppService) - { - return; - } + EnsureThat.EnsureArg.IsNotNull(_host); + + var appRegistryClient = _host.Services.GetRequiredService(); - using var appService = _host.Services.GetRequiredService(); try { + var currentVersion = Assembly.GetExecutingAssembly().GetName().Version ?? throw new Exception("No app version found"); + // Update application launch counter - await appService.GetProductAsync("SImulator"); + await appRegistryClient.Apps.PostAppUsageAsync( + AppId, + new AppUsageInfo(currentVersion, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture), + cancellationToken); var delayedErrors = Settings.DelayedErrors; + while (delayedErrors.Count > 0) { var error = delayedErrors[0]; - await appService.SendErrorReportAsync("SImulator", error.Error, Version.Parse(error.Version), error.Time); + + await appRegistryClient.Apps.SendAppErrorReportAsync( + AppId, + new AppErrorRequest(Version.Parse(error.Version), Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = error.Error, + ErrorTime = error.Time + }, + cancellationToken); + delayedErrors.RemoveAt(0); } } @@ -229,6 +246,8 @@ private async void ProcessAsync() private async void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { + e.Handled = true; + var msg = e.Exception.ToString(); if (msg.Contains("WmClose")) // Normal closing, it's ok @@ -282,21 +301,26 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa { // Do nothing } - else if (_useAppService - && _host != null + else if (_host != null && MessageBox.Show( string.Format(SImulator.Properties.Resources.ErrorSendConfirm, e.Exception.Message), MainViewModel.ProductName, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { - using var appService = _host.Services.GetRequiredService(); - var version = Assembly.GetExecutingAssembly().GetName().Version; + var appRegistryServiceClient = _host.Services.GetRequiredService(); + var version = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(); var errorMessage = e.Exception.ToStringDemystified(); try { - var result = await appService.SendErrorReportAsync("SImulator", errorMessage, version, DateTime.UtcNow); + var result = await appRegistryServiceClient.Apps.SendAppErrorReportAsync( + AppId, + new AppErrorRequest(version, Environment.OSVersion.Version, RuntimeInformation.OSArchitecture) + { + ErrorMessage = errorMessage, + ErrorTime = DateTimeOffset.UtcNow, + }); switch (result) { @@ -328,7 +352,7 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa if (Settings.DelayedErrors.Count < 10) { Settings.DelayedErrors.Add( - new ViewModel.Core.ErrorInfo + new ErrorInfo { Time = DateTime.Now, Error = errorMessage, @@ -346,7 +370,6 @@ private async void Application_DispatcherUnhandledException(object sender, Dispa MessageBoxImage.Error); } - e.Handled = true; Shutdown(); } diff --git a/src/SImulator/SImulator/CommandWindow.xaml b/src/SImulator/SImulator/CommandWindow.xaml index 43d6fd3b..35e8d5f4 100644 --- a/src/SImulator/SImulator/CommandWindow.xaml +++ b/src/SImulator/SImulator/CommandWindow.xaml @@ -689,7 +689,7 @@ @@ -956,7 +956,7 @@ - + - + diff --git a/src/SImulator/SImulator/Converters/FontFamilyConverter.cs b/src/SImulator/SImulator/Converters/FontFamilyConverter.cs index 7b8f89ae..6cf23808 100644 --- a/src/SImulator/SImulator/Converters/FontFamilyConverter.cs +++ b/src/SImulator/SImulator/Converters/FontFamilyConverter.cs @@ -3,6 +3,7 @@ using System.Windows; using SIUI.ViewModel.Core; using System.Globalization; +using SImulator.Properties; namespace SImulator.Converters; @@ -17,7 +18,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn if (fontFamily == Settings.DefaultTableFontFamily || fontFamily.StartsWith("pack:")) { - return "(по умолчанию)"; + return Resources.Default; } return fontFamily; diff --git a/src/SImulator/SImulator/Implementation/DesktopManager.cs b/src/SImulator/SImulator/Implementation/DesktopManager.cs index e8dd6467..fceb758f 100644 --- a/src/SImulator/SImulator/Implementation/DesktopManager.cs +++ b/src/SImulator/SImulator/Implementation/DesktopManager.cs @@ -15,6 +15,7 @@ using System.IO.Ports; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -188,7 +189,7 @@ public override int GetKeyNumber(GameKey key) { var storage = ServiceProvider.GetRequiredService(); storage.DefaultRestriction = ((App)Application.Current).Settings.Restriction; - storage.DefaultLanguage = "ru-RU"; + storage.DefaultLanguage = Thread.CurrentThread.CurrentUICulture.Name; storage.PropertyChanged += (s, e) => { diff --git a/src/SImulator/SImulator/PackageStoreWindow.xaml b/src/SImulator/SImulator/PackageStoreWindow.xaml index abc632a9..344e9931 100644 --- a/src/SImulator/SImulator/PackageStoreWindow.xaml +++ b/src/SImulator/SImulator/PackageStoreWindow.xaml @@ -102,11 +102,11 @@ - - + + - Ограничение + + Content="{x:Static lp:Resources.SelectPackage}" /> diff --git a/src/SImulator/SImulator/Properties/Resources.Designer.cs b/src/SImulator/SImulator/Properties/Resources.Designer.cs index 36102c3e..2e946a5a 100644 --- a/src/SImulator/SImulator/Properties/Resources.Designer.cs +++ b/src/SImulator/SImulator/Properties/Resources.Designer.cs @@ -393,6 +393,15 @@ public static string ComPortNumber { } } + /// + /// Ищет локализованную строку, похожую на (по умолчанию). + /// + public static string Default { + get { + return ResourceManager.GetString("Default", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Удалить. /// @@ -412,7 +421,7 @@ public static string Design { } /// - /// Ищет локализованную строку, похожую на Закончилось место на диске!. + /// Ищет локализованную строку, похожую на Закончилось место на диске. /// public static string DiskFullError { get { @@ -890,7 +899,7 @@ public static string Oral { } /// - /// Ищет локализованную строку, похожую на Недостаточно памяти для выполнения программы!. + /// Ищет локализованную строку, похожую на Недостаточно памяти для выполнения программы. /// public static string OutOfMemoryError { get { @@ -1177,6 +1186,15 @@ public static string ResetSums { } } + /// + /// Ищет локализованную строку, похожую на Ограничение. + /// + public static string Restriction { + get { + return ResourceManager.GetString("Restriction", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Правильный ответ. /// @@ -1241,7 +1259,7 @@ public static string RoundTimeout { } /// - /// Ищет локализованную строку, похожую на Не удалось создать главное окно программы! Похоже, на вашем компьютере повреждена среда выполнения программ: {0}. + /// Ищет локализованную строку, похожую на Не удалось создать главное окно программы. Похоже, на вашем компьютере повреждена среда выполнения программ: {0}. /// public static string RuntimeBrokenError { get { @@ -1267,6 +1285,15 @@ public static string SavingSettingsError { } } + /// + /// Ищет локализованную строку, похожую на Экран. + /// + public static string Screen { + get { + return ResourceManager.GetString("Screen", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Дополнительный. /// @@ -1357,6 +1384,15 @@ public static string SelectLogsFolder { } } + /// + /// Ищет локализованную строку, похожую на Выбрать пакет. + /// + public static string SelectPackage { + get { + return ResourceManager.GetString("SelectPackage", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Выберите пакет вопросов. /// @@ -1366,6 +1402,15 @@ public static string SelectQuestionPackage { } } + /// + /// Ищет локализованную строку, похожую на В отдельном окне. + /// + public static string SeparateWindow { + get { + return ResourceManager.GetString("SeparateWindow", resourceCulture); + } + } + /// /// Ищет локализованную строку, похожую на Задать кнопку. /// diff --git a/src/SImulator/SImulator/Properties/Resources.en-US.resx b/src/SImulator/SImulator/Properties/Resources.en-US.resx new file mode 100644 index 00000000..1c1dff03 --- /dev/null +++ b/src/SImulator/SImulator/Properties/Resources.en-US.resx @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + About + + + Add player + + + Add sum + + + Add to active player + + + Alias + + + (all) + + + Answer + + + Background color for the answering player + + + Answer options + + + Application language (requires restart) + + + SImulator + + + Audio + + + Author + + + Vladimir Khil + + + Automatic game management (without a host) + + + Back + + + Background color + + + Background image + + + Background video file + + + Button lock time (s) + + + Matches positions of players in the list + + + Button press + + + Buttons + + + COM port + + + External + + + Joystick + + + Keyboard + + + Do not use + + + Web access + + + Default + + + Select a player to answer + + + Select a player to start the game + + + Clear list + + + Comment + + + An error occurred in the application: {0}\r\n\r\nThe application will be closed. Please contact the developer. + + + Licenses of used components + + + COM port number + + + (by default) + + + Delete + + + Design + + + Disk space is running out + + + Rollback statistics when stepping back + + + Clear players sums and counters of their correct and incorrect answers + + + End the question on correct answer + + + This error is not reproducible. If you can reliably reproduce it, please contact the author. + + + Failed to connect to the server when sending the error report. The report will be sent later. + + + This error has been fixed in the new version of the program. Please update. + + + An error occurred in the application: {0} +The application will be closed. Send information to the developer? + + + In this mode, buttons not connected to the program (independent devices, claps, etc.) are used. The program displays a frame when it is possible to press (during the game with false starts). + + + Game with false starts + + + When playing with false starts: +question text is displayed on the screen; +when using buttons to start the question timer, an additional press of the Next button is required, and a colored frame lights up on the screen" + + + Multimedia with false starts + + + Prevents pressing the button during multimedia file playback + + + Error deleting file: {0} + + + Deleting theme in the final round + + + Final round thinking + + + Final round thinking time (s) + + + Finish game? + + + Font + + + Game + + + Game buttons + + + Simplified + + + Classic + + + Game package + + + Game rules + + + Game start + + + Game themes + + + Game type + + + Pass the turn + + + Grid color + + + Hide + + + Press the "Next" button to control the game progress + + + (Game themes) + + + (Splash screen) + + + HTML + + + Image + + + Splash video file + + + Error completing keyboard listening + + + Error listening to the keyboard + + + License + + + All exclusive copyrights to the Software Product and its documentation in print and/or electronic form belong to the Author. + +The program is distributed free of charge. + +The User has the right to freely use and distribute this Program, place it on websites, and carry out other activities not prohibited by the legislation of their country of operation with it for any period of time, provided that the copyrights are preserved. + +The program is provided "as is". The Author does not provide any guarantees that the operation of the Program will meet the User's expectations. The Author is not obligated to fix the Program if such non-compliance occurs. The Author is not responsible for any damage caused by the use of this Program. + +By installing and using the Program, you automatically accept this Agreement. You may transfer the rights to use the Program to a third party subject to their agreement to this Agreement. If you do not agree with any term of this Agreement, you do not have the right to use this Program. + + + The folder for logging "{0}" was not found. + + + Main + + + Make stake + + + Name + + + An error occurred while navigating to the program website ({0}). Make sure you have your default browser configured: {1} + + + Next + + + No answer + + + No Risk question + + + Not selected + + + (not set) + + + Oral + + + Not enough memory to run the program + + + Pass + + + Pause + + + Background + + + Replica + + + Players + + + Player table + + + Play sounds + + + Play special questions + + + To exit, press Escape, right mouse button, or tap slightly with your finger + + + Game scoreboard + + + Press the button + + + Background image + + + Previous round + + + Price + + + From {0} to {1} with step {2} + + + Minimum or maximum in the round + + + {0} or {1} + + + Question + + + Question library + + + Line spacing in question + + + Question selection + + + Question time (s) + + + No Risk question + + + Secret question + + + Secret question (money without question) + + + Secret question (theme and price known before passing the question) + + + Simple + + + Stake question + + + Recent files + + + Delete player + + + Delete sums and statistics + + + Restriction + + + Correct answer + + + Correct + + + Round + + + Round start + + + Round themes + + + Round time (s) + + + Round timeout + + + Failed to create the main window of the program. It seems that the runtime environment of the program on your computer is corrupted: {0} + + + Save logs to folder + + + Error saving program settings + + + Screen + + + Additional + + + Secret question + + + Select + + + Anyone + + + Anyone except yourself + + + File… + + + Select… + + + Can be given to + + + Question library… + + + Select a folder to save logs + + + Select Package + + + Select question package + + + In separate window + + + Assign a button + + + Show + + + Show players who lost the button + + + Show player list + + + Show frame when the button can be pressed + + + Show correct answers + + + Show title on the game scoreboard + + + Show text of questions during game without false starts + + + Accept player signals after thinking time expires + + + SI questions + + + Error working with question library: {0} + + + Sounds + + + Source + + + Special questions aliases + + + Thinking time for special question (s) + + + Stake + + + Stake question + + + Start + + + Start + + + End + + + End game + + + Deduct points for wrong answer + + + Subtract amount + + + Remove from active player + + + Sum + + + Game scoreboard hotkeys: +Space (left mouse button) - proceed +Backspace - go back +Escape (right mouse button) - end game + + + Test package + + + Text color + + + Theme + + + Theme comments + + + Thinking + + + Thinking time after button press (s) + + + Unknown type + + + Use keyboard selection on scoreboard + + + Allows selecting questions on the scoreboard using two consecutive presses of the number buttons (theme number, question number/Escape to cancel) and removing themes in the final round with one press + + + Version + + + Video + + + Connection address for game: http:// + + + In this mode, mobile devices of participants (phones, tablets, etc.) are used as buttons. Before the game starts, these devices are registered and associated with players. +To play, you need to organize a local network. + + + Web access port + + + Website + + + Window (new version) + + + Window + + + Wrong answer + + + Incorrect + + \ No newline at end of file diff --git a/src/SImulator/SImulator/Properties/Resources.resx b/src/SImulator/SImulator/Properties/Resources.resx index 4e332737..af257e44 100644 --- a/src/SImulator/SImulator/Properties/Resources.resx +++ b/src/SImulator/SImulator/Properties/Resources.resx @@ -228,6 +228,9 @@ Номер COM-порта + + (по умолчанию) + Удалить @@ -235,7 +238,7 @@ Внешний вид - Закончилось место на диске! + Закончилось место на диске Откатывать статистику при шаге назад @@ -403,7 +406,7 @@ Устный текст - Недостаточно памяти для выполнения программы! + Недостаточно памяти для выполнения программы Пас @@ -498,6 +501,9 @@ Удалить суммы и статистику + + Ограничение + Правильный ответ @@ -520,7 +526,7 @@ Окончание времени раунда - Не удалось создать главное окно программы! Похоже, на вашем компьютере повреждена среда выполнения программ: {0} + Не удалось создать главное окно программы. Похоже, на вашем компьютере повреждена среда выполнения программ: {0} Сохранять логи в папку @@ -528,6 +534,9 @@ Ошибка при сохранении настроек программы + + Экран + Дополнительный @@ -558,9 +567,15 @@ Выберите папку для записи логов + + Выбрать пакет + Выберите пакет вопросов + + В отдельном окне + Задать кнопку diff --git a/src/SImulator/SImulator/SImulator.csproj b/src/SImulator/SImulator/SImulator.csproj index 4a623e6a..6de24227 100644 --- a/src/SImulator/SImulator/SImulator.csproj +++ b/src/SImulator/SImulator/SImulator.csproj @@ -58,7 +58,6 @@ - @@ -76,6 +75,7 @@ + @@ -88,18 +88,12 @@ - - PreserveNewest - PreserveNewest PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/src/SImulator/SImulator/appsettings.Production.json b/src/SImulator/SImulator/appsettings.Production.json deleted file mode 100644 index d30d08cc..00000000 --- a/src/SImulator/SImulator/appsettings.Production.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AppServiceClient": { - "ServiceUri": "https://vladimirkhil.com/api/app/" - } -} \ No newline at end of file diff --git a/src/SImulator/SImulator/appsettings.json b/src/SImulator/SImulator/appsettings.json index 2866ee6e..0c85934b 100644 --- a/src/SImulator/SImulator/appsettings.json +++ b/src/SImulator/SImulator/appsettings.json @@ -1,6 +1,6 @@ { - "AppServiceClient": { - "ServiceUri": null + "AppRegistryServiceClient": { + "ServiceUri": "https://vladimirkhil.com/appregistry/" }, "SIStorageServiceClient": { "ServiceUri": "https://vladimirkhil.com/sistorage/" diff --git a/src/SImulator/SImulator/licenses/Newtonsoft.Json.LICENSE.md b/src/SImulator/SImulator/licenses/Newtonsoft.Json.LICENSE.md deleted file mode 100644 index dfaadbe4..00000000 --- a/src/SImulator/SImulator/licenses/Newtonsoft.Json.LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2007 James Newton-King - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/Common/Notions.Tests/Notions.Tests.csproj b/test/Common/Notions.Tests/Notions.Tests.csproj index 45dd1ba8..f2d5b805 100644 --- a/test/Common/Notions.Tests/Notions.Tests.csproj +++ b/test/Common/Notions.Tests/Notions.Tests.csproj @@ -8,14 +8,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Common/Notions.Tests/StringExtensionsTests.cs b/test/Common/Notions.Tests/StringExtensionsTests.cs index 7a2c516b..35434be2 100644 --- a/test/Common/Notions.Tests/StringExtensionsTests.cs +++ b/test/Common/Notions.Tests/StringExtensionsTests.cs @@ -13,7 +13,7 @@ public sealed class StringExtensionTests public void GrowFirstLetter_Ok(string input, string expectedOutput) { var result = input.GrowFirstLetter(); - Assert.AreEqual(expectedOutput, result); + Assert.That(expectedOutput, Is.EqualTo(result)); } [TestCase("a sample text", 8, "a sample")] @@ -23,6 +23,6 @@ public void GrowFirstLetter_Ok(string input, string expectedOutput) public void Shorten_Ok(string input, int maxLength, string expectedOutput) { var result = input.Shorten(maxLength); - Assert.AreEqual(expectedOutput, result); + Assert.That(expectedOutput, Is.EqualTo(result)); } } \ No newline at end of file diff --git a/test/Common/SIEngine.Core.Tests/SIEngine.Core.Tests.csproj b/test/Common/SIEngine.Core.Tests/SIEngine.Core.Tests.csproj index d0380738..0ee6e571 100644 --- a/test/Common/SIEngine.Core.Tests/SIEngine.Core.Tests.csproj +++ b/test/Common/SIEngine.Core.Tests/SIEngine.Core.Tests.csproj @@ -16,7 +16,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Common/SIEngine.Tests/SIEngine.Tests.csproj b/test/Common/SIEngine.Tests/SIEngine.Tests.csproj index c23d9e29..9ad343c9 100644 --- a/test/Common/SIEngine.Tests/SIEngine.Tests.csproj +++ b/test/Common/SIEngine.Tests/SIEngine.Tests.csproj @@ -9,10 +9,13 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/Common/SIPackages.Tests/SIPackages.Tests.csproj b/test/Common/SIPackages.Tests/SIPackages.Tests.csproj index 61789701..690b453a 100644 --- a/test/Common/SIPackages.Tests/SIPackages.Tests.csproj +++ b/test/Common/SIPackages.Tests/SIPackages.Tests.csproj @@ -10,13 +10,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/SICore/SICore.Tests/GameTests.cs b/test/SICore/SICore.Tests/GameTests.cs index 2fb0437c..8b6693a0 100644 --- a/test/SICore/SICore.Tests/GameTests.cs +++ b/test/SICore/SICore.Tests/GameTests.cs @@ -31,8 +31,8 @@ public void FinalStakersTest(int[] sums, int themesCount, int[] order) for (int i = 0; i < order.Length; i++) { - Assert.IsTrue(enumerator.MoveNext()); - Assert.AreEqual(enumerator.Current.PlayerIndex, order[i]); + Assert.That(enumerator.MoveNext(), Is.True); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(order[i])); if (enumerator.Current.PlayerIndex == -1) { @@ -42,7 +42,7 @@ public void FinalStakersTest(int[] sums, int themesCount, int[] order) if (playerSets[j].Intersect(enumerator.Current.PossibleIndicies).Any()) { found = true; - Assert.AreEqual(playerSets[j], enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(playerSets[j])); } } @@ -53,7 +53,7 @@ public void FinalStakersTest(int[] sums, int themesCount, int[] order) } } - Assert.IsFalse(enumerator.MoveNext()); + Assert.That(enumerator.MoveNext(), Is.False); enumerator.Reset(false); while (enumerator.MoveNext()) @@ -66,9 +66,9 @@ public void FinalStakersTest(int[] sums, int themesCount, int[] order) enumerator.Current.SetIndex(newIndex); - Assert.AreEqual(enumerator.Current.PlayerIndex, newIndex); - Assert.IsTrue(list.Count == count - 1); - Assert.IsTrue(!list.Contains(newIndex)); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(newIndex)); + Assert.That(list.Count, Is.EqualTo(count - 1)); + Assert.That(list.Contains(newIndex), Is.False); } } } diff --git a/test/SICore/SICore.Tests/SICore.Tests.csproj b/test/SICore/SICore.Tests/SICore.Tests.csproj index ebd304ff..37e6d9e6 100644 --- a/test/SICore/SICore.Tests/SICore.Tests.csproj +++ b/test/SICore/SICore.Tests/SICore.Tests.csproj @@ -14,9 +14,9 @@ - + - + diff --git a/test/SICore/SICore.Tests/ThemeDeletersEnumeratorTests.cs b/test/SICore/SICore.Tests/ThemeDeletersEnumeratorTests.cs index 9e86531b..8444a810 100644 --- a/test/SICore/SICore.Tests/ThemeDeletersEnumeratorTests.cs +++ b/test/SICore/SICore.Tests/ThemeDeletersEnumeratorTests.cs @@ -13,7 +13,7 @@ public void RemoveLast() var enumerator = new ThemeDeletersEnumerator(new ThemeDeletersEnumerator.IndexInfo[] { new ThemeDeletersEnumerator.IndexInfo(new HashSet(new int[] { 0 })) }); enumerator.RemoveAt(0); - Assert.AreEqual(true, enumerator.IsEmpty()); + Assert.That(enumerator.IsEmpty(), Is.True); } [Test] @@ -41,27 +41,27 @@ public void Remove() enumerator.Reset(false); enumerator.MoveNext(); - Assert.AreEqual(3, enumerator.Current.PlayerIndex); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(3)); enumerator.MoveNext(); - Assert.AreEqual(0, enumerator.Current.PlayerIndex); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(0)); enumerator.MoveNext(); - Assert.AreEqual(2, enumerator.Current.PlayerIndex); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(2)); enumerator.MoveNext(); - Assert.AreEqual(-1, enumerator.Current.PlayerIndex); - Assert.AreEqual(newVariants, enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(-1)); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(newVariants)); enumerator.MoveNext(); - Assert.AreEqual(-1, enumerator.Current.PlayerIndex); - Assert.AreEqual(newVariants, enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(-1)); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(newVariants)); enumerator.MoveNext(); - Assert.AreEqual(-1, enumerator.Current.PlayerIndex); - Assert.AreEqual(newVariants, enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(-1)); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(newVariants)); enumerator.MoveNext(); - Assert.AreEqual(-1, enumerator.Current.PlayerIndex); - Assert.AreEqual(newVariants, enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(-1)); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(newVariants)); enumerator.MoveNext(); - Assert.AreEqual(4, enumerator.Current.PlayerIndex); + Assert.That(enumerator.Current.PlayerIndex, Is.EqualTo(4)); - Assert.IsFalse(enumerator.MoveNext()); + Assert.That(enumerator.MoveNext(), Is.False); } [Test] @@ -85,6 +85,6 @@ public void RemoveNew() enumerator.MoveNext(); enumerator.MoveNext(); enumerator.MoveNext(); - Assert.AreEqual(newVariants, enumerator.Current.PossibleIndicies); + Assert.That(enumerator.Current.PossibleIndicies, Is.EqualTo(newVariants)); } } diff --git a/test/SIGame/SIGame.Tests/MainTest.cs b/test/SIGame/SIGame.Tests/MainTest.cs index b5332b3e..8abfbaa0 100644 --- a/test/SIGame/SIGame.Tests/MainTest.cs +++ b/test/SIGame/SIGame.Tests/MainTest.cs @@ -66,11 +66,11 @@ public async Task GameCreateAndRun_Ok_Async( await mainViewModel.Open.ExecuteAsync(null); var contentBox = mainViewModel.ActiveView as ContentBox; - Assert.IsNull(contentBox, ((LoginViewModel?)contentBox?.Data)?.Error); + Assert.That(contentBox, Is.Null, ((LoginViewModel?)contentBox?.Data)?.Error); var siOnline = (SIOnlineViewModel?)mainViewModel.ActiveView; - Assert.IsNotNull(siOnline); + Assert.That(siOnline, Is.Not.Null); await siOnline!.InitAsync(); @@ -105,7 +105,7 @@ public async Task GameCreateAndRun_Ok_Async( var package = gameSettings.StorageInfo.Model.Packages.FirstOrDefault(p => p.Model.Id == packageId); - Assert.IsNotNull(package, "Package not found"); + Assert.That(package, Is.Not.Null, "Package not found"); var storage = gameSettings.StorageInfo.Model.CurrentPackage = package; @@ -114,11 +114,11 @@ public async Task GameCreateAndRun_Ok_Async( await gameSettings.BeginGame.ExecuteAsync(null); - Assert.IsFalse(gameSettings.IsProgress); - Assert.IsNull(gameSettings.ErrorMessage); + Assert.That(gameSettings.IsProgress, Is.False); + Assert.That(gameSettings.ErrorMessage, Is.Null); var siOnlineError = mainViewModel.ActiveView as SIOnlineViewModel; - Assert.IsNull(siOnlineError, siOnlineError?.Error); + Assert.That(siOnlineError, Is.Null, siOnlineError?.Error); var game = (GameViewModel?)mainViewModel.ActiveView; @@ -146,7 +146,7 @@ public async Task GameCreateAndRun_Ok_Async( // Sometimes it does not have time to login again var contentBox2 = mainViewModel.ActiveView as ContentBox; - Assert.IsNull(contentBox2, ((LoginViewModel?)contentBox2?.Data)?.Error); + Assert.That(contentBox2, Is.Null, ((LoginViewModel?)contentBox2?.Data)?.Error); var siOnline2 = (SIOnlineViewModel?)mainViewModel.ActiveView; siOnline2?.Cancel.Execute(null); @@ -154,6 +154,11 @@ public async Task GameCreateAndRun_Ok_Async( private static async void TInfo_PropertyChanged(object? sender, PropertyChangedEventArgs e) { + if (sender == null) + { + return; + } + if (e.PropertyName == nameof(TableInfoViewModel.MediaSource)) { var mediaSource = ((TableInfoViewModel)sender).MediaSource; @@ -161,12 +166,12 @@ private static async void TInfo_PropertyChanged(object? sender, PropertyChangedE if (mediaSource != null) { var result = await HttpClient.GetAsync(mediaSource.Uri); - Assert.IsTrue(result.IsSuccessStatusCode); + Assert.That(result.IsSuccessStatusCode, Is.True); } } else if (e.PropertyName == nameof(TableInfoViewModel.SoundSource)) { - await HttpClient.GetAsync(((TableInfoViewModel)sender).SoundSource.Uri); + await HttpClient.GetAsync(((TableInfoViewModel)sender).SoundSource?.Uri); } } } diff --git a/test/SIGame/SIGame.Tests/SIGame.Tests.csproj b/test/SIGame/SIGame.Tests/SIGame.Tests.csproj index 7d6556ed..6652d16a 100644 --- a/test/SIGame/SIGame.Tests/SIGame.Tests.csproj +++ b/test/SIGame/SIGame.Tests/SIGame.Tests.csproj @@ -9,9 +9,9 @@ - + - +