Add custom integration support (#76)

This commit is contained in:
pizzaboxer 2023-02-14 18:46:17 +00:00
parent 8a4cc67893
commit 6cb72a8038
11 changed files with 163 additions and 44 deletions

View File

@ -201,28 +201,41 @@ namespace Bloxstrap
private bool CheckIfRunning(bool shutdown) private bool CheckIfRunning(bool shutdown)
{ {
App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] Checking if Roblox is running... (shutdown={shutdown})");
Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta"); Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta");
if (processes.Length == 0) if (processes.Length == 0)
return false;
if (shutdown)
{ {
Dialog?.PromptShutdown(); App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] Roblox is not running");
return false;
try
{
// try/catch just in case process was closed before prompt was answered
foreach (Process process in processes)
{
process.CloseMainWindow();
process.Close();
}
}
catch (Exception) { }
} }
App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] Roblox is running, found {processes.Length} process(es)");
if (!shutdown)
return true;
App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] Attempting to shutdown Roblox...");
Dialog?.PromptShutdown();
try
{
// try/catch just in case process was closed before prompt was answered
foreach (Process process in processes)
{
process.CloseMainWindow();
process.Close();
}
}
catch (Exception e)
{
App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] Failed to close process! {e}");
}
App.Logger.WriteLine($"[Bootstrapper::CheckIfRunning] All Roblox processes closed");
return true; return true;
} }
@ -246,12 +259,15 @@ namespace Bloxstrap
_launchCommandLine += " -startEvent " + startEventName; _launchCommandLine += " -startEvent " + startEventName;
// whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes
bool shouldWait = false; bool shouldWait = false;
Process gameClient = Process.Start(Path.Combine(_versionFolder, "RobloxPlayerBeta.exe"), _launchCommandLine); Process gameClient = Process.Start(Path.Combine(_versionFolder, "RobloxPlayerBeta.exe"), _launchCommandLine);
Process? rbxFpsUnlocker = null; List<Process> autocloseProcesses = new();
DiscordRichPresence? richPresence = null; DiscordRichPresence? richPresence = null;
Mutex? singletonMutex = null; Mutex? singletonMutex = null;
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Started Roblox (PID {gameClient.Id})");
using (SystemEvent startEvent = new(startEventName)) using (SystemEvent startEvent = new(startEventName))
{ {
bool startEventFired = await startEvent.WaitForEvent(); bool startEventFired = await startEvent.WaitForEvent();
@ -262,7 +278,7 @@ namespace Bloxstrap
return; return;
} }
if (App.Settings.Prop.RFUEnabled && Process.GetProcessesByName("rbxfpsunlocker").Length == 0) if (App.Settings.Prop.RFUEnabled && Process.GetProcessesByName(RbxFpsUnlocker.ApplicationName).Length == 0)
{ {
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Using rbxfpsunlocker"); App.Logger.WriteLine("[Bootstrapper::StartRoblox] Using rbxfpsunlocker");
@ -272,10 +288,13 @@ namespace Bloxstrap
FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe") FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe")
}; };
rbxFpsUnlocker = Process.Start(startInfo); Process process = Process.Start(startInfo)!;
if (App.Settings.Prop.RFUAutoclose) if (App.Settings.Prop.RFUAutoclose)
{
shouldWait = true; shouldWait = true;
autocloseProcesses.Add(process);
}
} }
if (App.Settings.Prop.UseDiscordRichPresence) if (App.Settings.Prop.UseDiscordRichPresence)
@ -293,6 +312,19 @@ namespace Bloxstrap
shouldWait = true; shouldWait = true;
} }
// launch custom integrations now
foreach (CustomIntegration integration in App.Settings.Prop.CustomIntegrations)
{
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Launching custom integration '{integration.Name}' ({integration.Location} {integration.LaunchArgs} - autoclose is {integration.AutoClose})");
Process process = Process.Start(integration.Location, integration.LaunchArgs);
if (integration.AutoClose)
{
shouldWait = true;
autocloseProcesses.Add(process);
}
}
// event fired, wait for 3 seconds then close // event fired, wait for 3 seconds then close
await Task.Delay(3000); await Task.Delay(3000);
Dialog?.CloseBootstrapper(); Dialog?.CloseBootstrapper();
@ -305,11 +337,15 @@ namespace Bloxstrap
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Waiting for Roblox to close"); App.Logger.WriteLine("[Bootstrapper::StartRoblox] Waiting for Roblox to close");
await gameClient.WaitForExitAsync(); await gameClient.WaitForExitAsync();
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Roblox exited with code {gameClient.ExitCode}");
richPresence?.Dispose(); richPresence?.Dispose();
if (App.Settings.Prop.RFUAutoclose) foreach (Process process in autocloseProcesses)
rbxFpsUnlocker?.Kill(); {
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Autoclosing process '{process.ProcessName}' (PID {process.Id})");
process.Kill();
}
} }
public void CancelInstall() public void CancelInstall()

