mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Custom Integrations Upgrade: Allow launching and closing based on specific games
This commit is contained in:
parent
1dc31867bb
commit
9318813bdb
@ -37,6 +37,8 @@
|
||||
<converters:StringFormatConverter x:Key="StringFormatConverter" />
|
||||
<converters:RangeConverter x:Key="RangeConverter" />
|
||||
<converters:EnumNameConverter x:Key="EnumNameConverter" />
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
@ -426,30 +426,32 @@ namespace Bloxstrap
|
||||
// launch custom integrations now
|
||||
foreach (var integration in App.Settings.Prop.CustomIntegrations)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Launching custom integration '{integration.Name}' ({integration.Location} {integration.LaunchArgs} - autoclose is {integration.AutoClose})");
|
||||
if (!integration.SpecifyGame) {
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Launching custom integration '{integration.Name}' ({integration.Location} {integration.LaunchArgs} - autoclose is {integration.AutoClose})");
|
||||
|
||||
int pid = 0;
|
||||
int pid = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
try
|
||||
{
|
||||
FileName = integration.Location,
|
||||
Arguments = integration.LaunchArgs.Replace("\r\n", " "),
|
||||
WorkingDirectory = Path.GetDirectoryName(integration.Location),
|
||||
UseShellExecute = true
|
||||
})!;
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = integration.Location,
|
||||
Arguments = integration.LaunchArgs.Replace("\r\n", " "),
|
||||
WorkingDirectory = Path.GetDirectoryName(integration.Location),
|
||||
UseShellExecute = true
|
||||
})!;
|
||||
|
||||
pid = process.Id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to launch integration '{integration.Name}'!");
|
||||
App.Logger.WriteLine(LOG_IDENT, ex.Message);
|
||||
}
|
||||
pid = process.Id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to launch integration '{integration.Name}'!");
|
||||
App.Logger.WriteLine(LOG_IDENT, ex.Message);
|
||||
}
|
||||
|
||||
if (integration.AutoClose && pid != 0)
|
||||
autoclosePids.Add(pid);
|
||||
if (integration.AutoClose && pid != 0)
|
||||
autoclosePids.Add(pid);
|
||||
}
|
||||
}
|
||||
|
||||
if (App.Settings.Prop.EnableActivityTracking || App.LaunchSettings.TestModeFlag.Active || autoclosePids.Any())
|
||||
|
100
Bloxstrap/Integrations/IntegrationWatcher.cs
Normal file
100
Bloxstrap/Integrations/IntegrationWatcher.cs
Normal file
@ -0,0 +1,100 @@
|
||||
namespace Bloxstrap.Integrations
|
||||
{
|
||||
public class IntegrationWatcher : IDisposable
|
||||
{
|
||||
private readonly ActivityWatcher _activityWatcher;
|
||||
private readonly Dictionary<int, CustomIntegration> _activeIntegrations = new();
|
||||
|
||||
public IntegrationWatcher(ActivityWatcher activityWatcher)
|
||||
{
|
||||
_activityWatcher = activityWatcher;
|
||||
|
||||
_activityWatcher.OnGameJoin += OnGameJoin;
|
||||
_activityWatcher.OnGameLeave += OnGameLeave;
|
||||
}
|
||||
|
||||
private void OnGameJoin(object? sender, EventArgs e)
|
||||
{
|
||||
if (!_activityWatcher.InGame)
|
||||
return;
|
||||
|
||||
long currentGameId = _activityWatcher.Data.PlaceId;
|
||||
|
||||
foreach (var integration in App.Settings.Prop.CustomIntegrations)
|
||||
{
|
||||
if (!integration.SpecifyGame || integration.GameID != currentGameId.ToString())
|
||||
continue;
|
||||
|
||||
LaunchIntegration(integration);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameLeave(object? sender, EventArgs e)
|
||||
{
|
||||
foreach (var pid in _activeIntegrations.Keys.ToList())
|
||||
{
|
||||
var integration = _activeIntegrations[pid];
|
||||
if (integration.AutoCloseOnGame)
|
||||
{
|
||||
TerminateProcess(pid);
|
||||
_activeIntegrations.Remove(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LaunchIntegration(CustomIntegration integration)
|
||||
{
|
||||
const string LOG_IDENT = "IntegrationWatcher::LaunchIntegration";
|
||||
|
||||
try
|
||||
{
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = integration.Location,
|
||||
Arguments = integration.LaunchArgs.Replace("\r\n", " "),
|
||||
WorkingDirectory = Path.GetDirectoryName(integration.Location),
|
||||
UseShellExecute = true
|
||||
});
|
||||
|
||||
if (process != null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Integration '{integration.Name}' launched for game ID '{integration.GameID}' (PID {process.Id}).");
|
||||
_activeIntegrations[process.Id] = integration;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to launch integration '{integration.Name}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void TerminateProcess(int pid)
|
||||
{
|
||||
const string LOG_IDENT = "IntegrationWatcher::TerminateProcess";
|
||||
|
||||
try
|
||||
{
|
||||
var process = Process.GetProcessById(pid);
|
||||
process.Kill();
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Terminated integration process (PID {pid}).");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to terminate process (PID {pid}), likely already exited.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var pid in _activeIntegrations.Keys)
|
||||
{
|
||||
TerminateProcess(pid);
|
||||
}
|
||||
|
||||
_activeIntegrations.Clear();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,9 @@
|
||||
public string Name { get; set; } = "";
|
||||
public string Location { get; set; } = "";
|
||||
public string LaunchArgs { get; set; } = "";
|
||||
public bool SpecifyGame { get; set; } = false;
|
||||
public string GameID { get; set; } = "";
|
||||
public bool AutoCloseOnGame { get; set; } = true;
|
||||
public bool AutoClose { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
29
Bloxstrap/Resources/Strings.Designer.cs
generated
29
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -2730,7 +2730,34 @@ namespace Bloxstrap.Resources {
|
||||
return ResourceManager.GetString("Menu.Integrations.Custom.AppLocation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Run on specific games.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_Custom_SpecifyGame {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.Custom.SpecifyGame", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Game ID.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_Custom_GameID {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.Custom.GameID", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto close when game closes.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_Custom_AutoCloseOnGame {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.Custom.AutoCloseOnGame", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto close when Roblox closes.
|
||||
/// </summary>
|
||||
|
@ -689,6 +689,15 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<data name="Menu.Integrations.Custom.AppLocation" xml:space="preserve">
|
||||
<value>Application Location</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.SpecifyGame" xml:space="preserve">
|
||||
<value>Run on a specific game</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.GameID" xml:space="preserve">
|
||||
<value>Game ID</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.AutoCloseOnGame" xml:space="preserve">
|
||||
<value>Auto close when the game closes</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.AutoClose" xml:space="preserve">
|
||||
<value>Auto close when Roblox closes</value>
|
||||
</data>
|
||||
|
21
Bloxstrap/UI/Converters/BooleanToVisibilityConverter.cs
Normal file
21
Bloxstrap/UI/Converters/BooleanToVisibilityConverter.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Bloxstrap.UI.Converters
|
||||
{
|
||||
public class BooleanToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
return boolValue ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Bloxstrap.UI.Converters
|
||||
{
|
||||
public class InverseBooleanToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
return boolValue ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -110,6 +110,7 @@
|
||||
<controls:MarkdownTextBlock MarkdownText="[Redusofficial](https://github.com/Redusofficial)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[srthMD](https://github.com/srthMD)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[axellse](https://github.com/axellse)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[CubesterYT](https://github.com/CubesterYT)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
|
||||
|
@ -68,7 +68,7 @@
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Title}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid Margin="0,8,0,0" MinHeight="325">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -109,7 +109,11 @@
|
||||
</Grid>
|
||||
<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="{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_SpecifyGame}" IsChecked="{Binding SelectedCustomIntegration.SpecifyGame, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock Margin="0,8,0,0" Text="{x:Static resources:Strings.Menu_Integrations_Custom_GameID}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" Visibility="{Binding SelectedCustomIntegration.SpecifyGame, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<ui:TextBox Margin="0,4,0,0" PlaceholderText="1818" Text="{Binding SelectedCustomIntegration.GameID}" Visibility="{Binding SelectedCustomIntegration.SpecifyGame, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox Margin="0,8,0,0" Content="{x:Static resources:Strings.Menu_Integrations_Custom_AutoCloseOnGame}" IsChecked="{Binding SelectedCustomIntegration.AutoCloseOnGame, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding SelectedCustomIntegration.SpecifyGame, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox Margin="0,8,0,0" Content="{x:Static resources:Strings.Menu_Integrations_Custom_AutoClose}" IsChecked="{Binding SelectedCustomIntegration.AutoClose, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding SelectedCustomIntegration.SpecifyGame, Converter={StaticResource InverseBooleanToVisibilityConverter}}" />
|
||||
</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.Style>
|
||||
|
@ -9,13 +9,15 @@ namespace Bloxstrap
|
||||
private readonly InterProcessLock _lock = new("Watcher");
|
||||
|
||||
private readonly WatcherData? _watcherData;
|
||||
|
||||
|
||||
private readonly NotifyIconWrapper? _notifyIcon;
|
||||
|
||||
public readonly ActivityWatcher? ActivityWatcher;
|
||||
|
||||
public readonly DiscordRichPresence? RichPresence;
|
||||
|
||||
public readonly IntegrationWatcher? IntegrationWatcher;
|
||||
|
||||
public Watcher()
|
||||
{
|
||||
const string LOG_IDENT = "Watcher";
|
||||
@ -63,6 +65,8 @@ namespace Bloxstrap
|
||||
|
||||
if (App.Settings.Prop.UseDiscordRichPresence)
|
||||
RichPresence = new(ActivityWatcher);
|
||||
|
||||
IntegrationWatcher = new IntegrationWatcher(ActivityWatcher);
|
||||
}
|
||||
|
||||
_notifyIcon = new(this);
|
||||
@ -122,6 +126,7 @@ namespace Bloxstrap
|
||||
{
|
||||
App.Logger.WriteLine("Watcher::Dispose", "Disposing Watcher");
|
||||
|
||||
IntegrationWatcher?.Dispose();
|
||||
_notifyIcon?.Dispose();
|
||||
RichPresence?.Dispose();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user