Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Bloxstrap/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
40 changes: 21 additions & 19 deletions Bloxstrap/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,30 +426,32 @@ private void StartRoblox()
// 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())
Expand Down
100 changes: 100 additions & 0 deletions Bloxstrap/Integrations/IntegrationWatcher.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
3 changes: 3 additions & 0 deletions Bloxstrap/Models/CustomIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ public class CustomIntegration
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 changes: 28 additions & 1 deletion Bloxstrap/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions Bloxstrap/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
21 changes: 21 additions & 0 deletions Bloxstrap/UI/Converters/BooleanToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
21 changes: 21 additions & 0 deletions Bloxstrap/UI/Converters/InverseBooleanToVisibilityConverter.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
1 change: 1 addition & 0 deletions Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
8 changes: 6 additions & 2 deletions Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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>
Expand Down
7 changes: 6 additions & 1 deletion Bloxstrap/Watcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ public class Watcher : IDisposable
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";
Expand Down Expand Up @@ -63,6 +65,8 @@ public Watcher()

if (App.Settings.Prop.UseDiscordRichPresence)
RichPresence = new(ActivityWatcher);

IntegrationWatcher = new IntegrationWatcher(ActivityWatcher);
}

_notifyIcon = new(this);
Expand Down Expand Up @@ -122,6 +126,7 @@ public void Dispose()
{
App.Logger.WriteLine("Watcher::Dispose", "Disposing Watcher");

IntegrationWatcher?.Dispose();
_notifyIcon?.Dispose();
RichPresence?.Dispose();

Expand Down