Compare commits

...
Author SHA1 Message Date
Aigio Liu 7366e2c904 🚧 Misc 2023-09-02 17:28:32 +08:00
ff m 5215e3d613 🐛打包卸载 启动 脚本 2023-09-02 14:27:58 +08:00
ff m 1c454123b8 ✨检查环境脚本 卸载安装脚本 2023-09-02 14:04:40 +08:00
RMBGAME 18080285bf 💄 Misc 2023-09-02 12:23:22 +08:00
Aigio Liu 1049355476 ✨ Application Crash(WPFMessageBox) 2023-09-02 02:22:09 +08:00
Aigio Liu dc74c2ae10 🚧 Misc 2023-09-02 02:02:14 +08:00
ff m b32cb40949 🐛 修复 系统版本获取字段 2023-09-01 20:49:47 +08:00
Aigio Liu 98324d95a8 🐛 WriteDefaultHostsContent & DisablePluginsAssemblyLoadContext 2023-09-01 19:56:20 +08:00
Aigio Liu bed2fbb98d 🐛 System.InvalidOperationException: No service for type 'BD.WTTS.Settings.Abstractions.IPartialGameLibrarySettings' has been registered. 2023-09-01 17:32:45 +08:00
Aigio Liu dd09e37048 🐛 HostsFileServiceImpl Ipc 2023-09-01 17:09:33 +08:00
Aigio Liu d359392fc0 ✨ stmupload 2023-09-01 15:24:44 +08:00
ff m 4601987cb4 🐛脚本安装默认启用 2023-09-01 15:17:25 +08:00
RMBGAME 7c21e8454e 🐛 Fixed Bugs 2023-09-01 13:40:46 +08:00
RMBGAME 6f60f10fc8 🐛 Fixed Bugs 2023-09-01 13:24:43 +08:00
RMBGAME 41b51f4e0a 🐛 Steam 令牌 闪退问题 2023-09-01 11:22:47 +08:00
Aigio 83952d6d76 🚧 3.0.0-preview.7 2023-09-01 06:43:18 +08:00
36 changed files with 445 additions and 198 deletions
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
run_path=$(dirname "$0")
dotnet_path="$run_path/dotnet"
dotnet_exec="$dotnet_path/dotnet"
export DOTNET_ROOT="$dotnet_path"
link_exec="$run_path/Steam++"
# 判断符号链接是否存在
if [ ! -L "$link_exec" ]; then
echo "创建符号链接 $dotnet_exec 到 $link_exec"
ln -s "$dotnet_exec" "$link_exec"
else
echo "符号链接 $link_exec 已存在"
fi
"$link_exec" "$run_path/assemblies/Steam++.dll" "$@"
exit
+48
View File
@@ -0,0 +1,48 @@
#!/bin/bash
base_path="$HOME/WattToolkit"
export LC_ALL=en_US.UTF-8
Install_certutil() {
# 判断发行版类型
if command -v certutil &>/dev/null; then
echo "certutil 工具已安装。"
else
echo "证书导入以及验证需要使用 certutil 工具。"
# 判断包管理器
if command -v apt &>/dev/null; then
# 使用 apt (Debian/Ubuntu)
sudo apt update
sudo apt install -y libnss3-tools
elif command -v dnf &>/dev/null; then
# 使用 dnf (Fedora)
sudo dnf install -y nss-tools
elif command -v yum &>/dev/null; then
# 使用 yum (CentOS/Red Hat)
sudo yum install -y nss-tools
elif command -v pacman &>/dev/null; then
# 使用 pacman (Arch Linux)
# sudo pacman -S nss
echo "请手动安装 certutil 工具。"
exit 1
else
echo "请手动安装 certutil 工具。"
exit 1
fi
echo "certutil 工具已安装。"
fi
}
certutil_Init() {
certutil -d sql:$HOME/.pki/nssdb -L
ret=$?
if [ $ret -ne 0 ]; then
echo "即将初始化 certutil sql:\$HOME/.pki/nssdb"
mkdir -p sql:$HOME/.pki
mkdir -p sql:$HOME/.pki/nssdb
chmod 700 sql:$HOME/.pki/nssdb
certutil -d sql:$HOME/.pki/nssdb -N --empty-password
else
echo "certutil nssdb 正常"
fi
}
Install_certutil
certutil_Init
+1 -18
View File
@@ -152,7 +152,7 @@ Get_NewVer() {
esac
# 获取发行版信息
read -r os_version <<<"$(cat /etc/os-release | grep -E 'VERSION=' | awk -F'=' '{ print $2 }' | tr -d '"')"
read -r os_version <<<"$(cat /etc/os-release | grep -E 'VERSION_ID=' | awk -F'=' '{ print $2 }' | tr -d '"')"
# 如果 VERSION 为空,则使用 BUILD_ID 填充
if [ -z "$os_version" ]; then
@@ -283,23 +283,6 @@ Decompression() {
else
chmod +x "$dotnet_exec"
fi
rm "$base_path/start.sh"
rm "$base_path/Steam++"
echo "#!/bin/bash
run_path=\$(dirname \"\$0\")
dotnet_path=\"\$run_path/dotnet\"
dotnet_exec=\"\$dotnet_path/dotnet\"
export DOTNET_ROOT=\"\$dotnet_path\"
link_exec=\"\$run_path/Steam++\"
# 判断符号链接是否存在
if [ ! -L \"\$link_exec\" ]; then
echo \"创建符号链接 \$dotnet_exec 到 \$link_exec\"
ln -s \"\$dotnet_exec\" \"\$link_exec\"
else
echo \"符号链接 \$link_exec 已存在\"
fi
\"\$link_exec\" \"\$run_path/assemblies/Steam++.dll\" \"\$@\"
exit" >"$base_path/$exec_name.sh"
chmod +x "$base_path/$exec_name.sh"
}
+60
View File
@@ -0,0 +1,60 @@
#!/bin/bash
base_path="$HOME/WattToolkit"
export LC_ALL=en_US.UTF-8
exec_name="Steam++"
app_name="Watt Toolkit"
AppData="$XDG_DATA_HOME/Steam++"
AppData_t="$HOME/.local/share/Steam++"
Cache="$XDG_CACHE_HOME/Steam++"
Cache_t="$HOME/.cache/Steam+"
PROCESS_NAMES=("$exec_name" "$app_name")
Kill_Process() {
# 尝试的次数
Kill_MAX_RETRIES=3
# 循环尝试终止进程
for process_name in "${PROCESS_NAMES[@]}"; do
kill_retry=1
while [ $kill_retry -le $Kill_MAX_RETRIES ]; do
pid=$(pgrep "$process_name")
if [ -n "$pid" ]; then
echo "尝试 $kill_retry: 进程 $process_name 正在运行中。正在终止..."
kill $pid
sleep 2
else
break
fi
kill_retry=$((kill_retry + 1))
done
done
for process_name in "${PROCESS_NAMES[@]}"; do
# 检查是否成功终止进程
if pgrep -x "$process_name" >/dev/null; then
echo "无法终止程序 $process_name。尝试次数已达上限。"
exit 1
else
echo "程序 $process_name 已成功终止。"
fi
done
}
Kill_Process
zenity --question --text="是否删除用户数据" --width=400
# 获取上一个命令的退出码
response=$?
if [ $response -eq 0 ]; then
rm -rfq "$AppData" 2>/dev/null
rm -rfq "$AppData_t" 2>/dev/null
else
echo "保留用户数据方便下次安装。"
echo "如需删除可手动删除该目录:$AppData 或者 $AppData_t"
fi
rm -rfq "$Cache" 2>/dev/null
rm -rfq "$Cache_t" 2>/dev/null
rm -rfq "$base_path" 2>/dev/null
rm -rfq "$HOME/Desktop/Watt Toolkit.desktop" 2>/dev/null
zenity --info --text="卸载完成!" --width=300
Binary file not shown.
+1 -1
View File
@@ -14,7 +14,7 @@ public static partial class AssemblyInfo
/// <summary>
/// 预览版本号,范围 1~9
/// </summary>
const string ver_for_preview = "6";
const string ver_for_preview = "7";
/// <summary>
/// RC 版本号,范围 0~9
+1 -1
View File
@@ -153,7 +153,7 @@ static class Strings
public static string Error_IncompatibleOS => GetString(l => l switch
{
Language.ChineseSimplified => "此应用程序仅兼容 Windows 11 与 Windows 10 版本 1809(OS 内部版本 17763)或更高版本",
Language.ChineseTraditional => "此應用程序僅相容 Windows 11 與 Windows 10 版本 1809(OS 內部版本17763)或更高版本",
Language.ChineseTraditional => "此應用程序僅相容 Windows 11 與 Windows 10 版本 1809(OS 內部版本 17763)或更高版本",
Language.Spanish => "Esta aplicación solo es compatible con Windows 11 y Windows 10 versión 1809 (versión interna del sistema operativo 17763) o superior",
Language.Italian => "Questa applicazione è compatibile solo con Windows 11 e Windows 10 versione 1809 (versione interna del sistema operativo 17763) o superiore",
Language.Japanese => "このアプリケーションは、Windows 11およびWindows 10 バージョン1809(OS内部バージョン17763)以降にのみ対応しています",
+1 -1
View File
@@ -200,7 +200,7 @@ sealed partial class Program : Startup
WatchTrace.Record("ConfigureDemandServices.Notification");
#endif
if (HasHosts)
if (HasHosts || HasIPCRoot)
{
// hosts 文件助手服务
services.AddHostsFileService();
@@ -269,11 +269,11 @@
<ScrollViewer
Name="ShopsScrollViewer"
Margin="0,3,0,20"
Margin="0,0,0,20"
Padding="0,0,0,15"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding Shops}">
<ItemsControl Padding="0,3,0,15" ItemsSource="{Binding Shops}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="10" />
@@ -281,55 +281,57 @@
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="spp:ShopRecommendGoodItem">
<spp:AppItem
Title="{Binding Name}"
MaxWidth="260"
Classes="Vertical Scale"
ClickCommand="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
Cursor="Hand"
Tags="{Binding GoodsAllCategorysText}"
ToolTip.Tip="{Binding Brief}">
<spp:AppItem.ClickCommandParameter>
<MultiBinding Converter="{StaticResource StringFormatConverter}">
<CompiledBinding Path="." Source="{x:Static spp:HomePageViewModel.WattGame_Goods_Detail_}" />
<CompiledBinding Path="Id" />
</MultiBinding>
</spp:AppItem.ClickCommandParameter>
<spp:AppItem.Image>
<spp:Image2 RenderOptions.BitmapInterpolationMode="HighQuality" Source="{Binding Image}" />
</spp:AppItem.Image>
<spp:AppItem.Status>
<Border Classes="Status">
<StackPanel Orientation="Horizontal">
<Border spp:Animations.EnableAnimations="True">
<spp:AppItem
Title="{Binding Name}"
MaxWidth="260"
Classes="Vertical Scale"
ClickCommand="{Binding OpenBrowserCommand, Source={x:Static spp:App.Instance}}"
Cursor="Hand"
Tags="{Binding GoodsAllCategorysText}"
ToolTip.Tip="{Binding Brief}">
<spp:AppItem.ClickCommandParameter>
<MultiBinding Converter="{StaticResource StringFormatConverter}">
<CompiledBinding Path="." Source="{x:Static spp:HomePageViewModel.WattGame_Goods_Detail_}" />
<CompiledBinding Path="Id" />
</MultiBinding>
</spp:AppItem.ClickCommandParameter>
<spp:AppItem.Image>
<spp:Image2 RenderOptions.BitmapInterpolationMode="HighQuality" Source="{Binding Image}" />
</spp:AppItem.Image>
<spp:AppItem.Status>
<Border Classes="Status">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
IsVisible="{Binding !!DiscountValue}"
Text="{Binding Mktprice, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}"
TextDecorations="Strikethrough" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding Price, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}" />
</StackPanel>
</Border>
</spp:AppItem.Status>
<spp:AppItem.ActionButton>
<Border
Height="24"
Padding="8,0"
Background="#4c6b22"
ClipToBounds="True"
CornerRadius="4"
IsVisible="{Binding !!DiscountValue}">
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
IsVisible="{Binding !!DiscountValue}"
Text="{Binding Mktprice, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}"
TextDecorations="Strikethrough" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding Price, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static spp:CurrencyCode.CNY}}" />
</StackPanel>
</Border>
</spp:AppItem.Status>
<spp:AppItem.ActionButton>
<Border
Height="24"
Padding="8,0"
Background="#4c6b22"
ClipToBounds="True"
CornerRadius="4"
IsVisible="{Binding !!DiscountValue}">
<TextBlock
VerticalAlignment="Center"
Foreground="#beee11"
Text="{Binding DiscountValue, StringFormat={}-{0}%}" />
</Border>
</spp:AppItem.ActionButton>
</spp:AppItem>
Foreground="#beee11"
Text="{Binding DiscountValue, StringFormat={}-{0}%}" />
</Border>
</spp:AppItem.ActionButton>
</spp:AppItem>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
@@ -80,7 +80,7 @@ public sealed class ScriptManager : GeneralHttpClientFactory, IScriptManager
script.MatchDomainNames = string.IsNullOrEmpty(matchs) ? includes : matchs;
// 忽略脚本 Enable 启动标签默认启动
//var enable = Regex.Match(userScript, string.Format(DescRegex, "@Enable"), RegexOptions.IgnoreCase).GetValue(s => s.Success == true);
script.Disable = true;
script.Disable = false;
//script.Enable = bool.TryParse(enable, out var e) && e;
return script;
}
@@ -204,7 +204,7 @@ partial class ProxyService
{
hosts.TryAdd(IReverseProxyService.Constants.LocalDomain, localhost);
}
var updateHostsResult = hostsFileService.UpdateHosts(hosts);
var updateHostsResult = await hostsFileService.UpdateHosts(hosts);
if (updateHostsResult.ResultType != OperationResultType.Success)
{
if (OperatingSystem2.IsMacOS() || OperatingSystem2.IsLinux())
@@ -265,7 +265,7 @@ partial class ProxyService
var needClear = hostsFileService.ContainsHostsByTag();
if (needClear)
{
var removeHostsResult = hostsFileService.RemoveHostsByTag();
var removeHostsResult = await hostsFileService.RemoveHostsByTag();
if (removeHostsResult.ResultType != OperationResultType.Success)
{
// 退出时候不打开浏览器
@@ -344,7 +344,7 @@ public sealed partial class ProxyService
}
}
public static void OnExitRestoreHosts()
public static async ValueTask OnExitRestoreHosts()
{
var s = Ioc.Get_Nullable<IHostsFileService>();
if (s != null)
@@ -352,7 +352,7 @@ public sealed partial class ProxyService
var needClear = s.ContainsHostsByTag();
if (needClear)
{
s.OnExitRestoreHosts();
await s.OnExitRestoreHosts();
}
}
}
@@ -560,13 +560,9 @@ public sealed partial class ProxyService
}
#endregion
public
#if WINDOWS
async
#endif
void FixNetwork()
public async void FixNetwork()
{
OnExitRestoreHosts();
await OnExitRestoreHosts();
#if WINDOWS
{
@@ -49,7 +49,7 @@ public sealed partial class AcceleratorPageViewModel
DeleteCertificateCommand = ReactiveCommand.Create(DeleteCertificate_OnClick);
EditHostsFileCommand = ReactiveCommand.Create(hostsFileService.OpenFile);
OpenHostsDirCommand = ReactiveCommand.Create(hostsFileService.OpenFileDir);
ResetHostsFileCommand = ReactiveCommand.Create(hostsFileService.ResetFile);
ResetHostsFileCommand = ReactiveCommand.CreateFromTask(hostsFileService.ResetFile);
NetworkFixCommand = ReactiveCommand.Create(ProxyService.Current.FixNetwork);
TrustCerCommand = ReactiveCommand.Create(TrustCer_OnClick);
OpenCertificateDirCommand = ReactiveCommand.Create(() =>
@@ -542,9 +542,8 @@
<spp:AdControl
Grid.Column="1"
Width="165"
MaxHeight="390"
Height="390"
VerticalAlignment="Top"
DockPanel.Dock="Right"
Standard="Vertical" />
</Grid>
</spp:PageBase>
@@ -189,7 +189,7 @@ public partial class AuthenticatorGeneralImportPageViewModel
if (AuthType == AuthType.TOTP)
{
var auth = new GoogleAuthenticator();
var auth = new TOTPAuthenticator();
auth.Enroll(privateKey);
auth.HMACType = HMACType;
@@ -299,7 +299,7 @@ public sealed partial class GameListPageViewModel : TabItemViewModel
try
{
GameLibrarySettings.HideGameList.Add(app.AppId, app.DisplayName);
SteamConnectService.Current.SteamApps.Remove(app);
SteamConnectService.Current.SteamApps.RemoveKey(app.AppId);
Toast.Show(ToastIcon.Success, Strings.GameList_HideAppsSuccess);
}
@@ -504,7 +504,7 @@
<ItemsRepeater ItemsSource="{Binding App.SaveFiles}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="10" />
<StackLayout Orientation="Vertical" Spacing="{StaticResource SettingsSpacing}" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamAppSaveFile}">
@@ -262,4 +262,19 @@ interface ICompressedPackageCommand : ICommand
{
}
}
static void CreateZipPack(string packPath, IEnumerable<AppPublishFileInfo> files)
{
using var archive = ZipFile.Open(packPath, ZipArchiveMode.Create);
foreach (var file in files)
{
#if DEBUG
Console.WriteLine($"正在压缩:{file.FilePath}");
#endif
var name = file.RelativePath;
if (Path.DirectorySeparatorChar != IOPath.UnixDirectorySeparatorChar)
name = name.Replace(Path.DirectorySeparatorChar, IOPath.UnixDirectorySeparatorChar);
archive.CreateEntryFromFile(file.FilePath, name);
}
}
}
@@ -31,11 +31,12 @@ interface IDotNetPublishCommand : ICommand
var force_sign = new Option<bool>("--force-sign", GetDefForceSign, "Mandatory verification must be digitally signed");
var sha256 = new Option<bool>("--sha256", () => true, "Calculate file hash value");
var sha384 = new Option<bool>("--sha384", () => true, "Calculate file hash value");
var stm_upload = new Option<bool>("--stm-upload", "Steam upload zip file");
var command = new Command(commandName, "DotNet publish app")
{
debug, rids, force_sign, sha256, sha384,
debug, rids, force_sign, sha256, sha384, stm_upload,
};
command.SetHandler(Handler, debug, rids, force_sign, sha256, sha384);
command.SetHandler(Handler, debug, rids, force_sign, sha256, sha384, stm_upload);
return command;
}
@@ -56,7 +57,7 @@ interface IDotNetPublishCommand : ICommand
}
}
internal static void Handler(bool debug, string[] rids, bool force_sign, bool sha256, bool sha384)
internal static void Handler(bool debug, string[] rids, bool force_sign, bool sha256, bool sha384, bool stm_upload)
{
var bgOriginalColor = Console.BackgroundColor;
var fgOriginalColor = Console.ForegroundColor;
@@ -166,7 +167,10 @@ interface IDotNetPublishCommand : ICommand
// 不能使用下划线
File.Copy(ico_path, Path.Combine(save_dir_path, "Watt-Toolkit.png"), true);
}
Console.WriteLine("Linux 复制启动 环境检查 卸载脚本");
var script_path = Path.Combine(ProjectUtils.ProjPath, "build", "linux");
CopyDirectory(script_path, Path.Combine(rootPublishDir, "script"), true);
File.Move(Path.Combine(rootPublishDir, "script", "Steam++.sh"), Path.Combine(rootPublishDir, "Steam++.sh"));
}
SetConsoleColor(ConsoleColor.White, ConsoleColor.DarkGreen);
@@ -377,6 +381,37 @@ interface IDotNetPublishCommand : ICommand
ICompressedPackageCommand.CreateGZipPack(packPath = GetPackPathWithTryDelete(FileEx.TAR_GZ), appPublish.Files);
break;
}
if (stm_upload)
{
List<AppPublishFileInfo> mainFiles = new();
List<AppPublishFileInfo> modulesFiles = new();
foreach (var item in appPublish.Files)
{
if (item.RelativePath.Contains("modules"))
{
modulesFiles.Add(item);
}
else
{
mainFiles.Add(item);
}
}
ICompressedPackageCommand.CreateZipPack(GetPackPathWithTryDelete("_main.stmupload.zip"), mainFiles);
var query = from module in modulesFiles
let split = module.RelativePath.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries)
let name = split.Length >= 2 ? split[1] : null
where name != null
group module by name;
var items = query.ToArray();
foreach (var item in items)
{
if (string.IsNullOrWhiteSpace(item.Key))
continue;
var files = item.ToArray();
ICompressedPackageCommand.CreateZipPack(GetPackPathWithTryDelete($"_{item.Key}.stmupload.zip"), files);
}
}
SetConsoleColor(ConsoleColor.White, ConsoleColor.DarkGreen);
Console.Write("创建成功【压缩包】,文件大小:");
if (packPath != null)
@@ -759,21 +794,21 @@ publish -c {0} -p:OutputType={1} -p:PublishDir=bin\{0}\Publish\win-any -p:Publis
const string appconfigFileName = "Steam++.exe.config";
var rootPublishDir = Path.Combine(publishDir, "..");
var cacheFilePath = Path.Combine(ProjectUtils.ProjPath,
"res", "windows", "Steam++.apphost");
// 使用缓存文件
if (File.Exists(cacheFilePath))
{
File.Copy(cacheFilePath, Path.Combine(rootPublishDir, "Steam++.exe"));
var sourceFileName = Path.Combine(ProjectUtils.ProjPath, "src", "BD.WTTS.Client.AppHost", "App.config");
var appconfigContent = File.ReadAllText(sourceFileName);
//var cacheFilePath = Path.Combine(ProjectUtils.ProjPath,
// "res", "windows", "Steam++.apphost");
//// 使用缓存文件
//if (File.Exists(cacheFilePath))
//{
// File.Copy(cacheFilePath, Path.Combine(rootPublishDir, "Steam++.exe"));
// var sourceFileName = Path.Combine(ProjectUtils.ProjPath, "src", "BD.WTTS.Client.AppHost", "App.config");
// var appconfigContent = File.ReadAllText(sourceFileName);
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(appconfigContent);
appconfigContent = xmlDoc.InnerXml;
File.WriteAllText(Path.Combine(rootPublishDir, appconfigFileName), appconfigContent);
return;
}
// var xmlDoc = new XmlDocument();
// xmlDoc.LoadXml(appconfigContent);
// appconfigContent = xmlDoc.InnerXml;
// File.WriteAllText(Path.Combine(rootPublishDir, appconfigFileName), appconfigContent);
// return;
//}
const string app_host_tfm = "net40"/*"net35"*/; // net35 在 Windows 10 LTSC 上即使 app.config 中配置了 4.x 兼容但依旧会打开设置窗口并且定位在可选功能
var configuration = PublishCommandArg.GetConfiguration(debug);
@@ -5,14 +5,14 @@ interface IGenerateCaCertCommand : ICommand
const string commandName = "gcert";
const string X500DistinguishedNameValue = $"C=CN, S=Hunan, L=Changsha, O=\u5F90\u5DDE\u7E41\u661F\u7F51\u7EDC\u79D1\u6280\u6709\u9650\u516C\u53F8, OU=\u6280\u672F\u90E8, CN=\u5F90\u5DDE\u7E41\u661F\u7F51\u7EDC\u79D1\u6280\u6709\u9650\u516C\u53F8";
public const int CertificateValidDays = 3650;
public const int CertificateValidDays = 365;
public const int KEY_SIZE_BITS = 4096;
static Command ICommand.GetCommand()
{
var type = new Option<int>("--type",
"Generate certificate type(1 code, 2 store-code, 3 csr)");
"Generate certificate type(1 code, 2 store-code)");
var path = new Option<string>("--path",
"Directory where certificate files need to be saved");
var cn = new Option<string>("--cn", "X500DistinguishedNameValue");
@@ -69,37 +69,38 @@ interface IGenerateCaCertCommand : ICommand
subjectKeyId = new(request.PublicKey, false);
request.CertificateExtensions.Add(subjectKeyId);
if (isCSR)
{
var signingRequestPem = request.CreateSigningRequestPem();
return (null!, null!, signingRequestPem);
}
var notBefore = DateTimeOffset.UtcNow;
var notAfter = notBefore.AddDays(CertificateValidDays);
using var cert = request.CreateSelfSigned(notBefore, notAfter);
var pfx = cert.Export(X509ContentType.Pfx, password); // 私钥
var cer = cert.Export(X509ContentType.Cert); // 公钥
if (isCSR)
{
csr = request.CreateSigningRequestPem();
}
return (pfx, cer, csr);
}
internal static void Handler(int type, string path, string cn, string password)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentOutOfRangeException(nameof(path), path, null);
(var pfx, var cer, var csr) = type switch
{
// 生成 BeyondDimension 代码签名证书
1 => GenerateCodeSigningCert(X500DistinguishedNameValue, password),
1 => GenerateCodeSigningCert(X500DistinguishedNameValue, password, true),
// 生成 上传应用商店的 代码签名证书
2 => GenerateCodeSigningCert($"CN={cn}", password),
3 => GenerateCodeSigningCert(X500DistinguishedNameValue, password, true),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
};
if (csr != default)
{
Console.WriteLine(csr);
return;
}
File.WriteAllBytes(path, pfx);
File.WriteAllBytes(path.TrimEnd(".pfx", StringComparison.OrdinalIgnoreCase) + ".cer", cer);
if (csr != default)
{
File.WriteAllText(path.TrimEnd(".pfx", StringComparison.OrdinalIgnoreCase) + ".txt", csr);
}
}
}
@@ -2,7 +2,7 @@
"profiles": {
"BD.WTTS.Client.Tools.Publish": {
"commandName": "Project",
"commandLineArgs": "run --rids win-x64"
"commandLineArgs": "run --rids win-x64 --stm-upload"
}
}
}
+36 -5
View File
@@ -17,6 +17,38 @@ public static class PluginsCore
{
}
protected override Assembly? Load(AssemblyName assemblyName)
{
var assemblyLoadContext = DefaultAssemblyLoadContext;
if (assemblyLoadContext != this)
{
try
{
var assembly = assemblyLoadContext.LoadFromAssemblyName(assemblyName);
if (assembly != null)
{
return assembly;
}
}
catch
{
}
}
return base.Load(assemblyName);
}
}
static AssemblyLoadContext DefaultAssemblyLoadContext
{
get
{
// 在 Windows 上通过自定义 AppHost 程序集加载上下文为 IsolatedComponentLoadContext 而不是 Default
var assemblyLoadContext = AssemblyLoadContext.GetLoadContext(typeof(PluginsCore).Assembly);
assemblyLoadContext ??= AssemblyLoadContext.Default;
return assemblyLoadContext;
}
}
static DisablePluginsAssemblyLoadContext disablePluginsAssemblyLoadContext = new();
@@ -183,9 +215,7 @@ public static class PluginsCore
}
else
{
// 在 Windows 上通过自定义 AppHost 程序集加载上下文为 IsolatedComponentLoadContext 而不是 Default
assemblyLoadContext = AssemblyLoadContext.GetLoadContext(typeof(PluginsCore).Assembly);
assemblyLoadContext ??= AssemblyLoadContext.Default;
assemblyLoadContext = DefaultAssemblyLoadContext;
}
var assembly = assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
return assembly;
@@ -208,7 +238,7 @@ public static class PluginsCore
ArchiSteamFarmPlus,
Authenticator,
GameTools,
SteamIdleCard
SteamIdleCard,
};
foreach (var item in modules)
{
@@ -355,7 +385,8 @@ public static class PluginsCore
try
{
using CompositionHost container = configuration.CreateContainer();
foreach (var plugin in container.GetExports<IPlugin>())
var exports = container.GetExports<IPlugin>().ToArray();
foreach (var plugin in exports)
{
if (string.IsNullOrWhiteSpace(plugin.UniqueEnglishName) ||
string.Equals(plugin.UniqueEnglishName,
+2 -2
View File
@@ -3304,7 +3304,7 @@ namespace BD.WTTS.Client.Resources {
}
/// <summary>
/// 查找类似 HOTP、TOTP通用令牌 的本地化字符串。
/// 查找类似 HOTP、TOTP 通用令牌 的本地化字符串。
/// </summary>
public static string HOTP {
get {
@@ -3863,7 +3863,7 @@ namespace BD.WTTS.Client.Resources {
}
/// <summary>
/// 查找类似 {0}导入 的本地化字符串。
/// 查找类似 {0} 导入 的本地化字符串。
/// </summary>
public static string LocalAuth_Import {
get {
+2 -2
View File
@@ -2314,7 +2314,7 @@
<value>Mafile 文件</value>
</data>
<data name="LocalAuth_Import" xml:space="preserve">
<value>{0}导入</value>
<value>{0} 导入</value>
</data>
<data name="LocalAuth_SDAImport" xml:space="preserve">
<value>从 Mafile 文件导入令牌</value>
@@ -2347,7 +2347,7 @@
<value>{0}密钥导入</value>
</data>
<data name="HOTP" xml:space="preserve">
<value>HOTP、TOTP通用令牌</value>
<value>HOTP、TOTP 通用令牌</value>
</data>
<data name="LocalAuth_HOTPImport" xml:space="preserve">
<value>添加 HOTP、TOTP 令牌时生成的通用密钥导入令牌</value>
@@ -460,6 +460,11 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
void ConfigureServices()
{
RegisterService<IPCPlatformService, IPlatformService>();
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
IHostsFileService? hostsFileService = Ioc.Get_Nullable<IHostsFileService>();
if (hostsFileService != null)
RegisterService(hostsFileService);
#endif
RegisterService<IPCToastService>(this);
}
@@ -15,20 +15,29 @@ internal sealed class HostsFileServiceImpl
readonly IPlatformService s;
readonly object lockObj = new();
readonly IHostsFileService? privilegedThis;
IHostsFileService? _privilegedThis;
const string TAG = "HostsFileS";
public HostsFileServiceImpl(IServiceProvider serviceProvider, IPlatformService s)
async ValueTask<IHostsFileService?> GetPrivilegedThisAsync()
{
this.s = s;
#if WINDOWS
if (Startup.Instance.IsMainProcess && !WindowsPlatformServiceImpl.IsPrivilegedProcess)
{
var ipc = serviceProvider.GetService<IPCSubProcessService>();
privilegedThis = ipc?.GetService<IHostsFileService>();
if (_privilegedThis == null)
{
var ipc = IPCMainProcessService.Instance;
_privilegedThis = await ipc.GetServiceAsync<IHostsFileService>(IPlatformService.IPCRoot.moduleName);
}
return _privilegedThis;
}
#endif
return null;
}
public HostsFileServiceImpl(IPlatformService s)
{
this.s = s;
}
#region Mark
@@ -496,7 +505,7 @@ internal sealed class HostsFileServiceImpl
public void OpenFileDir() => s.OpenFolder(s.HostsFilePath);
public async void ResetFile()
public async Task<bool> ResetFile()
{
if (Startup.Instance.IsMainProcess)
{
@@ -509,15 +518,21 @@ internal sealed class HostsFileServiceImpl
{
try
{
var isOk = true;
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
privilegedThis.ResetFile();
isOk = await privilegedThis.ResetFile();
}
else
{
s.WriteDefaultHostsContent();
}
Toast.Show(ToastIcon.Success, AppResources.CommunityFix_ResetHostsFileOk);
if (isOk)
Toast.Show(ToastIcon.Success, AppResources.CommunityFix_ResetHostsFileOk);
else
Toast.Show(ToastIcon.Error, AppResources.CommunityFix_ResetHostsFileCatchTip_.Format("IPC fail"));
}
catch (Exception e)
{
@@ -530,6 +545,7 @@ internal sealed class HostsFileServiceImpl
{
s.WriteDefaultHostsContent();
}
return true;
}
/// <summary>
@@ -541,7 +557,7 @@ internal sealed class HostsFileServiceImpl
EncodingType.ANSICodePage => s.Default,
EncodingType.UTF8 => Encoding.Default,
EncodingType.UTF8WithBOM => Encoding.UTF8,
_ => OperatingSystem2.IsWindows() ? Encoding.UTF8 : Encoding.Default,
_ => OperatingSystem.IsWindows() ? Encoding.UTF8 : Encoding.Default,
};
static Dictionary<string, string> ReadHostsAllLines(StreamReader? fileReader)
@@ -598,7 +614,7 @@ internal sealed class HostsFileServiceImpl
{
AppendData = new(ReadHostsAllLines_(fileReader)),
ResultType = OperationResultType.Success,
Message = AppResources.Hosts_ReadSuccess
Message = AppResources.Hosts_ReadSuccess,
};
}
catch (Exception ex)
@@ -632,7 +648,7 @@ internal sealed class HostsFileServiceImpl
{
AppendData = ReadHostsAllLines(fileReader),
ResultType = OperationResultType.Success,
Message = AppResources.Hosts_ReadSuccess
Message = AppResources.Hosts_ReadSuccess,
};
}
catch (Exception ex)
@@ -644,30 +660,47 @@ internal sealed class HostsFileServiceImpl
}
}
public OperationResult UpdateHosts(string ip, string domain)
static OperationResult GetOperationResultByIpcResult(OperationResult? result)
{
if (result == null)
return new OperationResult
{
ResultType = OperationResultType.Error,
Message = "Ipc call error",
};
return result;
}
public async Task<OperationResult> UpdateHosts(string ip, string domain)
{
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
return privilegedThis.UpdateHosts(ip, domain);
var r = await privilegedThis.UpdateHosts(ip, domain);
return GetOperationResultByIpcResult(r);
}
var dict = new Dictionary<string, string>
{
{ domain, ip },
};
return UpdateHosts(dict);
var r2 = await UpdateHosts(dict);
return r2;
}
public OperationResult UpdateHosts(IEnumerable<(string ip, string domain)> hosts)
public async Task<OperationResult> UpdateHosts(IEnumerable<(string ip, string domain)> hosts)
{
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
return privilegedThis.UpdateHosts(hosts);
var r = await privilegedThis.UpdateHosts(hosts);
return GetOperationResultByIpcResult(r);
}
//var value = hosts.ToDictionary(k => k.domain, v => v.ip);
var value = new Dictionary<string, string>();
foreach (var (ip, domain) in hosts)
value.TryAdd(domain, ip);
return UpdateHosts(value);
var r2 = await UpdateHosts(value);
return r2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -678,21 +711,25 @@ internal sealed class HostsFileServiceImpl
return new OperationResult(result);
}
public OperationResult UpdateHosts(IReadOnlyDictionary<string, string> hosts)
public async Task<OperationResult> UpdateHosts(IReadOnlyDictionary<string, string> hosts)
{
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
return privilegedThis.UpdateHosts(hosts);
var r = await privilegedThis.UpdateHosts(hosts);
return GetOperationResultByIpcResult(r);
}
var result = HandleHosts(isUpdateOrRemove: true, hosts);
return Convert(result);
}
public OperationResult RemoveHosts(string ip, string domain)
public async Task<OperationResult> RemoveHosts(string ip, string domain)
{
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
return privilegedThis.RemoveHosts(ip, domain);
var r = await privilegedThis.RemoveHosts(ip, domain);
return GetOperationResultByIpcResult(r);
}
var hosts = new Dictionary<string, string>
{
@@ -702,13 +739,15 @@ internal sealed class HostsFileServiceImpl
return Convert(result);
}
public OperationResult RemoveHosts(string domain) => RemoveHosts(string.Empty, domain);
public async Task<OperationResult> RemoveHosts(string domain) => await RemoveHosts(string.Empty, domain);
public OperationResult RemoveHostsByTag()
public async Task<OperationResult> RemoveHostsByTag()
{
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
return privilegedThis.RemoveHostsByTag();
var r = await privilegedThis.RemoveHostsByTag();
return GetOperationResultByIpcResult(r);
}
var result = HandleHosts(isUpdateOrRemove: false);
return Convert(result);
@@ -760,14 +799,14 @@ internal sealed class HostsFileServiceImpl
}
bool mOnExitRestoreHosts;
readonly object mOnExitRestoreHostsLock = new();
readonly AsyncLock mOnExitRestoreHostsLock = new();
public void OnExitRestoreHosts()
public async Task OnExitRestoreHosts()
{
lock (mOnExitRestoreHostsLock)
using (await mOnExitRestoreHostsLock.LockAsync())
{
if (mOnExitRestoreHosts) return;
RemoveHostsByTag();
await RemoveHostsByTag();
mOnExitRestoreHosts = true;
}
}
@@ -775,14 +814,17 @@ internal sealed class HostsFileServiceImpl
#if DEBUG
static FileStream? mOccupyHostsFileStream;
public void OccupyHosts()
public async Task<bool> OccupyHosts()
{
var isOK = true;
var privilegedThis = await GetPrivilegedThisAsync();
if (privilegedThis != null)
{
privilegedThis.OccupyHosts();
return;
// Ipc 调用失败时将返回默认值
isOK = await privilegedThis.OccupyHosts();
}
mOccupyHostsFileStream = new FileStream(s.HostsFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
return isOK;
}
#endif
@@ -46,15 +46,15 @@ public sealed class UserService : ReactiveObject
var cUser = await userManager.GetCurrentUserAsync();
if (cUser.HasValue()) return;
if (Random2.Next(2) == 1)
{
var vm = new LoginOrRegisterWindowViewModel();
await windowManager.ShowTaskDialogAsync(vm, vm.Title, isOkButton: false);
}
else
{
INavigationService.Instance.Navigate(typeof(LoginOrRegisterWindowViewModel), NavigationTransitionEffect.DrillIn);
}
//if (Random2.Next(2) == 1)
//{
var vm = new LoginOrRegisterWindowViewModel();
await windowManager.ShowTaskDialogAsync(vm, vm.Title, isOkButton: false);
//}
//else
//{
// INavigationService.Instance.Navigate(typeof(LoginOrRegisterWindowViewModel), NavigationTransitionEffect.DrillIn);
//}
}
public void NavigateUserCenterPage()
@@ -12,7 +12,8 @@ public sealed class NoticeService : ReactiveObject
//readonly INotificationRepository notificationRepo = Ioc.Get<INotificationRepository>();
public ObservableCollection<OfficialMessageItemDTO> Notices { get; }
[Reactive]
public ObservableCollection<OfficialMessageItemDTO> Notices { get; set; }
[Reactive]
public int UnreadNotificationsCount { get; set; }
@@ -54,13 +55,21 @@ public sealed class NoticeService : ReactiveObject
{
item.Unread = true;
//只弹最新的一条通知
if (UnreadNotificationsCount == 0 && GeneralSettings.MessagePopupNotification.Value)
INotificationService.Instance.Notify(item.Content, NotificationType.Announcement, title: item.Title);
UnreadNotificationsCount++;
}
}
//只弹最新的一条通知
if (UnreadNotificationsCount != 0 && GeneralSettings.MessagePopupNotification.Value)
{
//if (UnreadNotificationsCount > 1)
//{
// INotificationService.Instance.Notify($"{UnreadNotificationsCount}条通知未读", NotificationType.Announcement);
// return;
//}
var notice = Notices.First(x => x.Unread == true);
INotificationService.Instance.Notify(notice.Content, NotificationType.Announcement, title: notice.Title);
}
}
}
@@ -224,7 +224,7 @@ public sealed class SteamConnectService
public void RunAFKApps()
{
var aFKAppList = Ioc.Get<IPartialGameLibrarySettings>()?.AFKAppList;
var aFKAppList = Ioc.Get_Nullable<IPartialGameLibrarySettings>()?.AFKAppList;
if (aFKAppList?.Count > 0)
{
foreach (var item in aFKAppList)
@@ -291,7 +291,7 @@ public sealed class SteamConnectService
INotificationService.Instance.Notify($"{AppResources.Steam_CheckStarted}{(IsSteamChinaLauncher ? AppResources.Steam_SteamChina : AppResources.Steam_SteamWorld)}{Environment.NewLine}{AppResources.Steam_CurrentUser}{CurrentSteamUser.SteamNickName}{Environment.NewLine}{AppResources.Steam_CurrentIPCountry}{CurrentSteamUser.IPCountry}{Environment.NewLine}{AppResources.Steam_ServerTime}{steamServerTime:yyyy-MM-dd HH:mm:ss}", NotificationType.Message);
}
var isAutoAFKApps = Ioc.Get<IPartialGameLibrarySettings>()?.IsAutoAFKApps ?? default;
var isAutoAFKApps = Ioc.Get_Nullable<IPartialGameLibrarySettings>()?.IsAutoAFKApps ?? default;
if (isAutoAFKApps)
{
RunAFKApps();
@@ -28,7 +28,7 @@ public interface IHostsFileService
/// <summary>
/// 重置 hosts 文件
/// </summary>
void ResetFile();
Task<bool> ResetFile();
/// <summary>
/// 读取 hosts 文件
@@ -42,17 +42,17 @@ public interface IHostsFileService
/// <param name="ip"></param>
/// <param name="domain"></param>
/// <returns></returns>
OperationResult UpdateHosts(string ip, string domain);
Task<OperationResult> UpdateHosts(string ip, string domain);
/// <inheritdoc cref="UpdateHosts(IReadOnlyDictionary{string, string})"/>
OperationResult UpdateHosts(IEnumerable<(string ip, string domain)> hosts);
Task<OperationResult> UpdateHosts(IEnumerable<(string ip, string domain)> hosts);
/// <summary>
/// 更新多条 hosts 纪录
/// </summary>
/// <param name="hosts"></param>
/// <returns></returns>
OperationResult UpdateHosts(IReadOnlyDictionary<string, string> hosts);
Task<OperationResult> UpdateHosts(IReadOnlyDictionary<string, string> hosts);
/// <summary>
/// 移除一条 hosts 纪录
@@ -60,21 +60,21 @@ public interface IHostsFileService
/// <param name="ip"></param>
/// <param name="domain"></param>
/// <returns></returns>
OperationResult RemoveHosts(string ip, string domain);
Task<OperationResult> RemoveHosts(string ip, string domain);
/// <inheritdoc cref="RemoveHosts(string, string)"/>
OperationResult RemoveHosts(string domain);
Task<OperationResult> RemoveHosts(string domain);
/// <summary>
/// 移除当前程序写入的 hosts 纪录并还原写入时冲突的备份纪录
/// </summary>
/// <returns></returns>
OperationResult RemoveHostsByTag();
Task<OperationResult> RemoveHostsByTag();
/// <summary>
/// 当程序退出时还原 hosts 文件
/// </summary>
void OnExitRestoreHosts();
Task OnExitRestoreHosts();
/// <summary>
/// 获取当前 hosts 文件是否包含修改过的标签,注意:产生异常时也将返回 <see langword="false"/>,并 <see cref="Toast"/> 显示 <see cref="Exception"/>
@@ -83,7 +83,7 @@ public interface IHostsFileService
bool ContainsHostsByTag();
#if DEBUG
void OccupyHosts();
Task<bool> OccupyHosts();
#endif
}
@@ -32,7 +32,7 @@ partial interface IPlatformService
/// </summary>
void WriteDefaultHostsContent()
{
var fileStream = new FileStream(HostsFilePath,
using var fileStream = new FileStream(HostsFilePath,
FileMode.OpenOrCreate,
FileAccess.Write,
FileShare.ReadWrite | FileShare.Delete);
@@ -4,7 +4,6 @@ namespace BD.WTTS.Services;
partial interface IPlatformService
{
/// <summary>
/// 获取 排除代理地址
/// </summary>
@@ -28,7 +27,7 @@ partial interface IPlatformService
"172.31.*",
"192.168.*",
Constants.Urls.OfficialApiHostName,
Constants.Urls.OfficialShopApiHostName
Constants.Urls.OfficialShopApiHostName,
};
/// <summary>
@@ -89,6 +89,21 @@ partial class Startup // 全局异常处理
#endif
Logger.Error(ex, message, args);
#if WINDOWS
Task2.InBackground(() =>
{
var mbText =
$"""
{string.Format(message, args)}
{ex}
""";
const string mbTitle = $"Application Crash - {AssemblyInfo.Trademark}";
WPFMessageBox.Show(mbText, mbTitle,
WPFMessageBoxButton.OK,
WPFMessageBoxImage.Error);
});
#endif
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
Startup? s;
@@ -80,8 +80,6 @@ partial class Startup // OnStartup
{
Steamworks.Dispatch.OnException = (e) =>
{
Console.Error.WriteLine(e.Message);
Console.Error.WriteLine(e.StackTrace);
Log.Error(nameof(Steamworks), e, "Steamworks.SteamClient OnException.");
};
-6
View File
@@ -434,12 +434,6 @@ public abstract partial class Startup
catch (Exception ex)
{
GlobalExceptionHandler.Handler(ex, nameof(StartAsync));
#if WINDOWS
WPFMessageBox.Show(ex.ToString(),
AssemblyInfo.Trademark,
WPFMessageBoxButton.OK,
WPFMessageBoxImage.Error);
#endif
throw;
}
finally