diff --git a/Bloxstrap/GlobalCache.cs b/Bloxstrap/GlobalCache.cs new file mode 100644 index 0000000..fcd6ede --- /dev/null +++ b/Bloxstrap/GlobalCache.cs @@ -0,0 +1,9 @@ +namespace Bloxstrap +{ + public static class GlobalCache + { + public static readonly Dictionary PendingTasks = new(); + + public static readonly Dictionary ServerLocation = new(); + } +} diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs index c08c45f..e807455 100644 --- a/Bloxstrap/Installer.cs +++ b/Bloxstrap/Installer.cs @@ -494,7 +494,7 @@ namespace Bloxstrap string oldStartPath = Path.Combine(Paths.WindowsStartMenu, "Bloxstrap"); if (File.Exists(oldDesktopPath)) - File.Move(oldDesktopPath, DesktopShortcut); + File.Move(oldDesktopPath, DesktopShortcut, true); if (Directory.Exists(oldStartPath)) { diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index 164c4d9..15d9676 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -127,7 +127,6 @@ namespace Bloxstrap.Integrations } } - // TODO: i need to double check how this handles failed game joins (connection error, invalid permissions, etc) private void ReadLogEntry(string entry) { const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; @@ -144,7 +143,17 @@ namespace Bloxstrap.Integrations App.Logger.WriteLine(LOG_IDENT, $"Read {_logEntriesRead} log entries"); if (entry.Contains(GameLeavingEntry)) - OnAppClose?.Invoke(this, new EventArgs()); + { + App.Logger.WriteLine(LOG_IDENT, "User is back into the desktop app"); + + OnAppClose?.Invoke(this, EventArgs.Empty); + + if (Data.PlaceId != 0 && !InGame) + { + App.Logger.WriteLine(LOG_IDENT, "User appears to be leaving from a cancelled/errored join"); + Data = new(); + } + } if (!InGame && Data.PlaceId == 0) { @@ -183,6 +192,9 @@ namespace Bloxstrap.Integrations Data.JobId = match.Groups[1].Value; Data.MachineAddress = match.Groups[3].Value; + if (App.Settings.Prop.ShowServerDetails && Data.MachineAddressValid) + _ = Data.QueryServerLocation(); + if (_teleportMarker) { Data.IsTeleport = true; @@ -195,7 +207,7 @@ namespace Bloxstrap.Integrations _reservedTeleportMarker = false; } - App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); + App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({Data})"); } } else if (!InGame && Data.PlaceId != 0) @@ -234,7 +246,7 @@ namespace Bloxstrap.Integrations { var lastActivity = History.First(); - if (lastActivity is not null && Data.UniverseId == lastActivity.UniverseId && Data.IsTeleport) + if (Data.UniverseId == lastActivity.UniverseId && Data.IsTeleport) Data.RootActivity = lastActivity.RootActivity ?? lastActivity; } } @@ -251,7 +263,10 @@ namespace Bloxstrap.Integrations Data.MachineAddress = match.Groups[1].Value; - App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); + if (App.Settings.Prop.ShowServerDetails) + _ = Data.QueryServerLocation(); + + App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({Data})"); } else if (entry.Contains(GameJoinedEntry)) { @@ -264,7 +279,7 @@ namespace Bloxstrap.Integrations return; } - App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); + App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({Data})"); InGame = true; Data.TimeJoined = DateTime.Now; @@ -278,7 +293,7 @@ namespace Bloxstrap.Integrations if (entry.Contains(GameDisconnectedEntry)) { - App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); + App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({Data})"); Data.TimeLeft = DateTime.Now; History.Insert(0, Data); @@ -290,7 +305,7 @@ namespace Bloxstrap.Integrations } else if (entry.Contains(GameTeleportingEntry)) { - App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); + App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({Data})"); _teleportMarker = true; } else if (_teleportMarker && entry.Contains(GameJoiningReservedServerEntry)) @@ -378,44 +393,6 @@ namespace Bloxstrap.Integrations } } - public async Task GetServerLocation() - { - const string LOG_IDENT = "ActivityWatcher::GetServerLocation"; - - if (GeolocationCache.ContainsKey(Data.MachineAddress)) - return GeolocationCache[Data.MachineAddress]; - - try - { - string location = ""; - var ipInfo = await Http.GetJson($"https://ipinfo.io/{Data.MachineAddress}/json"); - - if (String.IsNullOrEmpty(ipInfo.City)) - throw new InvalidHTTPResponseException("Reported city was blank"); - - if (ipInfo.City == ipInfo.Region) - location = $"{ipInfo.Region}, {ipInfo.Country}"; - else - location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}"; - - GeolocationCache[Data.MachineAddress] = location; - - if (!InGame) - return ""; - - return location; - } - catch (Exception ex) - { - App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {Data.MachineAddress}"); - App.Logger.WriteException(LOG_IDENT, ex); - - Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning); - - return "?"; - } - } - public void Dispose() { IsDisposed = true; diff --git a/Bloxstrap/Models/ActivityData.cs b/Bloxstrap/Models/ActivityData.cs index abd2ef5..fdc1520 100644 --- a/Bloxstrap/Models/ActivityData.cs +++ b/Bloxstrap/Models/ActivityData.cs @@ -1,4 +1,5 @@ using System.Web; +using System.Windows; using System.Windows.Input; using CommunityToolkit.Mvvm.Input; @@ -37,6 +38,8 @@ namespace Bloxstrap.Models public string MachineAddress { get; set; } = String.Empty; public string UserId { get; set; } = String.Empty; + + public bool MachineAddressValid => !String.IsNullOrEmpty(MachineAddress) && !MachineAddress.StartsWith("10."); public bool IsTeleport { get; set; } = false; @@ -85,6 +88,55 @@ namespace Bloxstrap.Models return deeplink; } + public async Task QueryServerLocation() + { + const string LOG_IDENT = "ActivityData::QueryServerLocation"; + + if (!MachineAddressValid) + throw new InvalidOperationException($"Machine address is invalid ({MachineAddress})"); + + if (GlobalCache.PendingTasks.TryGetValue(MachineAddress, out Task? task)) + await task; + + if (GlobalCache.ServerLocation.TryGetValue(MachineAddress, out string? location)) + return location; + + try + { + location = ""; + var ipInfoTask = Http.GetJson($"https://ipinfo.io/{MachineAddress}/json"); + + GlobalCache.PendingTasks.Add(MachineAddress, ipInfoTask); + + var ipInfo = await ipInfoTask; + + GlobalCache.PendingTasks.Remove(MachineAddress); + + if (String.IsNullOrEmpty(ipInfo.City)) + throw new InvalidHTTPResponseException("Reported city was blank"); + + if (ipInfo.City == ipInfo.Region) + location = $"{ipInfo.Region}, {ipInfo.Country}"; + else + location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}"; + + GlobalCache.ServerLocation[MachineAddress] = location; + + return location; + } + catch (Exception ex) + { + App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {MachineAddress}"); + App.Logger.WriteException(LOG_IDENT, ex); + + Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning); + + return "?"; + } + } + + public override string ToString() => $"{PlaceId}/{JobId}"; + private void RejoinServer() { string playerPath = Path.Combine(Paths.Versions, App.State.Prop.PlayerVersionGuid, "RobloxPlayerBeta.exe"); diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index f38705d..a21f089 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -719,15 +719,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to Loading, please wait.... - /// - public static string ContextMenu_ServerInformation_Loading { - get { - return ResourceManager.GetString("ContextMenu.ServerInformation.Loading", resourceCulture); - } - } - /// /// Looks up a localized string similar to Location. /// @@ -2727,7 +2718,25 @@ namespace Bloxstrap.Resources { } /// - /// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running.. + /// Looks up a localized string similar to When in-game, you'll be able to see where your server is located via [ipinfo.io]({0}).. + /// + public static string Menu_Integrations_QueryServerLocation_Description { + get { + return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Query server location. + /// + public static string Menu_Integrations_QueryServerLocation_Title { + get { + return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0}).. /// public static string Menu_Integrations_RequiresActivityTracking { get { @@ -2753,24 +2762,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to When you join a game, you'll be notified of where your server's located. Won't show in fullscreen.. - /// - public static string Menu_Integrations_ShowServerDetails_Description { - get { - return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to See server location when joining a game. - /// - public static string Menu_Integrations_ShowServerDetails_Title { - get { - return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Title", resourceCulture); - } - } - /// /// Looks up a localized string similar to Integrations. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 2380203..c60c4fb 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -268,9 +268,6 @@ Your ReShade configuration files will still be saved, and you can locate them by Instance ID - - Loading, please wait... - Location @@ -761,7 +758,7 @@ Selecting 'No' will ignore this warning and continue installation. Enable activity tracking - This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. + This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0}). The Roblox game you're playing will be shown on your Discord profile. [Not working?]({0}) @@ -769,11 +766,11 @@ Selecting 'No' will ignore this warning and continue installation. Show game activity - - When you join a game, you'll be notified of where your server's located. Won't show in fullscreen. + + When in-game, you'll be able to see where your server is located via [ipinfo.io]({0}). - - See server location when joining a game + + Query server location Integrations diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs index 25f1d68..388752e 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -28,6 +28,8 @@ namespace Bloxstrap.UI.Elements.ContextMenu private ServerInformation? _serverInformationWindow; + private ServerHistory? _gameHistoryWindow; + public MenuContainer(Watcher watcher) { InitializeComponent(); @@ -51,14 +53,14 @@ namespace Bloxstrap.UI.Elements.ContextMenu { if (_serverInformationWindow is null) { - _serverInformationWindow = new ServerInformation(_watcher); + _serverInformationWindow = new(_watcher); _serverInformationWindow.Closed += (_, _) => _serverInformationWindow = null; } if (!_serverInformationWindow.IsVisible) - _serverInformationWindow.Show(); - - _serverInformationWindow.Activate(); + _serverInformationWindow.ShowDialog(); + else + _serverInformationWindow.Activate(); } public void ActivityWatcher_OnLogOpen(object? sender, EventArgs e) => @@ -135,7 +137,16 @@ namespace Bloxstrap.UI.Elements.ContextMenu if (_activityWatcher is null) throw new ArgumentNullException(nameof(_activityWatcher)); - new ServerHistory(_activityWatcher).ShowDialog(); + if (_gameHistoryWindow is null) + { + _gameHistoryWindow = new(_activityWatcher); + _gameHistoryWindow.Closed += (_, _) => _gameHistoryWindow = null; + } + + if (!_gameHistoryWindow.IsVisible) + _gameHistoryWindow.ShowDialog(); + else + _gameHistoryWindow.Activate(); } } } diff --git a/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml b/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml index 381215c..3f12302 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml +++ b/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml @@ -46,14 +46,14 @@ - - + +