diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 1844cf57..969966b6 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -281,19 +281,21 @@ private void StartRoblox() SetStatus(Strings.Bootstrapper_Status_Starting); - if (App.Settings.Prop.ForceRobloxLanguage) + if (_launchMode == LaunchMode.Player) { - var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant); + if (App.Settings.Prop.ForceRobloxLanguage) + { + var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant); - if (match.Groups.Count == 2) - _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); - } + if (match.Groups.Count == 2) + _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); + } - // needed for the start event to fire - if (!String.IsNullOrEmpty(_launchCommandLine)) - _launchCommandLine += " "; + if (!String.IsNullOrEmpty(_launchCommandLine)) + _launchCommandLine += " "; - _launchCommandLine += "-isInstallerLaunch"; + _launchCommandLine += "-isInstallerLaunch"; + } var startInfo = new ProcessStartInfo() { @@ -308,7 +310,7 @@ private void StartRoblox() return; } - using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "www.roblox.com/robloxStartedEvent"); + using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent); // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now int gameClientPid; @@ -357,7 +359,6 @@ private void StartRoblox() autoclosePids.Add(pid); } - string args = gameClientPid.ToString(); if (autoclosePids.Any()) diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index fbca8d11..f1ab955e 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -1,4 +1,6 @@ -namespace Bloxstrap.Integrations +using System.Web; + +namespace Bloxstrap.Integrations { public class ActivityWatcher : IDisposable { @@ -43,6 +45,7 @@ public class ActivityWatcher : IDisposable public string ActivityMachineAddress = ""; public bool ActivityMachineUDMUX = false; public bool ActivityIsTeleport = false; + public string ActivityLaunchData = ""; public ServerType ActivityServerType = ServerType.Public; public bool IsDisposed = false; @@ -119,6 +122,16 @@ public async void Start() } } + public string GetActivityDeeplink() + { + string deeplink = $"roblox://experiences/start?placeId={ActivityPlaceId}&gameInstanceId={ActivityJobId}"; + + if (!String.IsNullOrEmpty(ActivityLaunchData)) + deeplink += "&launchData=" + HttpUtility.UrlEncode(ActivityLaunchData); + + return deeplink; + } + private void ReadLogEntry(string entry) { const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; @@ -222,6 +235,7 @@ private void ReadLogEntry(string entry) ActivityMachineAddress = ""; ActivityMachineUDMUX = false; ActivityIsTeleport = false; + ActivityLaunchData = ""; ActivityServerType = ServerType.Public; OnGameLeave?.Invoke(this, new EventArgs()); @@ -280,6 +294,35 @@ private void ReadLogEntry(string entry) return; } + if (message.Command == "SetLaunchData") + { + string? data; + + try + { + data = message.Data.Deserialize(); + } + catch (Exception) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); + return; + } + + if (data is null) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); + return; + } + + if (data.Length > 200) + { + App.Logger.WriteLine(LOG_IDENT, "Data cannot be longer than 200 characters"); + return; + } + + ActivityLaunchData = data; + } + OnRPCMessage?.Invoke(this, message); LastRPCRequest = DateTime.Now; diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index 7f30aab6..9dfc6249 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -51,7 +51,7 @@ public void ProcessRPCMessage(Message message, bool implicitUpdate = true) { const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage"; - if (message.Command != "SetRichPresence") + if (message.Command != "SetRichPresence" && message.Command != "SetLaunchData") return; if (_currentPresence is null || _currentPresenceCopy is null) @@ -61,95 +61,107 @@ public void ProcessRPCMessage(Message message, bool implicitUpdate = true) return; } - Models.BloxstrapRPC.RichPresence? presenceData; - // a lot of repeated code here, could this somehow be cleaned up? - try - { - presenceData = message.Data.Deserialize(); - } - catch (Exception) + if (message.Command == "SetLaunchData") { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); - return; - } + var buttonQuery = _currentPresence.Buttons.Where(x => x.Label == "Join server"); - if (presenceData is null) - { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); - return; - } + if (!buttonQuery.Any()) + return; - if (presenceData.Details is not null) - { - if (presenceData.Details.Length > 128) - App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters"); - else if (presenceData.Details == "") - _currentPresence.Details = _currentPresenceCopy.Details; - else - _currentPresence.Details = presenceData.Details; + buttonQuery.First().Url = _activityWatcher.GetActivityDeeplink(); } - - if (presenceData.State is not null) + else if (message.Command == "SetRichPresence") { - if (presenceData.State.Length > 128) - App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters"); - else if (presenceData.State == "") - _currentPresence.State = _currentPresenceCopy.State; - else - _currentPresence.State = presenceData.State; - } - - if (presenceData.TimestampStart == 0) - _currentPresence.Timestamps.Start = null; - else if (presenceData.TimestampStart is not null) - _currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000; + Models.BloxstrapRPC.RichPresence? presenceData; - if (presenceData.TimestampEnd == 0) - _currentPresence.Timestamps.End = null; - else if (presenceData.TimestampEnd is not null) - _currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000; - - if (presenceData.SmallImage is not null) - { - if (presenceData.SmallImage.Clear) + try { - _currentPresence.Assets.SmallImageKey = ""; + presenceData = message.Data.Deserialize(); } - else if (presenceData.SmallImage.Reset) + catch (Exception) { - _currentPresence.Assets.SmallImageText = _currentPresenceCopy.Assets.SmallImageText; - _currentPresence.Assets.SmallImageKey = _currentPresenceCopy.Assets.SmallImageKey; + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); + return; } - else - { - if (presenceData.SmallImage.AssetId is not null) - _currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}"; - if (presenceData.SmallImage.HoverText is not null) - _currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText; + if (presenceData is null) + { + App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); + return; } - } - if (presenceData.LargeImage is not null) - { - if (presenceData.LargeImage.Clear) + if (presenceData.Details is not null) { - _currentPresence.Assets.LargeImageKey = ""; + if (presenceData.Details.Length > 128) + App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters"); + else if (presenceData.Details == "") + _currentPresence.Details = _currentPresenceCopy.Details; + else + _currentPresence.Details = presenceData.Details; } - else if (presenceData.LargeImage.Reset) + + if (presenceData.State is not null) { - _currentPresence.Assets.LargeImageText = _currentPresenceCopy.Assets.LargeImageText; - _currentPresence.Assets.LargeImageKey = _currentPresenceCopy.Assets.LargeImageKey; + if (presenceData.State.Length > 128) + App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters"); + else if (presenceData.State == "") + _currentPresence.State = _currentPresenceCopy.State; + else + _currentPresence.State = presenceData.State; } - else + + if (presenceData.TimestampStart == 0) + _currentPresence.Timestamps.Start = null; + else if (presenceData.TimestampStart is not null) + _currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000; + + if (presenceData.TimestampEnd == 0) + _currentPresence.Timestamps.End = null; + else if (presenceData.TimestampEnd is not null) + _currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000; + + if (presenceData.SmallImage is not null) { - if (presenceData.LargeImage.AssetId is not null) - _currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}"; + if (presenceData.SmallImage.Clear) + { + _currentPresence.Assets.SmallImageKey = ""; + } + else if (presenceData.SmallImage.Reset) + { + _currentPresence.Assets.SmallImageText = _currentPresenceCopy.Assets.SmallImageText; + _currentPresence.Assets.SmallImageKey = _currentPresenceCopy.Assets.SmallImageKey; + } + else + { + if (presenceData.SmallImage.AssetId is not null) + _currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}"; + + if (presenceData.SmallImage.HoverText is not null) + _currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText; + } + } - if (presenceData.LargeImage.HoverText is not null) - _currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText; + if (presenceData.LargeImage is not null) + { + if (presenceData.LargeImage.Clear) + { + _currentPresence.Assets.LargeImageKey = ""; + } + else if (presenceData.LargeImage.Reset) + { + _currentPresence.Assets.LargeImageText = _currentPresenceCopy.Assets.LargeImageText; + _currentPresence.Assets.LargeImageKey = _currentPresenceCopy.Assets.LargeImageKey; + } + else + { + if (presenceData.LargeImage.AssetId is not null) + _currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}"; + + if (presenceData.LargeImage.HoverText is not null) + _currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText; + } } } diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs index c4412ba7..bd908444 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -104,7 +104,7 @@ private void Window_Loaded(object? sender, RoutedEventArgs e) private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked); - private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}"); + private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject(_activityWatcher?.GetActivityDeeplink()); private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow();