Compare commits

..
Author SHA1 Message Date
LY 579666be50 🚀 升级 .NET 版本至 11.0 并更新子模块
将项目的 .NET 版本从 10.0 升级到 11.0,以支持最新特性和改进。同时同步更新了 DirectoryPackages 子模块的提交版本,保持依赖项的最新状态。
2026-02-11 18:32:26 +08:00
43 changed files with 2137 additions and 1236 deletions
+1728
View File
File diff suppressed because it is too large Load Diff
-178
View File
@@ -1,178 +0,0 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="ARM" />
<Platform Name="ARM64" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/0.Root/">
<File Path=".github/workflows/CI.yml" />
<File Path=".gitignore" />
<File Path=".gitmodules" />
<File Path="nuget.config" />
<File Path="README.en.md" />
<File Path="README.md" />
<File Path="ref/DirectoryPackages/Directory.Packages.props" />
<File Path="src/.editorconfig" />
<File Path="src/Directory.Build.props" />
</Folder>
<Folder Name="/0.Root/Build/">
<File Path="build/publish_apphost_linux_x64.sh" />
<File Path="build/settings_v4_app.json" />
</Folder>
<Folder Name="/0.Root/Document/">
<File Path="doc/file-system.md" />
<File Path="doc/open-source-library.md" />
<File Path="doc/openwrt.md" />
<File Path="doc/publish.md" />
<File Path="doc/release-keylol.md" />
</Folder>
<Folder Name="/0.Root/Document/Program File Structure/">
<File Path="doc/program-file-structure/Linux.md" />
<File Path="doc/program-file-structure/macOS.md" />
<File Path="doc/program-file-structure/Windows.md" />
</Folder>
<Folder Name="/0.Root/Source/">
<File Path="src/AssemblyInfo.Constants.cs" />
<File Path="src/AssemblyInfo.cs" />
<File Path="src/AssemblyInfo.Version.Max.cs" />
<File Path="src/README.md" />
<File Path="src/Utils.cs" />
</Folder>
<Folder Name="/0.Root/Source/ImplicitUsings/">
<File Path="src/ImplicitUsings.ArchiSteamFarm.cs" />
<File Path="src/ImplicitUsings.AspNetCore.cs" />
<File Path="src/ImplicitUsings.AutoMapper.cs" />
<File Path="src/ImplicitUsings.Avalonia.cs" />
<File Path="src/ImplicitUsings.BCL.cs" />
<File Path="src/ImplicitUsings.Common.cs" />
<File Path="src/ImplicitUsings.Controllers.cs" />
<File Path="src/ImplicitUsings.Data.cs" />
<File Path="src/ImplicitUsings.EntityFrameworkCore.cs" />
<File Path="src/ImplicitUsings.Identity.cs" />
<File Path="src/ImplicitUsings.Jobs.cs" />
<File Path="src/ImplicitUsings.JsonProperty.cs" />
<File Path="src/ImplicitUsings.JWT.cs" />
<File Path="src/ImplicitUsings.MessagePack.cs" />
<File Path="src/ImplicitUsings.Models.cs" />
<File Path="src/ImplicitUsings.MSEX.cs" />
<File Path="src/ImplicitUsings.Nito.cs" />
<File Path="src/ImplicitUsings.NLog.cs" />
<File Path="src/ImplicitUsings.Plugins.cs" />
<File Path="src/ImplicitUsings.Quartz.cs" />
<File Path="src/ImplicitUsings.ReactiveUI.cs" />
<File Path="src/ImplicitUsings.Repositories.cs" />
<File Path="src/ImplicitUsings.Services.cs" />
<File Path="src/ImplicitUsings.Settings.cs" />
<File Path="src/ImplicitUsings.SQLite.cs" />
<File Path="src/ImplicitUsings.Steam.cs" />
<File Path="src/ImplicitUsings.UI.cs" />
<File Path="src/ImplicitUsings.UI.ViewModels.cs" />
<File Path="src/ImplicitUsings.UI.Views.cs" />
<File Path="src/ImplicitUsings.UnitTest.cs" />
<File Path="src/ImplicitUsings.WTTS.cs" />
</Folder>
<Folder Name="/0.Root/TFM/">
<File Path="src/TFM_NETX.props" />
<File Path="src/TFM_NETX_SINGLE.props" />
<File Path="src/TFM_NETX_WINDOWS.props" />
<File Path="src/TFM_NETX_WITH_ALL.props" />
<File Path="src/TFM_NETX_WITH_DESKTOP.props" />
<File Path="src/TFM_NETX_WITH_WINDOWS.props" />
</Folder>
<Folder Name="/1.AppHost/">
<Project Path="src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj" />
<Project Path="src/BD.WTTS.Client.AppHost/BD.WTTS.Client.AppHost.csproj" />
</Folder>
<Folder Name="/2.Reference/" />
<Folder Name="/2.Reference/AppCenter/">
<Project Path="ref/appcenter-sdk-dotnet/SDK/AppCenter/BD.AppCenter.Any/BD.AppCenter.Any.csproj" />
<Project Path="ref/appcenter-sdk-dotnet/SDK/AppCenterAnalytics/BD.AppCenter.Analytics.Any/BD.AppCenter.Analytics.Any.csproj" />
</Folder>
<Folder Name="/2.Reference/Avalonia/">
<Project Path="ref/Avalonia.Image2/Avalonia.Gif/Avalonia.Gif.csproj" />
<Project Path="ref/Avalonia.Image2/LibAPNG/LibAPNG.csproj" />
</Folder>
<Folder Name="/2.Reference/Common/">
<Project Path="ref/Common/src/BD.Common.Area/BD.Common.Area.csproj" />
<Project Path="ref/Common/src/BD.Common.Essentials.Maui.Runtime/BD.Common.Essentials.Maui.Runtime.csproj" />
<Project Path="ref/Common/src/BD.Common.Essentials.Preferences.SQLite/BD.Common.Essentials.Preferences.SQLite.csproj" />
<Project Path="ref/Common/src/BD.Common.Essentials.Primitives/BD.Common.Essentials.Primitives.csproj" />
<Project Path="ref/Common/src/BD.Common.Essentials.Utils/BD.Common.Essentials.Utils.csproj" />
<Project Path="ref/Common/src/BD.Common.Essentials/BD.Common.Essentials.csproj" />
<Project Path="ref/Common/src/BD.Common.Mvvm.ReactiveUI/BD.Common.Mvvm.ReactiveUI.csproj" />
<Project Path="ref/Common/src/BD.Common.Mvvm/BD.Common.Mvvm.csproj" />
<Project Path="ref/Common/src/BD.Common.Pinyin.TinyPinyin/BD.Common.Pinyin.TinyPinyin.csproj" />
<Project Path="ref/Common/src/BD.Common.Pinyin/BD.Common.Pinyin.csproj" />
<Project Path="ref/Common/src/BD.Common.Primitives/BD.Common.Primitives.csproj" />
<Project Path="ref/Common/src/BD.Common.Repositories.SQLitePCL/BD.Common.Repositories.SQLitePCL.csproj" />
<Project Path="ref/Common/src/BD.Common.Security/BD.Common.Security.csproj" />
<Project Path="ref/Common/src/BD.Common.Settings/BD.Common.Settings.csproj" />
<Project Path="ref/Common/src/BD.Common/BD.Common.csproj" />
</Folder>
<Folder Name="/2.Reference/Steam/">
<Project Path="ref/ArchiSteamFarm/ArchiSteamFarm/ArchiSteamFarm.csproj" />
<Project Path="ref/Facepunch.Steamworks/Facepunch.Steamworks/Facepunch.Steamworks.Posix.csproj" />
<Project Path="ref/Facepunch.Steamworks/Facepunch.Steamworks/Facepunch.Steamworks.Win64.csproj" />
<Project Path="ref/Gameloop.Vdf/Gameloop.Vdf/Gameloop.Vdf.csproj" />
<Project Path="ref/Steam4NET/Steam4NET/Steam4NET.csproj" />
<Project Path="ref/SteamClient/ref/ValveKeyValue/ValveKeyValue/ValveKeyValue/ValveKeyValue.csproj" />
<Project Path="ref/SteamClient/src/BD.SteamClient.Models.Protobuf/BD.SteamClient.Models.Protobuf.csproj" />
<Project Path="ref/SteamClient/src/BD.SteamClient/BD.SteamClient.csproj" />
</Folder>
<Folder Name="/2.Reference/WinAuth/">
<Project Path="ref/WinAuth/src/WinAuth/WinAuth.csproj" />
</Folder>
<Folder Name="/3.Test/">
<Project Path="src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj" />
</Folder>
<Folder Name="/4.Shared/">
<Project Path="src/BD.Avalonia8.Image2.Compat/BD.Avalonia8.Image2.Compat.csproj" />
<Project Path="src/BD.WTTS.Client.Avalonia/BD.WTTS.Client.Avalonia.csproj" />
<Project Path="src/BD.WTTS.Client.IPC/BD.WTTS.Client.IPC.csproj" />
<Project Path="src/BD.WTTS.Client/BD.WTTS.Client.csproj" />
<Project Path="src/OSShuttingDownHelper/OSShuttingDownHelper.shproj" />
</Folder>
<Folder Name="/4.Shared/Avalonia.Ref/">
<Project Path="src/Avalonia.Base.Internals/Avalonia.Base.Internals.csproj" />
<Project Path="src/Avalonia.Base/Avalonia.Base.csproj" />
<Project Path="src/Avalonia.Controls.Internals/Avalonia.Controls.Internals.csproj" />
<Project Path="src/Avalonia.Desktop/Avalonia.Desktop.csproj" />
<Project Path="src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj" />
<Project Path="src/Avalonia.Native/Avalonia.Native.csproj" />
<Project Path="src/Avalonia.Skia.Internals/Avalonia.Skia.Internals.csproj" />
<Project Path="src/Avalonia.WebView2/Avalonia.WebView2.csproj" />
<Project Path="src/Avalonia.Win32/Avalonia.Win32.csproj" />
<Project Path="src/Avalonia.X11/Avalonia.X11.csproj" />
</Folder>
<Folder Name="/4.Shared/ClientSDK/">
<Project Path="ref/WTTS.MicroServices.ClientSDK/ref/WTTS.Public/src/BD.WTTS.MicroServices.Primitives.Resources/BD.WTTS.MicroServices.Primitives.Resources.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/ref/WTTS.Public/src/BD.WTTS.Primitives.Resources/BD.WTTS.Primitives.Resources.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.ClientSDK/BD.WTTS.MicroServices.ClientSDK.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives.Models/BD.WTTS.MicroServices.Primitives.Models.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives.ViewModels/BD.WTTS.MicroServices.Primitives.ViewModels.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.MicroServices.Primitives/BD.WTTS.MicroServices.Primitives.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives.Models/BD.WTTS.Primitives.Models.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives.ViewModels/BD.WTTS.Primitives.ViewModels.csproj" />
<Project Path="ref/WTTS.MicroServices.ClientSDK/src/BD.WTTS.Primitives/BD.WTTS.Primitives.csproj" />
</Folder>
<Folder Name="/5.Plugins/">
<Project Path="src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.Accelerator/BD.WTTS.Client.Plugins.Accelerator.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.Authenticator/BD.WTTS.Client.Plugins.Authenticator.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.GameAccount/BD.WTTS.Client.Plugins.GameAccount.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.GameList/BD.WTTS.Client.Plugins.GameList.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.GameTools/BD.WTTS.Client.Plugins.GameTools.csproj" />
<Project Path="src/BD.WTTS.Client.Plugins.SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.csproj" />
</Folder>
<Folder Name="/6.Tools/">
<Project Path="ref/Common/src/BD.Common.Settings.V4.SourceGenerator.Tools/BD.Common.Settings.V4.SourceGenerator.Tools.csproj" />
<Project Path="src/BD.WTTS.Client.Avalonia.Designer.HostApp/BD.WTTS.Client.Avalonia.Designer.HostApp.csproj" />
<Project Path="src/BD.WTTS.Client.Tools.HostsTest/BD.WTTS.Client.Tools.HostsTest.csproj" />
<Project Path="src/BD.WTTS.Client.Tools.OpenSourceLibraryList/BD.WTTS.Client.Tools.OpenSourceLibraryList.csproj" />
<Project Path="src/BD.WTTS.Client.Tools.Publish/BD.WTTS.Client.Tools.Publish.csproj" />
</Folder>
<Project Path="src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj" />
</Solution>
+2 -2
View File
@@ -18,14 +18,14 @@ public static partial class AssemblyInfo
/// <summary>
/// 预览版本号,范围 1~9,只增不减
/// </summary>
const string ver_for_preview = "5";
const string ver_for_preview = "1";
/// <summary>
/// RC 版本号,范围 0~9,只增不减
/// </summary>
const string ver_for_rc = "0";
public const string FileVersion = $"{Version2}.2{ver_for_rc}2{ver_for_preview}.0"; // msix 需要保持第四位为0
public const string FileVersion = $"{Version2}.{VersionBuild}.{ver_for_rc}2{ver_for_preview}";
public const string InformationalVersion = Version;
//public const string InformationalVersion = $"{Version}-preview.{ver_for_preview}";
+1 -1
View File
@@ -9,6 +9,6 @@
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
</runtime>
<appSettings>
<add key="d" value="10.0.3"/>
<add key="d" value="10.0.2"/>
</appSettings>
</configuration>
+1 -1
View File
@@ -10,6 +10,6 @@
<legacyCorruptedStateExceptionsPolicy enabled="true"/>
</runtime>
<appSettings>
<add key="d" value="10.0.3"/>
<add key="d" value="10.0.2"/>
</appSettings>
</configuration>
+1 -1
View File
@@ -18,7 +18,7 @@ static unsafe partial class Program
{
dotnet_version_major = "10";
dotnet_version_minor = "0";
dotnet_version_build = "3";
dotnet_version_build = "2";
dotnet_version = $"{dotnet_version_major}.{dotnet_version_minor}.{dotnet_version_build}";
}
@@ -6,6 +6,9 @@
<RootNamespace>BD.WTTS</RootNamespace>
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
<ApplicationId>net.steampp.app</ApplicationId>
<ServerGarbageCollection>false</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
<RetainVMGarbageCollection>false</RetainVMGarbageCollection>
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
@@ -33,18 +36,6 @@
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<!-- 👇 GC 配置 MSBuild 属性 -->
<ServerGarbageCollection>false</ServerGarbageCollection>
<RetainVMGarbageCollection>false</RetainVMGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
</PropertyGroup>
<ItemGroup>
<!-- 👇 GC 配置 runtimeconfig.json -->
<RuntimeHostConfigurationOption Include="System.GC.HighMemoryPercent" Value="75" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\ImplicitUsings.MessagePack.cs">
<LinkBase>Properties</LinkBase>
+2 -2
View File
@@ -7,9 +7,9 @@
<key>CFBundleDisplayName</key>
<string>Watt Toolkit</string>
<key>CFBundleShortVersionString</key>
<string>3.1.0</string>
<string>3.0.0</string>
<key>CFBundleVersion</key>
<string>3.1.2021.0</string>
<string>3.0.1219.0</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
@@ -16,48 +16,48 @@
x:CompileBindings="True"
x:DataType="spp:MainWindowViewModel"
mc:Ignorable="d">
<UserControl.Styles>
<Style Selector="ui|NavigationView.AppNav">
<Setter Property="IsPaneToggleButtonVisible" Value="False" />
<Setter Property="OpenPaneLength" Value="72" />
<Setter Property="IsPaneOpen" Value="True" />
</Style>
<UserControl.Styles>
<Style Selector="ui|NavigationView.AppNav">
<Setter Property="IsPaneToggleButtonVisible" Value="False" />
<Setter Property="OpenPaneLength" Value="72" />
<Setter Property="IsPaneOpen" Value="True" />
</Style>
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=False] SplitView /template/ ContentPresenter#PART_PanePresenter">
<Setter Property="Margin" Value="0 42 0 0" />
</Style>
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=True] SplitView /template/ ContentPresenter#PART_PanePresenter">
<Setter Property="Margin" Value="0 2 0 0" />
</Style>
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=False] SplitView /template/ ContentPresenter#PART_PanePresenter">
<Setter Property="Margin" Value="0 42 0 0" />
</Style>
<Style Selector="ui|NavigationView.AppNav[IsBackButtonVisible=True] SplitView /template/ ContentPresenter#PART_PanePresenter">
<Setter Property="Margin" Value="0 2 0 0" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav">
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.05" />
</Transitions>
</Setter.Value>
</Setter>
</Style>
<Style Selector="ui|NavigationViewItem.AppNav">
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.05" />
</Transitions>
</Setter.Value>
</Setter>
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter">
<Setter Property="Width" Value="72" />
<Setter Property="MinHeight" Value="75" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
<Setter Property="Template">
<ControlTemplate>
<Border
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter">
<Setter Property="Width" Value="72" />
<Setter Property="MinHeight" Value="75" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="LayoutRoot"
Margin="4,6"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
TemplatedControl.IsTemplateFocusTarget="True">
<Panel>
<Panel HorizontalAlignment="Left" VerticalAlignment="Center">
<Panel>
<Panel HorizontalAlignment="Left" VerticalAlignment="Center">
<Border
<Border
Name="SelectionIndicator"
Width="3"
Height="20"
@@ -65,11 +65,11 @@
Background="{DynamicResource NavigationViewSelectionIndicatorForeground}"
CornerRadius="{StaticResource ControlCornerRadius}"
Opacity="0" />
</Panel>
</Panel>
<DockPanel>
<ContentPresenter
<DockPanel>
<ContentPresenter
Name="ContentPresenter"
Grid.Row="1"
Margin="0,-15,0,3"
@@ -80,80 +80,80 @@
ContentTemplate="{TemplateBinding ContentTemplate}"
DockPanel.Dock="Bottom"
FontSize="10">
<ContentPresenter.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</ContentPresenter.Styles>
</ContentPresenter>
<ContentPresenter.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</ContentPresenter.Styles>
</ContentPresenter>
<Viewbox
<Viewbox
Name="IconBox"
Height="28"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ContentPresenter Name="Icon" Content="{Binding TemplateSettings.Icon, RelativeSource={RelativeSource TemplatedParent}}" />
</Viewbox>
<ContentPresenter Name="Icon" Content="{Binding TemplateSettings.Icon, RelativeSource={RelativeSource TemplatedParent}}" />
</Viewbox>
</DockPanel>
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
</DockPanel>
</Panel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pointerover /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:pressed /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#ContentPresenter">
<Setter Property="IsVisible" Value="False" />
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#ContentPresenter">
<Setter Property="IsVisible" Value="False" />
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
</Style>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
</Style>
</UserControl.Styles>
<Style Selector="ui|NavigationViewItem.AppNav uip|NavigationViewItemPresenter:selected /template/ ContentPresenter#Icon">
<Setter Property="Foreground" Value="{DynamicResource AccentFillColorDefaultBrush}" />
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto, *">
<spp:TitleBar Name="TitleBarHost">
<spp:TitleBar.ActionContent>
<StackPanel Orientation="Horizontal" Spacing="8">
<DropDownButton
<Grid RowDefinitions="Auto, *">
<spp:TitleBar Name="TitleBarHost">
<spp:TitleBar.ActionContent>
<StackPanel Orientation="Horizontal" Spacing="8">
<DropDownButton
Width="40"
Height="40"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding Path=MarkNotificationsHasRead, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
Theme="{DynamicResource TransparentButton}">
<Panel>
<ui:InfoBadge
<Panel>
<ui:InfoBadge
Margin="0,-3,-3,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Classes="Attention Dot"
IsVisible="{Binding Path=!!UnreadNotificationsCount, Mode=OneWay, Source={x:Static s:NoticeService.Current}}" />
<Viewbox
<Viewbox
Width="22"
Height="22"
VerticalAlignment="Center">
<ui:SymbolIcon Symbol="Alert" />
</Viewbox>
</Panel>
<ui:SymbolIcon Symbol="Alert" />
</Viewbox>
</Panel>
<DropDownButton.Flyout>
<!--<ui:FAMenuFlyout
<DropDownButton.Flyout>
<!--<ui:FAMenuFlyout
FlyoutPresenterClasses="NoPadding"
ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
Placement="BottomEdgeAlignedRight">
@@ -166,81 +166,79 @@
</DataTemplate>
</ui:FAMenuFlyout.ItemTemplate>
</ui:FAMenuFlyout>-->
<Flyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
<spp:NoticeFlyout Margin="5" />
</Flyout>
</DropDownButton.Flyout>
</DropDownButton>
<Flyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
<spp:NoticeFlyout Margin="5" />
</Flyout>
</DropDownButton.Flyout>
</DropDownButton>
<DropDownButton Padding="4" Theme="{DynamicResource TransparentButton}">
<Border
<DropDownButton Padding="4" Theme="{DynamicResource TransparentButton}">
<Border
Width="32"
Height="32"
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
ClipToBounds="True"
CornerRadius="30">
<spp:Image2
<spp:Image2
DecodeWidth="36"
FallbackSource="{StaticResource DefaultAvatar}"
RenderOptions.BitmapInterpolationMode="HighQuality"
KeepSourceStreamOpen="True"
Source="{Binding Path=AvatarPath, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
</Border>
<DropDownButton.Flyout>
<MenuFlyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
<MenuItem
</Border>
<DropDownButton.Flyout>
<MenuFlyout FlyoutPresenterClasses="NoPadding" Placement="BottomEdgeAlignedRight">
<MenuItem
Command="{Binding Path=ShowWindow, Source={x:Static s:UserService.Current}}"
CommandParameter="{x:Static spp:AppEndPoint.LoginOrRegister}"
Cursor="Hand"
IsHitTestVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}}">
<MenuItem.Header>
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Border
<MenuItem.Header>
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<Border
Width="64"
Height="64"
Margin="0,0,8,0"
ClipToBounds="True"
CornerRadius="64"
DockPanel.Dock="Left">
<spp:Image2
<spp:Image2
DecodeWidth="64"
FallbackSource="{StaticResource DefaultAvatar}"
RenderOptions.BitmapInterpolationMode="HighQuality"
KeepSourceStreamOpen="True"
Source="{Binding Path=AvatarPath, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
</Border>
<Panel>
<TextBlock
</Border>
<Panel>
<TextBlock
VerticalAlignment="Center"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}}"
Text="{Binding Path=Res.User_UnLogin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<StackPanel VerticalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
<Border
<StackPanel VerticalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<TextBlock Text="{Binding Path=User.NickName, Mode=OneWay, Source={x:Static s:UserService.Current}}" />
<Border
Padding="5,1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CornerRadius="2"
IsVisible="{Binding Path=User.UserType, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource EnumEqualValueConverter}, ConverterParameter={x:Static enum:UserType.Sponsor}}">
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#453a94" />
<GradientStop Offset="1.0" Color="#f43b47" />
</LinearGradientBrush>
</Border.Background>
<TextBlock
<Border.Background>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#453a94" />
<GradientStop Offset="1.0" Color="#f43b47" />
</LinearGradientBrush>
</Border.Background>
<TextBlock
VerticalAlignment="Center"
FontSize="10"
Foreground="White"
Text="{Binding Path=Res.User_Sponsor, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}❤️{0}}" />
</Border>
</Border>
</StackPanel>
</Panel>
</DockPanel>
<!--<WrapPanel HorizontalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
</StackPanel>
</Panel>
</DockPanel>
<!--<WrapPanel HorizontalAlignment="Center" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}">
<Button Margin="5,0" FontSize="10">
<ToolTip.Tip>
<MultiBinding StringFormat="{}{0} : {1} / {2}">
@@ -275,33 +273,33 @@
</Button.Content>
</Button>
</WrapPanel>-->
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
CommandParameter="https://steampp.net/account/center/info"
Header="{Binding Path=Res.PersonalCenter, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
<MenuItem
<!--<MenuItem Header="{Binding Path=Res.MyOrder, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />-->
<MenuItem
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
CommandParameter="https://steampp.net/account/center/bind"
Header="{Binding Path=Res.BindThirdPartyAccount, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
<MenuItem
Command="{Binding Path=OpenAuthOfficialCommand, Source={x:Static s:UserService.Current}}"
CommandParameter="https://steampp.net/account/center/safe"
Header="{Binding Path=Res.PersonalSafe, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
<MenuItem Header="-" IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<MenuItem
Command="{Binding Path=SignOut, Mode=OneWay, Source={x:Static s:UserService.Current}}"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Header="{Binding Path=Res.SignOut, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IsVisible="{Binding Path=User, Mode=OneWay, Source={x:Static s:UserService.Current}, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}" />
<!--<MenuItem
<!--<MenuItem
Command="{Binding Path=ShowWindow,
Source={x:Static s:UserService.Current}}"
CommandParameter="{x:Static spp:AppEndPoint.LoginOrRegister}"
@@ -313,14 +311,14 @@
Mode=OneWay,
Source={x:Static s:UserService.Current},
Converter={StaticResource IsNullConverter}}" />-->
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</spp:TitleBar.ActionContent>
</spp:TitleBar>
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</spp:TitleBar.ActionContent>
</spp:TitleBar>
<ui:NavigationView
<ui:NavigationView
Name="NavView"
Grid.RowSpan="2"
AlwaysShowHeader="False"
@@ -332,35 +330,35 @@
IsSettingsVisible="False"
MenuItemsSource="{Binding TabItems}"
PaneDisplayMode="Left">
<ui:NavigationView.Resources>
<!-- Override this -->
<Thickness x:Key="NavigationViewContentMargin">0,50,0,0</Thickness>
</ui:NavigationView.Resources>
<ui:NavigationView.Styles>
<Style Selector="Button#NavigationViewBackButton">
<Setter Property="FontFamily" Value="{DynamicResource SymbolThemeFontFamily}" />
<Setter Property="Width" Value="36" />
<Setter Property="Height" Value="36" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Content" Value="&#xF0D5;" />
</Style>
<Style Selector="Grid#PaneToggleButtonGrid">
<Setter Property="Margin" Value="5 0 0 0" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="ToolTip.Placement" Value="Bottom" />
</Style>
</ui:NavigationView.Styles>
<ui:NavigationView.MenuItemTemplate>
<DataTemplate DataType="spp:TabItemViewModel">
<ui:NavigationViewItem Classes="AppNav" Content="{Binding Name}">
<ui:NavigationViewItem.IconSource>
<!--<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding IconKey, Converter={StaticResource ResourceKeyValueConverter}, ConverterParameter=fonticon}" />-->
<ui:ImageIconSource Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}" />
</ui:NavigationViewItem.IconSource>
</ui:NavigationViewItem>
</DataTemplate>
</ui:NavigationView.MenuItemTemplate>
<ui:Frame Name="FrameView" />
</ui:NavigationView>
</Grid>
<ui:NavigationView.Resources>
<!-- Override this -->
<Thickness x:Key="NavigationViewContentMargin">0,50,0,0</Thickness>
</ui:NavigationView.Resources>
<ui:NavigationView.Styles>
<Style Selector="Button#NavigationViewBackButton">
<Setter Property="FontFamily" Value="{DynamicResource SymbolThemeFontFamily}" />
<Setter Property="Width" Value="36" />
<Setter Property="Height" Value="36" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Content" Value="&#xF0D5;" />
</Style>
<Style Selector="Grid#PaneToggleButtonGrid">
<Setter Property="Margin" Value="5 0 0 0" />
<Setter Property="ToolTip.Tip" Value="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Setter Property="ToolTip.Placement" Value="Bottom" />
</Style>
</ui:NavigationView.Styles>
<ui:NavigationView.MenuItemTemplate>
<DataTemplate DataType="spp:TabItemViewModel">
<ui:NavigationViewItem Classes="AppNav" Content="{Binding Name}">
<ui:NavigationViewItem.IconSource>
<!--<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="{Binding IconKey, Converter={StaticResource ResourceKeyValueConverter}, ConverterParameter=fonticon}" />-->
<ui:ImageIconSource Source="{Binding IconKey, Converter={StaticResource BitmapAssetValueConverter}}" />
</ui:NavigationViewItem.IconSource>
</ui:NavigationViewItem>
</DataTemplate>
</ui:NavigationView.MenuItemTemplate>
<ui:Frame Name="FrameView" />
</ui:NavigationView>
</Grid>
</UserControl>
@@ -30,12 +30,6 @@ sealed class HttpLocalRequestMiddleware
/// <returns></returns>
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (TryGetInjectScriptLocalId(context.Request.Path, out var injectScriptLid))
{
await HandleScriptRequestAsync(context, injectScriptLid);
return;
}
if (HttpMethods.IsOptions(context.Request.Method) && context.Request.Headers.ContainsKey("Access-Control-Request-Private-Network"))
{
// https://wicg.github.io/private-network-access/
@@ -66,50 +60,34 @@ sealed class HttpLocalRequestMiddleware
await HandleHttpRequestAsync(context);
return;
default: //默认处理脚本匹配
if (!int.TryParse(context.Request.Path.Value?.Trim('/'), out var lid) || lid <= 0)
if (!int.TryParse(context.Request.Path.Value?.Trim('/'), out var lid) && lid <= 0)
{
await Handle404NotFoundAsync(context);
return;
}
// TODO: Scripts
if (reverseProxyConfig.TryGetScriptContent(lid, out string? content))
{
if (string.IsNullOrEmpty(content))
{
await Handle404NotFoundAsync(context);
return;
}
context.Response.Headers.ContentType = "text/javascript;charset=UTF-8";
await context.Response.WriteAsync(content);
return;
}
else
{
await Handle404NotFoundAsync(context);
return;
}
await HandleScriptRequestAsync(context, lid);
return;
}
}
await next(context);
}
static bool TryGetInjectScriptLocalId(PathString path, out int localId)
{
localId = default;
var pathValue = path.Value;
if (string.IsNullOrEmpty(pathValue))
return false;
var prefix = IReverseProxyService.Constants.InjectScriptPathPrefix;
if (!pathValue.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
return false;
var idPart = pathValue[prefix.Length..];
if (!idPart.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
return false;
idPart = idPart[..^3];
return int.TryParse(idPart, out localId) && localId > 0;
}
async Task HandleScriptRequestAsync(HttpContext context, int lid)
{
if (reverseProxyConfig.TryGetScriptContent(lid, out var content) && !string.IsNullOrEmpty(content))
{
context.Response.Headers.ContentType = "application/javascript;charset=UTF-8";
await context.Response.WriteAsync(content);
return;
}
await Handle404NotFoundAsync(context);
}
/// <summary>
/// 处理脚本所需要的 HTTP 请求
/// </summary>
@@ -90,122 +90,4 @@ partial class HttpReverseProxyMiddleware
notfound: insertPosition = -1;
return false;
}
internal static bool FindScriptInjectInsertPositionForGithub(byte[] buffer_, Encoding encoding, out ReadOnlyMemory<byte> buffer, out int insertPosition)
{
buffer = buffer_.AsMemory();
ReadOnlySpan<byte> mark = "<script"u8;
var lastScriptWithSrcStart = -1;
var span = buffer_.AsSpan();
if (span.Length >= mark.Length)
{
for (var i = 0; i <= span.Length - mark.Length; i++)
{
if (!EqualsAsciiIgnoreCase(span.Slice(i, mark.Length), mark))
{
continue;
}
var afterMarkIndex = i + mark.Length;
if (afterMarkIndex < span.Length && IsHtmlAttributeNameChar(span[afterMarkIndex]))
{
continue;
}
var tagEndOffset = span.Slice(afterMarkIndex).IndexOf((byte)'>');
if (tagEndOffset < 0)
{
break;
}
var tagEndIndex = afterMarkIndex + tagEndOffset;
var scriptTag = span.Slice(i, tagEndIndex - i + 1);
if (ScriptTagHasSrcAttribute(scriptTag))
{
lastScriptWithSrcStart = i;
}
i = tagEndIndex;
}
}
if (lastScriptWithSrcStart >= 0)
{
insertPosition = lastScriptWithSrcStart;
return true;
}
return FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out insertPosition);
}
static bool ScriptTagHasSrcAttribute(ReadOnlySpan<byte> scriptTag)
{
ReadOnlySpan<byte> src = "src"u8;
if (scriptTag.Length < src.Length)
return false;
for (var i = 0; i <= scriptTag.Length - src.Length; i++)
{
if (!EqualsAsciiIgnoreCase(scriptTag.Slice(i, src.Length), src))
continue;
if (i > 0 && IsHtmlAttributeNameChar(scriptTag[i - 1]))
continue;
var j = i + src.Length;
while (j < scriptTag.Length && IsAsciiWhitespace(scriptTag[j]))
{
j++;
}
if (j < scriptTag.Length && scriptTag[j] == (byte)'=')
{
return true;
}
}
return false;
}
static bool EqualsAsciiIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
{
if (left.Length != right.Length)
return false;
for (var i = 0; i < left.Length; i++)
{
var l = ToUpperAscii(left[i]);
var r = ToUpperAscii(right[i]);
if (l != r)
return false;
}
return true;
}
static byte ToUpperAscii(byte value)
{
if (value is >= (byte)'a' and <= (byte)'z')
return (byte)(value - 32);
return value;
}
static bool IsAsciiWhitespace(byte value) =>
value == (byte)' ' ||
value == (byte)'\t' ||
value == (byte)'\r' ||
value == (byte)'\n' ||
value == (byte)'\f';
static bool IsHtmlAttributeNameChar(byte value) =>
(value is >= (byte)'a' and <= (byte)'z') ||
(value is >= (byte)'A' and <= (byte)'Z') ||
(value is >= (byte)'0' and <= (byte)'9') ||
value == (byte)'-' ||
value == (byte)'_' ||
value == (byte)':' ||
value == (byte)'.';
}
@@ -327,12 +327,7 @@ Actual value was {actualValue}.
}
}
var isGithubHost = IsGithubHost(context.Request.Host.Host);
var isFindPosition = isGithubHost
? FindScriptInjectInsertPositionForGithub(buffer_, encoding, out var buffer, out var position)
: FindScriptInjectInsertPosition(buffer_, encoding, out buffer, out position);
if (isFindPosition)
if (FindScriptInjectInsertPosition(buffer_, encoding, out var buffer, out var position))
{
using var bodyWriter = new MemoryStream();
using Stream? bodyCompress = GetStreamByContentCompression(bodyWriter, contentCompression, CompressionMode.Compress, true);
@@ -355,8 +350,8 @@ Actual value was {actualValue}.
// var html_start_string = encoding.GetString(html_start.Span);
//#endif
await bodyCoreWriter.WriteAsync(html_start);
var script_xml_start = Encoding.ASCII.GetBytes($"<script type=\"text/javascript\" src=\"{IReverseProxyService.Constants.InjectScriptPathPrefix}");
ReadOnlyMemory<byte> script_xml_end = ".js\"></script>"u8.ToArray();
ReadOnlyMemory<byte> script_xml_start = "<script type=\"text/javascript\" src=\"https://local.steampp.net/"u8.ToArray();
ReadOnlyMemory<byte> script_xml_end = "\"></script>"u8.ToArray();
foreach (var script in scripts)
{
await bodyCoreWriter.WriteAsync(script_xml_start);
@@ -403,15 +398,6 @@ Actual value was {actualValue}.
context.Response.Body = originalBody;
}
}
static bool IsGithubHost(string? host)
{
if (string.IsNullOrWhiteSpace(host))
return false;
return host.Equals("github.com", StringComparison.OrdinalIgnoreCase) ||
host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase);
}
}
static Stream? GetStreamByContentCompression(Stream stream, string contentCompression, CompressionMode mode, bool leaveOpen) => contentCompression switch
@@ -154,9 +154,8 @@ abstract class ReverseProxyServiceImpl : IReverseProxySettings
{
reverseProxySettings = Serializable.DMP2<ReverseProxySettings>(reverseProxySettings_);
}
catch (Exception e)
catch
{
OnException(e);
return StartProxyResultCode.DeserializeReverseProxySettingsFail;
}
@@ -34,11 +34,6 @@ public partial interface IReverseProxyService : IDisposable
public const string LocalDomain = "local.steampp.net";
/// <summary>
/// 同源脚本注入路径前缀,避免使用外域 script src 造成页面脚本 publicPath 污染。
/// </summary>
public const string InjectScriptPathPrefix = "/WattToolkit_Inject/";
public static IPAddress DefaultProxyIp => IPAddress.Any;
}
@@ -74,9 +74,6 @@ partial class ProxyService
bool useDoh = ProxySettings.UseDoh.Value;
string? customDohAddres = ProxySettings.CustomDohAddres2.Value;
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
await TryRequestServerSideProxyToken() : null;
if (ProxySettings.ProxyBeforeDNSCheck.Value)
{
if (useDoh)
@@ -232,6 +229,9 @@ partial class ProxyService
}
}
string? proxyToken = proxyDomains.Any_Nullable(s => s.ProxyType == ProxyType.ServerAccelerate || s.Items.Any_Nullable(x => x.ProxyType == ProxyType.ServerAccelerate)) ?
await TryRequestServerSideProxyToken() : null;
ReverseProxySettings reverseProxySettings = new(proxyDomains, scripts,
isEnableScript, isOnlyWorkSteamBrowser, proxyPort,
proxyIp, proxyMode, isProxyGOG, onlyEnableProxyScript,
@@ -122,7 +122,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
#region 自定义图片保存
//var mostRecentUser = ISteamService.Instance.GetRememberUserList().Where(s => s.MostRecent).FirstOrDefault();
var mostRecentUser = SteamConnectService.Current.SteamUsers.Items.Where(s => s.MostRecent).FirstOrDefault();
if (!(App.EditHeaderLogoStream is FileStream fs3 && (await App.HeaderLogoStream) is FileStream headerLogoStream && fs3.Name == headerLogoStream.Name))
if (!(App.EditHeaderLogoStream is FileStream fs3 && fs3.Name == (await App.HeaderLogoStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -135,7 +135,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
else
this.RaisePropertyChanged(nameof(App.HeaderLogoStream));
}
if (!(App.EditLibraryLogoStream is FileStream fs && (await App.LibraryLogoStream) is FileStream libraryLogoStream && fs.Name == libraryLogoStream.Name))
if (!(App.EditLibraryLogoStream is FileStream fs && fs.Name == (await App.LibraryLogoStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -148,7 +148,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
else
this.RaisePropertyChanged(nameof(App.LibraryLogoStream));
}
if (!(App.EditLibraryHeroStream is FileStream fs1 && (await App.LibraryHeroStream) is FileStream libraryHeroStream && fs1.Name == libraryHeroStream.Name))
if (!(App.EditLibraryHeroStream is FileStream fs1 && fs1.Name == (await App.LibraryHeroStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -161,7 +161,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
else
this.RaisePropertyChanged(nameof(App.LibraryHeroStream));
}
if (!(App.EditLibraryGridStream is FileStream fs2 && (await App.LibraryGridStream) is FileStream libraryGridStream && fs2.Name == libraryGridStream.Name))
if (!(App.EditLibraryGridStream is FileStream fs2 && fs2.Name == (await App.LibraryGridStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -246,16 +246,16 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
switch (type)
{
case SteamGridItemType.Header:
App.EditHeaderLogoStream = (await App.HeaderLogoStream);
App.EditHeaderLogoStream = (await App.HeaderLogoStream)?.Stream;
break;
case SteamGridItemType.Grid:
App.EditLibraryGridStream = (await App.LibraryGridStream);
App.EditLibraryGridStream = (await App.LibraryGridStream)?.Stream;
break;
case SteamGridItemType.Hero:
App.EditLibraryHeroStream = (await App.LibraryHeroStream);
App.EditLibraryHeroStream = (await App.LibraryHeroStream)?.Stream;
break;
case SteamGridItemType.Logo:
App.EditLibraryLogoStream = (await App.LibraryLogoStream);
App.EditLibraryLogoStream = (await App.LibraryLogoStream)?.Stream;
break;
}
return;
@@ -16,9 +16,6 @@ public sealed class EditAppsPageViewModel : ViewModelBase
public bool IsSteamEditedAppsEmpty => !SteamEditedApps.Any_Nullable();
[Reactive]
public bool IsExportingBackup { get; set; }
public ICommand EditAppInfoClickCommand { get; }
public EditAppsPageViewModel()
@@ -59,114 +56,6 @@ public sealed class EditAppsPageViewModel : ViewModelBase
}
}
public async Task ExportSteamEditedAppsBackup()
{
if (IsExportingBackup)
{
return;
}
if (!SteamEditedApps.Any())
{
Toast.Show(ToastIcon.Warning, "没有可导出的已修改游戏数据。");
return;
}
var result = await FilePicker2.SaveAsync(new PickOptions
{
PickerTitle = "导出编辑游戏数据备份",
InitialFileName = $"steam-edited-apps-{DateTime.Now:yyyyMMddHHmmss}.stmbak",
});
if (result == null)
{
return;
}
try
{
IsExportingBackup = true;
var exportData = await Task.Run(async () =>
{
var stmService = ISteamService.Instance;
var applist = await stmService.GetAppInfos(true);
var editApps = SteamConnectService.Current.SteamApps.Items
.Where(s => s.IsEdited)
.ToDictionary(s => s.AppId, s => s);
var modifiedApps = new List<ModifiedApp>();
foreach (var app in applist)
{
if (editApps.TryGetValue(app.AppId, out var editApp))
{
app.SetEditProperty(editApp);
modifiedApps.Add(new ModifiedApp(app));
}
}
return (ModifiedApps: modifiedApps, Bytes: Serializable.SMP(modifiedApps));
});
if (!exportData.ModifiedApps.Any())
{
Toast.Show(ToastIcon.Error, "导出失败:没有可序列化的修改数据。");
return;
}
using var stream = result.OpenWrite();
await stream.WriteAsync(exportData.Bytes, 0, exportData.Bytes.Length);
await stream.FlushAsync();
Toast.Show(ToastIcon.Success, $"导出成功,共 {exportData.ModifiedApps.Count} 条修改数据。");
}
catch
{
Toast.Show(ToastIcon.Error, "导出失败,请检查文件权限后重试。");
}
finally
{
IsExportingBackup = false;
}
}
public async Task ImportSteamEditedAppsBackup()
{
var file = await FilePicker2.PickAsync(new PickOptions
{
PickerTitle = "导入编辑游戏数据备份",
});
if (file == null)
{
return;
}
try
{
await using var stream = await file.OpenReadAsync();
var modifiedApps = Serializable.DMP<List<ModifiedApp>>(stream);
if (!modifiedApps.Any_Nullable())
{
Toast.Show(ToastIcon.Warning, "导入文件中没有可还原的数据。");
return;
}
var applyCount = SteamConnectService.Current.ApplyModifiedApps(modifiedApps);
if (applyCount <= 0)
{
Toast.Show(ToastIcon.Warning, "导入完成,但未匹配到可还原的游戏。\n请确认账号或游戏库与备份来源一致。");
return;
}
this.RaisePropertyChanged(nameof(IsSteamEditedAppsEmpty));
Toast.Show(ToastIcon.Success, $"导入成功,已还原 {applyCount} 个游戏的修改数据。");
}
catch
{
Toast.Show(ToastIcon.Error, "导入失败,文件格式可能不正确。");
}
}
//public async Task ClearSteamEditedApps()
//{
// if (await MessageBox.ShowAsync("确定要重置所有的已修改数据吗?(该操作不可还原)", AssemblyInfo.Trademark, MessageBox.Button.OKCancel) == MessageBox.Result.OK)
@@ -200,32 +200,10 @@ public sealed class AchievementAppPageViewModel : WindowViewModel
continue;
}
UserStatType type;
// schema in the new format
var typeNode = stat["type"];
if (typeNode.Valid == true && typeNode.Type == KeyValueType.String)
{
if (Enum.TryParse((string)typeNode.Value, true, out type) == false)
{
type = UserStatType.Invalid;
}
}
else
{
type = UserStatType.Invalid;
}
// schema in the old format
if (type == UserStatType.Invalid)
{
var typeIntNode = stat["type_int"];
var rawType = typeIntNode.Valid == true
? typeIntNode.AsInteger(0)
: typeNode.AsInteger(0);
type = (UserStatType)rawType;
}
var rawType = stat["type_int"].Valid
? stat["type_int"].AsInteger(0)
: stat["type"].AsInteger(0);
var type = (UserStatType)rawType;
switch (type)
{
case UserStatType.Invalid:
@@ -67,18 +67,6 @@
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<ui:MenuFlyoutItem
Command="{Binding ExportSteamEditedAppsBackup}"
IsEnabled="{Binding !IsExportingBackup}"
Text="{Binding Path=Res.Export,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem
Command="{Binding ImportSteamEditedAppsBackup}"
Text="{Binding Path=Res.Import,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutSeparator />
<!--<ui:MenuFlyoutItem
Command="{Binding DeleteAllButton_Click}"
IconSource="Delete"
@@ -112,76 +100,88 @@
IsLoading="false"
IsShowNoResultText="{Binding !!!SteamEditedApps.Count, Mode=OneWay}"
NoResultMessage="{Binding Path=Res.SaveEditedAppInfo_AppsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:ItemsRepeater ItemsSource="{Binding SteamEditedApps}">
<ui:ItemsRepeater.Layout>
<ui:UniformGridLayout
ItemsJustification="Start"
ItemsStretch="Uniform"
MinColumnSpacing="10"
MinItemHeight="310"
MinItemWidth="150"
MinRowSpacing="10" />
</ui:ItemsRepeater.Layout>
<ui:ItemsRepeater.ItemTemplate>
<ItemsControl ItemsSource="{Binding SteamEditedApps}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="spp:SteamApp">
<spp:AppItem
Title="{Binding DisplayName}"
Classes="Vertical"
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
ClickCommandParameter="{Binding}"
Cursor="Hand">
<spp:AppItem.Status>
<Border Classes="Status">
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</Border>
</spp:AppItem.Status>
<spp:AppItem.Image>
<Panel Width="{Binding $parent[spp:AppItem].Bounds.Width}">
<spp:Image2
Name="AppImage"
DecodeWidth="120"
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding LibraryGridStream^}"
Stretch="UniformToFill" />
<TextBlock
Margin="8,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
IsVisible="{Binding #AppImage.IsFailed}"
Text="{Binding DisplayName}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
<Border
Margin="10,0,10,-3"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Background="#3D4450"
CornerRadius="3"
IsVisible="{Binding IsInstalled}">
<DockPanel Margin="10,3" HorizontalAlignment="Center">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontSize="11"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
Text="{Binding InstalledDrive}" />
<TextBlock
VerticalAlignment="Center"
FontSize="11"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
TextWrapping="Wrap" />
</DockPanel>
<Border Margin="5" spp:Animations.EnableAnimations="False">
<spp:AppItem
Title="{Binding DisplayName}"
Height="270"
MaxWidth="120"
Classes="Vertical"
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
ClickCommandParameter="{Binding}"
Cursor="Hand">
<spp:AppItem.Status>
<Border Classes="Status">
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</Border>
</Panel>
</spp:AppItem.Image>
</spp:AppItem>
</spp:AppItem.Status>
<!--<spp:AppItem.ActionButton>
<Button
Command="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).DeleteButtonCommand}"
CommandParameter="{Binding}"
Cursor="Hand"
ToolTip.Tip="{Binding Path=Res.GameList_RemoveItemBtn, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<Viewbox>
<ui:SymbolIcon Symbol="Delete" />
</Viewbox>
</Button>
</spp:AppItem.ActionButton>-->
<spp:AppItem.Image>
<Panel MinWidth="{Binding $parent[spp:AppItem].MinWidth}" MaxWidth="{Binding $parent[spp:AppItem].MaxWidth}">
<spp:Image2
Name="AppImage"
DecodeWidth="120"
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding LibraryGridStream^}"
Stretch="UniformToFill" />
<TextBlock
Margin="8,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
IsVisible="{Binding #AppImage.IsFailed}"
Text="{Binding DisplayName}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
<Border
Margin="10,0,10,-3"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Background="#3D4450"
CornerRadius="3"
IsVisible="{Binding IsInstalled}">
<DockPanel Margin="10,3" HorizontalAlignment="Center">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontSize="11"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
IsVisible="{Binding InstalledDrive, Converter={StaticResource IsNullConverter}, ConverterParameter=invert}"
Text="{Binding InstalledDrive}" />
<TextBlock
VerticalAlignment="Center"
FontSize="11"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Text="{Binding SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}"
TextWrapping="Wrap" />
</DockPanel>
</Border>
</Panel>
</spp:AppItem.Image>
</spp:AppItem>
</Border>
</DataTemplate>
</ui:ItemsRepeater.ItemTemplate>
</ui:ItemsRepeater>
</ItemsControl.ItemTemplate>
</ItemsControl>
</spp:ContentLoader>
</spp:PageBase>
@@ -5,9 +5,6 @@ namespace BD.WTTS.Models;
public class IdleApp : ReactiveObject
{
private bool isBlacklisted;
private bool isPrivateGame;
public SteamApp App { get; }
public Badge Badge { get; }
@@ -48,50 +45,6 @@ public class IdleApp : ReactiveObject
}
}
public bool IsBlacklisted
{
get => isBlacklisted;
set
{
if (isBlacklisted != value)
{
this.RaiseAndSetIfChanged(ref isBlacklisted, value);
this.RaisePropertyChanged(nameof(IsExcludedFromIdle));
this.RaisePropertyChanged(nameof(ExcludedReasonText));
}
}
}
public bool IsPrivateGame
{
get => isPrivateGame;
set
{
if (isPrivateGame != value)
{
this.RaiseAndSetIfChanged(ref isPrivateGame, value);
this.RaisePropertyChanged(nameof(IsExcludedFromIdle));
this.RaisePropertyChanged(nameof(ExcludedReasonText));
}
}
}
public bool IsExcludedFromIdle => IsBlacklisted || IsPrivateGame;
public string ExcludedReasonText
{
get
{
if (IsPrivateGame && IsBlacklisted)
return "私密游戏(不掉卡) + 已拉黑";
if (IsPrivateGame)
return "私密游戏(不掉卡)";
if (IsBlacklisted)
return "已拉黑";
return string.Empty;
}
}
public IdleApp(Badge badge)
{
Badge = badge;
@@ -55,11 +55,6 @@ public partial interface ISteamIdleSettings
/// </summary>
double RefreshBadgesTime { get; set; }
/// <summary>
/// 挂卡黑名单游戏列表
/// </summary>
Dictionary<uint, string?>? BlacklistAppList { get; set; }
/// <summary>
/// 挂卡状态更新时间的默认值
/// </summary>
@@ -95,9 +90,4 @@ public partial interface ISteamIdleSettings
/// </summary>
static readonly double DefaultRefreshBadgesTime = 6;
/// <summary>
/// 挂卡黑名单游戏列表的默认值
/// </summary>
static readonly Dictionary<uint, string?> DefaultBlacklistAppList = [];
}
@@ -85,12 +85,6 @@ public sealed partial class SteamIdleSettings_ : ISteamIdleSettings, ISettings,
[MPKey(6), MP2Key(6), JsonPropertyOrder(6)]
public double RefreshBadgesTime { get; set; } = ISteamIdleSettings.DefaultRefreshBadgesTime;
/// <summary>
/// 挂卡黑名单游戏列表
/// </summary>
[MPKey(7), MP2Key(7), JsonPropertyOrder(7)]
public Dictionary<uint, string?>? BlacklistAppList { get; set; } = ISteamIdleSettings.DefaultBlacklistAppList;
}
public static partial class SteamIdleSettings
@@ -137,10 +131,4 @@ public static partial class SteamIdleSettings
public static SettingsStructProperty<double, SteamIdleSettings_> RefreshBadgesTime { get; }
= new(DefaultRefreshBadgesTime);
/// <summary>
/// 挂卡黑名单游戏列表
/// </summary>
public static SettingsProperty<uint, string?, Dictionary<uint, string?>, SteamIdleSettings_> BlacklistAppList { get; }
= new(DefaultBlacklistAppList);
}
@@ -30,7 +30,6 @@ public sealed partial class IdleCardPageViewModel
});
this.PriorityRunIdle = ReactiveCommand.CreateFromTask<IdleApp>(PriorityRunIdleGame);
this.ToggleBlacklistIdle = ReactiveCommand.CreateFromTask<IdleApp>(ToggleBlacklistIdleGame);
this.IdleRunStartOrStop = ReactiveCommand.Create(IdleRunStartOrStop_Click);
this.IdleManualRunNext = ReactiveCommand.CreateFromTask(ManualRunNext);
this.LoginSteamCommand = ReactiveCommand.Create(async () =>
@@ -176,52 +175,16 @@ public sealed partial class IdleCardPageViewModel
/// <returns></returns>
public async Task PriorityRunIdleGame(IdleApp idleApp)
{
if (idleApp.IsExcludedFromIdle)
{
Toast.Show(ToastIcon.Warning, "该游戏已被排除,不参与挂卡。请先取消拉黑或移除私密限制。");
return;
}
using (await asyncLock.LockAsync())
{
StopIdle();
PauseAutoNext(true);
StartSoloIdle(idleApp);
CurrentIdleIndex = GetRunnableIdleGames().IndexOf(idleApp);
CurrentIdleIndex = IdleGameList.IndexOf(idleApp);
ChangeRunTxt();
}
}
public async Task ToggleBlacklistIdleGame(IdleApp idleApp)
{
using (await asyncLock.LockAsync())
{
var blackList = SteamIdleSettings.BlacklistAppList.Value ?? [];
if (blackList.ContainsKey(idleApp.AppId))
blackList.Remove(idleApp.AppId);
else
blackList[idleApp.AppId] = idleApp.AppName;
SteamIdleSettings.BlacklistAppList.Value = blackList;
var wasRun = RunState;
if (wasRun)
{
StopIdle();
PauseAutoNext(true);
ResetCurrentIdle();
}
SteamAppsSort();
if (wasRun)
{
StartIdle();
ChangeRunTxt();
}
}
}
#region PrivateFields
/// <summary>
@@ -249,8 +212,6 @@ public sealed partial class IdleCardPageViewModel
/// </summary>
private bool IsReloaded;
private HashSet<uint> PrivateGameAppIds = [];
#endregion
#region Private Method
@@ -414,7 +375,6 @@ public sealed partial class IdleCardPageViewModel
HandleBadges:
Badges.Clear();
Badges.Add(badges!);
//await RefreshPrivateGameAppIds(steam_id);
TotalCardsRemaining = 0;
TotalCardsAvgPrice = 0;
@@ -422,9 +382,6 @@ public sealed partial class IdleCardPageViewModel
foreach (var badge in badges)
{
if (IsExcludedApp(badge.AppId))
continue;
TotalCardsAvgPrice += badge.RegularAvgPrice * badge.CardsRemaining;
TotalCardsRemaining += badge.CardsRemaining;
}
@@ -446,21 +403,7 @@ public sealed partial class IdleCardPageViewModel
IdleSequentital.Mostcards => badges.OrderByDescending(o => o.CardsRemaining).Select(s => new IdleApp(s)),
IdleSequentital.Mostvalue => badges.OrderByDescending(o => o.RegularAvgPrice).Select(s => new IdleApp(s)),
_ => badges.Select(s => new IdleApp(s)),
}).ToList();
foreach (var app in apps)
{
app.IsPrivateGame = PrivateGameAppIds.Contains(app.AppId);
app.IsBlacklisted = IsBlacklistedApp(app.AppId);
}
ExcludedPrivateGameCount = apps.Count(x => x.IsPrivateGame);
ExcludedBlacklistGameCount = apps.Count(x => x.IsBlacklisted);
HasPrivateGameExcluded = ExcludedPrivateGameCount > 0;
PrivateGameExcludeTip = HasPrivateGameExcluded
? $"检测到 {ExcludedPrivateGameCount} 个私密游戏,这些游戏将自动排除,不参与挂卡。"
: string.Empty;
}).ToImmutableList();
Dispatcher.UIThread.Invoke(() =>
{
IdleGameList.Clear();
@@ -475,48 +418,6 @@ public sealed partial class IdleCardPageViewModel
}
}
private bool IsBlacklistedApp(uint appId)
{
var blackList = SteamIdleSettings.BlacklistAppList.Value;
return blackList != null && blackList.ContainsKey(appId);
}
private bool IsExcludedApp(uint appId)
{
return PrivateGameAppIds.Contains(appId) || IsBlacklistedApp(appId);
}
private async Task RefreshPrivateGameAppIds(string steamId)
{
try
{
var (appIds, status) = await IdleCard.GetPrivateGameAppIdsAsync(steamId);
if (status == HttpStatusCode.OK)
{
PrivateGameAppIds = appIds.Count > 0 ? appIds.ToHashSet() : [];
}
else
{
PrivateGameAppIds.Clear();
}
}
catch (Exception ex)
{
ex.LogAndShowT();
PrivateGameAppIds.Clear();
}
}
private List<IdleApp> GetRunnableIdleGames()
{
return IdleGameList.Where(x => !x.IsExcludedFromIdle).ToList();
}
private int GetRunnableCardsRemaining()
{
return GetRunnableIdleGames().Sum(s => s.Badge.CardsRemaining);
}
private async Task<bool> ReadyToGoIdle(bool isFirstStart = false)
{
ResetCurrentIdle();
@@ -537,9 +438,8 @@ public sealed partial class IdleCardPageViewModel
private void StartIdle(bool isNext = false)
{
IdleApp idleApp;
var runnableGames = GetRunnableIdleGames();
if (!runnableGames.Any())
if (!IdleGameList.Any())
{
IdleComplete();
return;
@@ -547,9 +447,9 @@ public sealed partial class IdleCardPageViewModel
if (SteamIdleSettings.IdleRule.Value == IdleRule.FastMode)
{
var multi = runnableGames.Where(z => z.Badge.HoursPlayed >= SteamIdleSettings.MinRunTime.Value).ToList();
var multi = IdleGameList.Where(z => z.Badge.HoursPlayed >= SteamIdleSettings.MinRunTime.Value).ToList();
var isLastSingle = multi.Count == 1 && runnableGames.Count == 1; // 是否只剩最后一个游戏
var isLastSingle = multi.Count == 1 && IdleGameList.Count == 1; // 是否只剩最后一个游戏
if (multi.Count > 1 || isLastSingle)
{
idleApp = VerifyIsNext(multi, isNext);
@@ -569,14 +469,14 @@ public sealed partial class IdleCardPageViewModel
if (SteamIdleSettings.IdleRule.Value == IdleRule.OnlyOneGame)
{
idleApp = VerifyIsNext(runnableGames, isNext);
idleApp = VerifyIsNext(IdleGameList, isNext);
StartSoloIdle(idleApp);
}
else
{
if (SteamIdleSettings.IdleRule.Value == IdleRule.OneThenMany)
{
var multi_Idles = runnableGames.Where(z => z.Badge.HoursPlayed >= SteamIdleSettings.MinRunTime.Value).ToList();
var multi_Idles = IdleGameList.Where(z => z.Badge.HoursPlayed >= SteamIdleSettings.MinRunTime.Value).ToList();
if (multi_Idles.Count >= 1)
{
idleApp = VerifyIsNext(multi_Idles, isNext);
@@ -589,14 +489,14 @@ public sealed partial class IdleCardPageViewModel
}
else
{
var multi_AFKs = runnableGames.Where(z => z.Badge.HoursPlayed < SteamIdleSettings.MinRunTime.Value).ToList();
var multi_AFKs = IdleGameList.Where(z => z.Badge.HoursPlayed < SteamIdleSettings.MinRunTime.Value).ToList();
if (multi_AFKs.Count >= 2)
{
StartMultipleIdle();
}
else
{
idleApp = VerifyIsNext(multi_AFKs.Count <= 0 ? runnableGames : multi_AFKs, isNext);
idleApp = VerifyIsNext(multi_AFKs.Count <= 0 ? IdleGameList : multi_AFKs, isNext);
StartSoloIdle(idleApp);
}
}
@@ -633,9 +533,6 @@ public sealed partial class IdleCardPageViewModel
/// <param name="item"></param>
private void StartSoloIdle(IdleApp item)
{
if (item.IsExcludedFromIdle)
return;
CurrentIdle = item;
SteamConnectService.Current.RuningSteamApps.TryGetValue(item.AppId, out var runState);
if (runState == null)
@@ -658,18 +555,17 @@ public sealed partial class IdleCardPageViewModel
private void StartMultipleIdle()
{
var runnableGames = GetRunnableIdleGames();
foreach (var item in runnableGames)
foreach (var item in IdleGameList)
{
if (item.Badge.HoursPlayed >= SteamIdleSettings.MinRunTime.Value)
StopSoloIdle(item.App);
if (item.Badge.HoursPlayed < SteamIdleSettings.MinRunTime.Value && runnableGames.Count(x => x.App.Process != null) < SteamIdleSettings.MaxIdleCount)
if (item.Badge.HoursPlayed < SteamIdleSettings.MinRunTime.Value && IdleGameList.Count(x => x.App.Process != null) < SteamIdleSettings.MaxIdleCount)
StartSoloIdle(item);
}
ResetCurrentIdle();
if (!runnableGames.Any(x => x.App.Process != null))
if (!IdleGameList.Any(x => x.App.Process != null))
StartIdle();
}
@@ -786,7 +682,7 @@ public sealed partial class IdleCardPageViewModel
{
return;
}
if (GetRunnableCardsRemaining() == 0) // 如果挂卡列表可挂卡数量为0,重新获取徽章数据挂卡
if (IdleGameList.Sum(s => s.Badge.CardsRemaining) == 0) // 如果挂卡列表可挂卡数量为0,重新获取徽章数据挂卡
{
if (IsReloaded == false)
{
@@ -857,12 +753,8 @@ public sealed partial class IdleCardPageViewModel
uint? nextIdleAppId = null;
if (CurrentIdle != null)
{
var runnableBeforeRefresh = GetRunnableIdleGames();
currentIdleAppId = CurrentIdle.AppId;
var currentIdleIndex = runnableBeforeRefresh.FindIndex(x => x.AppId == currentIdleAppId.Value);
nextIdleAppId = (currentIdleIndex >= 0 && (runnableBeforeRefresh.Count - 1) >= (currentIdleIndex + 1))
? runnableBeforeRefresh[currentIdleIndex + 1].AppId
: null;
nextIdleAppId = (IdleGameList.Count - 1) >= (++CurrentIdleIndex) ? IdleGameList[CurrentIdleIndex].AppId : null;
}
StopIdle();
@@ -871,25 +763,24 @@ public sealed partial class IdleCardPageViewModel
ResetCurrentIdle();
IdleApp? idleApp = null;
var runnableGames = GetRunnableIdleGames();
if (currentIdleAppId.HasValue)
{
if (!Badges.Any(x => x.AppId == currentIdleAppId && x.CardsRemaining > 0)) // 当前游戏挂卡完成
{
if (nextIdleAppId.HasValue) // 挂卡下一个游戏
idleApp = runnableGames.Where(x => x.AppId == nextIdleAppId).FirstOrDefault();
idleApp = IdleGameList.Where(x => x.AppId == nextIdleAppId).FirstOrDefault();
}
else // 继续挂卡当前游戏
idleApp = runnableGames.Where(x => x.AppId == currentIdleAppId).FirstOrDefault();
idleApp = IdleGameList.Where(x => x.AppId == currentIdleAppId).First();
}
var isMultipleIdle = runnableGames.Count(x => x.App.Process != null) > 1;
var isMultipleIdle = IdleGameList.Count(x => x.App.Process != null) > 1;
if (isMultipleIdle || idleApp == null) // 重头开始挂卡
StartIdle();
else
{
CurrentIdleIndex = runnableGames.IndexOf(idleApp);
CurrentIdleIndex = IdleGameList.IndexOf(idleApp);
StartSoloIdle(idleApp);
}
ChangeRunTxt();
@@ -13,8 +13,6 @@ public sealed partial class IdleCardPageViewModel : TabItemViewModel
/// </summary>
public ICommand PriorityRunIdle { get; }
public ICommand ToggleBlacklistIdle { get; }
public ICommand LoginSteamCommand { get; }
public ICommand IdleRunStartOrStop { get; }
@@ -83,18 +81,6 @@ public sealed partial class IdleCardPageViewModel : TabItemViewModel
[Reactive]
public decimal TotalCardsAvgPrice { get; set; }
[Reactive]
public bool HasPrivateGameExcluded { get; set; }
[Reactive]
public int ExcludedPrivateGameCount { get; set; }
[Reactive]
public int ExcludedBlacklistGameCount { get; set; }
[Reactive]
public string PrivateGameExcludeTip { get; set; } = string.Empty;
/// <summary>
/// 当前挂卡游戏
/// </summary>
@@ -28,20 +28,7 @@
Command="{Binding $parent[spp:PageBase].((spp:IdleCardPageViewModel)DataContext).PriorityRunIdle}"
CommandParameter="{Binding}"
IconSource="Play"
IsEnabled="{Binding !IsExcludedFromIdle}"
Text="优先运行此游戏" />
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:IdleCardPageViewModel)DataContext).ToggleBlacklistIdle}"
CommandParameter="{Binding}"
IconSource="Hide"
IsVisible="{Binding !IsBlacklisted}"
Text="移入挂卡黑名单" />
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:IdleCardPageViewModel)DataContext).ToggleBlacklistIdle}"
CommandParameter="{Binding}"
IconSource="Hide"
IsVisible="{Binding IsBlacklisted}"
Text="移出挂卡黑名单" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:IdleCardPageViewModel)DataContext).NavAppToSteamViewCommand}"
@@ -566,41 +553,28 @@
<spp:AppItem.Status>
<Border Classes="Status">
<Panel>
<WrapPanel IsVisible="{Binding IsExcludedFromIdle}">
<WrapPanel IsVisible="{Binding App.Process, Converter={StaticResource IsNullConverter}, ConverterParameter=invert, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#FAAD14" />
Fill="#47A42B" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding ExcludedReasonText}" />
Text="{Binding Path=Res.GameList_RunState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<WrapPanel IsVisible="{Binding App.Process, Converter={StaticResource IsNullConverter}, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#FF4F58" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Path=Res.GameList_StopState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<Panel IsVisible="{Binding !IsExcludedFromIdle}">
<WrapPanel IsVisible="{Binding App.Process, Converter={StaticResource IsNullConverter}, ConverterParameter=invert, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#47A42B" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Path=Res.GameList_RunState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<WrapPanel IsVisible="{Binding App.Process, Converter={StaticResource IsNullConverter}, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#FF4F58" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Path=Res.GameList_StopState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
</Panel>
</Panel>
</Border>
</spp:AppItem.Status>
@@ -729,12 +703,6 @@
</DataTemplate>
</ui:ItemsRepeater.ItemTemplate>
</ui:ItemsRepeater>
<TextBlock
Margin="0,0,0,10"
HorizontalAlignment="Left"
Foreground="#FAAD14"
IsVisible="{Binding HasPrivateGameExcluded}"
Text="{Binding PrivateGameExcludeTip}" />
</Panel>
</spp:OptionsDisplayItem.Content>
</spp:OptionsDisplayItem>
@@ -758,11 +758,6 @@ $"""
{
var libFileName = GetLibraryFileName(libraryName, platform);
MoveNativeLibrary(libFileName);
if (libFileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
{
var pdbFileName = $"{libFileName[..^4]}.pdb";
MoveNativeLibrary(pdbFileName);
}
}
switch (platform)
{
@@ -1234,7 +1229,6 @@ publish -c {0} -p:OutputType={1} -p:PublishDir=bin\{0}\Publish\win-any -p:Publis
arg.IsDebug = isDebug;
bool isWindows = false;
bool isMacOS = false;
switch (platform)
{
case Platform.Windows:
@@ -1242,9 +1236,6 @@ publish -c {0} -p:OutputType={1} -p:PublishDir=bin\{0}\Publish\win-any -p:Publis
case Platform.WinUI:
isWindows = true;
break;
case Platform.Apple:
isMacOS = true;
break;
}
arg.Framework = $"net{Environment.Version.Major}.{Environment.Version.Minor}";
@@ -1252,14 +1243,7 @@ publish -c {0} -p:OutputType={1} -p:PublishDir=bin\{0}\Publish\win-any -p:Publis
arg.SingleFile = true;
arg.ReadyToRun = false;
arg.Trimmed = false;
if (isMacOS)
{
arg.SelfContained = true;
}
else
{
arg.SelfContained = false;
}
arg.SelfContained = false;
if (isWindows)
{
//arg.Framework = $"net{Environment.Version.Major}.{Environment.Version.Minor}-windows{windowssdkver}";
@@ -344,8 +344,8 @@ $"""
string? pfxFilePath = null,
string? workingDirectory = null)
{
//if (pfxFilePath != pfxFilePath_MSStore_CodeSigning)
// return;
if (pfxFilePath != pfxFilePath_MSStore_CodeSigning)
return;
ProcessStartInfo psi;
switch (pfxFilePath)
@@ -366,42 +366,35 @@ sign /fd SHA256 /sha1 {pfxFilePath_HSM_CodeSigning} /tr {timestamp_url} /td SHA2
default:
{
pfxFilePath ??= pfxFilePath_BeyondDimension_CodeSigning;
if (!File.Exists(pfxFilePath))
{
// 无证书文件则跳过
return;
}
var pwdTxtPath = $"{pfxFilePath}.txt";
string? pwdS = null;
if (File.Exists(pwdTxtPath))
if (!File.Exists(pwdTxtPath))
{
var parentDirPath = Path.GetDirectoryName(pfxFilePath);
parentDirPath.ThrowIsNull();
var optionalEntropy = File.ReadAllBytes(Path.Combine(parentDirPath, "optionalEntropy.txt"));
optionalEntropy.ThrowIsNull();
var pwd = File.ReadAllBytes(pwdTxtPath);
#pragma warning disable CA1416 // 验证平台兼容性
pwd = ProtectedData.Unprotect(pwd,
optionalEntropy,
DataProtectionScope.LocalMachine);
#pragma warning restore CA1416 // 验证平台兼容性
pwdS = Encoding.UTF8.GetString(pwd);
if (force_sign) throw new FileNotFoundException(null, pwdTxtPath);
return; // 文件不存在则跳过代码签名
}
var parentDirPath = Path.GetDirectoryName(pfxFilePath);
parentDirPath.ThrowIsNull();
var optionalEntropy = File.ReadAllBytes(Path.Combine(parentDirPath, "optionalEntropy.txt"));
optionalEntropy.ThrowIsNull();
var pwd = File.ReadAllBytes(pwdTxtPath);
#pragma warning disable CA1416 // 验证平台兼容性
pwd = ProtectedData.Unprotect(pwd,
optionalEntropy,
DataProtectionScope.LocalMachine);
#pragma warning restore CA1416 // 验证平台兼容性
var pwdS = Encoding.UTF8.GetString(pwd);
psi = new ProcessStartInfo
{
FileName = GetSignToolPath(),
UseShellExecute = false,
Arguments =
$"""
sign /a /fd SHA256 /f "{pfxFilePath}"{(string.IsNullOrEmpty(pwdS) ? null : $" /p \"{pwdS}\"")} /tr {timestamp_url} /td SHA256 {fileName}
""",
$"""
sign /a /fd SHA256 /f "{pfxFilePath}" /p "{pwdS}" /tr {timestamp_url} /td SHA256 {fileName}
""",
};
}
break;
@@ -13,14 +13,6 @@
"commandName": "Project",
"commandLineArgs": "run --rids win-x64 --stm-upload"
},
"Publish(macos-arm64) Debug": {
"commandName": "Project",
"commandLineArgs": "run --rids osx-arm64 --debug"
},
"Publish(macos-arm64)": {
"commandName": "Project",
"commandLineArgs": "run --rids osx-arm64"
},
"Publish Setup(win-x64 hsm-sign)": {
"commandName": "Project",
"commandLineArgs": "nsis --rids win-x64 --hsm-sign --t latest"
@@ -109,16 +109,16 @@ public static class SendSmsUIHelper
return response;
}
//public static bool CanSubmit(this IViewModel i)
//{
// if (!i.SendSmsCodeSuccess)
// {
// Toast.Show(ToastIcon.Error, AppResources.User_SMSCode_Error);
// return false;
// }
public static bool CanSubmit(this IViewModel i)
{
if (!i.SendSmsCodeSuccess)
{
Toast.Show(ToastIcon.Error, AppResources.User_SMSCode_Error);
return false;
}
// return true;
//}
return true;
}
/// <summary>
/// 移除相关委托,在 View 层销毁时调用,因平台 View 层实现与关联 ViewModel 可能不一致,所以不能在 ViewModel 释放时调用
@@ -538,10 +538,10 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
{
if (!disposedValue)
{
//if (moduleNames.Count != 0)
//{
// await ExitModules(moduleNames);
//}
if (moduleNames.Any())
{
await ExitModules(moduleNames);
}
// 释放托管状态(托管对象)
try
@@ -289,15 +289,13 @@ public sealed class UserService : ReactiveObject
if (avatarUri != null)
{
var avatarStream = await ImageSource.GetAsync(avatarUri, isCircle: false, cache: true);
if (avatarStream != null)
AvatarPath = avatarStream;
//if (avatarStream?.Stream != null)
//{
// var avatarPath = avatarStream.Clone();
// avatarPath.Circle = true;
// AvatarPath = avatarPath;
//}
//CurrentSteamUser.AvatarStream = avatarStream;
if (avatarStream?.Stream != null)
{
var avatarPath = avatarStream.Clone();
avatarPath.Circle = true;
AvatarPath = avatarPath;
}
}
return true;
}
@@ -383,42 +383,29 @@ public sealed class SteamConnectService
if (apps.Any_Nullable())
{
SteamApps.AddOrUpdate(apps);
ApplyModifiedApps(ISteamService.Instance.GetModifiedApps());
}
}
public int ApplyModifiedApps(IEnumerable<ModifiedApp>? modifiedApps)
{
var appliedCount = 0;
if (!modifiedApps.Any_Nullable())
{
return appliedCount;
}
foreach (var modifiedApp in modifiedApps)
{
modifiedApp.ReadChanges();
if (modifiedApp.Changes == null)
var modifiedApps = ISteamService.Instance.GetModifiedApps();
if (modifiedApps.Any_Nullable())
{
continue;
}
foreach (var modifiedApp in modifiedApps)
{
modifiedApp.ReadChanges();
var optional = SteamApps.Lookup(modifiedApp.AppId);
if (!optional.HasValue)
{
continue;
if (modifiedApp.Changes != null)
{
var optional = SteamApps.Lookup(modifiedApp.AppId);
if (optional.HasValue)
{
var value = optional.Value;
value.ExtractReaderProperty(modifiedApp.Changes);
value.OriginalData = modifiedApp.OriginalData;
value.IsEdited = true;
SteamApps.Refresh(value);
}
}
}
}
var value = optional.Value;
value.ExtractReaderProperty(modifiedApp.Changes);
value.OriginalData = modifiedApp.OriginalData;
value.IsEdited = true;
SteamApps.Refresh(value);
appliedCount++;
}
return appliedCount;
}
public async void InitializeGameList()
@@ -191,21 +191,15 @@ partial class Startup // 自定义控制台命令参数
{
Description = "打开云存档管理窗口",
};
var app_silence = new Option<bool>("-silence")
{
DefaultValueFactory = _ => false,
Description = "挂运行服务,不加载窗口,内存占用更小",
};
var run_SteamApp = new Command("app", "运行 Steam 应用")
{
app_id, app_achievement, app_cloudmanager, app_silence,
app_id, app_achievement, app_cloudmanager,
};
run_SteamApp.SetAction(async parseResult =>
{
var id = parseResult.GetValue(app_id);
var achievement = parseResult.GetValue(app_achievement);
var cloudmanager = parseResult.GetValue(app_cloudmanager);
var silence = parseResult.GetValue(app_silence);
int exitCode = default;
if (id <= 0)
-4
View File
@@ -527,11 +527,7 @@ Failed to initialize FileSystem, access denied.
try
{
#if MACOS
Ioc.Dispose();
#else
await Ioc.DisposeAsync();
#endif
}
catch (Exception ex)
{
@@ -56,7 +56,7 @@ public partial class LoginOrRegisterWindowViewModel : WindowViewModel, SendSmsUI
async Task SubmitAsync()
{
if (IsLoading /*|| !this.CanSubmit()*/) return;
if (IsLoading || !this.CanSubmit()) return;
var request = new LoginOrRegisterRequest
{
@@ -64,24 +64,16 @@ public partial class LoginOrRegisterWindowViewModel : WindowViewModel, SendSmsUI
SmsCode = SmsCode
};
IsLoading = true;
try
{
var response = await IMicroServiceClient.Instance.Account.LoginOrRegister(request);
if (response.IsSuccess)
{
await SuccessAsync(response.Content!, Close);
return;
}
}
catch (Exception ex)
var response = await IMicroServiceClient.Instance.Account.LoginOrRegister(request);
if (response.IsSuccess)
{
Toast.Show(ToastIcon.Error, ex.GetAllMessage());
}
finally
{
IsLoading = false;
await SuccessAsync(response.Content!, Close);
return;
}
IsLoading = false;
}
async void OpenHyperlink_(string parameter)
@@ -32,8 +32,8 @@ public sealed class HttpReverseProxyMiddlewareUnitTest
Assert.That(position, Is.GreaterThan(0));
var html_start = buffer[..position];
var script_xml_start = "<script type=\"text/javascript\" src=\"/.watt-toolkit-inject/"u8.ToArray();
var script_xml_end = ".js\"></script>"u8.ToArray();
var script_xml_start = "<script type=\"text/javascript\" src=\"https://local.steampp.net/"u8.ToArray();
var script_xml_end = "\"></script>"u8.ToArray();
using var s = new MemoryStream();
s.Write(html_start);
@@ -46,55 +46,4 @@ public sealed class HttpReverseProxyMiddlewareUnitTest
var new_html = encoding.GetString(s.ToArray());
TestContext.WriteLine(new_html);
}
[Test]
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScript()
{
var html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<script src="/first.js"></script>
<script src="/runtime.js"></script>
</body>
</html>
""";
var buffer = Encoding.UTF8.GetBytes(html);
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
Assert.That(ok, Is.True);
var expected = html.LastIndexOf("<script src=\"/runtime.js\">", StringComparison.OrdinalIgnoreCase);
Assert.That(position, Is.EqualTo(expected));
}
[Test]
public void TestFindScriptInjectInsertPositionForGithub_BeforeLastScriptWithSrc()
{
var html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="/first.js"></script>
<script>window.__INLINE__ = true;</script>
<script src="/runtime.js" data-rspack="@github-ui/github-ui:runtime"></script>
<script>window.__AFTER_RUNTIME__ = true;</script>
</body>
</html>
""";
var buffer = Encoding.UTF8.GetBytes(html);
var ok = HttpReverseProxyMiddleware.FindScriptInjectInsertPositionForGithub(buffer, Encoding.UTF8, out _, out var position);
Assert.That(ok, Is.True);
var expected = html.LastIndexOf("<script src=\"/runtime.js\"", StringComparison.OrdinalIgnoreCase);
Assert.That(position, Is.EqualTo(expected));
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<DotNet_Version>10.0</DotNet_Version>
<DotNet_Version>11.0</DotNet_Version>
<NoWarn>$(NoWarn);NU1507;1591;SA1612;IL2026;IL2091</NoWarn>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
+5 -11
View File
@@ -35,21 +35,15 @@ public static partial class ProjectUtils
{
#pragma warning disable IDE0079 // 请删除不必要的忽略
#pragma warning disable SA1003 // Symbols should be spaced correctly
var slnfiles =
if (!
#pragma warning restore SA1003 // Symbols should be spaced correctly
#pragma warning restore IDE0079 // 请删除不必要的忽略
#if NET35
Directory.GetFiles
Directory.GetFiles
#else
Directory.EnumerateFiles
#endif
(path, "*.sln")
.Concat(
#if NET35
Directory.GetFiles
#else
Directory.EnumerateFiles
#endif
(path, "*.slnx"));
if (!slnfiles.Any())
(path, "*.sln").Any())
{
var parent = Directory.GetParent(path);
if (parent == null)