Skip to content

Commit 91109a6

Browse files
committed
Update v3.3.3
1 parent 9492495 commit 91109a6

12 files changed

Lines changed: 1032 additions & 354 deletions

File tree

.gitignore

Lines changed: 359 additions & 350 deletions
Large diffs are not rendered by default.

Bloxstrap/Bloxstrap.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
<UseWPF>true</UseWPF>
88
<UseWindowsForms>True</UseWindowsForms>
99
<ApplicationIcon>BoneFish.ico</ApplicationIcon>
10-
<Version>3.2.2</Version>
11-
<FileVersion>3.2.2</FileVersion>
10+
<Version>3.3.3</Version>
11+
<FileVersion>3.3.3</FileVersion>
1212
<ApplicationManifest>app.manifest</ApplicationManifest>
1313
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1414
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
@@ -75,6 +75,8 @@
7575
<PackageReference Include="SharpZipLib" Version="1.4.2" />
7676
<PackageReference Include="System.Resources.ResourceManager" Version="4.3.0" />
7777
<PackageReference Include="XamlAnimatedGif" Version="2.3.2" />
78+
<PackageReference Include="CommunityToolkit.Notifications" Version="1.0.1" />
79+
<PackageReference Include="Windows.Win32" Version="55.0.45" />
7880
</ItemGroup>
7981

