From 0d98ac94ab62e283a9a5fce20982c1feeefca298 Mon Sep 17 00:00:00 2001 From: Dime Date: Sat, 23 May 2026 17:33:38 +0800 Subject: [PATCH 1/2] feat: Supports installation by non-administrators --- BedrockLauncher.Core.slnx | 1 + BedrockLauncher.Core/BedrockCore.cs | 19 ++--- .../UwpRegister/UwpRegister.cs | 70 ++++++++++++------- CoreTest/LaunchTest.cs | 6 +- TestProject/Program.cs | 24 +++++++ TestProject/TestProject.csproj | 15 ++++ 6 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 TestProject/Program.cs create mode 100644 TestProject/TestProject.csproj diff --git a/BedrockLauncher.Core.slnx b/BedrockLauncher.Core.slnx index b4a7a98..91566be 100644 --- a/BedrockLauncher.Core.slnx +++ b/BedrockLauncher.Core.slnx @@ -1,4 +1,5 @@ + diff --git a/BedrockLauncher.Core/BedrockCore.cs b/BedrockLauncher.Core/BedrockCore.cs index e62e9e6..69ca208 100644 --- a/BedrockLauncher.Core/BedrockCore.cs +++ b/BedrockLauncher.Core/BedrockCore.cs @@ -56,7 +56,7 @@ public async Task InitAsync() { if (!GetWindowsDevelopmentState()) { - OpenWindowsDevelopment(); + throw new BedrockCoreException("Windows Developer Mode is required for non-admin UWP loose package registration. Please enable Developer Mode in Windows settings."); } } @@ -100,18 +100,9 @@ public bool GetWindowsDevelopmentState() } /// - /// Enables Windows Developer Mode by modifying the system registry to allow development without a developer license. + /// Opens Windows Settings so the current user can enable Developer Mode without requiring this process to run elevated. /// - /// - /// Administrator privileges are required to modify the system registry. Enabling Developer Mode - /// allows the installation and testing of apps without a developer license. Use with caution, as modifying the - /// registry can affect system stability and security. - /// - /// true if Developer Mode is successfully enabled; otherwise, false. - /// - /// Thrown if the operation fails to enable Developer Mode, such as due to insufficient permissions or registry access - /// errors. - /// + /// true if the settings page was opened; otherwise, false. public bool OpenWindowsDevelopment() { try @@ -162,7 +153,7 @@ bool CheckVersion(string[] archli) }; isHasVCwin32 = CheckVersion(registryPaths); var packageManager = new PackageManager(); - var packages = packageManager.FindPackages(); + var packages = packageManager.FindPackagesForUser(string.Empty); var vcRuntimePackages = packages.Where(p => p.Id.Name.Contains("Microsoft.VCLibs.140") @@ -360,7 +351,7 @@ public async Task LaunchGameAsync(LaunchOptions options) } PackageManager packageManager = new PackageManager(); bool twice_launch = false; - foreach (var package in packageManager.FindPackages()) + foreach (var package in packageManager.FindPackagesForUser(string.Empty)) { if (package.Id.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) { diff --git a/BedrockLauncher.Core/UwpRegister/UwpRegister.cs b/BedrockLauncher.Core/UwpRegister/UwpRegister.cs index 910e2af..75dd4d4 100644 --- a/BedrockLauncher.Core/UwpRegister/UwpRegister.cs +++ b/BedrockLauncher.Core/UwpRegister/UwpRegister.cs @@ -43,7 +43,7 @@ public class DeploymentOptionsConfig public class UwpRegister { /// - /// Registers an appx package with flexible configuration (no admin required) + /// Registers an unpacked appx package for the current user in developer mode (no admin required). /// /// Deployment configuration options /// Deployment result containing operation status @@ -51,22 +51,17 @@ public class UwpRegister public static async Task RegisterAppxAsync(DeploymentOptionsConfig config) { ValidateConfig(config); - - // 处理包路径,确保是文件夹路径 - string packageFolderPath = config.PackagePath; - if (packageFolderPath.StartsWith("file://")) - { - packageFolderPath = new Uri(packageFolderPath).LocalPath; - } - - // 移除签名文件(如果存在) + + var packageUri = GetPackageUri(config.PackagePath); + var packageFolderPath = GetPackageFolderPath(packageUri); + RemoveSignatureFile(packageFolderPath); var manager = new PackageManager(); var asyncOperation = manager.RegisterPackageAsync( - new Uri(config.PackagePath), + packageUri, null, - config.DeploymentOptions | DeploymentOptions.DevelopmentMode); + ToCurrentUserDevelopmentOptions(config.DeploymentOptions)); return await ExecuteWithTimeout(asyncOperation, config); } @@ -80,26 +75,44 @@ public static async Task RegisterAppxAsync(DeploymentOptionsCo public static async Task AddAppxAsync(DeploymentOptionsConfig config) { ValidateConfig(config); - - // 处理包路径,确保是文件夹路径 - string packageFolderPath = config.PackagePath; - if (packageFolderPath.StartsWith("file://")) - { - packageFolderPath = new Uri(packageFolderPath).LocalPath; - } - - // 移除签名文件(如果存在) + + var packageUri = GetPackageUri(config.PackagePath); + var packageFolderPath = GetPackageFolderPath(packageUri); + RemoveSignatureFile(packageFolderPath); var packageManager = new PackageManager(); var asyncOperation = packageManager.AddPackageAsync( - new Uri(config.PackagePath), + packageUri, null, - config.DeploymentOptions | DeploymentOptions.DevelopmentMode); + ToCurrentUserDevelopmentOptions(config.DeploymentOptions)); return await ExecuteWithTimeout(asyncOperation, config); } + private static Uri GetPackageUri(string packagePath) + { + if (Uri.TryCreate(packagePath, UriKind.Absolute, out var uri) && uri.IsFile) + { + return uri; + } + + return new Uri(Path.GetFullPath(packagePath)); + } + + private static string GetPackageFolderPath(Uri packageUri) + { + var localPath = packageUri.LocalPath; + return File.Exists(localPath) + ? Path.GetDirectoryName(localPath) ?? localPath + : localPath; + } + + private static DeploymentOptions ToCurrentUserDevelopmentOptions(DeploymentOptions options) + { + return options | DeploymentOptions.DevelopmentMode; + } + /// /// Removes the signature file from the package folder /// @@ -141,7 +154,7 @@ public static bool CheckForPackageVersion(string packageName, string version) { PackageManager packageManager = new PackageManager(); - foreach (var package in packageManager.FindPackages()) + foreach (var package in packageManager.FindPackagesForUser(string.Empty)) { if (package.Id.Name == packageName) { @@ -164,7 +177,7 @@ public static bool IsPackageInstalled(string packageName) PackageManager packageManager = new PackageManager(); - foreach (var package in packageManager.FindPackages()) + foreach (var package in packageManager.FindPackagesForUser(string.Empty)) { if (package.Id.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase)) { @@ -180,8 +193,11 @@ private static void ValidateConfig(DeploymentOptionsConfig config) if (string.IsNullOrWhiteSpace(config.PackagePath)) throw new ArgumentException("Package path cannot be null or empty", nameof(config)); - if (!Uri.TryCreate(config.PackagePath, UriKind.Absolute, out _)) - throw new ArgumentException("Package path must be a valid absolute URI", nameof(config)); + if (Uri.TryCreate(config.PackagePath, UriKind.Absolute, out var uri) && uri.IsFile) + return; + + if (!Path.IsPathFullyQualified(config.PackagePath)) + throw new ArgumentException("Package path must be an absolute file path or file URI", nameof(config)); } } } \ No newline at end of file diff --git a/CoreTest/LaunchTest.cs b/CoreTest/LaunchTest.cs index f398291..ae48030 100644 --- a/CoreTest/LaunchTest.cs +++ b/CoreTest/LaunchTest.cs @@ -16,9 +16,9 @@ public void Test() bedrockCore.InitAsync().Wait(); var launchOptions = new LaunchOptions() { - GameFolder = Path.GetFullPath(@"E:\test2\plugins\Glacie\bedrock_versions\1.26.2"), - MinecraftBuildType = MinecraftBuildTypeVersion.GDK, - GameType = MinecraftGameTypeVersion.Release, + GameFolder = Path.GetFullPath(@"D:\BedrockBoot\bedrock_versions\1.21.12020"), + MinecraftBuildType = MinecraftBuildTypeVersion.UWP, + GameType = MinecraftGameTypeVersion.Preview, LaunchArgs = null }; var process = bedrockCore.LaunchGameAsync(launchOptions).Result; diff --git a/TestProject/Program.cs b/TestProject/Program.cs new file mode 100644 index 0000000..592d848 --- /dev/null +++ b/TestProject/Program.cs @@ -0,0 +1,24 @@ +using BedrockLauncher.Core; +using BedrockLauncher.Core.CoreOption; +using System.Diagnostics; + +namespace TestProject +{ + internal class Program + { + static void Main(string[] args) + { + var bedrockCore = new BedrockCore(); + bedrockCore.InitAsync().Wait(); + var launchOptions = new LaunchOptions() + { + GameFolder = Path.GetFullPath(@"E:\Bedrock\bedrock_versions\1.21.11401"), + MinecraftBuildType = MinecraftBuildTypeVersion.UWP, + GameType = MinecraftGameTypeVersion.Release, + LaunchArgs = null + }; + var process = bedrockCore.LaunchGameAsync(launchOptions).Result; + Console.WriteLine(process.Id); + } + } +} diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj new file mode 100644 index 0000000..910364a --- /dev/null +++ b/TestProject/TestProject.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0-windows10.0.19041.0 + enable + enable + 10.0.19041.0 + + + + + + + From c905ae3a52953e4c52764e7d3a4033709a30a068 Mon Sep 17 00:00:00 2001 From: Dime Date: Sat, 23 May 2026 17:35:38 +0800 Subject: [PATCH 2/2] chore: update version --- BedrockLauncher.Core/BedrockLauncher.Core.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BedrockLauncher.Core/BedrockLauncher.Core.csproj b/BedrockLauncher.Core/BedrockLauncher.Core.csproj index 4a7bcc7..59a717a 100644 --- a/BedrockLauncher.Core/BedrockLauncher.Core.csproj +++ b/BedrockLauncher.Core/BedrockLauncher.Core.csproj @@ -20,11 +20,11 @@ true - 2.0.4.2 + 2.0.4.3-dev - 2.0.4.1 + 2.0.4.3