Skip to content

Commit

Permalink
Improvements to server location querying
Browse files Browse the repository at this point in the history
  • Loading branch information
pizzaboxer committed Sep 5, 2024
1 parent 0d5be93 commit 15dc2df
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 99 deletions.
9 changes: 9 additions & 0 deletions Bloxstrap/GlobalCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Bloxstrap
{
public static class GlobalCache
{
public static readonly Dictionary<string, Task> PendingTasks = new();

public static readonly Dictionary<string, string> ServerLocation = new();
}
}
54 changes: 11 additions & 43 deletions Bloxstrap/Integrations/ActivityWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ private void ReadLogEntry(string entry)
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;
Expand All @@ -202,7 +205,7 @@ private void ReadLogEntry(string entry)
_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)
Expand Down Expand Up @@ -243,7 +246,10 @@ private void ReadLogEntry(string entry)

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))
{
Expand All @@ -256,7 +262,7 @@ private void ReadLogEntry(string entry)
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;
Expand All @@ -270,7 +276,7 @@ private void ReadLogEntry(string entry)

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);
Expand All @@ -283,7 +289,7 @@ private void ReadLogEntry(string entry)
}
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))
Expand Down Expand Up @@ -371,44 +377,6 @@ private void ReadLogEntry(string entry)
}
}

public async Task<string> GetServerLocation()
{
const string LOG_IDENT = "ActivityWatcher::GetServerLocation";

if (GeolocationCache.ContainsKey(Data.MachineAddress))
return GeolocationCache[Data.MachineAddress];

try
{
string location = "";
var ipInfo = await Http.GetJson<IPInfoResponse>($"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;
Expand Down
52 changes: 52 additions & 0 deletions Bloxstrap/Models/ActivityData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Web;
using System.Windows;
using System.Windows.Input;

using CommunityToolkit.Mvvm.Input;
Expand Down Expand Up @@ -36,6 +37,8 @@ public long UniverseId

public string MachineAddress { get; set; } = String.Empty;

public bool MachineAddressValid => !String.IsNullOrEmpty(MachineAddress) && !MachineAddress.StartsWith("10.");

public bool IsTeleport { get; set; } = false;

public ServerType ServerType { get; set; } = ServerType.Public;
Expand Down Expand Up @@ -83,6 +86,55 @@ public string GetInviteDeeplink(bool launchData = true)
return deeplink;
}

public async Task<string> 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<IPInfoResponse>($"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");
Expand Down
39 changes: 15 additions & 24 deletions Bloxstrap/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 5 additions & 8 deletions Bloxstrap/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,6 @@ Your ReShade configuration files will still be saved, and you can locate them by
<data name="ContextMenu.ServerInformation.InstanceId" xml:space="preserve">
<value>Instance ID</value>
</data>
<data name="ContextMenu.ServerInformation.Loading" xml:space="preserve">
<value>Loading, please wait...</value>
</data>
<data name="ContextMenu.ServerInformation.Location" xml:space="preserve">
<value>Location</value>
</data>
Expand Down Expand Up @@ -755,19 +752,19 @@ Selecting 'No' will ignore this warning and continue installation.</value>
<value>Enable activity tracking</value>
</data>
<data name="Menu.Integrations.RequiresActivityTracking" xml:space="preserve">
<value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running.</value>
<value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0}).</value>
</data>
<data name="Menu.Integrations.ShowGameActivity.Description" xml:space="preserve">
<value>The Roblox game you're playing will be shown on your Discord profile. [Not working?]({0})</value>
</data>
<data name="Menu.Integrations.ShowGameActivity.Title" xml:space="preserve">
<value>Show game activity</value>
</data>
<data name="Menu.Integrations.ShowServerDetails.Description" xml:space="preserve">
<value>When you join a game, you'll be notified of where your server's located. Won't show in fullscreen.</value>
<data name="Menu.Integrations.QueryServerLocation.Description" xml:space="preserve">
<value>When in-game, you'll be able to see where your server is located via [ipinfo.io]({0}).</value>
</data>
<data name="Menu.Integrations.ShowServerDetails.Title" xml:space="preserve">
<value>See server location when joining a game</value>
<data name="Menu.Integrations.QueryServerLocation.Title" xml:space="preserve">
<value>Query server location</value>
</data>
<data name="Menu.Integrations.Title" xml:space="preserve">
<value>Integrations</value>
Expand Down
6 changes: 3 additions & 3 deletions Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_InstanceId}" />
<TextBlock Grid.Row="1" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding InstanceId, Mode=OneWay}" />