8082
<ItemGroup>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Bloxstrap.UI.Elements;
2+
3+
namespace Bloxstrap.Integrations
4+
{
5+
/// <summary>
6+
/// Service untuk menampilkan FPS Monitor overlay saat game berjalan
7+
/// </summary>
8+
public class FpsMonitorService : IDisposable
9+
{
10+
private const string LOG_IDENT = "FpsMonitorService";
11+
12+
private readonly ActivityWatcher _activityWatcher;
13+
private FpsMonitorOverlay? _fpsOverlay;
14+
private bool _isRunning = false;
15+
16+
public FpsMonitorService(ActivityWatcher activityWatcher)
17+
{
18+
_activityWatcher = activityWatcher;
19+
20+
_activityWatcher.OnGameJoin += (_, _) => StartMonitoring();
21+
_activityWatcher.OnGameLeave += (_, _) => StopMonitoring();
22+
}
23+
24+
public void StartMonitoring()
25+
{
26+
if (App.Settings.Prop.EnableFpsMonitor == false || _isRunning)
27+
return;
28+
29+
try
30+
{
31+
App.Logger.WriteLine(LOG_IDENT, "Starting FPS Monitor");
32+
33+
_isRunning = true;
34+
35+
// Create dan show FPS overlay di main thread
36+
if (Application.Current.Dispatcher != null)
37+
{
38+
Application.Current.Dispatcher.Invoke(() =>
39+
{
40+
if (_fpsOverlay == null || !_fpsOverlay.IsLoaded)
41+
{
42+
_fpsOverlay = new FpsMonitorOverlay();
43+
_fpsOverlay.Show();
44+
App.Logger.WriteLine(LOG_IDENT, "FPS Monitor overlay shown");
45+
}
46+
});
47+
}
48+
}
49+
catch (Exception ex)
50+
{
51+
App.Logger.WriteLine(LOG_IDENT, $"Error starting FPS Monitor: {ex.Message}");
52+
_isRunning = false;
53+
}
54+
}
55+
56+
public void StopMonitoring()
57+
{
58+
if (!_isRunning)
59+
return;
60+
61+
try
62+
{
63+
App.Logger.WriteLine(LOG_IDENT, "Stopping FPS Monitor");
64+
65+
_isRunning = false;
66+
67+
if (Application.Current.Dispatcher != null)
68+
{
69+
Application.Current.Dispatcher.Invoke(() =>
70+
{
71+
if (_fpsOverlay != null && _fpsOverlay.IsLoaded)
72+
{
73+
_fpsOverlay.Close();
74+
_fpsOverlay = null;
75+
App.Logger.WriteLine(LOG_IDENT, "FPS Monitor overlay closed");
76+
}
77+
});
78+
}
79+
}
80+
catch (Exception ex)
81+
{
82+
App.Logger.WriteLine(LOG_IDENT, $"Error stopping FPS Monitor: {ex.Message}");
83+
}
84+
}
85+
86+
public void Dispose()
87+
{
88+
StopMonitoring();
89+
App.Logger.WriteLine(LOG_IDENT, "FPS Monitor service disposed");
90+
}
91+
}
92+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
using System.Windows;
2+
using Bloxstrap.Models.RobloxApi;
3+
using Windows.UI.Notifications;
4+
using Windows.Data.Xml.Dom;
5+
6+
namespace Bloxstrap.Integrations
7+
{
8+
/// <summary>
9+
/// Service untuk menampilkan notifikasi Windows native untuk Roblox
10+
/// </summary>
11+
public class RobloxNotification : IDisposable
12+
{
13+
private const string LOG_IDENT = "RobloxNotification";
14+
private const string APP_ID = "BoneFish";
15+
16+
private readonly ActivityWatcher _activityWatcher;
17+
private CancellationTokenSource? _notificationCheckToken;
18+
private Task? _notificationCheckTask;
19+
private HashSet<string> _notifiedUsers = new();
20+
21+
public event EventHandler<FriendNotificationEventArgs>? OnFriendOnline;
22+
public event EventHandler<NotificationEventArgs>? OnNotification;
23+
24+
public RobloxNotification(ActivityWatcher activityWatcher)
25+
{
26+
_activityWatcher = activityWatcher;
27+
28+
_activityWatcher.OnGameJoin += (_, _) => StartNotificationMonitoring();
29+
_activityWatcher.OnGameLeave += (_, _) => StopNotificationMonitoring();
30+
}
31+
32+
public void StartNotificationMonitoring()
33+
{
34+
if (App.Settings.Prop.EnableRobloxNotifications == false)
35+
return;
36+
37+
const string LOG_IDENT = "RobloxNotification";
38+
App.Logger.WriteLine(LOG_IDENT, "Starting notification monitoring");
39+
40+
StopNotificationMonitoring();
41+
42+
_notificationCheckToken = new CancellationTokenSource();
43+
_notifiedUsers.Clear();
44+
45+
_notificationCheckTask = Task.Run(async () =>
46+
{
47+
while (!_notificationCheckToken.Token.IsCancellationRequested)
48+
{
49+
try
50+
{
51+
int delayMs = App.Settings.Prop.OptimizeForLowEnd ? 15000 : 5000;
52+
await Task.Delay(delayMs, _notificationCheckToken.Token); // Check interval configurable based on low-end optimization
53+
54+
if (App.Settings.Prop.EnableFriendOnlineNotifications)
55+
{
56+
// Placeholder: Dalam implementasi nyata, ini akan mengambil data teman dari Roblox API
57+
// Untuk sekarang, ini adalah skeleton yang menunggu integrasi API
58+
await CheckFriendsStatus();
59+
}
60+
}
61+
catch (OperationCanceledException)
62+
{
63+
break;
64+
}
65+
catch (Exception ex)
66+
{
67+
App.Logger.WriteLine(LOG_IDENT, $"Error in notification monitoring: {ex.Message}");
68+
}
69+
}
70+
}, _notificationCheckToken.Token);
71+
}
72+
73+
public void StopNotificationMonitoring()
74+
{
75+
if (_notificationCheckToken != null)
76+
{
77+
_notificationCheckToken.Cancel();
78+
_notificationCheckToken.Dispose();
79+
_notificationCheckToken = null;
80+
}
81+
82+
_notifiedUsers.Clear();
83+
}
84+
85+
private async Task CheckFriendsStatus()
86+
{
87+
try
88+
{
89+
// TODO: Implementasi untuk mengambil status teman dari Roblox API
90+
// Untuk sekarang, ini adalah placeholder
91+
await Task.Delay(0);
92+
}
93+
catch (Exception ex)
94+
{
95+
App.Logger.WriteLine(LOG_IDENT, $"Error checking friends status: {ex.Message}");
96+
}
97+
}
98+
99+
public void ShowFriendOnlineNotification(string username)
100+
{
101+
try
102+
{
103+
if (_notifiedUsers.Contains(username))
104+
return;
105+
106+
_notifiedUsers.Add(username);
107+
108+
// Buat XML untuk toast notification
109+
string toastXml = $@"
110+
<toast>
111+
<visual>
112+
<binding template='ToastText02'>
113+
<text id='1'>👋 {System.Net.WebUtility.HtmlEncode(username)} ada online!</text>
114+
<text id='2'>Apa mau main bareng?</text>
115+
</binding>
116+
</visual>
117+
<audio src='ms-winsoundevent:Notification.Default'/>
118+
</toast>";
119+
120+
var xmlDoc = new XmlDocument();
121+
xmlDoc.LoadXml(toastXml);
122+
123+
var toast = new ToastNotification(xmlDoc);
124+
toast.ExpirationTime = DateTime.Now.AddSeconds(30);
125+
126+
try
127+
{
128+
ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);
129+
}
130+
catch
131+
{
132+
// Fallback jika toast notifier tidak tersedia
133+
ShowGeneralNotification($"{username} ada online!", "Apa mau main bareng?");
134+
}
135+
136+
App.Logger.WriteLine(LOG_IDENT, $"Menampilkan notifikasi untuk teman online: {username}");
137+
OnFriendOnline?.Invoke(this, new FriendNotificationEventArgs { Username = username });
138+
}
139+
catch (Exception ex)
140+
{
141+
App.Logger.WriteLine(LOG_IDENT, $"Error showing friend notification: {ex.Message}");
142+
}
143+
}
144+
145+
public void ShowGeneralNotification(string title, string message)
146+
{
147+
try
148+
{
149+
// Buat XML untuk toast notification umum
150+
string toastXml = $@"
151+
<toast>
152+
<visual>
153+
<binding template='ToastText02'>
154+
<text id='1'>{System.Net.WebUtility.HtmlEncode(title)}</text>
155+
<text id='2'>{System.Net.WebUtility.HtmlEncode(message)}</text>
156+
</binding>
157+
</visual>
158+
<audio src='ms-winsoundevent:Notification.Default'/>
159+
</toast>";
160+
161+
var xmlDoc = new XmlDocument();
162+
xmlDoc.LoadXml(toastXml);
163+
164+
var toast = new ToastNotification(xmlDoc);
165+
toast.ExpirationTime = DateTime.Now.AddSeconds(15);
166+
167+
try
168+
{
169+
ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);
170+
}
171+
catch
172+
{
173+
// Fallback jika toast notifier tidak tersedia
174+
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Information);
175+
}
176+
177+
App.Logger.WriteLine(LOG_IDENT, $"Menampilkan notifikasi: {title}");
178+
OnNotification?.Invoke(this, new NotificationEventArgs { Title = title, Message = message });
179+
}
180+
catch (Exception ex)
181+
{
182+
App.Logger.WriteLine(LOG_IDENT, $"Error showing notification: {ex.Message}");
183+
}
184+
}
185+
186+
public void ResetFriendNotification(string username)
187+
{
188+
_notifiedUsers.Remove(username);
189+
}
190+
191+
public void Dispose()
192+
{
193+
StopNotificationMonitoring();
194+
}
195+
}
196+
197+
public class FriendNotificationEventArgs : EventArgs
198+
{
199+
public string Username { get; set; } = "";
200+
}
201+
202+
public class NotificationEventArgs : EventArgs
203+
{
204+
public string Title { get; set; } = "";
205+
public string Message { get; set; } = "";
206+
}
207+
}

