From c51f151c586b087dbf808717cadc3f5a5492d40a Mon Sep 17 00:00:00 2001
From: denglihong2007 <98096191+denglihong2007@users.noreply.github.com>
Date: Sat, 26 Jul 2025 18:02:55 +0800
Subject: [PATCH] =?UTF-8?q?chore:=20=E4=B8=80=E4=BA=9B=E6=9D=82=E4=B9=B1?=
=?UTF-8?q?=E7=9A=84=E6=9B=B4=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CRSim.Core/Abstractions/PluginBase.cs | 2 +
CRSim.Core/CRSim.Core.csproj | 5 +-
CRSim.Core/Models/JsonSerializationContext.cs | 11 +++-
CRSim.Core/Models/Plugin/PluginInfo.cs | 3 -
CRSim.Core/Models/Plugin/PluginManifest.cs | 14 +----
CRSim.Core/Models/StyleInfo.cs | 6 +-
CRSim.Core/Services/NetworkService.cs | 2 +-
CRSim.Core/Services/PluginService.cs | 58 +++++++++++--------
CRSim.Core/Utils/Downloader.cs | 41 +++++++++++++
.../CRSim.ExamplePlugin.csproj | 5 +-
CRSim.ExamplePlugin/Plugin.cs | 4 +-
CRSim.ExamplePlugin/manifest.json | 11 ++++
CRSim.ExamplePlugin/manifest.yml | 9 ---
CRSim.ExamplePlugin/style.json | 5 ++
CRSim.ExamplePlugin/style.yml | 5 --
.../CRSim.ScreenSimulator.csproj | 3 +-
CRSim/App.xaml.cs | 1 +
CRSim/CRSim.csproj | 13 ++---
.../PluginManagementPageViewModel.cs | 14 +++--
.../ScreenSimulatorPageViewModel.cs | 3 +-
CRSim/Views/PluginManagementPage.xaml | 2 +-
CRSim/Views/PluginManagementPage.xaml.cs | 5 ++
22 files changed, 137 insertions(+), 85 deletions(-)
create mode 100644 CRSim.Core/Utils/Downloader.cs
create mode 100644 CRSim.ExamplePlugin/manifest.json
delete mode 100644 CRSim.ExamplePlugin/manifest.yml
create mode 100644 CRSim.ExamplePlugin/style.json
delete mode 100644 CRSim.ExamplePlugin/style.yml
diff --git a/CRSim.Core/Abstractions/PluginBase.cs b/CRSim.Core/Abstractions/PluginBase.cs
index 305238f..0056c3d 100644
--- a/CRSim.Core/Abstractions/PluginBase.cs
+++ b/CRSim.Core/Abstractions/PluginBase.cs
@@ -10,6 +10,8 @@ public abstract class PluginBase
///
public PluginInfo Info { get; internal set; } = null!;
+ public abstract Type ViewModel { get; }
+ public abstract Type View { get; }
///
/// 初始化插件。一般在这个方法中完成插件的各项服务的注册。
///
diff --git a/CRSim.Core/CRSim.Core.csproj b/CRSim.Core/CRSim.Core.csproj
index b87a12d..df72598 100644
--- a/CRSim.Core/CRSim.Core.csproj
+++ b/CRSim.Core/CRSim.Core.csproj
@@ -5,16 +5,13 @@
10.0.17763.0
enable
enable
- partial
AnyCPU;x64
-
-
-
+
diff --git a/CRSim.Core/Models/JsonSerializationContext.cs b/CRSim.Core/Models/JsonSerializationContext.cs
index 8f5691a..6e431a4 100644
--- a/CRSim.Core/Models/JsonSerializationContext.cs
+++ b/CRSim.Core/Models/JsonSerializationContext.cs
@@ -1,4 +1,5 @@
-using System.Text.Json.Serialization;
+using CRSim.Core.Models.Plugin;
+using System.Text.Json.Serialization;
namespace CRSim.Core.Models
{
@@ -7,4 +8,12 @@ namespace CRSim.Core.Models
public partial class JsonContext : JsonSerializerContext
{
}
+
+ [JsonSerializable(typeof(List))]
+ [JsonSerializable(typeof(StyleInfo))]
+ [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
+ public partial class JsonContextWithCamelCase : JsonSerializerContext
+ {
+ }
}
+
diff --git a/CRSim.Core/Models/Plugin/PluginInfo.cs b/CRSim.Core/Models/Plugin/PluginInfo.cs
index dbfd2f7..62c8fc8 100644
--- a/CRSim.Core/Models/Plugin/PluginInfo.cs
+++ b/CRSim.Core/Models/Plugin/PluginInfo.cs
@@ -1,13 +1,10 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CRSim.Core.Abstractions;
using CRSim.Core.Enums;
-using Downloader;
namespace CRSim.Core.Models.Plugin;
public partial class PluginInfo : ObservableRecipient
{
- public DownloadService? DownloadService;
-
[ObservableProperty]
private int _downloadProgress = 0;
diff --git a/CRSim.Core/Models/Plugin/PluginManifest.cs b/CRSim.Core/Models/Plugin/PluginManifest.cs
index 476afc1..a9874cf 100644
--- a/CRSim.Core/Models/Plugin/PluginManifest.cs
+++ b/CRSim.Core/Models/Plugin/PluginManifest.cs
@@ -1,7 +1,4 @@
-using CommunityToolkit.Mvvm.ComponentModel;
-using System.Text.Json.Serialization;
-
-namespace CRSim.Core.Models.Plugin;
+namespace CRSim.Core.Models.Plugin;
///
/// 插件元数据
@@ -12,54 +9,45 @@ public class PluginManifest
/// 入口程序集。加载插件时,将在此入口程序集中搜索插件类。
///
/// MyPlugin.dll
- [JsonPropertyName("entranceAssembly")]
public string EntranceAssembly { get; set; } = "";
///
/// 插件显示名称。
///
- [JsonPropertyName("name")]
public string Name { get; set; } = "";
///
/// 插件ID。
///
- [JsonPropertyName("id")]
public string Id { get; set; } = "";
///
/// 插件自述。
///
- [JsonPropertyName("description")]
public string Description { get; set; } = "";
///
/// 项目 Url
///
- [JsonPropertyName("url")]
public string? Url { get; set; }
///
/// 插件版本
///
- [JsonPropertyName("version")]
public string Version { get; set; } = "";
///
/// 插件目标 CRSim 版本
///
- [JsonPropertyName("apiVersion")]
public string ApiVersion { get; set; } = "";
///
/// 插件作者
///
- [JsonPropertyName("author")]
public string Author { get; set; } = "";
///
/// 插件类型
///
- [JsonPropertyName("type")]
public string Type { get; set; } = "";
}
\ No newline at end of file
diff --git a/CRSim.Core/Models/StyleInfo.cs b/CRSim.Core/Models/StyleInfo.cs
index 981c8ee..cee4ce3 100644
--- a/CRSim.Core/Models/StyleInfo.cs
+++ b/CRSim.Core/Models/StyleInfo.cs
@@ -4,8 +4,8 @@ namespace CRSim.Core.Models
{
public class StyleInfo
{
- public required string[] Parameters;
- public required string Type;
- public required string Region;
+ public string[] Parameters { get; set; } = [];
+ public string Type { get; set; } = "";
+ public string Region { get; set; } = "";
}
}
diff --git a/CRSim.Core/Services/NetworkService.cs b/CRSim.Core/Services/NetworkService.cs
index 7172ec5..34bbaee 100644
--- a/CRSim.Core/Services/NetworkService.cs
+++ b/CRSim.Core/Services/NetworkService.cs
@@ -144,7 +144,7 @@ namespace CRSim.Core.Services
{
var client = new HttpClient();
var response = client.GetStringAsync(url).Result;
- return JsonSerializer.Deserialize>(response);
+ return JsonSerializer.Deserialize(response, JsonContextWithCamelCase.Default.ListPluginManifest);
}
catch
{
diff --git a/CRSim.Core/Services/PluginService.cs b/CRSim.Core/Services/PluginService.cs
index f475654..2e582cb 100644
--- a/CRSim.Core/Services/PluginService.cs
+++ b/CRSim.Core/Services/PluginService.cs
@@ -4,32 +4,29 @@ using CRSim.Core.Enums;
using CRSim.Core.Models;
using CRSim.Core.Models.Plugin;
using CRSim.Core.Utils;
-using Downloader;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Reflection;
+using System.Text.Json;
using Windows.System;
-using YamlDotNet.Serialization;
-using YamlDotNet.Serialization.NamingConventions;
namespace CRSim.Core.Services;
public class PluginService : IPluginService
{
- public static readonly string PluginManifestFileName = "manifest.yml";
+ public static readonly string PluginManifestFileName = "manifest.json";
+ public static readonly string StyleInfoFileName = "style.json";
private string IndexUrl => $"{_settings.ApiUri}/GetFile?fileName=plugins.json";
public static void InitializePlugins(HostBuilderContext context, IServiceCollection services,string externalPluginPath)
{
- if(!Directory.Exists(AppPaths.PluginsRootPath))
+ Console.WriteLine("ʼز");
+ if (!Directory.Exists(AppPaths.PluginsRootPath))
{
Directory.CreateDirectory(AppPaths.PluginsRootPath);
}
- var deserializer = new DeserializerBuilder()
- .IgnoreUnmatchedProperties()
- .WithNamingConvention(CamelCaseNamingConvention.Instance)
- .Build();
+
var pluginDirs = Directory.EnumerateDirectories(AppPaths.PluginsRootPath);
if(!string.IsNullOrEmpty(externalPluginPath))
{
@@ -38,6 +35,7 @@ public class PluginService : IPluginService
foreach (var pluginDir in pluginDirs)
{
+ Console.WriteLine($"ǰѰĿ¼: {pluginDir}");
if (string.IsNullOrWhiteSpace(pluginDir))
continue;
var manifestPath = Path.Combine(pluginDir, PluginManifestFileName);
@@ -47,17 +45,26 @@ public class PluginService : IPluginService
}
var manifestYaml = File.ReadAllText(manifestPath);
- var manifest = deserializer.Deserialize(manifestYaml);
- if (manifest == null)
+ PluginManifest manifest = new();
+ try
{
+ Console.WriteLine(manifestYaml);
+ manifest = JsonSerializer.Deserialize(manifestYaml,JsonContextWithCamelCase.Default.PluginManifest);
+ Console.WriteLine("ɹYAMLļ: " + manifestPath);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("YAMLʧ");
+ Console.WriteLine(ex.ToString());
continue;
}
+ Console.WriteLine("ҵ: " + manifest.Id);
var info = new PluginInfo
{
Manifest = manifest,
PluginFolderPath = Path.GetFullPath(pluginDir),
RealIconPath = Path.Combine(Path.GetFullPath(pluginDir), "icon.png"),
- StyleInfo = manifest.Type == "ScreenStyle" ? deserializer.Deserialize(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), "style.yml"))) : null,
+ StyleInfo = JsonSerializer.Deserialize(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), StyleInfoFileName)),JsonContextWithCamelCase.Default.StyleInfo),
};
if (info.IsUninstalling)
{
@@ -81,6 +88,7 @@ public class PluginService : IPluginService
var pluginDir = info.PluginFolderPath;
try
{
+ Console.WriteLine("Լز: " + manifest.Id);
var fullPath = Path.GetFullPath(Path.Combine(pluginDir, manifest.EntranceAssembly));
var asm = Assembly.LoadFrom(fullPath);
var entrance = asm.ExportedTypes.FirstOrDefault(x =>
@@ -102,10 +110,11 @@ public class PluginService : IPluginService
services.AddSingleton(typeof(PluginBase), entranceObj);
services.AddSingleton(entrance, entranceObj);
info.LoadStatus = PluginLoadStatus.Loaded;
- Console.WriteLine($"Initialize plugin: {pluginDir} ({manifest.Version})");
+ Console.WriteLine($"زɹ: {pluginDir} ({manifest.Version})");
}
catch (Exception ex)
{
+ Console.WriteLine($"زʧ: {ex}");
info.Exception = ex;
info.LoadStatus = PluginLoadStatus.Error;
}
@@ -145,20 +154,21 @@ public class PluginService : IPluginService
Directory.CreateDirectory(tempDir);
var packagePath = Path.Combine(tempDir, $"{id}{IPluginService.PluginPackageExtension}");
DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();
- plugin.DownloadService = new DownloadService();
- plugin.DownloadService.DownloadProgressChanged += (s, e) =>
- {
- dispatcherQueue.TryEnqueue(() =>
- {
- plugin.DownloadProgress = (int)(e.ProgressPercentage);
- Console.WriteLine(e.ProgressPercentage);
- });
- };
+ var downloader = new Downloader();
+ plugin.DownloadProgress = 0;
+
try
{
- await plugin.DownloadService.DownloadFileTaskAsync(packageUrl,packagePath);
+ await downloader.DownloadFileAsync(packageUrl, packagePath, new Progress(p =>
+ {
+ dispatcherQueue.TryEnqueue(() =>
+ {
+ plugin.DownloadProgress = (int)p;
+ Console.WriteLine(p);
+ });
+ }));
}
- catch(Exception e)
+ catch (Exception e)
{
Console.WriteLine(e.Message);
plugin.DownloadProgress = 0;
diff --git a/CRSim.Core/Utils/Downloader.cs b/CRSim.Core/Utils/Downloader.cs
new file mode 100644
index 0000000..c671144
--- /dev/null
+++ b/CRSim.Core/Utils/Downloader.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CRSim.Core.Utils
+{
+ public class Downloader
+ {
+ private readonly HttpClient _httpClient = new();
+
+ public async Task DownloadFileAsync(string url, string outputPath, IProgress? progress = null)
+ {
+ using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
+ response.EnsureSuccessStatusCode();
+
+ var totalBytes = response.Content.Headers.ContentLength ?? -1L;
+ var canReportProgress = totalBytes != -1 && progress != null;
+
+ await using var contentStream = await response.Content.ReadAsStreamAsync();
+ await using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
+
+ var buffer = new byte[8192];
+ long totalRead = 0;
+ int bytesRead;
+
+ while ((bytesRead = await contentStream.ReadAsync(buffer)) > 0)
+ {
+ await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead));
+ totalRead += bytesRead;
+
+ if (canReportProgress)
+ {
+ double percentage = totalRead * 100d / totalBytes;
+ progress!.Report(percentage);
+ }
+ }
+ }
+ }
+}
diff --git a/CRSim.ExamplePlugin/CRSim.ExamplePlugin.csproj b/CRSim.ExamplePlugin/CRSim.ExamplePlugin.csproj
index 741e289..7d48045 100644
--- a/CRSim.ExamplePlugin/CRSim.ExamplePlugin.csproj
+++ b/CRSim.ExamplePlugin/CRSim.ExamplePlugin.csproj
@@ -9,7 +9,6 @@
AnyCPU;x64
enable
enable
- partial
@@ -23,10 +22,10 @@
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/CRSim.ExamplePlugin/Plugin.cs b/CRSim.ExamplePlugin/Plugin.cs
index 85867a8..d80be26 100644
--- a/CRSim.ExamplePlugin/Plugin.cs
+++ b/CRSim.ExamplePlugin/Plugin.cs
@@ -11,8 +11,8 @@ namespace CRSim.ExamplePlugin;
[PluginEntrance]
public class Plugin : PluginBase
{
- public Type ViewModel => typeof(ScreenViewModel);
- public Type View => typeof(ScreenView);
+ public override Type ViewModel => typeof(ScreenViewModel);
+ public override Type View => typeof(ScreenView);
public override void Initialize(HostBuilderContext context, IServiceCollection services)
{
services.AddTransient(ViewModel);
diff --git a/CRSim.ExamplePlugin/manifest.json b/CRSim.ExamplePlugin/manifest.json
new file mode 100644
index 0000000..0df8be5
--- /dev/null
+++ b/CRSim.ExamplePlugin/manifest.json
@@ -0,0 +1,11 @@
+{
+ "entranceAssembly": "CRSim.ExamplePlugin.dll",
+ "name": "示例插件",
+ "id": "CRSim.ExamplePlugin",
+ "url": "https://crsim.tech",
+ "version": "0.0.0.0",
+ "apiVersion": "3.0.0.0",
+ "author": "电排骨",
+ "type": "ScreenStyle",
+ "description": "这是一个示例插件。"
+}
\ No newline at end of file
diff --git a/CRSim.ExamplePlugin/manifest.yml b/CRSim.ExamplePlugin/manifest.yml
deleted file mode 100644
index 8309aa0..0000000
--- a/CRSim.ExamplePlugin/manifest.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-entranceAssembly: "CRSim.ExamplePlugin.dll"
-name: "示例插件"
-id: "denglihong2007.ExamplePlugin"
-url: "https://crsim.tech"
-version: "0.0.0.0"
-apiVersion: "3.0.0.0"
-author: "电排骨"
-type: "ScreenStyle"
-description: "这是一个示例插件。"
\ No newline at end of file
diff --git a/CRSim.ExamplePlugin/style.json b/CRSim.ExamplePlugin/style.json
new file mode 100644
index 0000000..018c1d5
--- /dev/null
+++ b/CRSim.ExamplePlugin/style.json
@@ -0,0 +1,5 @@
+{
+ "parameters": [ "station", "text" ],
+ "region": "广元",
+ "type": "车站大屏"
+}
\ No newline at end of file
diff --git a/CRSim.ExamplePlugin/style.yml b/CRSim.ExamplePlugin/style.yml
deleted file mode 100644
index 89562e7..0000000
--- a/CRSim.ExamplePlugin/style.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-parameters :
- - "station"
- - "text"
-region: "广元"
-type: "车站大屏"
\ No newline at end of file
diff --git a/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj b/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj
index 3f5ef60..d0529ec 100644
--- a/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj
+++ b/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj
@@ -4,14 +4,13 @@
net9.0-windows10.0.19041.0
10.0.17763.0
enable
- partial
true
AnyCPU;x64
enable
-
+
diff --git a/CRSim/App.xaml.cs b/CRSim/App.xaml.cs
index a8ba0f1..7a3c95c 100644
--- a/CRSim/App.xaml.cs
+++ b/CRSim/App.xaml.cs
@@ -46,6 +46,7 @@
AppHost.Services.GetRequiredService().Start();
MainWindow = AppHost.Services.GetService();
MainWindow?.Activate();
+ var win = AppHost.Services.GetService();
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
diff --git a/CRSim/CRSim.csproj b/CRSim/CRSim.csproj
index 82ce86a..cfea50b 100644
--- a/CRSim/CRSim.csproj
+++ b/CRSim/CRSim.csproj
@@ -13,8 +13,6 @@
enable
None
preview
- partial
- true
2.3.102.0
@@ -56,10 +54,10 @@
-
-
-
-
+
+
+
+
@@ -131,11 +129,12 @@
False
True
False
- True
+ False
10.0.17763.0
Assets\CRSimIcon.ico
False
CRSimIcon.png
true
+ False
\ No newline at end of file
diff --git a/CRSim/ViewModels/PluginManagementPageViewModel.cs b/CRSim/ViewModels/PluginManagementPageViewModel.cs
index 6365747..58ba52f 100644
--- a/CRSim/ViewModels/PluginManagementPageViewModel.cs
+++ b/CRSim/ViewModels/PluginManagementPageViewModel.cs
@@ -15,7 +15,7 @@ public partial class PluginManagementPageViewModel : ObservableObject
[ObservableProperty]
public partial PluginInfo? SelectedPlugin { get; set; }
- public ObservableCollection Plugins = [];
+ public ObservableCollection Plugins = IPluginService.OnlinePlugins;
private IPluginService _pluginService;
@@ -66,12 +66,14 @@ public partial class PluginManagementPageViewModel : ObservableObject
}
[RelayCommand]
- public void SourceSelected(SelectorBarItem selectorBarItem)
+ public void SourceSelected(object args)
{
- SelectedPlugin = null;
- Plugins = (selectorBarItem?.Text == "本地" || selectorBarItem is null)
- ? IPluginService.OnlinePlugins : IPluginService.LoadedPlugins;
- OnPropertyChanged(nameof(Plugins));
+ if(args is SelectorBarItem selectorBarItem)
+ {
+ SelectedPlugin = null;
+ Plugins = (selectorBarItem?.Text == "本地") ? IPluginService.OnlinePlugins : IPluginService.LoadedPlugins;
+ OnPropertyChanged(nameof(Plugins));
+ }
}
[RelayCommand]
diff --git a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs
index 4496c0c..6f4fc97 100644
--- a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs
+++ b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs
@@ -1,4 +1,5 @@
using CRSim.Core.Models.Plugin;
+using System.Diagnostics.CodeAnalysis;
namespace CRSim.ViewModels;
@@ -132,7 +133,7 @@ public partial class ScreenSimulatorPageViewModel(IServiceProvider serviceProvid
[RelayCommand]
public async Task StartSimulation()
{
- StyleManager.ShowWindow(((dynamic)SelectedStylePlugin).View,
+ StyleManager.ShowWindow(SelectedStylePlugin.View,
databaseService.GetStationByName(SelectedStationName),
TicketCheckNeeded ? SelectedTicketCheck : string.Empty,
PlatformNeeded ? SelectedPlatformName : string.Empty,
diff --git a/CRSim/Views/PluginManagementPage.xaml b/CRSim/Views/PluginManagementPage.xaml
index 03a8379..0876b9d 100644
--- a/CRSim/Views/PluginManagementPage.xaml
+++ b/CRSim/Views/PluginManagementPage.xaml
@@ -47,7 +47,7 @@
-
+
diff --git a/CRSim/Views/PluginManagementPage.xaml.cs b/CRSim/Views/PluginManagementPage.xaml.cs
index 43a2f0f..173b39c 100644
--- a/CRSim/Views/PluginManagementPage.xaml.cs
+++ b/CRSim/Views/PluginManagementPage.xaml.cs
@@ -11,4 +11,9 @@ public sealed partial class PluginManagementPage : Page
InitializeComponent();
DataContext = ViewModel;
}
+
+ private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
+ {
+ Console.WriteLine($"SelectorBar selection changed");
+ }
}