<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" />
<TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" />
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
<TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
</Grid>

<Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
<Button MinWidth="100" Content="{x:Static resources:Strings.ContextMenu_ServerInformation_CopyInstanceId}" Command="{Binding CopyInstanceIdCommand, Mode=OneTime}" />
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Command="{Binding CloseWindowCommand, Mode=OneTime}" />
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" IsCancel="True" />
</StackPanel>
</Border>
</Grid>
Expand Down
6 changes: 1 addition & 5 deletions Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ public partial class ServerInformation
{
public ServerInformation(Watcher watcher)
{
var viewModel = new ServerInformationViewModel(watcher);

viewModel.RequestCloseEvent += (_, _) => Close();

DataContext = viewModel;
DataContext = new ServerInformationViewModel(watcher);
InitializeComponent();
}
}
Expand Down
7 changes: 4 additions & 3 deletions Bloxstrap/UI/Elements/Settings/Pages/IntegrationsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
</controls:OptionControl>

<controls:OptionControl
Header="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Title}"
Description="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Description}"
Header="{x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Title}"
Description="{Binding Source={x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ipinfo.io'}"
HelpLink="https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#server-location-querying"
IsEnabled="{Binding InnerContent.IsChecked, ElementName=ActivityTrackingOption, Mode=OneWay}">
<ui:ToggleSwitch IsChecked="{Binding ShowServerDetailsEnabled, Mode=TwoWay}" />
</controls:OptionControl>
Expand All @@ -40,7 +41,7 @@
</controls:OptionControl>

<TextBlock Text="{x:Static resources:Strings.Common_DiscordRichPresence}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#discord-rich-presence'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />

<controls:OptionControl
Header="{x:Static resources:Strings.Menu_Integrations_ShowGameActivity_Title}"
Expand Down
7 changes: 4 additions & 3 deletions Bloxstrap/UI/NotifyIconWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public NotifyIconWrapper(Watcher watcher)

_watcher = watcher;

_notifyIcon = new()
_notifyIcon = new(new System.ComponentModel.Container())
{
Icon = Properties.Resources.IconBloxstrap,
Text = App.ProjectName,
Expand All @@ -35,7 +35,7 @@ public NotifyIconWrapper(Watcher watcher)

_notifyIcon.MouseClick += MouseClickEventHandler;

if (_activityWatcher is not null)
if (_activityWatcher is not null && App.Settings.Prop.ShowServerDetails)
_activityWatcher.OnGameJoin += OnGameJoin;

_menuContainer = new(_watcher);
Expand All @@ -59,7 +59,7 @@ public async void OnGameJoin(object? sender, EventArgs e)
if (_activityWatcher is null)
return;

string serverLocation = await _activityWatcher.GetServerLocation();
string serverLocation = await _activityWatcher.Data.QueryServerLocation();

if (string.IsNullOrEmpty(serverLocation))
return;
Expand All @@ -81,6 +81,7 @@ public async void OnGameJoin(object? sender, EventArgs e)
}
#endregion

// we may need to create our own handler for this, because this sorta sucks
public void ShowAlert(string caption, string message, int duration, EventHandler? clickHandler)
{
string id = Guid.NewGuid().ToString()[..8];
Expand Down
Loading

0 comments on commit 15dc2df

Please sign in to comment.