Bloxstrap/Models/Persistable/Settings.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ public class Settings
5555

5656
// experimental features
5757
public bool EnableSystemTrayOnClose { get; set; } = false;
58+
public bool EnableRobloxNotifications { get; set; } = false;
59+
public bool EnableFriendOnlineNotifications { get; set; } = false;
60+
public bool EnableNotificationSound { get; set; } = true;
61+
public bool EnableFpsMonitor { get; set; } = false;
62+
public double FpsMonitorX { get; set; } = 0;
63+
public double FpsMonitorY { get; set; } = 0;
64+
public bool OptimizeForLowEnd { get; set; } = false; // when true, reduce timers and visual updates to save CPU on older devices
5865

5966
// performance preset
6067
public string SelectedPerformancePreset { get; set; } = "None";
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<Window x:Class="Bloxstrap.UI.Elements.FpsMonitorOverlay"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
Title="FPS Monitor"
5+
Width="150"
6+
Height="100"
7+
Background="Transparent"
8+
AllowsTransparency="True"
9+
WindowStyle="None"
10+
ShowInTaskbar="False"
11+
Topmost="True"
12+
ResizeMode="NoResize"
13+
MouseDown="Window_MouseDown"
14+
MouseMove="Window_MouseMove"
15+
MouseUp="Window_MouseUp"
16+
MouseEnter="Window_MouseEnter"
17+
MouseLeave="Window_MouseLeave">
18+
<Grid Background="#1F1F1F" Opacity="0.85" CornerRadius="10" Margin="5">
19+
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
20+
<TextBlock
21+
Text="FPS Monitor"
22+
Foreground="White"
23+
FontSize="12"
24+
FontWeight="Bold"
25+
HorizontalAlignment="Center"
26+
Margin="0,0,0,5"/>
27+
28+
<TextBlock
29+
Name="FpsValueText"
30+
Text="0 FPS"
31+
Foreground="#00FF00"
32+
FontSize="20"
33+
FontWeight="Bold"
34+
FontFamily="Courier New"
35+
HorizontalAlignment="Center"
36+
Margin="0,0,0,3"/>
37+
38+
<TextBlock
39+
Name="FrameTimeText"
40+
Text="0.0 ms"
41+
Foreground="#FFD700"
42+
FontSize="11"
43+
FontFamily="Courier New"
44+
HorizontalAlignment="Center"
45+
Margin="0,0,0,3"/>
46+
47+
<TextBlock
48+
Name="StatusText"
49+
Text="Initializing..."
50+
Foreground="#87CEEB"
51+
FontSize="9"
52+
HorizontalAlignment="Center"/>
53+
</StackPanel>
54+
</Grid>
55+
</Window>

0 commit comments

Comments
 (0)