View File

@ -39,11 +39,11 @@ namespace Bloxstrap.Helpers.Integrations
App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Updated presence"); App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Updated presence");
RichPresence.OnConnectionEstablished += (_, e) => RichPresence.OnConnectionEstablished += (_, e) =>
App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Established connection with Discord RPC!"); App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Established connection with Discord RPC");
//spams log as it tries to connect every ~15 sec when discord is closed so not now //spams log as it tries to connect every ~15 sec when discord is closed so not now
//RichPresence.OnConnectionFailed += (_, e) => //RichPresence.OnConnectionFailed += (_, e) =>
// App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Failed to establish connection with Discord RPC!"); // App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Failed to establish connection with Discord RPC");
RichPresence.OnClose += (_, e) => RichPresence.OnClose += (_, e) =>
App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Lost connection to Discord RPC - {e.Reason} ({e.Code})"); App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Lost connection to Discord RPC - {e.Reason} ({e.Code})");
@ -219,6 +219,7 @@ namespace Bloxstrap.Helpers.Integrations
public void Dispose() public void Dispose()
{ {
App.Logger.WriteLine("[DiscordRichPresence::Dispose] Cleaning up Discord RPC and Presence");
RichPresence.ClearPresence(); RichPresence.ClearPresence();
RichPresence.Dispose(); RichPresence.Dispose();
} }

View File

@ -31,14 +31,14 @@ namespace Bloxstrap.Helpers.Integrations
if (processes.Length == 0) if (processes.Length == 0)
return; return;
App.Logger.WriteLine("[RbxFpsUnlocker::CheckIfRunning] Closing currently running rbxfpsunlocker processes...");
try try
{ {
// try/catch just in case process was closed before prompt was answered
foreach (Process process in processes) foreach (Process process in processes)
{ {
if (process.MainModule is null || process.MainModule.FileName is null) if (process.MainModule?.FileName is null)
continue; continue;
if (!process.MainModule.FileName.Contains(App.BaseDirectory)) if (!process.MainModule.FileName.Contains(App.BaseDirectory))
@ -48,7 +48,10 @@ namespace Bloxstrap.Helpers.Integrations
process.Close(); process.Close();
} }
} }
catch (Exception) { } catch (Exception e)
{
App.Logger.WriteLine($"[RbxFpsUnlocker::CheckIfRunning] Could not close rbxfpsunlocker process! {e}");
}
} }
public static async Task CheckInstall() public static async Task CheckInstall()

View File

