diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 1844cf5..969966b 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -281,20 +281,22 @@ namespace Bloxstrap 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); + } + + if (!String.IsNullOrEmpty(_launchCommandLine)) + _launchCommandLine += " "; + + _launchCommandLine += "-isInstallerLaunch"; } - // needed for the start event to fire - if (!String.IsNullOrEmpty(_launchCommandLine)) - _launchCommandLine += " "; - - _launchCommandLine += "-isInstallerLaunch"; - var startInfo = new ProcessStartInfo() { FileName = _playerLocation, @@ -308,7 +310,7 @@ namespace Bloxstrap 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 @@ namespace Bloxstrap autoclosePids.Add(pid); } - string args = gameClientPid.ToString(); if (autoclosePids.Any()) diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index fbca8d1..f1ab955 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 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 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 @@ ActivityMachineAddress = ""; ActivityMachineUDMUX = false; ActivityIsTeleport = false; + ActivityLaunchData = ""; ActivityServerType = ServerType.Public; OnGameLeave?.Invoke(this, new EventArgs()); @@ -280,6 +294,35 @@ 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 7f30aab..9dfc624 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -51,7 +51,7 @@ namespace Bloxstrap.Integrations { 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 @@ namespace Bloxstrap.Integrations return; } - Models.BloxstrapRPC.RichPresence? presenceData; - // a lot of repeated code here, could this somehow be cleaned up? - try + if (message.Command == "SetLaunchData") { - presenceData = message.Data.Deserialize(); + var buttonQuery = _currentPresence.Buttons.Where(x => x.Label == "Join server"); + + if (!buttonQuery.Any()) + return; + + buttonQuery.First().Url = _activityWatcher.GetActivityDeeplink(); } - catch (Exception) + else if (message.Command == "SetRichPresence") { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)"); - return; - } + Models.BloxstrapRPC.RichPresence? presenceData; - if (presenceData is null) - { - App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)"); - 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; - } - - if (presenceData.State is not null) - { - 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; - - 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) - { - _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; + if (presenceData.State is not null) + { + 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; + + 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) + { + _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 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 c4412ba..bd90844 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -104,7 +104,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu 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();