diff --git a/BedrockLauncher.Core/BedrockCore.cs b/BedrockLauncher.Core/BedrockCore.cs
index 374aa9c..894fa1b 100644
--- a/BedrockLauncher.Core/BedrockCore.cs
+++ b/BedrockLauncher.Core/BedrockCore.cs
@@ -51,8 +51,7 @@ public BedrockCore(CoreOptions options)
///
public async Task InitAsync()
{
- await Task.Run((() =>
- {
+
if (Options.IsAutoOpenDevelopment)
{
if (!GetWindowsDevelopmentState())
@@ -69,9 +68,14 @@ await Task.Run((() =>
VCRuntimeHelper.CompleteVCRuntimeAsync(RuntimeInformation.OSArchitecture).Wait();
}
}
- }));
- }
+ if (Options.IsAutoCompleteGameInput)
+ {
+ await AutoCompleteGameInput();
+ }
+
+ }
+
///
/// Get Windows Development state
///
@@ -239,11 +243,11 @@ await ZipExtractor.ExtractWithProgressAsync(options.FileFullPath, options.Instal
return null;
}
///
- /// Starts the Minecraft game process based on the specified launch options
+ /// Launch the Minecraft game process based on the specified launch options
///
/// The launch options containing game folder, arguments, and build type
/// The Process object representing the launched game instance
- public async Task StartGameAsync(LaunchOptions options)
+ public async Task LaunchGameAsync(LaunchOptions options)
{
var process = new Process();
if (options.MinecraftBuildType == MinecraftBuildTypeVersion.GDK)
@@ -254,6 +258,7 @@ public async Task StartGameAsync(LaunchOptions options)
info.Arguments = options.LaunchArgs;
info.UseShellExecute = false;
info.CreateNoWindow = true;
+ info.WorkingDirectory = options.GameFolder;
process.StartInfo = info;
process.Start();
options.Progress?.Report(LaunchState.Launched);
@@ -323,7 +328,7 @@ public async Task StartGameAsync(LaunchOptions options)
{
TargetApplicationPackageFamilyName = packageFamily
};
- if (options.LaunchArgs != null)
+ if (options?.LaunchArgs == string.Empty)
{
await Launcher.LaunchUriAsync(new Uri(options.LaunchArgs), options_st);
}
@@ -399,4 +404,21 @@ public async Task GetPackageUri(BuildInfo buildInfo,Architecture device
throw new BedrockCoreNoAvailbaleVersionUri("There is no available Uri to download");
return await GetPackageUriInside(find.MetaData.Last());
}
+ ///
+ /// Ensures that the GameInput runtime is installed on the system, installing it if necessary.
+ ///
+ /// This method checks for the presence of the GameInput runtime using its MSI product identifier. If
+ /// the runtime is not installed, it initiates the installation process. Callers can await the returned task to ensure
+ /// the operation completes before proceeding.
+ /// A task that represents the asynchronous operation.
+ public async Task AutoCompleteGameInput()
+ {
+ var isMsiInstalled = MsiHelper.IsMsiProductInstalledByGuid("64d0ccb1-329e-d507-0886-47e53d59ae21");
+ if (!isMsiInstalled)
+ {
+ await VCRuntimeHelper.InstallGameInput();
+ }
+ return;
+ }
+
}
\ No newline at end of file
diff --git a/BedrockLauncher.Core/BedrockLauncher.Core.csproj b/BedrockLauncher.Core/BedrockLauncher.Core.csproj
index 3c839a7..2fd6634 100644
--- a/BedrockLauncher.Core/BedrockLauncher.Core.csproj
+++ b/BedrockLauncher.Core/BedrockLauncher.Core.csproj
@@ -20,11 +20,11 @@
true
- 2.0.0-rc.2-dev
+ 2.0.0-dev
- 2.0.0-rc.2
+ 2.0.0
diff --git a/BedrockLauncher.Core/CoreOption/CoreOptions.cs b/BedrockLauncher.Core/CoreOption/CoreOptions.cs
index 65f61b4..4579cbd 100644
--- a/BedrockLauncher.Core/CoreOption/CoreOptions.cs
+++ b/BedrockLauncher.Core/CoreOption/CoreOptions.cs
@@ -19,4 +19,8 @@ public class CoreOptions
/// Gets a value indicating whether MD5 checksum verification is enabled.
///
public bool IsCheckMD5 { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the input is treated as game input for auto-complete operations.
+ ///
+ public bool IsAutoCompleteGameInput { get; set; }
}
\ No newline at end of file
diff --git a/BedrockLauncher.Core/DependsComplete/VCRuntimeHelper.cs b/BedrockLauncher.Core/DependsComplete/VCRuntimeHelper.cs
index 39c3240..25a54d3 100644
--- a/BedrockLauncher.Core/DependsComplete/VCRuntimeHelper.cs
+++ b/BedrockLauncher.Core/DependsComplete/VCRuntimeHelper.cs
@@ -5,6 +5,7 @@
using System.Text;
using BedrockLauncher.Core.UwpRegister;
using Windows.Management.Deployment;
+using BedrockLauncher.Core.Utils;
namespace BedrockLauncher.Core.DependsComplete;
@@ -106,34 +107,6 @@ await UwpRegister.UwpRegister.AddAppxAsync(new DeploymentOptionsConfig
}
}
}
- [DllImport("msi.dll", CharSet = CharSet.Unicode)]
- static extern Int32 MsiInstallProduct(string szPackagePath, string szCommandLine);
-
- [DllImport("msi.dll", CharSet = CharSet.Unicode)]
- static extern Int32 MsiConfigureProduct(string szProduct, int iInstallLevel, int eInstallState);
-
- [DllImport("msi.dll", SetLastError = true)]
- static extern int MsiGetProductInfo(string productCode, string property,
- [Out] StringBuilder valueBuf, ref int len);
-
- ///
- /// Use Windows Installer API To Install MSI
- ///
- private static bool InstallUsingMsiApi(string msiPath)
- {
- try
- {
- string commandLine = "ACTION=INSTALL REBOOT=ReallySuppress UILevel=2";
-
- int result = MsiInstallProduct(msiPath, commandLine);
-
- return result == 0; // ERROR_SUCCESS
- }
- catch
- {
- return false;
- }
- }
public static async Task InstallGameInput()
{
try
@@ -149,7 +122,8 @@ async Task DownloadPackageAsync(string uri)
var packages = await DownloadPackageAsync(VCUri.GameInputRedist);
var fileName = Path.GetTempFileName()+".msi";
- _ = InstallUsingMsiApi(fileName);
+ await File.WriteAllBytesAsync(fileName, packages);
+ _ = MsiHelper.InstallMsiSilently(fileName);
}
}
catch
diff --git a/BedrockLauncher.Core/Utils/CheckUwp.cs b/BedrockLauncher.Core/Utils/CheckUwp.cs
new file mode 100644
index 0000000..2a5362f
--- /dev/null
+++ b/BedrockLauncher.Core/Utils/CheckUwp.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Windows.Management.Deployment;
+
+namespace BedrockLauncher.Core.Utils
+{
+ public static class CheckUwp
+ {
+ public static bool IsUwpPackageInstalled(string packageFamilyName)
+ {
+ if (string.IsNullOrWhiteSpace(packageFamilyName))
+ throw new ArgumentException("PackageFamilyName can't be empty", nameof(packageFamilyName));
+
+ try
+ {
+ var packageManager = new PackageManager();
+
+ var packages = packageManager.FindPackagesForUser(string.Empty);
+
+ foreach (var package in packages)
+ {
+ if (package.Id.FamilyName.Equals(packageFamilyName, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ catch
+ {
+ throw;
+ }
+ }
+ }
+}
diff --git a/BedrockLauncher.Core/Utils/MsiInstaller.cs b/BedrockLauncher.Core/Utils/MsiInstaller.cs
new file mode 100644
index 0000000..b2138ad
--- /dev/null
+++ b/BedrockLauncher.Core/Utils/MsiInstaller.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32;
+
+namespace BedrockLauncher.Core.Utils
+{
+ public static class MsiHelper
+ {
+ ///
+ /// Silently installs an MSI package
+ ///
+ /// Full path to the MSI file
+ /// Additional installation parameters
+ /// True if installation succeeded, false otherwise
+ public static int InstallMsiSilently(string msiFilePath, string additionalArgs = "")
+ {
+ try
+ {
+ // Validate that the file exists
+ if (!File.Exists(msiFilePath))
+ {
+ throw new IOException($"Error: MSI file not found '{msiFilePath}'");
+ }
+
+ // Use msiexec.exe to install
+ Process process = new Process();
+ process.StartInfo.FileName = "msiexec.exe";
+
+ // Key parameters:
+ // /i - Perform installation
+ // /qn - Completely silent, no UI
+ // /norestart - Do not restart after installation
+ // /l*v - Log verbose output (optional, for debugging)
+ string arguments = $"/i \"{msiFilePath}\" /qn /norestart {additionalArgs}";
+
+ process.StartInfo.Arguments = arguments;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true; // Don't create a window
+ process.StartInfo.RedirectStandardOutput = false; // No output needed
+
+ process.Start();
+ process.WaitForExit(); // Wait for installation to complete
+
+ // Check exit code
+ return process.ExitCode;
+
+ }
+ catch (Exception ex)
+ {
+ throw;
+ }
+ }
+ public static bool IsMsiProductInstalledByGuid(string productGuid)
+ {
+
+ string[] registryPaths = {
+ @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
+ @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
+ };
+
+ using (RegistryKey localMachine = Registry.LocalMachine)
+ {
+ foreach (string registryPath in registryPaths)
+ {
+ using (RegistryKey key = localMachine.OpenSubKey(registryPath))
+ {
+ if (key == null) continue;
+
+ foreach (string subKeyName in key.GetSubKeyNames())
+ {
+ using (RegistryKey subKey = key.OpenSubKey(subKeyName))
+ {
+ // 检查ProductCode
+ string productCode = subKey?.GetValue("ProductCode") as string;
+ if (!string.IsNullOrEmpty(productCode) &&
+ productCode.Equals(productGuid, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ // 或者检查UninstallString中是否包含ProductCode
+ string uninstallString = subKey?.GetValue("UninstallString") as string;
+ if (!string.IsNullOrEmpty(uninstallString) &&
+ uninstallString.Contains(productGuid, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
+}
diff --git a/BedrockLauncher.Core/VersionJsons/SoureJson.cs b/BedrockLauncher.Core/VersionJsons/SoureJson.cs
index a3acf4f..a9f2e1e 100644
--- a/BedrockLauncher.Core/VersionJsons/SoureJson.cs
+++ b/BedrockLauncher.Core/VersionJsons/SoureJson.cs
@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using System.Text.Json;
+using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using BedrockLauncher.Core;
using BedrockLauncher.Core.SoureGenerate;
@@ -10,22 +11,32 @@ public class BuildDatabase
[JsonExtensionData] public Dictionary ExtensionData { get; set; } = new();
- [JsonIgnore] public Dictionary Builds => GetBuildsFromExtensionData();
+ [JsonIgnore] public IAsyncEnumerable> Builds => GetBuildsFromExtensionData();
- private Dictionary GetBuildsFromExtensionData()
+ private async IAsyncEnumerable> GetBuildsFromExtensionData()
{
- var result = new Dictionary();
-
foreach (var (key, value) in ExtensionData)
+ {
if (value is JsonElement element && element.ValueKind == JsonValueKind.Object)
{
- var buildInfo = JsonSerializer.Deserialize(
- element.GetRawText(),
- BuildDatabaseContext.Default.DictionaryStringBuildInfo);
- if (buildInfo != null) result = buildInfo;
- }
+ foreach (var jsonProperty in element.EnumerateObject())
+ {
+ var buildInfo = JsonSerializer.Deserialize(
+ jsonProperty.Value.GetRawText(),
+ BuildDatabaseContext.Default.BuildInfo);
+
+ if (buildInfo != null)
+ {
+ yield return new KeyValuePair(jsonProperty.Name, buildInfo);
+ }
- return result;
+
+ await Task.Yield();
+ }
+
+
+ }
+ }
}
}
diff --git a/BedrockLauncher.Core/VersionJsons/VersionsHelper.cs b/BedrockLauncher.Core/VersionJsons/VersionsHelper.cs
index eb5d137..bcfb4d6 100644
--- a/BedrockLauncher.Core/VersionJsons/VersionsHelper.cs
+++ b/BedrockLauncher.Core/VersionJsons/VersionsHelper.cs
@@ -25,8 +25,20 @@ public static class VersionsHelper
{
using (var client = new HttpClient())
{
- var data = await client.GetStringAsync(httpAddress, cancellationToken);
- var builds = JsonSerializer.Deserialize(data, BuildDatabaseContext.Default.BuildDatabase);
+ var response = await client.GetAsync(
+ httpAddress,
+ HttpCompletionOption.ResponseHeadersRead,
+ cancellationToken);
+
+ response.EnsureSuccessStatusCode();
+
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
+
+ var builds = await JsonSerializer.DeserializeAsync(
+ stream,
+ BuildDatabaseContext.Default.BuildDatabase,
+ cancellationToken);
+
return builds;
}
}
diff --git a/CoreTest/LaunchTest.cs b/CoreTest/LaunchTest.cs
index 0c427be..54e21cb 100644
--- a/CoreTest/LaunchTest.cs
+++ b/CoreTest/LaunchTest.cs
@@ -1,5 +1,6 @@
using BedrockLauncher.Core;
using BedrockLauncher.Core.CoreOption;
+using BedrockLauncher.Core.DependsComplete;
namespace CoreTest;
@@ -13,11 +14,24 @@ public void Test()
bedrockCore.InitAsync().Wait();
var launchOptions = new LaunchOptions()
{
- GameFolder = Path.GetFullPath("C:\\Users\\Administrator\\AppData\\Roaming\\RoundStudio\\BedrockBoot\\Bedrock_Data\\bedrock_versions\\1.21.120202"),
- MinecraftBuildType = MinecraftBuildTypeVersion.UWP,
- GameType = MinecraftGameTypeVersion.Preview,
- LaunchArgs = "minecraft://creator/?Editor=true"
+ GameFolder = Path.GetFullPath("D:\\Windows11\\newdesk\\Code\\bedrock_versions\\1.21.13101"),
+ MinecraftBuildType = MinecraftBuildTypeVersion.GDK,
+ GameType = MinecraftGameTypeVersion.Release,
+ LaunchArgs = null
};
- bedrockCore.StartGameAsync(launchOptions).Wait();
+ bedrockCore.LaunchGameAsync(launchOptions).Wait();
+ }
+ [TestMethod]
+ public void MsiTest()
+ {
+ var bedrockCore = new BedrockCore();
+ var installGameInput = VCRuntimeHelper.InstallGameInput();
+ installGameInput.Wait();
+ }
+ [TestMethod]
+ public void GameInputInstallTest()
+ {
+ var bedrockCore = new BedrockCore();
+ bedrockCore.AutoCompleteGameInput().Wait();
}
}
diff --git a/CoreTest/UriGetTest.cs b/CoreTest/UriGetTest.cs
index 00208cc..79ccfc1 100644
--- a/CoreTest/UriGetTest.cs
+++ b/CoreTest/UriGetTest.cs
@@ -8,12 +8,18 @@ namespace CoreTest;
public class UriGetTest
{
[TestMethod]
- public void Test()
+ public async Task TestAsync()
{
var bedrockCore = new BedrockCore();
bedrockCore.InitAsync().Wait();
var buildDatabaseAsync = VersionsHelper.GetBuildDatabaseAsync("https://data.mcappx.com/v2/bedrock.json").Result;
- var result = bedrockCore.GetPackageUri(buildDatabaseAsync.Builds["1.21.114"],Architecture.X64).Result;
+ BuildInfo build = null;
+ await foreach (var kvp in buildDatabaseAsync.Builds)
+ {
+ if (kvp.Key == "1.21.131")
+ build = kvp.Value;
+ }
+ var result = bedrockCore.GetPackageUri(build,Architecture.X64).Result;
Console.WriteLine(result);
}
}