@ -303,7 +303,7 @@ namespace Bloxstrap.Helpers.Integrations
if (!Directory.Exists(PresetsFolder)) if (!Directory.Exists(PresetsFolder))
return; return;
App.Logger.WriteLine("[ReShade::UninstallExtraviPresets] Uninstalling Extravi's presets..."); App.Logger.WriteLine("[ReShade::UninstallExtraviPresets] Uninstalling Extravi's ReShade presets...");
FileInfo[] presets = new DirectoryInfo(PresetsFolder).GetFiles(); FileInfo[] presets = new DirectoryInfo(PresetsFolder).GetFiles();
@ -319,32 +319,39 @@ namespace Bloxstrap.Helpers.Integrations
public static async Task CheckModifications() public static async Task CheckModifications()
{ {
App.Logger.WriteLine("[ReShade::CheckModifications] Checking ReShade modifications... "); App.Logger.WriteLine("[ReShade::CheckModifications] Checking ReShade modifications...");
string injectorLocation = Path.Combine(Directories.Modifications, "dxgi.dll"); string injectorLocation = Path.Combine(Directories.Modifications, "dxgi.dll");
if (!App.Settings.Prop.UseReShadeExtraviPresets) if (!App.Settings.Prop.UseReShadeExtraviPresets && !String.IsNullOrEmpty(App.State.Prop.ExtraviReShadePresetsVersion))
{ {
UninstallExtraviPresets(); UninstallExtraviPresets();
App.State.Prop.ExtraviReShadePresetsVersion = ""; App.State.Prop.ExtraviReShadePresetsVersion = "";
App.State.Save(); App.State.Save();
} }
if (!App.Settings.Prop.UseReShade) if (!App.Settings.Prop.UseReShade)
{ {
App.Logger.WriteLine("[ReShade::CheckModifications] ReShade is not enabled");
// we should already be uninstalled
// we want to ensure this is done one-time only as this could possibly interfere with other rendering hooks using dxgi.dll
if (String.IsNullOrEmpty(App.State.Prop.ReShadeConfigVersion))
return;
App.Logger.WriteLine("[ReShade::CheckModifications] Uninstalling ReShade..."); App.Logger.WriteLine("[ReShade::CheckModifications] Uninstalling ReShade...");
// delete any stock config files // delete any stock config files
File.Delete(injectorLocation); File.Delete(injectorLocation);
File.Delete(ConfigLocation); File.Delete(ConfigLocation);
App.State.Prop.ReShadeConfigVersion = "";
App.State.Save();
//DeleteShaders("Stock");
if (Directory.Exists(BaseDirectory)) if (Directory.Exists(BaseDirectory))
Directory.Delete(BaseDirectory, true); Directory.Delete(BaseDirectory, true);
App.State.Prop.ReShadeConfigVersion = "";
App.State.Save();
return; return;
} }

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bloxstrap.Models
{
public class CustomIntegration
{
public string Name { get; set; } = null!;
public string Location { get; set; } = null!;
public string LaunchArgs { get; set; } = null!;
public bool AutoClose { get; set; } = false;
public override string ToString()
{
return Name;
}
}
}

View File

@ -1,4 +1,5 @@
using Bloxstrap.Enums; using System.Collections.Generic;
using Bloxstrap.Enums;
using Bloxstrap.Helpers; using Bloxstrap.Helpers;
namespace Bloxstrap.Models namespace Bloxstrap.Models
@ -24,6 +25,7 @@ namespace Bloxstrap.Models
public bool RFUAutoclose { get; set; } = false; public bool RFUAutoclose { get; set; } = false;
public bool UseReShade { get; set; } = false; public bool UseReShade { get; set; } = false;
public bool UseReShadeExtraviPresets { get; set; } = false; public bool UseReShadeExtraviPresets { get; set; } = false;
public List<CustomIntegration> CustomIntegrations { get; set; } = new();
// mod preset configuration // mod preset configuration
public bool UseOldDeathSound { get; set; } = true; public bool UseOldDeathSound { get; set; } = true;

View File

@ -1,4 +1,5 @@
using System.ComponentModel; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
@ -8,6 +9,7 @@ using CommunityToolkit.Mvvm.Input;
using Wpf.Ui.Mvvm.Contracts; using Wpf.Ui.Mvvm.Contracts;
using Bloxstrap.Helpers; using Bloxstrap.Helpers;
using Bloxstrap.Models;
using Bloxstrap.Views.Pages; using Bloxstrap.Views.Pages;
namespace Bloxstrap.ViewModels namespace Bloxstrap.ViewModels
@ -27,6 +29,12 @@ namespace Bloxstrap.ViewModels
public IntegrationsViewModel(Page page) public IntegrationsViewModel(Page page)
{ {
_page = page; _page = page;
if (CustomIntegrations.Count > 0)
{
CustomIntegrationsVisibility = Visibility.Visible;
OnPropertyChanged(nameof(CustomIntegrationsVisibility));
}
} }
private void OpenReShadeFolder() private void OpenReShadeFolder()
@ -93,5 +101,15 @@ namespace Bloxstrap.ViewModels
get => App.Settings.Prop.RFUAutoclose; get => App.Settings.Prop.RFUAutoclose;
set => App.Settings.Prop.RFUAutoclose = value; set => App.Settings.Prop.RFUAutoclose = value;
} }
public Visibility CustomIntegrationsVisibility { get; set; } = Visibility.Collapsed;
public List<CustomIntegration> CustomIntegrations
{
get => App.Settings.Prop.CustomIntegrations;
set => App.Settings.Prop.CustomIntegrations = value;
}
public CustomIntegration SelectedCustomIntegration { get; set; }
} }
} }

