feat: 支持车站资源插件

This commit is contained in:
denglihong2007
2026-01-01 00:03:12 +08:00
parent a2380398a2
commit 08046e5a14
5 changed files with 43 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
using CRSim.Core.Models.Plugin;
using CRSim.Core.Models;
using CRSim.Core.Models.Plugin;
using System.Collections.ObjectModel;
namespace CRSim.Core.Abstractions;
@@ -28,4 +29,5 @@ public interface IPluginService
Task InstallPluginLocalAsync(string filePath);
Task PackPluginAsync(PluginInfo plugin, string filePath);
Task LoadOnlinePluginsAsync();
List<(string, Station)> GetStationData();
}

View File

@@ -83,4 +83,7 @@ public partial class PluginInfo : ObservableRecipient
[ObservableProperty]
private StyleInfo? styleInfo;
[ObservableProperty]
private Json? data;
}

View File

@@ -65,6 +65,7 @@ public class PluginService(INetworkService networkService, ISettingsService sett
PluginFolderPath = Path.GetFullPath(pluginDir),
RealIconPath = Path.Combine(Path.GetFullPath(pluginDir), "icon.png"),
StyleInfo = manifest.Type == "ScreenStyle" ? JsonSerializer.Deserialize(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), StyleInfoFileName)),JsonContextWithCamelCase.Default.StyleInfo) : null,
Data = manifest.Type == "StationData" ? JsonSerializer.Deserialize(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), manifest.EntranceAssembly)), JsonContext.Default.Json) : null,
};
if (info.IsUninstalling)
{
@@ -77,8 +78,15 @@ public class PluginService(INetworkService networkService, ISettingsService sett
}
IPluginService.LoadedPluginsInternal.Add(info);
}
foreach (var info in IPluginService.LoadedPluginsInternal.Where(x => x.Manifest.Type == "StationData"))
{
if (info.LoadStatus != PluginLoadStatus.Disabled)
{
info.LoadStatus = PluginLoadStatus.Loaded;
}
}
foreach (var info in IPluginService.LoadedPluginsInternal)
foreach (var info in IPluginService.LoadedPluginsInternal.Where(x => x.Manifest.Type == "ScreenStyle"))
{
if(info.LoadStatus == PluginLoadStatus.Disabled)
{
@@ -88,7 +96,7 @@ public class PluginService(INetworkService networkService, ISettingsService sett
var pluginDir = info.PluginFolderPath;
try
{
Console.WriteLine("尝试加载插件: " + manifest.Id);
Console.WriteLine("尝试加载插件DLL: " + manifest.Id);
var fullPath = Path.GetFullPath(Path.Combine(pluginDir, manifest.EntranceAssembly));
var loadContext = new PluginLoadContext(fullPath);
var assembly = loadContext.LoadFromAssemblyPath(fullPath);
@@ -111,11 +119,11 @@ public class PluginService(INetworkService networkService, ISettingsService sett
services.AddSingleton(typeof(PluginBase), entranceObj);
services.AddSingleton(entrance, entranceObj);
info.LoadStatus = PluginLoadStatus.Loaded;
Console.WriteLine($"加载插件成功: {pluginDir} ({manifest.Version})");
Console.WriteLine($"加载插件DLL成功: {pluginDir} ({manifest.Version})");
}
catch (Exception ex)
{
Console.WriteLine($"加载插件失败: {ex}");
Console.WriteLine($"加载插件DLL失败: {ex}");
info.Exception = ex;
info.LoadStatus = PluginLoadStatus.Error;
}
@@ -221,7 +229,14 @@ public class PluginService(INetworkService networkService, ISettingsService sett
IPluginService.LoadedPluginsInternal.Add(info);
await Task.CompletedTask;
}
public List<(string, Station)> GetStationData()
{
var result = IPluginService.LoadedPluginsInternal
.Where(p => p.Manifest.Type == "StationData" && p.Data is not null)
.SelectMany(p => p.Data.Stations.Select(s => (p.Manifest.Name, s)))
.ToList();
return result;
}
public async Task PackPluginAsync(PluginInfo plugin, string filePath)
{
var pluginDir = plugin.PluginFolderPath;

View File

@@ -11,6 +11,9 @@
<RadioButton x:Name="Blank" GroupName="options" Content="新建空白车站" Margin="0,0,0,4" IsChecked="True" Checked="Validate"/>
<TextBox x:Name="textBoxBlank" IsEnabled="{x:Bind ToBoolean(Blank.IsChecked), Mode=OneWay}"
PlaceholderText="车站名" Margin="0,0,0,8" TextChanged="Validate"/>
<RadioButton x:Name="Plugin" GroupName="options" Content="从插件资源导入" Margin="0,0,0,4" Checked="Validate"/>
<ComboBox x:Name="comboBoxPlugin" IsEnabled="{x:Bind ToBoolean(Plugin.IsChecked), Mode=OneWay}" HorizontalAlignment="Stretch"
PlaceholderText="车站" Margin="0,0,0,8" SelectionChanged="Validate" ItemsSource="{x:Bind StationRecords}" DisplayMemberPath="Name" SelectedValuePath="Station"/>
<RadioButton x:Name="btnFile" GroupName="options" Content="从车站文件导入" Margin="0,0,0,4" Checked="Validate"/>
<ContentControl IsEnabled="{x:Bind ToBoolean(btnFile.IsChecked), Mode=OneWay}" HorizontalContentAlignment="Stretch">
<Grid Margin="0,0,0,8">

View File

@@ -4,9 +4,16 @@ public sealed partial class CreateStationDialog : Page
private readonly IDialogService _dialogService = App.GetService<IDialogService>();
private readonly IPluginService _pluginService = App.GetService<IPluginService>();
private Station? _parsedStation = null;
public Station? GeneratedStation => ToBoolean(Blank.IsChecked) ? new Station
private record StationRecord(string Name, Station Station);
private List<StationRecord> StationRecords { get; }
public Station? GeneratedStation => ToBoolean(Plugin.IsChecked) ? (Station)comboBoxPlugin.SelectedValue :
ToBoolean(Blank.IsChecked) ? new Station
{
Name = textBoxBlank.Text,
Platforms = [],
@@ -23,12 +30,16 @@ public sealed partial class CreateStationDialog : Page
private void Validate(object sender, object e)
{
if (textBoxBlank == null) return;
if (textBoxBlank == null) return;//检查窗体初始化
bool isValid;
if (ToBoolean(Blank.IsChecked))
{
isValid = !string.IsNullOrWhiteSpace(textBoxBlank.Text);
}
else if(ToBoolean(Plugin.IsChecked))
{
isValid = comboBoxPlugin.SelectedValue is not null;
}
else
{
isValid = _parsedStation != null;
@@ -38,6 +49,7 @@ public sealed partial class CreateStationDialog : Page
public CreateStationDialog(Action<bool> onValidityChanged)
{
StationRecords = _pluginService.GetStationData().Select(x => new StationRecord($"{x.Item1}-{x.Item2.Name}",x.Item2)).ToList();
_onValidityChanged = onValidityChanged;
InitializeComponent();
}