mirror of
https://github.com/denglihong2007/CRSim
synced 2026-08-07 01:55:50 +08:00
feat: #203 添加插件市场
This commit is contained in:
@@ -1,15 +1,12 @@
|
||||
using CRSim.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CRSim.Core.Models.Plugin;
|
||||
|
||||
namespace CRSim.Core.Abstractions
|
||||
{
|
||||
public interface INetworkService
|
||||
{
|
||||
Task<List<TrainStop>?> GetTimeTableAsync(string number);
|
||||
Task<List<TrainStop>> GetTrainNumbersAsync(string name);
|
||||
Task<List<TrainStop>?> GetTrainNumbersAsync(string name);
|
||||
List<PluginManifest>? GetOnlinePlugins(string uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,19 @@ public interface IPluginService
|
||||
/// <summary>
|
||||
/// 插件包文件扩展名。
|
||||
/// </summary>
|
||||
public static readonly string PluginPackageExtension = ".cp";
|
||||
public static readonly string PluginPackageExtension = ".crsp";
|
||||
|
||||
internal static ObservableCollection<PluginInfo> LoadedPluginsInternal { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 已加载的插件信息列表。
|
||||
/// </summary>
|
||||
public static IReadOnlyList<PluginInfo> LoadedPlugins => LoadedPluginsInternal;
|
||||
public static ObservableCollection<PluginInfo> LoadedPlugins => LoadedPluginsInternal;
|
||||
|
||||
internal static ObservableCollection<PluginInfo> OnlinePluginsInternal { get; } = [];
|
||||
|
||||
public static ObservableCollection<PluginInfo> OnlinePlugins => OnlinePluginsInternal;
|
||||
|
||||
Task InstallPluginAsync(PluginInfo plugin);
|
||||
void LoadOnlinePlugins();
|
||||
}
|
||||
@@ -5,12 +5,14 @@
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TrimMode>partial</TrimMode>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Downloader" Version="4.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.6" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CRSim.Core.Abstractions;
|
||||
using CRSim.Core.Enums;
|
||||
using Downloader;
|
||||
|
||||
namespace CRSim.Core.Models.Plugin;
|
||||
public partial class PluginInfo : ObservableRecipient
|
||||
{
|
||||
//private DownloadProgress? _downloadProgress;
|
||||
//private bool _isAvailableOnMarket = false;
|
||||
public DownloadService? DownloadService;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _downloadProgress = 0;
|
||||
|
||||
public bool IsAvailableOnMarket => IPluginService.OnlinePlugins.Any(x => x.Manifest.Id == Manifest.Id);
|
||||
|
||||
public bool IsUpdateAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsAvailableOnMarket)
|
||||
{
|
||||
if(IPluginService.OnlinePlugins.Where(x => x.Manifest.Id == Manifest.Id).FirstOrDefault() is PluginInfo onlinePlugin)
|
||||
{
|
||||
return onlinePlugin.Manifest.Version != Manifest.Version;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private PluginManifest _manifest = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _restartRequired = false;
|
||||
|
||||
public PluginLoadStatus LoadStatus { get; internal set; } = PluginLoadStatus.NotLoaded;
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get
|
||||
@@ -32,6 +57,7 @@ public partial class PluginInfo : ObservableRecipient
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string PluginFolderPath { get; internal set; } = "";
|
||||
public string RealIconPath { get; set; } = "";
|
||||
public bool IsUninstalling
|
||||
@@ -45,17 +71,18 @@ public partial class PluginInfo : ObservableRecipient
|
||||
var path = Path.Combine(PluginFolderPath, ".uninstall");
|
||||
if (value)
|
||||
{
|
||||
RestartRequired = true;
|
||||
File.WriteAllText(path, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
RestartRequired = false;
|
||||
File.Delete(path);
|
||||
}
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public Exception? Exception { get; internal set; }
|
||||
//private bool _isUpdateAvailable = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private StyleInfo? styleInfo;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CRSim.Core.Models.Plugin;
|
||||
|
||||
@@ -11,50 +12,54 @@ public class PluginManifest
|
||||
/// 入口程序集。加载插件时,将在此入口程序集中搜索插件类。
|
||||
/// </summary>
|
||||
/// <example>MyPlugin.dll</example>
|
||||
[JsonPropertyName("entranceAssembly")]
|
||||
public string EntranceAssembly { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件显示名称。
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件ID。
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件图标路径。默认为icon.png。
|
||||
/// </summary>
|
||||
public string Icon { get; set; } = "icon.png";
|
||||
|
||||
/// <summary>
|
||||
/// 插件自述。
|
||||
/// </summary>
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 项目 Url
|
||||
/// </summary>
|
||||
[JsonPropertyName("url")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件版本
|
||||
/// </summary>
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件目标 CRSim 版本
|
||||
/// </summary>
|
||||
[JsonPropertyName("apiVersion")]
|
||||
public string ApiVersion { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件作者
|
||||
/// </summary>
|
||||
[JsonPropertyName("author")]
|
||||
public string Author { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 插件类型
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = "";
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace CRSim.Core.Models
|
||||
/// </summary>
|
||||
public TimeSpan StopDisplayFromArrivalDuration { get; set; } = TimeSpan.FromMinutes(10);
|
||||
public TimeSpan StopCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(2);
|
||||
|
||||
public string ApiUri { get; set; } = "http://47.122.74.193:25565";
|
||||
public int MaxPages { get; set; } = 3;
|
||||
public int SwitchPageSeconds { get; set; } = 20;
|
||||
public string UserKey { get; set; } = "";
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using CRSim.Core.Abstractions;
|
||||
using CRSim.Core.Models;
|
||||
using CRSim.Core.Models.Plugin;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace CRSim.Core.Services
|
||||
{
|
||||
public class NetworkService(IDatabaseService databaseService) : INetworkService
|
||||
public class NetworkService() : INetworkService
|
||||
{
|
||||
private readonly IDatabaseService _databaseService = databaseService;
|
||||
|
||||
public async Task<List<TrainStop>?> GetTimeTableAsync(string number)
|
||||
{
|
||||
try
|
||||
@@ -62,10 +61,10 @@ namespace CRSim.Core.Services
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TimeSpan? ParseTime(string timeStr)
|
||||
{
|
||||
timeStr = string.Concat(timeStr.AsSpan(0, 2), ":", timeStr.AsSpan(2, 2));
|
||||
@@ -78,58 +77,79 @@ namespace CRSim.Core.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<List<TrainStop>> GetTrainNumbersAsync(string name)
|
||||
public async Task<List<TrainStop>?> GetTrainNumbersAsync(string name)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
var stations = (await client.GetStringAsync("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")).Split("|||");
|
||||
string tel = "";
|
||||
foreach (string station in stations)
|
||||
try
|
||||
{
|
||||
if (station.StartsWith("';")) continue;
|
||||
if (station.Split("|")[1] == name)
|
||||
var client = new HttpClient();
|
||||
var stations = (await client.GetStringAsync("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")).Split("|||");
|
||||
string tel = "";
|
||||
foreach (string station in stations)
|
||||
{
|
||||
tel = station.Split("|")[2];
|
||||
if (station.StartsWith("';")) continue;
|
||||
if (station.Split("|")[1] == name)
|
||||
{
|
||||
tel = station.Split("|")[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tel == "")
|
||||
{
|
||||
return [];
|
||||
}
|
||||
var baseAddress = new Uri("https://mobile.12306.cn/wxxcx/wechat/bigScreen/queryTrainByStation");
|
||||
var cookieContainer = new CookieContainer();
|
||||
cookieContainer.Add(baseAddress, new Cookie("BIGipServerweixin_xiaochengxu", "2028077578.5670.0000"));
|
||||
var handler = new HttpClientHandler() { CookieContainer = cookieContainer, UseCookies = true };
|
||||
if (tel == "")
|
||||
{
|
||||
return [];
|
||||
}
|
||||
var baseAddress = new Uri("https://mobile.12306.cn/wxxcx/wechat/bigScreen/queryTrainByStation");
|
||||
var cookieContainer = new CookieContainer();
|
||||
cookieContainer.Add(baseAddress, new Cookie("BIGipServerweixin_xiaochengxu", "2028077578.5670.0000"));
|
||||
var handler = new HttpClientHandler() { CookieContainer = cookieContainer, UseCookies = true };
|
||||
|
||||
HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||
{
|
||||
{"train_station_code",tel },
|
||||
{"train_start_date",DateTime.Now.ToString("yyyyMMdd")}
|
||||
});
|
||||
client = new HttpClient(handler) { BaseAddress = baseAddress };
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11581");
|
||||
var response = await client.PostAsync(baseAddress, content);
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
if (json.Contains("操作失败,请稍后重试")) return [];
|
||||
var list = JsonDocument.Parse(json).RootElement.GetProperty("data").EnumerateArray();
|
||||
if (!list.Any()) return [];
|
||||
List<TrainStop> trainStops = [];
|
||||
foreach (var item in list)
|
||||
{
|
||||
var arriveTimeStr = item.GetProperty("arrive_time").GetString();
|
||||
var startTimeStr = item.GetProperty("start_time").GetString();
|
||||
TimeSpan? arriveTime = null;
|
||||
TimeSpan? startTime = null;
|
||||
if (arriveTimeStr != "----")
|
||||
HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||
{
|
||||
arriveTime = TimeSpan.Parse(arriveTimeStr);
|
||||
}
|
||||
if (startTimeStr != arriveTimeStr)
|
||||
{"train_station_code",tel },
|
||||
{"train_start_date",DateTime.Now.ToString("yyyyMMdd")}
|
||||
});
|
||||
client = new HttpClient(handler) { BaseAddress = baseAddress };
|
||||
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11581");
|
||||
var response = await client.PostAsync(baseAddress, content);
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
if (json.Contains("操作失败,请稍后重试")) return [];
|
||||
var list = JsonDocument.Parse(json).RootElement.GetProperty("data").EnumerateArray();
|
||||
if (!list.Any()) return [];
|
||||
List<TrainStop> trainStops = [];
|
||||
foreach (var item in list)
|
||||
{
|
||||
startTime = TimeSpan.Parse(startTimeStr);
|
||||
var arriveTimeStr = item.GetProperty("arrive_time").GetString();
|
||||
var startTimeStr = item.GetProperty("start_time").GetString();
|
||||
TimeSpan? arriveTime = null;
|
||||
TimeSpan? startTime = null;
|
||||
if (arriveTimeStr != "----")
|
||||
{
|
||||
arriveTime = TimeSpan.Parse(arriveTimeStr);
|
||||
}
|
||||
if (startTimeStr != arriveTimeStr)
|
||||
{
|
||||
startTime = TimeSpan.Parse(startTimeStr);
|
||||
}
|
||||
trainStops.Add(new TrainStop() { Number = item.GetProperty("station_train_code").ToString(), Terminal = item.GetProperty("end_station_name").ToString(), Origin = item.GetProperty("start_station_name").ToString(), ArrivalTime = arriveTime, DepartureTime = startTime });
|
||||
}
|
||||
trainStops.Add(new TrainStop() { Number = item.GetProperty("station_train_code").ToString(), Terminal = item.GetProperty("end_station_name").ToString(), Origin = item.GetProperty("start_station_name").ToString(), ArrivalTime = arriveTime, DepartureTime = startTime });
|
||||
return trainStops;
|
||||
}
|
||||
return trainStops;
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<PluginManifest>? GetOnlinePlugins(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new HttpClient();
|
||||
var response = client.GetStringAsync(url).Result;
|
||||
return JsonSerializer.Deserialize<List<PluginManifest>>(response);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ 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 Windows.System;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
@@ -14,7 +16,9 @@ namespace CRSim.Core.Services;
|
||||
|
||||
public class PluginService : IPluginService
|
||||
{
|
||||
public static readonly string PluginManifestFileName = "manifest.yml";
|
||||
public static readonly string PluginManifestFileName = "manifest.yml";
|
||||
|
||||
private string IndexUrl => $"{_settings.ApiUri}/GetFile?fileName=plugins.json";
|
||||
|
||||
public static void InitializePlugins(HostBuilderContext context, IServiceCollection services,string externalPluginPath)
|
||||
{
|
||||
@@ -52,7 +56,7 @@ public class PluginService : IPluginService
|
||||
{
|
||||
Manifest = manifest,
|
||||
PluginFolderPath = Path.GetFullPath(pluginDir),
|
||||
RealIconPath = Path.Combine(Path.GetFullPath(pluginDir), manifest.Icon),
|
||||
RealIconPath = Path.Combine(Path.GetFullPath(pluginDir), "icon.png"),
|
||||
StyleInfo = manifest.Type == "ScreenStyle" ? deserializer.Deserialize<StyleInfo?>(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), "style.yml"))) : null,
|
||||
};
|
||||
if (info.IsUninstalling)
|
||||
@@ -60,15 +64,19 @@ public class PluginService : IPluginService
|
||||
Directory.Delete(pluginDir, true);
|
||||
continue;
|
||||
}
|
||||
IPluginService.LoadedPluginsInternal.Add(info);
|
||||
if (!info.IsEnabled)
|
||||
{
|
||||
info.LoadStatus = PluginLoadStatus.Disabled;
|
||||
}
|
||||
IPluginService.LoadedPluginsInternal.Add(info);
|
||||
}
|
||||
|
||||
foreach (var info in IPluginService.LoadedPluginsInternal)
|
||||
{
|
||||
if(info.LoadStatus == PluginLoadStatus.Disabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var manifest = info.Manifest;
|
||||
var pluginDir = info.PluginFolderPath;
|
||||
try
|
||||
@@ -103,5 +111,71 @@ public class PluginService : IPluginService
|
||||
}
|
||||
}
|
||||
}
|
||||
private readonly Models.Settings _settings;
|
||||
private readonly INetworkService _networkService;
|
||||
public PluginService(INetworkService networkService,ISettingsService settingsService)
|
||||
{
|
||||
_networkService = networkService;
|
||||
_settings = settingsService.GetSettings();
|
||||
LoadOnlinePlugins();
|
||||
}
|
||||
public void LoadOnlinePlugins()
|
||||
{
|
||||
var pluginManifests = _networkService.GetOnlinePlugins(IndexUrl);
|
||||
IPluginService.OnlinePluginsInternal.Clear();
|
||||
foreach (var Manifest in pluginManifests ?? [])
|
||||
{
|
||||
var localInfo = IPluginService.LoadedPluginsInternal.FirstOrDefault(x => x.Manifest.Id == Manifest.Id);
|
||||
var info = new PluginInfo
|
||||
{
|
||||
Manifest = Manifest,
|
||||
RealIconPath = $"{_settings.ApiUri}/GetFile?fileName=icons/{Manifest.Id}.png",
|
||||
LoadStatus = localInfo?.LoadStatus ?? PluginLoadStatus.NotLoaded,
|
||||
PluginFolderPath = localInfo?.PluginFolderPath ?? string.Empty,
|
||||
};
|
||||
IPluginService.OnlinePluginsInternal.Add(info);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InstallPluginAsync(PluginInfo plugin)
|
||||
{
|
||||
var id = plugin.Manifest.Id;
|
||||
var packageUrl = $"{_settings.ApiUri}/GetFile?fileName=plugins/{id}{IPluginService.PluginPackageExtension}";
|
||||
var tempDir = Path.Combine(AppPaths.TempPath, "Plugins", Guid.NewGuid().ToString());
|
||||
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);
|
||||
});
|
||||
};
|
||||
try
|
||||
{
|
||||
await plugin.DownloadService.DownloadFileTaskAsync(packageUrl,packagePath);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
plugin.DownloadProgress = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// 解压到插件目录
|
||||
var destDir = Path.Combine(AppPaths.PluginsRootPath, id);
|
||||
if (Directory.Exists(destDir))
|
||||
Directory.Delete(destDir, true);
|
||||
Directory.CreateDirectory(destDir);
|
||||
System.IO.Compression.ZipFile.ExtractToDirectory(packagePath, destDir);
|
||||
|
||||
// 清理临时文件
|
||||
try { Directory.Delete(tempDir, true); } catch { }
|
||||
|
||||
plugin.DownloadProgress = 0;
|
||||
plugin.RestartRequired = true;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace CRSim.Core.Services
|
||||
{
|
||||
_key.SetValue("TimeOffset", (int)_settings.TimeOffset.TotalMinutes);
|
||||
_key.SetValue("SwitchPageSeconds", _settings.SwitchPageSeconds);
|
||||
_key.SetValue("ApiUri", _settings.ApiUri);
|
||||
_key.SetValue("MaxPages", _settings.MaxPages);
|
||||
_key.SetValue("StopCheckInAdvanceDuration", (int)_settings.StopCheckInAdvanceDuration.TotalMinutes);
|
||||
_key.SetValue("StopDisplayUntilDepartureDuration", (int)_settings.StopDisplayUntilDepartureDuration.TotalMinutes);
|
||||
@@ -41,6 +42,7 @@ namespace CRSim.Core.Services
|
||||
_settings = new Settings();
|
||||
if (_key.GetValue("TimeOffset") != null) _settings.TimeOffset = TimeSpan.FromMinutes((int)_key.GetValue("TimeOffset"));
|
||||
if (_key.GetValue("SwitchPageSeconds") != null) _settings.SwitchPageSeconds = (int)_key.GetValue("SwitchPageSeconds");
|
||||
if (_key.GetValue("ApiUri") != null) _settings.ApiUri = (string)_key.GetValue("ApiUri");
|
||||
if (_key.GetValue("MaxPages") != null) _settings.MaxPages = (int)_key.GetValue("MaxPages");
|
||||
if (_key.GetValue("StopCheckInAdvanceDuration") != null) _settings.StopCheckInAdvanceDuration = TimeSpan.FromMinutes((int)_key.GetValue("StopCheckInAdvanceDuration"));
|
||||
if (_key.GetValue("StopDisplayUntilDepartureDuration") != null) _settings.StopDisplayUntilDepartureDuration = TimeSpan.FromMinutes((int)_key.GetValue("StopDisplayUntilDepartureDuration"));
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
{
|
||||
public static class AppPaths
|
||||
{
|
||||
public static string AppDataFolder =>
|
||||
public static string AppDataPath =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CRSim");
|
||||
public static string TempPath =>
|
||||
Path.Combine(Path.GetTempPath(), "CRSim");
|
||||
|
||||
public static string ConfigFilePath =>
|
||||
Path.Combine(AppDataFolder, "data.json");
|
||||
Path.Combine(AppDataPath, "data.json");
|
||||
|
||||
public static string PluginsRootPath =>
|
||||
Path.Combine(AppDataFolder, "Plugins");
|
||||
Path.Combine(AppDataPath, "Plugins");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TrimMode>partial</TrimMode>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CRSim.Core\CRSim.Core.csproj">
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<TrimMode>partial</TrimMode>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TrimMode>partial</TrimMode>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<Version>2.3.101.1</Version>
|
||||
<Version>2.3.102.0</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="Assets\CRSimIcon.png" />
|
||||
|
||||
Reference in New Issue
Block a user