Skip to content

Commit 091dc14

Browse files
committed
changed Froststrap Studio RPC to local server type to avoid spamming output
1 parent d74ee34 commit 091dc14

3 files changed

Lines changed: 140 additions & 70 deletions

File tree

Bloxstrap/Bootstrapper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,7 @@ private async void StartRoblox()
10251025

10261026
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {_appPid}), waiting for log file");
10271027

1028+
// should i increase timeout ? since i think watcher dosent launh sometimes cause it cannot find the log file in time.
10281029
logCreatedEvent.WaitOne(TimeSpan.FromSeconds(15));
10291030

10301031
if (String.IsNullOrEmpty(logFileName))

Bloxstrap/Integrations/ActivityWatcher.cs

Lines changed: 139 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ namespace Bloxstrap.Integrations
55
public class ActivityWatcher : IDisposable
66
{
77
private const string GameMessageEntry = "[FLog::Output] [BloxstrapRPC]";
8-
private const string StudioMessageEntry = "[FLog::Output] [FroststrapStudioRPC]";
98
private const string GameJoiningEntry = "[FLog::Output] ! Joining game";
109

1110
// these entries are technically volatile!
@@ -32,7 +31,6 @@ public class ActivityWatcher : IDisposable
3231
private const string GameJoiningUDMUXPattern = @"UDMUX Address = ([0-9\.]+), Port = [0-9]+ \| RCC Server Address = ([0-9\.]+), Port = [0-9]+";
3332
private const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|[0-9]+";
3433
private const string GameMessageEntryPattern = @"\[BloxstrapRPC\] (.*)";
35-
private const string StudioMessagePattern = @"\[FroststrapStudioRPC\] (.*)";
3634

3735
private int _logEntriesRead = 0;
3836
private bool _teleportMarker = false;
@@ -64,6 +62,12 @@ public class ActivityWatcher : IDisposable
6462
public bool InStudioPlace = false;
6563
public bool InRobloxStudio = false;
6664

65+
private const int HttpPort = 4875;
66+
private HttpListener? _httpListener;
67+
private Thread? _httpListenerThread;
68+
private bool _isHttpListenerRunning = false;
69+
private readonly CancellationTokenSource _httpCancellationTokenSource = new();
70+
6771
public ActivityData Data { get; private set; } = new();
6872

6973
/// <summary>
@@ -78,7 +82,7 @@ public ActivityWatcher(string? logFile = null, LaunchMode launchMode = LaunchMod
7882
if (!String.IsNullOrEmpty(logFile))
7983
LogLocation = logFile;
8084

81-
_launchMode = launchMode;
85+
_launchMode = launchMode;
8286

8387
if (_launchMode == LaunchMode.Studio || _launchMode == LaunchMode.StudioAuth)
8488
InRobloxStudio = true;
@@ -208,6 +212,7 @@ private void ProcessStudioLogEntry(string logMessage)
208212
InStudioPlace = true;
209213
App.Logger.WriteLine(LOG_IDENT, "Studio place opened");
210214

215+
StartHTTPServer();
211216
OnStudioPlaceOpened?.Invoke(this, EventArgs.Empty);
212217
}
213218
}
@@ -218,74 +223,9 @@ private void ProcessStudioLogEntry(string logMessage)
218223
App.Logger.WriteLine(LOG_IDENT, "Studio place closed");
219224
InStudioPlace = false;
220225

226+
StopHTTPServer();
221227
OnStudioPlaceClosed?.Invoke(this, EventArgs.Empty);
222228
}
223-
else if (logMessage.StartsWith(StudioMessageEntry))
224-
{
225-
var match = Regex.Match(logMessage, StudioMessagePattern);
226-
227-
if (match.Groups.Count != 2)
228-
{
229-
App.Logger.WriteLine(LOG_IDENT, "Failed to parse Studio RPC message");
230-
return;
231-
}
232-
233-
string studioMessage = match.Groups[1].Value;
234-
App.Logger.WriteLine(LOG_IDENT, $"Studio RPC: {studioMessage}");
235-
236-
if (studioMessage.Contains("| Workspace: Game"))
237-
{
238-
App.Logger.WriteLine(LOG_IDENT, "Ignoring message because workspace is 'Game'");
239-
return;
240-
}
241-
242-
string workspace = "";
243-
string activityState = studioMessage;
244-
bool testing = false;
245-
string scriptType = "developing";
246-
247-
string[] parts = studioMessage.Split(new[] { " | " }, StringSplitOptions.None);
248-
249-
foreach (string part in parts)
250-
{
251-
if (part.StartsWith("Workspace:"))
252-
{
253-
workspace = part.Substring(10).Trim();
254-
}
255-
else if (part.StartsWith("Testing:"))
256-
{
257-
string testingStr = part.Substring(8).Trim();
258-
testing = testingStr.Equals("True", StringComparison.OrdinalIgnoreCase);
259-
}
260-
else if (part.StartsWith("Type:"))
261-
{
262-
scriptType = part.Substring(5).Trim();
263-
}
264-
else if (!part.Contains("Workspace:") && !part.Contains("Testing:") && !part.Contains("Type:"))
265-
{
266-
activityState = part.Trim();
267-
}
268-
}
269-
270-
var studioRpc = new StudioMessage
271-
{
272-
Data = new StudioRichPresence
273-
{
274-
Details = activityState,
275-
State = !string.IsNullOrEmpty(workspace) ? $"Workspace: {workspace}" : null!,
276-
Testing = testing,
277-
ScriptType = scriptType
278-
}
279-
};
280-
281-
string json = JsonSerializer.Serialize(studioRpc);
282-
var rpcMessage = JsonSerializer.Deserialize<StudioMessage>(json);
283-
284-
if (rpcMessage != null)
285-
{
286-
OnStudioRPCMessage?.Invoke(this, rpcMessage);
287-
}
288-
}
289229
}
290230
}
291231