View File

@ -10,7 +10,7 @@
mc:Ignorable="d" mc:Ignorable="d"
Title="Bloxstrap Menu" Title="Bloxstrap Menu"
MinWidth="880" MinWidth="880"
Width="920" Width="960"
Height="580" Height="580"
Background="{ui:ThemeResource ApplicationBackgroundBrush}" Background="{ui:ThemeResource ApplicationBackgroundBrush}"
ExtendsContentIntoTitleBar="True" ExtendsContentIntoTitleBar="True"
@ -58,9 +58,9 @@
<ui:NavigationFluent x:Name="RootNavigation" Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" Frame="{Binding ElementName=RootFrame}" SelectedPageIndex="0"> <ui:NavigationFluent x:Name="RootNavigation" Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" Frame="{Binding ElementName=RootFrame}" SelectedPageIndex="0">
<ui:NavigationFluent.Items> <ui:NavigationFluent.Items>
<ui:NavigationItem Content="Integrations" PageType="{x:Type pages:IntegrationsPage}" Icon="AddSquareMultiple20" Tag="integrations" /> <ui:NavigationItem Content="Integrations" PageType="{x:Type pages:IntegrationsPage}" Icon="Add28" Tag="integrations" />
<ui:NavigationItem Content="Mods" PageType="{x:Type pages:ModsPage}" Icon="DocumentToolbox24" Tag="mods" /> <ui:NavigationItem Content="Mods" PageType="{x:Type pages:ModsPage}" Icon="WrenchScrewdriver20" Tag="mods" />
<ui:NavigationItem Content="Bootstrapper" PageType="{x:Type pages:BootstrapperPage}" Icon="Window48" Tag="bootstrapper" /> <ui:NavigationItem Content="Bootstrapper" PageType="{x:Type pages:BootstrapperPage}" Icon="WindowWrench48" Tag="bootstrapper" />
<ui:NavigationItem Content="Installation" PageType="{x:Type pages:InstallationPage}" Icon="HardDrive20" Tag="installation" /> <ui:NavigationItem Content="Installation" PageType="{x:Type pages:InstallationPage}" Icon="HardDrive20" Tag="installation" />
<!--<ui:NavigationSeparator />--> <!--<ui:NavigationSeparator />-->
<ui:NavigationItem Content="ReShade Help" PageType="{x:Type pages:ReShadeHelpPage}" Icon="BookQuestionMark24" Tag="reshadehelp" Visibility="Collapsed" /> <ui:NavigationItem Content="ReShade Help" PageType="{x:Type pages:ReShadeHelpPage}" Icon="BookQuestionMark24" Tag="reshadehelp" Visibility="Collapsed" />

