mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge branch 'main' into user-pfp-discord-rpc
This commit is contained in:
commit
b61adf1a79
@ -6,9 +6,6 @@ using System.Windows.Threading;
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
using Bloxstrap.Models.SettingTasks.Base;
|
using Bloxstrap.Models.SettingTasks.Base;
|
||||||
using Bloxstrap.UI.Elements.About.Pages;
|
|
||||||
using Bloxstrap.UI.Elements.About;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Bloxstrap
|
namespace Bloxstrap
|
||||||
{
|
{
|
||||||
@ -18,7 +15,11 @@ namespace Bloxstrap
|
|||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
public const string ProjectName = "Bloxstrap";
|
public const string ProjectName = "Bloxstrap";
|
||||||
|
public const string ProjectOwner = "pizzaboxer";
|
||||||
public const string ProjectRepository = "pizzaboxer/bloxstrap";
|
public const string ProjectRepository = "pizzaboxer/bloxstrap";
|
||||||
|
public const string ProjectDownloadLink = "https://bloxstrap.pizzaboxer.xyz";
|
||||||
|
public const string ProjectHelpLink = "https://github.com/pizzaboxer/bloxstrap/wiki";
|
||||||
|
public const string ProjectSupportLink = "https://github.com/pizzaboxer/bloxstrap/issues/new";
|
||||||
|
|
||||||
public const string RobloxPlayerAppName = "RobloxPlayerBeta";
|
public const string RobloxPlayerAppName = "RobloxPlayerBeta";
|
||||||
public const string RobloxStudioAppName = "RobloxStudioBeta";
|
public const string RobloxStudioAppName = "RobloxStudioBeta";
|
||||||
@ -106,6 +107,27 @@ namespace Bloxstrap
|
|||||||
Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
|
Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<GithubRelease?> GetLatestRelease()
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "App::GetLatestRelease";
|
||||||
|
|
||||||
|
GithubRelease? releaseInfo = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
|
||||||
|
|
||||||
|
if (releaseInfo is null || releaseInfo.Assets is null)
|
||||||
|
Logger.WriteLine(LOG_IDENT, "Encountered invalid data");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.WriteException(LOG_IDENT, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return releaseInfo;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "App::OnStartup";
|
const string LOG_IDENT = "App::OnStartup";
|
||||||
@ -224,7 +246,7 @@ namespace Bloxstrap
|
|||||||
Locale.Set(Settings.Prop.Locale);
|
Locale.Set(Settings.Prop.Locale);
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
if (!LaunchSettings.UninstallFlag.Active)
|
if (!LaunchSettings.BypassUpdateCheck)
|
||||||
Installer.HandleUpgrade();
|
Installer.HandleUpgrade();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,4 +1,17 @@
|
|||||||
using System.Windows;
|
// To debug the automatic updater:
|
||||||
|
// - Uncomment the definition below
|
||||||
|
// - Publish the executable
|
||||||
|
// - Launch the executable (click no when it asks you to upgrade)
|
||||||
|
// - Launch Roblox (for testing web launches, run it from the command prompt)
|
||||||
|
// - To re-test the same executable, delete it from the installation folder
|
||||||
|
|
||||||
|
// #define DEBUG_UPDATER
|
||||||
|
|
||||||
|
#if DEBUG_UPDATER
|
||||||
|
#warning "Automatic updater debugging is enabled"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
@ -152,9 +165,14 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
await RobloxDeployment.GetInfo(RobloxDeployment.DefaultChannel);
|
await RobloxDeployment.GetInfo(RobloxDeployment.DefaultChannel);
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG || DEBUG_UPDATER
|
||||||
if (App.Settings.Prop.CheckForUpdates)
|
if (App.Settings.Prop.CheckForUpdates && !App.LaunchSettings.UpgradeFlag.Active)
|
||||||
await CheckForUpdates();
|
{
|
||||||
|
bool updatePresent = await CheckForUpdates();
|
||||||
|
|
||||||
|
if (updatePresent)
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ensure only one instance of the bootstrapper is running at the time
|
// ensure only one instance of the bootstrapper is running at the time
|
||||||
@ -212,7 +230,9 @@ namespace Bloxstrap
|
|||||||
await mutex.ReleaseAsync();
|
await mutex.ReleaseAsync();
|
||||||
|
|
||||||
if (!App.LaunchSettings.NoLaunchFlag.Active && !_cancelFired)
|
if (!App.LaunchSettings.NoLaunchFlag.Active && !_cancelFired)
|
||||||
await StartRoblox();
|
StartRoblox();
|
||||||
|
|
||||||
|
Dialog?.CloseBootstrapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckLatestVersion()
|
private async Task CheckLatestVersion()
|
||||||
@ -273,12 +293,14 @@ namespace Bloxstrap
|
|||||||
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
|
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartRoblox()
|
private void StartRoblox()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "Bootstrapper::StartRoblox";
|
const string LOG_IDENT = "Bootstrapper::StartRoblox";
|
||||||
|
|
||||||
SetStatus(Strings.Bootstrapper_Status_Starting);
|
SetStatus(Strings.Bootstrapper_Status_Starting);
|
||||||
|
|
||||||
|
if (_launchMode == LaunchMode.Player)
|
||||||
|
{
|
||||||
if (App.Settings.Prop.ForceRobloxLanguage)
|
if (App.Settings.Prop.ForceRobloxLanguage)
|
||||||
{
|
{
|
||||||
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
|
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
|
||||||
@ -287,6 +309,12 @@ namespace Bloxstrap
|
|||||||
_launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase);
|
_launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(_launchCommandLine))
|
||||||
|
_launchCommandLine += " ";
|
||||||
|
|
||||||
|
_launchCommandLine += "-isInstallerLaunch";
|
||||||
|
}
|
||||||
|
|
||||||
var startInfo = new ProcessStartInfo()
|
var startInfo = new ProcessStartInfo()
|
||||||
{
|
{
|
||||||
FileName = _playerLocation,
|
FileName = _playerLocation,
|
||||||
@ -297,10 +325,11 @@ namespace Bloxstrap
|
|||||||
if (_launchMode == LaunchMode.StudioAuth)
|
if (_launchMode == LaunchMode.StudioAuth)
|
||||||
{
|
{
|
||||||
Process.Start(startInfo);
|
Process.Start(startInfo);
|
||||||
Dialog?.CloseBootstrapper();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent);
|
||||||
|
|
||||||
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
|
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
|
||||||
int gameClientPid;
|
int gameClientPid;
|
||||||
using (var gameClient = Process.Start(startInfo)!)
|
using (var gameClient = Process.Start(startInfo)!)
|
||||||
@ -308,20 +337,16 @@ namespace Bloxstrap
|
|||||||
gameClientPid = gameClient.Id;
|
gameClientPid = gameClient.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid})");
|
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid}), waiting for start event");
|
||||||
|
|
||||||
using (var startEvent = new SystemEvent(AppData.StartEvent))
|
if (!startEvent.WaitOne(TimeSpan.FromSeconds(10)))
|
||||||
{
|
{
|
||||||
// TODO: get rid of this
|
Frontend.ShowPlayerErrorDialog();
|
||||||
bool startEventFired = await startEvent.WaitForEvent();
|
|
||||||
|
|
||||||
startEvent.Close();
|
|
||||||
|
|
||||||
// TODO: this cannot silently exit like this
|
|
||||||
if (!startEventFired)
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Start event signalled");
|
||||||
|
|
||||||
var autoclosePids = new List<int>();
|
var autoclosePids = new List<int>();
|
||||||
|
|
||||||
// launch custom integrations now
|
// launch custom integrations now
|
||||||
@ -352,20 +377,16 @@ namespace Bloxstrap
|
|||||||
autoclosePids.Add(pid);
|
autoclosePids.Add(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var proclock = new InterProcessLock("Watcher"))
|
|
||||||
{
|
|
||||||
string args = gameClientPid.ToString();
|
string args = gameClientPid.ToString();
|
||||||
|
|
||||||
if (autoclosePids.Any())
|
if (autoclosePids.Any())
|
||||||
args += $";{String.Join(',', autoclosePids)}";
|
args += $";{String.Join(',', autoclosePids)}";
|
||||||
|
|
||||||
if (proclock.IsAcquired)
|
using (var ipl = new InterProcessLock("Watcher"))
|
||||||
|
{
|
||||||
|
if (ipl.IsAcquired)
|
||||||
Process.Start(Paths.Process, $"-watcher \"{args}\"");
|
Process.Start(Paths.Process, $"-watcher \"{args}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
// event fired, wait for 3 seconds then close
|
|
||||||
await Task.Delay(3000);
|
|
||||||
Dialog?.CloseBootstrapper();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelInstall()
|
public void CancelInstall()
|
||||||
@ -446,53 +467,56 @@ namespace Bloxstrap
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForUpdates()
|
private async Task<bool> CheckForUpdates()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "Bootstrapper::CheckForUpdates";
|
const string LOG_IDENT = "Bootstrapper::CheckForUpdates";
|
||||||
|
|
||||||
// don't update if there's another instance running (likely running in the background)
|
// don't update if there's another instance running (likely running in the background)
|
||||||
if (Process.GetProcessesByName(App.ProjectName).Count() > 1)
|
// i don't like this, but there isn't much better way of doing it /shrug
|
||||||
|
if (Process.GetProcessesByName(App.ProjectName).Length > 1)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"More than one Bloxstrap instance running, aborting update check");
|
App.Logger.WriteLine(LOG_IDENT, $"More than one Bloxstrap instance running, aborting update check");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Checking for updates...");
|
App.Logger.WriteLine(LOG_IDENT, "Checking for updates...");
|
||||||
|
|
||||||
GithubRelease? releaseInfo;
|
#if !DEBUG_UPDATER
|
||||||
|
var releaseInfo = await App.GetLatestRelease();
|
||||||
|
|
||||||
try
|
if (releaseInfo is null)
|
||||||
{
|
return false;
|
||||||
releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to fetch releases: {ex}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (releaseInfo is null || releaseInfo.Assets is null)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"No updates found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var versionComparison = Utilities.CompareVersions(App.Version, releaseInfo.TagName);
|
var versionComparison = Utilities.CompareVersions(App.Version, releaseInfo.TagName);
|
||||||
|
|
||||||
// check if we aren't using a deployed build, so we can update to one if a new version comes out
|
// check if we aren't using a deployed build, so we can update to one if a new version comes out
|
||||||
if (versionComparison == VersionComparison.Equal && App.IsProductionBuild || versionComparison == VersionComparison.GreaterThan)
|
if (App.IsProductionBuild && versionComparison == VersionComparison.Equal || versionComparison == VersionComparison.GreaterThan)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"No updates found");
|
App.Logger.WriteLine(LOG_IDENT, "No updates found");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string version = releaseInfo.TagName;
|
||||||
|
#else
|
||||||
|
string version = App.Version;
|
||||||
|
#endif
|
||||||
|
|
||||||
SetStatus(Strings.Bootstrapper_Status_UpgradingBloxstrap);
|
SetStatus(Strings.Bootstrapper_Status_UpgradingBloxstrap);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 64-bit is always the first option
|
#if DEBUG_UPDATER
|
||||||
GithubReleaseAsset asset = releaseInfo.Assets[0];
|
string downloadLocation = Path.Combine(Paths.TempUpdates, "Bloxstrap.exe");
|
||||||
string downloadLocation = Path.Combine(Paths.LocalAppData, "Temp", asset.Name);
|
|
||||||
|
Directory.CreateDirectory(Paths.TempUpdates);
|
||||||
|
|
||||||
|
File.Copy(Paths.Process, downloadLocation, true);
|
||||||
|
#else
|
||||||
|
var asset = releaseInfo.Assets![0];
|
||||||
|
|
||||||
|
string downloadLocation = Path.Combine(Paths.TempUpdates, asset.Name);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Paths.TempUpdates);
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Downloading {releaseInfo.TagName}...");
|
App.Logger.WriteLine(LOG_IDENT, $"Downloading {releaseInfo.TagName}...");
|
||||||
|
|
||||||
@ -500,25 +524,35 @@ namespace Bloxstrap
|
|||||||
{
|
{
|
||||||
var response = await App.HttpClient.GetAsync(asset.BrowserDownloadUrl);
|
var response = await App.HttpClient.GetAsync(asset.BrowserDownloadUrl);
|
||||||
|
|
||||||
await using var fileStream = new FileStream(downloadLocation, FileMode.CreateNew);
|
await using var fileStream = new FileStream(downloadLocation, FileMode.OpenOrCreate, FileAccess.Write);
|
||||||
await response.Content.CopyToAsync(fileStream);
|
await response.Content.CopyToAsync(fileStream);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Starting {releaseInfo.TagName}...");
|
App.Logger.WriteLine(LOG_IDENT, $"Starting {version}...");
|
||||||
|
|
||||||
ProcessStartInfo startInfo = new()
|
ProcessStartInfo startInfo = new()
|
||||||
{
|
{
|
||||||
FileName = downloadLocation,
|
FileName = downloadLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
startInfo.ArgumentList.Add("-upgrade");
|
||||||
|
|
||||||
foreach (string arg in App.LaunchSettings.Args)
|
foreach (string arg in App.LaunchSettings.Args)
|
||||||
startInfo.ArgumentList.Add(arg);
|
startInfo.ArgumentList.Add(arg);
|
||||||
|
|
||||||
|
if (_launchMode == LaunchMode.Player && !startInfo.ArgumentList.Contains("-player"))
|
||||||
|
startInfo.ArgumentList.Add("-player");
|
||||||
|
else if (_launchMode == LaunchMode.Studio && !startInfo.ArgumentList.Contains("-studio"))
|
||||||
|
startInfo.ArgumentList.Add("-studio");
|
||||||
|
|
||||||
App.Settings.Save();
|
App.Settings.Save();
|
||||||
|
|
||||||
|
new InterProcessLock("AutoUpdater");
|
||||||
|
|
||||||
Process.Start(startInfo);
|
Process.Start(startInfo);
|
||||||
|
|
||||||
App.Terminate();
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -526,10 +560,14 @@ namespace Bloxstrap
|
|||||||
App.Logger.WriteException(LOG_IDENT, ex);
|
App.Logger.WriteException(LOG_IDENT, ex);
|
||||||
|
|
||||||
Frontend.ShowMessageBox(
|
Frontend.ShowMessageBox(
|
||||||
string.Format(Strings.Bootstrapper_AutoUpdateFailed, releaseInfo.TagName),
|
string.Format(Strings.Bootstrapper_AutoUpdateFailed, version),
|
||||||
MessageBoxImage.Information
|
MessageBoxImage.Information
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Utilities.ShellExecute(App.ProjectDownloadLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -848,6 +886,7 @@ namespace Bloxstrap
|
|||||||
foreach (FontFace fontFace in fontFamilyData.Faces)
|
foreach (FontFace fontFace in fontFamilyData.Faces)
|
||||||
fontFace.AssetId = "rbxasset://fonts/CustomFont.ttf";
|
fontFace.AssetId = "rbxasset://fonts/CustomFont.ttf";
|
||||||
|
|
||||||
|
// TODO: writing on every launch is not necessary
|
||||||
File.WriteAllText(modFilepath, JsonSerializer.Serialize(fontFamilyData, new JsonSerializerOptions { WriteIndented = true }));
|
File.WriteAllText(modFilepath, JsonSerializer.Serialize(fontFamilyData, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,6 +938,8 @@ namespace Bloxstrap
|
|||||||
// the manifest is primarily here to keep track of what files have been
|
// the manifest is primarily here to keep track of what files have been
|
||||||
// deleted from the modifications folder, so that we know when to restore the original files from the downloaded packages
|
// deleted from the modifications folder, so that we know when to restore the original files from the downloaded packages
|
||||||
// now check for files that have been deleted from the mod folder according to the manifest
|
// now check for files that have been deleted from the mod folder according to the manifest
|
||||||
|
|
||||||
|
// TODO: this needs to extract the files from packages in bulk, this is way too slow
|
||||||
foreach (string fileLocation in App.State.Prop.ModManifest)
|
foreach (string fileLocation in App.State.Prop.ModManifest)
|
||||||
{
|
{
|
||||||
if (modFolderFiles.Contains(fileLocation))
|
if (modFolderFiles.Contains(fileLocation))
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
using System.DirectoryServices;
|
using System.Windows;
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Metadata.Ecma335;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using Bloxstrap.Resources;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace Bloxstrap
|
namespace Bloxstrap
|
||||||
@ -50,12 +45,13 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
uninstallKey.SetValue("InstallLocation", Paths.Base);
|
uninstallKey.SetValue("InstallLocation", Paths.Base);
|
||||||
uninstallKey.SetValue("NoRepair", 1);
|
uninstallKey.SetValue("NoRepair", 1);
|
||||||
uninstallKey.SetValue("Publisher", "pizzaboxer");
|
uninstallKey.SetValue("Publisher", App.ProjectOwner);
|
||||||
uninstallKey.SetValue("ModifyPath", $"\"{Paths.Application}\" -settings");
|
uninstallKey.SetValue("ModifyPath", $"\"{Paths.Application}\" -settings");
|
||||||
uninstallKey.SetValue("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet");
|
uninstallKey.SetValue("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet");
|
||||||
uninstallKey.SetValue("UninstallString", $"\"{Paths.Application}\" -uninstall");
|
uninstallKey.SetValue("UninstallString", $"\"{Paths.Application}\" -uninstall");
|
||||||
uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{App.ProjectRepository}");
|
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink);
|
||||||
uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{App.ProjectRepository}/releases/latest");
|
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink);
|
||||||
|
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// only register player, for the scenario where the user installs bloxstrap, closes it,
|
// only register player, for the scenario where the user installs bloxstrap, closes it,
|
||||||
@ -331,8 +327,9 @@ namespace Bloxstrap
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// 2.0.0 downloads updates to <BaseFolder>/Updates so lol
|
// 2.0.0 downloads updates to <BaseFolder>/Updates so lol
|
||||||
// TODO: 2.8.0 will download them to <Temp>/Bloxstrap/Updates
|
bool isAutoUpgrade = App.LaunchSettings.UpgradeFlag.Active
|
||||||
bool isAutoUpgrade = Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates")) || Paths.Process.StartsWith(Path.Combine(Paths.LocalAppData, "Temp"));
|
|| Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates"))
|
||||||
|
|| Paths.Process.StartsWith(Paths.Temp);
|
||||||
|
|
||||||
var existingVer = FileVersionInfo.GetVersionInfo(Paths.Application).ProductVersion;
|
var existingVer = FileVersionInfo.GetVersionInfo(Paths.Application).ProductVersion;
|
||||||
var currentVer = FileVersionInfo.GetVersionInfo(Paths.Process).ProductVersion;
|
var currentVer = FileVersionInfo.GetVersionInfo(Paths.Process).ProductVersion;
|
||||||
@ -353,7 +350,7 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// silently upgrade version if the command line flag is set or if we're launching from an auto update
|
// silently upgrade version if the command line flag is set or if we're launching from an auto update
|
||||||
if (!App.LaunchSettings.UpgradeFlag.Active && !isAutoUpgrade)
|
if (!isAutoUpgrade)
|
||||||
{
|
{
|
||||||
var result = Frontend.ShowMessageBox(
|
var result = Frontend.ShowMessageBox(
|
||||||
Strings.InstallChecker_VersionDifferentThanInstalled,
|
Strings.InstallChecker_VersionDifferentThanInstalled,
|
||||||
@ -365,41 +362,38 @@ namespace Bloxstrap
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Doing upgrade");
|
||||||
|
|
||||||
Filesystem.AssertReadOnly(Paths.Application);
|
Filesystem.AssertReadOnly(Paths.Application);
|
||||||
|
|
||||||
// TODO: make this use a mutex somehow
|
using (var ipl = new InterProcessLock("AutoUpdater", TimeSpan.FromSeconds(5)))
|
||||||
// yes, this is EXTREMELY hacky, but the updater process that launched the
|
|
||||||
// new version may still be open and so we have to wait for it to close
|
|
||||||
int attempts = 0;
|
|
||||||
while (attempts < 10)
|
|
||||||
{
|
{
|
||||||
attempts++;
|
if (!ipl.IsAcquired)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not obtain singleton mutex)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete(Paths.Application);
|
File.Copy(Paths.Process, Paths.Application, true);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (attempts == 1)
|
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not replace executable)");
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Waiting for write permissions to update version");
|
App.Logger.WriteException(LOG_IDENT, ex);
|
||||||
|
|
||||||
Thread.Sleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempts == 10)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not get write permissions after 5 seconds)");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Copy(Paths.Process, Paths.Application);
|
|
||||||
|
|
||||||
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
|
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
|
||||||
{
|
{
|
||||||
uninstallKey.SetValue("DisplayVersion", App.Version);
|
uninstallKey.SetValue("DisplayVersion", App.Version);
|
||||||
|
|
||||||
|
uninstallKey.SetValue("Publisher", App.ProjectOwner);
|
||||||
|
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink);
|
||||||
|
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink);
|
||||||
|
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update migrations
|
// update migrations
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace Bloxstrap.Integrations
|
using System.Web;
|
||||||
|
|
||||||
|
namespace Bloxstrap.Integrations
|
||||||
{
|
{
|
||||||
public class ActivityWatcher : IDisposable
|
public class ActivityWatcher : IDisposable
|
||||||
{
|
{
|
||||||
@ -46,6 +48,7 @@
|
|||||||
public string ActivityMachineAddress = "";
|
public string ActivityMachineAddress = "";
|
||||||
public bool ActivityMachineUDMUX = false;
|
public bool ActivityMachineUDMUX = false;
|
||||||
public bool ActivityIsTeleport = false;
|
public bool ActivityIsTeleport = false;
|
||||||
|
public string ActivityLaunchData = "";
|
||||||
public ServerType ActivityServerType = ServerType.Public;
|
public ServerType ActivityServerType = ServerType.Public;
|
||||||
|
|
||||||
public bool IsDisposed = false;
|
public bool IsDisposed = false;
|
||||||
@ -122,6 +125,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetActivityDeeplink()
|
||||||
|
{
|
||||||
|
string deeplink = $"roblox://experiences/start?placeId={ActivityPlaceId}&gameInstanceId={ActivityJobId}";
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(ActivityLaunchData))
|
||||||
|
deeplink += "&launchData=" + HttpUtility.UrlEncode(ActivityLaunchData);
|
||||||
|
|
||||||
|
return deeplink;
|
||||||
|
}
|
||||||
|
|
||||||
private void ReadLogEntry(string entry)
|
private void ReadLogEntry(string entry)
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "ActivityWatcher::ReadLogEntry";
|
const string LOG_IDENT = "ActivityWatcher::ReadLogEntry";
|
||||||
@ -238,6 +251,7 @@
|
|||||||
ActivityMachineAddress = "";
|
ActivityMachineAddress = "";
|
||||||
ActivityMachineUDMUX = false;
|
ActivityMachineUDMUX = false;
|
||||||
ActivityIsTeleport = false;
|
ActivityIsTeleport = false;
|
||||||
|
ActivityLaunchData = "";
|
||||||
ActivityServerType = ServerType.Public;
|
ActivityServerType = ServerType.Public;
|
||||||
ActivityUserId = "";
|
ActivityUserId = "";
|
||||||
|
|
||||||
@ -297,6 +311,35 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.Command == "SetLaunchData")
|
||||||
|
{
|
||||||
|
string? data;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = message.Data.Deserialize<string>();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data is null)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.Length > 200)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Data cannot be longer than 200 characters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityLaunchData = data;
|
||||||
|
}
|
||||||
|
|
||||||
OnRPCMessage?.Invoke(this, message);
|
OnRPCMessage?.Invoke(this, message);
|
||||||
|
|
||||||
LastRPCRequest = DateTime.Now;
|
LastRPCRequest = DateTime.Now;
|
||||||
|
@ -9,7 +9,7 @@ namespace Bloxstrap.Integrations
|
|||||||
|
|
||||||
private DiscordRPC.RichPresence? _currentPresence;
|
private DiscordRPC.RichPresence? _currentPresence;
|
||||||
private DiscordRPC.RichPresence? _currentPresenceCopy;
|
private DiscordRPC.RichPresence? _currentPresenceCopy;
|
||||||
private Message? _stashedRPCMessage;
|
private Queue<Message> _messageQueue = new();
|
||||||
|
|
||||||
private bool _visible = true;
|
private bool _visible = true;
|
||||||
private long _currentUniverseId;
|
private long _currentUniverseId;
|
||||||
@ -17,7 +17,7 @@ namespace Bloxstrap.Integrations
|
|||||||
|
|
||||||
public DiscordRichPresence(ActivityWatcher activityWatcher)
|
public DiscordRichPresence(ActivityWatcher activityWatcher)
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "DiscordRichPresence::DiscordRichPresence";
|
const string LOG_IDENT = "DiscordRichPresence";
|
||||||
|
|
||||||
_activityWatcher = activityWatcher;
|
_activityWatcher = activityWatcher;
|
||||||
|
|
||||||
@ -47,30 +47,35 @@ namespace Bloxstrap.Integrations
|
|||||||
_rpcClient.Initialize();
|
_rpcClient.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessRPCMessage(Message message)
|
public void ProcessRPCMessage(Message message, bool implicitUpdate = true)
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage";
|
const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage";
|
||||||
|
|
||||||
if (message.Command != "SetRichPresence")
|
if (message.Command != "SetRichPresence" && message.Command != "SetLaunchData")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_currentPresence is null || _currentPresenceCopy is null)
|
if (_currentPresence is null || _currentPresenceCopy is null)
|
||||||
{
|
{
|
||||||
if (_activityWatcher.ActivityInGame)
|
App.Logger.WriteLine(LOG_IDENT, "Presence is not set, enqueuing message");
|
||||||
{
|
_messageQueue.Enqueue(message);
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Presence is not yet set, but is currently in game, stashing presence set request");
|
|
||||||
_stashedRPCMessage = message;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Presence is not set, aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Models.BloxstrapRPC.RichPresence? presenceData;
|
|
||||||
|
|
||||||
// a lot of repeated code here, could this somehow be cleaned up?
|
// a lot of repeated code here, could this somehow be cleaned up?
|
||||||
|
|
||||||
|
if (message.Command == "SetLaunchData")
|
||||||
|
{
|
||||||
|
var buttonQuery = _currentPresence.Buttons.Where(x => x.Label == "Join server");
|
||||||
|
|
||||||
|
if (!buttonQuery.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
buttonQuery.First().Url = _activityWatcher.GetActivityDeeplink();
|
||||||
|
}
|
||||||
|
else if (message.Command == "SetRichPresence")
|
||||||
|
{
|
||||||
|
Models.BloxstrapRPC.RichPresence? presenceData;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
presenceData = message.Data.Deserialize<Models.BloxstrapRPC.RichPresence>();
|
presenceData = message.Data.Deserialize<Models.BloxstrapRPC.RichPresence>();
|
||||||
@ -158,7 +163,9 @@ namespace Bloxstrap.Integrations
|
|||||||
_currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText;
|
_currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (implicitUpdate)
|
||||||
UpdatePresence();
|
UpdatePresence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +190,7 @@ namespace Bloxstrap.Integrations
|
|||||||
App.Logger.WriteLine(LOG_IDENT, "Not in game, clearing presence");
|
App.Logger.WriteLine(LOG_IDENT, "Not in game, clearing presence");
|
||||||
|
|
||||||
_currentPresence = _currentPresenceCopy = null;
|
_currentPresence = _currentPresenceCopy = null;
|
||||||
_stashedRPCMessage = null;
|
_messageQueue.Clear();
|
||||||
|
|
||||||
UpdatePresence();
|
UpdatePresence();
|
||||||
return true;
|
return true;
|
||||||
@ -314,17 +321,13 @@ namespace Bloxstrap.Integrations
|
|||||||
// this is used for configuration from BloxstrapRPC
|
// this is used for configuration from BloxstrapRPC
|
||||||
_currentPresenceCopy = _currentPresence.Clone();
|
_currentPresenceCopy = _currentPresence.Clone();
|
||||||
|
|
||||||
// TODO: use queue for stashing messages
|
if (_messageQueue.Any())
|
||||||
if (_stashedRPCMessage is not null)
|
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Found stashed RPC message, invoking presence set command now");
|
App.Logger.WriteLine(LOG_IDENT, "Processing queued messages");
|
||||||
ProcessRPCMessage(_stashedRPCMessage);
|
ProcessRPCMessage(_messageQueue.Dequeue(), false);
|
||||||
_stashedRPCMessage = null;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
UpdatePresence();
|
UpdatePresence();
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
public LaunchFlag StudioFlag { get; } = new("studio");
|
public LaunchFlag StudioFlag { get; } = new("studio");
|
||||||
|
|
||||||
|
public bool BypassUpdateCheck => UninstallFlag.Active || WatcherFlag.Active;
|
||||||
|
|
||||||
public LaunchMode RobloxLaunchMode { get; set; } = LaunchMode.None;
|
public LaunchMode RobloxLaunchMode { get; set; } = LaunchMode.None;
|
||||||
|
|
||||||
public string RobloxLaunchArgs { get; private set; } = "";
|
public string RobloxLaunchArgs { get; private set; } = "";
|
||||||
@ -37,7 +39,7 @@ namespace Bloxstrap
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] Args { get; private set; }
|
public string[] Args { get; private set; }
|
||||||
|
|
||||||
private Dictionary<string, LaunchFlag> _flagMap = new();
|
private readonly Dictionary<string, LaunchFlag> _flagMap = new();
|
||||||
|
|
||||||
public LaunchSettings(string[] args)
|
public LaunchSettings(string[] args)
|
||||||
{
|
{
|
||||||
@ -68,7 +70,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
string identifier = arg[1..];
|
string identifier = arg[1..];
|
||||||
|
|
||||||
if (_flagMap[identifier] is not LaunchFlag flag)
|
if (!_flagMap.TryGetValue(identifier, out LaunchFlag? flag) || flag is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
flag.Active = true;
|
flag.Active = true;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
{
|
{
|
||||||
// note that these are directories that aren't tethered to the basedirectory
|
// note that these are directories that aren't tethered to the basedirectory
|
||||||
// so these can safely be called before initialization
|
// so these can safely be called before initialization
|
||||||
|
public static string Temp => Path.Combine(Path.GetTempPath(), App.ProjectName);
|
||||||
public static string UserProfile => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
public static string UserProfile => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
public static string LocalAppData => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
public static string LocalAppData => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
public static string Desktop => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
public static string Desktop => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||||
@ -12,6 +13,9 @@
|
|||||||
|
|
||||||
public static string Process => Environment.ProcessPath!;
|
public static string Process => Environment.ProcessPath!;
|
||||||
|
|
||||||
|
public static string TempUpdates => Path.Combine(Temp, "Updates");
|
||||||
|
public static string TempLogs => Path.Combine(Temp, "Logs");
|
||||||
|
|
||||||
public static string Base { get; private set; } = "";
|
public static string Base { get; private set; } = "";
|
||||||
public static string Downloads { get; private set; } = "";
|
public static string Downloads { get; private set; } = "";
|
||||||
public static string Logs { get; private set; } = "";
|
public static string Logs { get; private set; } = "";
|
||||||
|
40
Bloxstrap/Resources/Strings.Designer.cs
generated
40
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -106,7 +106,7 @@ namespace Bloxstrap.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page..
|
/// Looks up a localized string similar to Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Bootstrapper_AutoUpdateFailed {
|
public static string Bootstrapper_AutoUpdateFailed {
|
||||||
get {
|
get {
|
||||||
@ -886,6 +886,33 @@ namespace Bloxstrap.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Roblox has crashed..
|
||||||
|
/// </summary>
|
||||||
|
public static string Dialog_PlayerError_Crash {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Dialog.PlayerError.Crash", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Roblox failed to launch..
|
||||||
|
/// </summary>
|
||||||
|
public static string Dialog_PlayerError_FailedLaunch {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Dialog.PlayerError.FailedLaunch", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Please read the following help information, which will open in your web browser when you close this dialog..
|
||||||
|
/// </summary>
|
||||||
|
public static string Dialog_PlayerError_HelpInformation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Dialog.PlayerError.HelpInformation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Early 2015.
|
/// Looks up a localized string similar to Early 2015.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2519,15 +2546,6 @@ namespace Bloxstrap.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Looks up a localized string similar to e.g. C:\Windows\System32\cmd.exe.
|
|
||||||
/// </summary>
|
|
||||||
public static string Menu_Integrations_Custom_AppLocation_Placeholder {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("Menu.Integrations.Custom.AppLocation.Placeholder", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Auto close when Roblox closes.
|
/// Looks up a localized string similar to Auto close when Roblox closes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2556,7 +2574,7 @@ namespace Bloxstrap.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to e.g. /k echo Roblox is running!.
|
/// Looks up a localized string similar to Roblox is running!.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Menu_Integrations_Custom_LaunchArgs_Placeholder {
|
public static string Menu_Integrations_Custom_LaunchArgs_Placeholder {
|
||||||
get {
|
get {
|
||||||
|
@ -124,7 +124,7 @@
|
|||||||
<value>lookup failed</value>
|
<value>lookup failed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Bootstrapper.AutoUpdateFailed" xml:space="preserve">
|
<data name="Bootstrapper.AutoUpdateFailed" xml:space="preserve">
|
||||||
<value>Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page.</value>
|
<value>Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Bootstrapper.ConfirmLaunch" xml:space="preserve">
|
<data name="Bootstrapper.ConfirmLaunch" xml:space="preserve">
|
||||||
<value>Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching?</value>
|
<value>Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching?</value>
|
||||||
@ -733,9 +733,6 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
|||||||
<data name="Menu.Integrations.Custom.AppLocation" xml:space="preserve">
|
<data name="Menu.Integrations.Custom.AppLocation" xml:space="preserve">
|
||||||
<value>Application Location</value>
|
<value>Application Location</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Menu.Integrations.Custom.AppLocation.Placeholder" xml:space="preserve">
|
|
||||||
<value>e.g. C:\Windows\System32\cmd.exe</value>
|
|
||||||
</data>
|
|
||||||
<data name="Menu.Integrations.Custom.AutoClose" xml:space="preserve">
|
<data name="Menu.Integrations.Custom.AutoClose" xml:space="preserve">
|
||||||
<value>Auto close when Roblox closes</value>
|
<value>Auto close when Roblox closes</value>
|
||||||
</data>
|
</data>
|
||||||
@ -746,7 +743,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
|||||||
<value>Launch Arguments</value>
|
<value>Launch Arguments</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Menu.Integrations.Custom.LaunchArgs.Placeholder" xml:space="preserve">
|
<data name="Menu.Integrations.Custom.LaunchArgs.Placeholder" xml:space="preserve">
|
||||||
<value>e.g. /k echo Roblox is running!</value>
|
<value>Roblox is running!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Menu.Integrations.Custom.NewIntegration" xml:space="preserve">
|
<data name="Menu.Integrations.Custom.NewIntegration" xml:space="preserve">
|
||||||
<value>New Integration</value>
|
<value>New Integration</value>
|
||||||
@ -1150,4 +1147,13 @@ If not, then please report this exception to the maintainers of this fork. Do NO
|
|||||||
Issues may occur and your settings may be altered. A reinstall is recommended.
|
Issues may occur and your settings may be altered. A reinstall is recommended.
|
||||||
Are you sure you want to continue?</value>
|
Are you sure you want to continue?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Dialog.PlayerError.FailedLaunch" xml:space="preserve">
|
||||||
|
<value>Roblox failed to launch.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Dialog.PlayerError.Crash" xml:space="preserve">
|
||||||
|
<value>Roblox has crashed.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Dialog.PlayerError.HelpInformation" xml:space="preserve">
|
||||||
|
<value>Please read the following help information, which will open in your web browser when you close this dialog.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
@ -104,7 +104,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
|||||||
|
|
||||||
private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked);
|
private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked);
|
||||||
|
|
||||||
private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}");
|
private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject(_activityWatcher?.GetActivityDeeplink());
|
||||||
|
|
||||||
private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow();
|
private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow();
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
|||||||
|
|
||||||
case MessageBoxImage.Warning:
|
case MessageBoxImage.Warning:
|
||||||
iconFilename = "Warning";
|
iconFilename = "Warning";
|
||||||
sound = SystemSounds.Asterisk;
|
sound = SystemSounds.Exclamation;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MessageBoxImage.Information:
|
case MessageBoxImage.Information:
|
||||||
|
@ -102,11 +102,11 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<ui:TextBox Grid.Column="0" Margin="0,0,0,0" PlaceholderText="{x:Static resources:Strings.Menu_Integrations_Custom_AppLocation_Placeholder}" Text="{Binding SelectedCustomIntegration.Location}" />
|
<ui:TextBox Grid.Column="0" Margin="0,0,0,0" PlaceholderText="C:\Windows\System32\cmd.exe" Text="{Binding SelectedCustomIntegration.Location}" />
|
||||||
<ui:Button Grid.Column="1" Margin="8,0,0,0" Height="34" Icon="Folder24" Content="{x:Static resources:Strings.Common_Browse}" Command="{Binding BrowseIntegrationLocationCommand}" />
|
<ui:Button Grid.Column="1" Margin="8,0,0,0" Height="34" Icon="Folder24" Content="{x:Static resources:Strings.Common_Browse}" Command="{Binding BrowseIntegrationLocationCommand}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock Margin="0,8,0,0" Text="{x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
<TextBlock Margin="0,8,0,0" Text="{x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||||
<ui:TextBox Margin="0,4,0,0" PlaceholderText="{x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs_Placeholder}" Text="{Binding SelectedCustomIntegration.LaunchArgs}" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" />
|
<ui:TextBox Margin="0,4,0,0" PlaceholderText="{Binding Source='/k echo {0}', Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs_Placeholder}}" Text="{Binding SelectedCustomIntegration.LaunchArgs}" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" />
|
||||||
<CheckBox Margin="0,8,0,0" Content="{x:Static resources:Strings.Menu_Integrations_Custom_AutoClose}" IsChecked="{Binding SelectedCustomIntegration.AutoClose}" />
|
<CheckBox Margin="0,8,0,0" Content="{x:Static resources:Strings.Menu_Integrations_Custom_AutoClose}" IsChecked="{Binding SelectedCustomIntegration.AutoClose}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TextBlock Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{x:Static resources:Strings.Menu_Integrations_Custom_NoneSelected}" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center">
|
<TextBlock Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{x:Static resources:Strings.Menu_Integrations_Custom_NoneSelected}" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||||
|
@ -33,6 +33,20 @@ namespace Bloxstrap.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ShowPlayerErrorDialog(bool crash = false)
|
||||||
|
{
|
||||||
|
if (App.LaunchSettings.QuietFlag.Active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string topLine = Strings.Dialog_PlayerError_FailedLaunch;
|
||||||
|
|
||||||
|
if (crash)
|
||||||
|
topLine = Strings.Dialog_PlayerError_Crash;
|
||||||
|
|
||||||
|
ShowMessageBox($"{topLine}\n\n{Strings.Dialog_PlayerError_HelpInformation}", MessageBoxImage.Error);
|
||||||
|
Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Roblox-crashes-or-does-not-launch");
|
||||||
|
}
|
||||||
|
|
||||||
public static void ShowExceptionDialog(Exception exception)
|
public static void ShowExceptionDialog(Exception exception)
|
||||||
{
|
{
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
@ -5,7 +5,6 @@ using Bloxstrap.UI.Elements.About;
|
|||||||
|
|
||||||
namespace Bloxstrap.UI.ViewModels.Installer
|
namespace Bloxstrap.UI.ViewModels.Installer
|
||||||
{
|
{
|
||||||
// TODO: have it so it shows "Launch Roblox"/"Install and Launch Roblox" depending on state of /App/ folder
|
|
||||||
public class LaunchMenuViewModel
|
public class LaunchMenuViewModel
|
||||||
{
|
{
|
||||||
public string Version => string.Format(Strings.Menu_About_Version, App.Version);
|
public string Version => string.Format(Strings.Menu_About_Version, App.Version);
|
||||||
|
@ -19,33 +19,16 @@
|
|||||||
// called by codebehind on page load
|
// called by codebehind on page load
|
||||||
public async void DoChecks()
|
public async void DoChecks()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "WelcomeViewModel::DoChecks";
|
var releaseInfo = await App.GetLatestRelease();
|
||||||
|
|
||||||
// TODO: move into unified function that bootstrapper can use too
|
if (releaseInfo is not null)
|
||||||
GithubRelease? releaseInfo = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest");
|
|
||||||
|
|
||||||
if (releaseInfo is null || releaseInfo.Assets is null)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Encountered invalid data when fetching GitHub releases");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (Utilities.CompareVersions(App.Version, releaseInfo.TagName) == VersionComparison.LessThan)
|
if (Utilities.CompareVersions(App.Version, releaseInfo.TagName) == VersionComparison.LessThan)
|
||||||
{
|
{
|
||||||
VersionNotice = String.Format(Resources.Strings.Installer_Welcome_UpdateNotice, App.Version, releaseInfo.TagName.Replace("v", ""));
|
VersionNotice = String.Format(Strings.Installer_Welcome_UpdateNotice, App.Version, releaseInfo.TagName.Replace("v", ""));
|
||||||
OnPropertyChanged(nameof(VersionNotice));
|
OnPropertyChanged(nameof(VersionNotice));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Error occurred when fetching GitHub releases");
|
|
||||||
App.Logger.WriteException(LOG_IDENT, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
CanContinue = true;
|
CanContinue = true;
|
||||||
OnPropertyChanged(nameof(CanContinue));
|
OnPropertyChanged(nameof(CanContinue));
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Roblox Studio Mod Manager (ProjectSrc/Utility/SystemEvent.cs)
|
|
||||||
* MIT License
|
|
||||||
* Copyright (c) 2015-present MaximumADHD
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Bloxstrap.Utility
|
|
||||||
{
|
|
||||||
public class SystemEvent : EventWaitHandle
|
|
||||||
{
|
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public SystemEvent(string name, bool init = false, EventResetMode mode = EventResetMode.AutoReset) : base(init, mode, name)
|
|
||||||
{
|
|
||||||
if (init)
|
|
||||||
Reset();
|
|
||||||
else
|
|
||||||
Set();
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> WaitForEvent()
|
|
||||||
{
|
|
||||||
return Task.Run(WaitOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> WaitForEvent(TimeSpan timeout, bool exitContext = false)
|
|
||||||
{
|
|
||||||
return Task.Run(() => WaitOne(timeout, exitContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> WaitForEvent(int millisecondsTimeout, bool exitContext = false)
|
|
||||||
{
|
|
||||||
return Task.Run(() => WaitOne(millisecondsTimeout, exitContext));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,4 @@
|
|||||||
using Bloxstrap.Integrations;
|
using Bloxstrap.Integrations;
|
||||||
using System.CodeDom;
|
|
||||||
using System.Security.Permissions;
|
|
||||||
|
|
||||||
namespace Bloxstrap
|
namespace Bloxstrap
|
||||||
{
|
{
|
||||||
@ -54,7 +52,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
if (split.Length >= 2)
|
if (split.Length >= 2)
|
||||||
{
|
{
|
||||||
foreach (string strPid in split[0].Split(';'))
|
foreach (string strPid in split[1].Split(','))
|
||||||
{
|
{
|
||||||
if (int.TryParse(strPid, out int pid) && pid != 0)
|
if (int.TryParse(strPid, out int pid) && pid != 0)
|
||||||
_autoclosePids.Add(pid);
|
_autoclosePids.Add(pid);
|
||||||
@ -86,38 +84,33 @@ namespace Bloxstrap
|
|||||||
_notifyIcon = new(this);
|
_notifyIcon = new(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void KillRobloxProcess() => KillProcess(_gameClientPid);
|
public void KillRobloxProcess() => CloseProcess(_gameClientPid, true);
|
||||||
|
|
||||||
public void KillProcess(int pid)
|
public void CloseProcess(int pid, bool force = false)
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "Watcher::CloseProcess";
|
||||||
|
try
|
||||||
{
|
{
|
||||||
using var process = Process.GetProcessById(pid);
|
using var process = Process.GetProcessById(pid);
|
||||||
|
|
||||||
App.Logger.WriteLine("Watcher::KillProcess", $"Killing process '{process.ProcessName}' (PID {process.Id})");
|
App.Logger.WriteLine(LOG_IDENT, $"Killing process '{process.ProcessName}' (pid={pid}, force={force})");
|
||||||
|
|
||||||
if (process.HasExited)
|
if (process.HasExited)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine("Watcher::KillProcess", $"PID {process.Id} has already exited");
|
App.Logger.WriteLine(LOG_IDENT, $"PID {pid} has already exited");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (force)
|
||||||
process.Kill();
|
process.Kill();
|
||||||
process.Close();
|
else
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseProcess(int pid)
|
|
||||||
{
|
|
||||||
using var process = Process.GetProcessById(pid);
|
|
||||||
|
|
||||||
App.Logger.WriteLine("Watcher::CloseProcess", $"Closing process '{process.ProcessName}' (PID {process.Id})");
|
|
||||||
|
|
||||||
if (process.HasExited)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine("Watcher::CloseProcess", $"PID {process.Id} has already exited");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.CloseMainWindow();
|
process.CloseMainWindow();
|
||||||
process.Close();
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, $"PID {pid} could not be closed");
|
||||||
|
App.Logger.WriteException(LOG_IDENT, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run()
|
public async Task Run()
|
||||||
|
Loading…
Reference in New Issue
Block a user