@@ -595,6 +535,135 @@ private void ProcessPlayerLogEntry(string logMessage)
595535
}
596536
}
597537

538+
private void StartHTTPServer()
539+
{
540+
try
541+
{
542+
_httpListener = new HttpListener();
543+
544+
_httpListener.Prefixes.Add($"http://localhost:{HttpPort}/");
545+
546+
_httpListener.Start();
547+
548+
_isHttpListenerRunning = true;
549+
_httpListenerThread = new Thread(() => ListenForHTTPRequests(_httpCancellationTokenSource.Token));
550+
_httpListenerThread.IsBackground = true;
551+
_httpListenerThread.Name = "StudioRPC-HTTP-Listener";
552+
_httpListenerThread.Start();
553+
554+
App.Logger.WriteLine("ActivityWatcher::StartHTTPServer", $"HTTP server started on port {HttpPort}");
555+
}
556+
catch (Exception ex)
557+
{
558+
App.Logger.WriteException("ActivityWatcher::StartHTTPServer", ex);
559+
}
560+
}
561+
562+
private async void ListenForHTTPRequests(CancellationToken cancellationToken)
563+
{
564+
while (_isHttpListenerRunning && _httpListener != null && _httpListener.IsListening && !cancellationToken.IsCancellationRequested)
565+
{
566+
try
567+
{
568+
var context = await _httpListener.GetContextAsync().WaitAsync(cancellationToken);
569+
_ = Task.Run(() => ProcessHTTPRequest(context), cancellationToken);
570+
}
571+
catch (HttpListenerException)
572+
{
573+
break;
574+
}
575+
catch (OperationCanceledException)
576+
{
577+
break;
578+
}
579+
catch (Exception ex)
580+
{
581+
App.Logger.WriteException("ActivityWatcher::ListenForHTTPRequests", ex);
582+
await Task.Delay(1000, cancellationToken);
583+
}
584+
}
585+
}
586+
587+
private void ProcessHTTPRequest(HttpListenerContext context)
588+
{
589+
const string LOG_IDENT = "ActivityWatcher::ProcessHTTPRequest";
590+
591+
try
592+
{
593+
if (context.Request.HttpMethod == "POST" && context.Request.Url?.AbsolutePath == "/rpc")
594+
{
595+
using (var reader = new StreamReader(context.Request.InputStream, Encoding.UTF8))
596+
{
597+
string json = reader.ReadToEnd();
598+
App.Logger.WriteLine(LOG_IDENT, $"Received HTTP RPC: {json}");
599+
600+
var studioMessage = JsonSerializer.Deserialize<StudioMessage>(json);
601+
602+
if (studioMessage != null)
603+
{
604+
if (studioMessage.Data == null)
605+
{
606+
studioMessage.Data = new StudioRichPresence();
607+
}
608+
609+
OnStudioRPCMessage?.Invoke(this, studioMessage);
610+
}
611+
else
612+
{
613+
App.Logger.WriteLine(LOG_IDENT, "Failed to parse JSON message");
614+
}
615+
}
616+
617+
context.Response.StatusCode = 200;
618+
context.Response.Close();
619+
}
620+
else
621+
{
622+
context.Response.StatusCode = 404;
623+
context.Response.Close();
624+
}
625+
}
626+
catch (JsonException jsonEx)
627+
{
628+
App.Logger.WriteLine(LOG_IDENT, $"JSON parsing error: {jsonEx.Message}");
629+
context.Response.StatusCode = 400;
630+
context.Response.Close();
631+
}
632+
catch (Exception ex)
633+
{
634+
App.Logger.WriteException(LOG_IDENT, ex);
635+
context.Response.StatusCode = 500;
636+
context.Response.Close();
637+
}
638+
}
639+
640+
private void StopHTTPServer()
641+
{
642+
_isHttpListenerRunning = false;
643+
_httpCancellationTokenSource.Cancel();
644+
645+
if (_httpListener != null)
646+
{
647+
try
648+
{
649+
_httpListener.Stop();
650+
_httpListener.Close();
651+
}
652+
catch (Exception ex)
653+
{
654+
App.Logger.WriteException("ActivityWatcher::StopHTTPServer", ex);
655+
}
656+
_httpListener = null;
657+
}
658+
659+
if (_httpListenerThread != null && _httpListenerThread.IsAlive)
660+
{
661+
_httpListenerThread.Join(2000);
662+
}
663+
664+
_httpCancellationTokenSource.Dispose();
665+
}
666+
598667
public void LoadGameHistory()
599668
{
600669
try
@@ -710,6 +779,7 @@ private void AddToHistory(ActivityData activity)
710779
public void Dispose()
711780
{
712781
IsDisposed = true;
782+
StopHTTPServer();
713783
GC.SuppressFinalize(this);
714784
}
715785
}

Bloxstrap/Utility/Shortcut.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Windows;
2-
using Bloxstrap.Resources;
32

43
namespace Bloxstrap.Utility
54
{

0 commit comments

Comments
 (0)