View File

@ -12,7 +12,7 @@
<StackPanel Margin="0,0,14,14"> <StackPanel Margin="0,0,14,14">
<TextBlock Text="Configure how Bloxstrap and Roblox are installed." FontSize="14" Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> <TextBlock Text="Configure how Bloxstrap and Roblox are installed." FontSize="14" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<ui:CardExpander Margin="0,16,0,0"> <ui:CardExpander Margin="0,16,0,0" IsExpanded="True">
<ui:CardExpander.Header> <ui:CardExpander.Header>
<StackPanel> <StackPanel>
<TextBlock FontSize="13" FontWeight="Medium" Text="Install Location" /> <TextBlock FontSize="13" FontWeight="Medium" Text="Install Location" />
@ -29,7 +29,7 @@
</Grid> </Grid>
</ui:CardExpander> </ui:CardExpander>
<ui:CardExpander Margin="0,8,0,0"> <ui:CardExpander Margin="0,8,0,0" IsExpanded="True">
<ui:CardExpander.Header> <ui:CardExpander.Header>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>

View File

@ -114,5 +114,26 @@
<ui:ToggleSwitch IsChecked="{Binding RbxFpsUnlockerAutocloseEnabled, Mode=TwoWay}" /> <ui:ToggleSwitch IsChecked="{Binding RbxFpsUnlockerAutocloseEnabled, Mode=TwoWay}" />
</ui:CardControl> </ui:CardControl>
</StackPanel> </StackPanel>
<TextBlock Text="Custom Integrations" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
<TextBlock Margin="0,4,0,0" TextWrapping="Wrap">
Using custom integrations, you can have other programs launch with Roblox automatically like how rbxfpsunlocker does.<LineBreak />
To manage custom integrations, you'll have to manually edit your Settings.json configuration file in your Bloxstrap folder. If you have any configured, you'll be able to preview them here.
</TextBlock>
<Grid Margin="0,8,0,0" Visibility="{Binding CustomIntegrationsVisibility}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding CustomIntegrations}" SelectionChanged="CustomIntegrationSelection" SelectedIndex="0"/>
<StackPanel Grid.Column="1" Margin="8,0,0,0">
<TextBlock FontSize="13" FontWeight="Medium" Text="Application Location" />
<TextBlock Text="{Binding SelectedCustomIntegration.Location}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<TextBlock Margin="0,8,0,0" FontSize="13" FontWeight="Medium" Text="Launch Arguments" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{Binding SelectedCustomIntegration.LaunchArgs}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<TextBlock Margin="0,8,0,0" FontSize="13" FontWeight="Medium" Text="Auto close when Roblox closes" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<TextBlock Text="{Binding SelectedCustomIntegration.AutoClose}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
</StackPanel>
</Grid>
</StackPanel> </StackPanel>
</ui:UiPage> </ui:UiPage>

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using Bloxstrap.Models;
using Bloxstrap.ViewModels; using Bloxstrap.ViewModels;
namespace Bloxstrap.Views.Pages namespace Bloxstrap.Views.Pages
@ -18,5 +20,12 @@ namespace Bloxstrap.Views.Pages
if (!Environment.Is64BitOperatingSystem) if (!Environment.Is64BitOperatingSystem)
this.RbxFpsUnlockerOptions.Visibility = Visibility.Collapsed; this.RbxFpsUnlockerOptions.Visibility = Visibility.Collapsed;
} }
public void CustomIntegrationSelection(object sender, SelectionChangedEventArgs e)
{
IntegrationsViewModel viewModel = (IntegrationsViewModel)DataContext;
viewModel.SelectedCustomIntegration = (CustomIntegration)((ListBox)sender).SelectedItem;
viewModel.OnPropertyChanged(nameof(viewModel.SelectedCustomIntegration));
}
} }
} }