Compare commits

...
2 Commits
Author SHA1 Message Date
amacocianandGitHub bca9f6d66b Fix loading screen flickering (#295)
* Fix loading screen flickering

* Increment version
2023-06-29 15:01:41 +00:00
amacocianandGitHub d6f71cb2eb Performance improvements (#293)
Ability to disable minimap
Closes #291
Fix main player health/energy proper values
Better granularity for memory access
Focus view uses less memory footprint to gather necesarry resources
2023-06-29 13:38:28 +00:00
20 changed files with 541 additions and 301 deletions
@@ -5,10 +5,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls.FocusViewComponents"
xmlns:controls="clr-namespace:Daybreak.Controls"
xmlns:converters="clr-namespace:Daybreak.Converters"
x:Name="_this"
mc:Ignorable="d"
Loaded="Control_Loaded"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:BooleanToGridLengthConverter x:Key="ResourceBarGridLengthConverter" VisibleValue="*" />
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
@@ -25,7 +28,6 @@
Text="{Binding ElementName=_this,Path=ExperienceBarText, Mode=OneWay}"
BarColor="{DynamicResource Daybreak.Brushes.Experience}"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
Height="113"
FontSize="14"
Cursor="Hand"
MouseLeftButtonDown="ExperienceBar_MouseLeftButtonDown">
@@ -63,13 +65,13 @@
StrokeThickness="0.08"
Stroke="{DynamicResource MahApps.Brushes.ThemeForeground}"></Polygon>
</Viewbox>
<StackPanel
<Grid
VerticalAlignment="Center"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
Grid.RowSpan="2">
<StackPanel.OpacityMask>
<Grid.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
@@ -92,12 +94,18 @@
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</StackPanel.OpacityMask>
</Grid.OpacityMask>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=User.CurrentKurzickPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.MaxKurzickPoints, Mode=OneWay}"
Grid.Row="0"
CurrentResourceValue="{Binding Path=User.User.CurrentKurzickPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.User.MaxKurzickPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Kurzick}"
@@ -110,9 +118,9 @@
MouseLeftButtonDown="KurzickBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=User.CurrentLuxonPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.MaxLuxonPoints, Mode=OneWay}"
Grid.Row="1"
CurrentResourceValue="{Binding Path=User.User.CurrentLuxonPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.User.MaxLuxonPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Luxon}"
@@ -125,9 +133,9 @@
MouseLeftButtonDown="LuxonBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=User.CurrentImperialPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.MaxImperialPoints, Mode=OneWay}"
Grid.Row="2"
CurrentResourceValue="{Binding Path=User.User.CurrentImperialPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.User.MaxImperialPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Imperial}"
@@ -140,9 +148,9 @@
MouseLeftButtonDown="ImperialBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=User.CurrentBalthazarPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.MaxBalthazarPoints, Mode=OneWay}"
Grid.Row="3"
CurrentResourceValue="{Binding Path=User.User.CurrentBalthazarPoints, Mode=OneWay}"
MaxResourceValue="{Binding Path=User.User.MaxBalthazarPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Balthazar}"
@@ -153,7 +161,7 @@
BorderThickness="1"
Text="{Binding ElementName=_this, Path=BalthazarBarText, Mode=OneWay}"
MouseLeftButtonDown="BalthazarBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
</StackPanel>
</Grid>
<Viewbox
Grid.Column="0"
Grid.ColumnSpan="2"
@@ -166,14 +174,14 @@
Height="10.2"
Stroke="{DynamicResource MahApps.Brushes.ThemeForeground}"></Polygon>
</Viewbox>
<StackPanel
<Grid
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Grid.Column="3"
Grid.ColumnSpan="2"
Grid.Row="2"
Grid.RowSpan="2">
<StackPanel.OpacityMask>
<Grid.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
@@ -196,11 +204,17 @@
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</StackPanel.OpacityMask>
</Grid.OpacityMask>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding ElementName=_this, Path=Vanquishing, Mode=OneWay, Converter={StaticResource ResourceBarGridLengthConverter}}"></RowDefinition>
<RowDefinition Height="{Binding ElementName=_this, Path=TitleActive, Mode=OneWay, Converter={StaticResource ResourceBarGridLengthConverter}}"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=Session.FoesKilled, Mode=OneWay}"
Grid.Row="0"
CurrentResourceValue="{Binding Path=Session.Session.FoesKilled, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=TotalFoes, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
@@ -216,7 +230,7 @@
MouseLeftButtonDown="VanquishingBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
Grid.Row="1"
CurrentResourceValue="{Binding ElementName=_this, Path=PointsInCurrentRank, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=PointsForNextRank, Mode=OneWay}"
VerticalAlignment="Bottom"
@@ -232,9 +246,9 @@
Visibility="{Binding ElementName=_this, Path=TitleActive, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=MainPlayer.CurrentHealth, Mode=OneWay}"
MaxResourceValue="{Binding Path=MainPlayer.MaxHealth, Mode=OneWay}"
Grid.Row="2"
CurrentResourceValue="{Binding Path=Player.PlayerInformation.CurrentHealth, Mode=OneWay}"
MaxResourceValue="{Binding Path=Player.PlayerInformation.MaxHealth, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Health}"
@@ -248,9 +262,9 @@
MouseLeftButtonDown="HealthBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding Path=MainPlayer.CurrentEnergy, Mode=OneWay}"
MaxResourceValue="{Binding Path=MainPlayer.MaxEnergy, Mode=OneWay}"
Grid.Row="3"
CurrentResourceValue="{Binding Path=Player.PlayerInformation.CurrentEnergy, Mode=OneWay}"
MaxResourceValue="{Binding Path=Player.PlayerInformation.MaxEnergy, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="{DynamicResource Daybreak.Brushes.Energy}"
@@ -262,7 +276,7 @@
BorderBrush="{DynamicResource MahApps.Brushes.ThemeForeground}"
BorderThickness="1"
MouseLeftButtonDown="EnergyBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
</StackPanel>
</Grid>
<Viewbox
Grid.Column="3"
Grid.ColumnSpan="2"
@@ -17,8 +17,6 @@ namespace Daybreak.Controls.FocusViewComponents;
/// </summary>
public partial class PlayerResourcesComponent : UserControl
{
private const double BarsTotalSize = 116; // Size of the bars on one side of the screen.
private readonly IExperienceCalculator experienceCalculator;
private readonly ILiveUpdateableOptions<FocusViewOptions> liveOptions;
@@ -58,11 +56,6 @@ public partial class PlayerResourcesComponent : UserControl
[GenerateDependencyProperty]
private string vanquishingText = string.Empty;
[GenerateDependencyProperty]
private double leftSideBarSize;
[GenerateDependencyProperty]
private double rightSideBarSize;
public PlayerResourcesComponent()
: this(
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IExperienceCalculator>(),
@@ -77,34 +70,17 @@ public partial class PlayerResourcesComponent : UserControl
this.experienceCalculator = experienceCalculator.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
this.InitializeComponent();
this.LeftSideBarSize = 25;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == VanquishingProperty &&
e.OldValue != e.NewValue)
{
this.UpdateRightSideBarsLayout();
}
else if (e.Property == TitleActiveProperty &&
e.OldValue != e.NewValue)
{
this.UpdateRightSideBarsLayout();
}
else if (e.Property == DataContextProperty)
if (e.Property == DataContextProperty)
{
this.UpdateGameData();
}
}
private void Control_Loaded(object sender, RoutedEventArgs e)
{
this.UpdateRightSideBarsLayout();
}
private void ExperienceBar_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
{
switch (this.liveOptions.Value.ExperienceDisplay)
@@ -260,40 +236,23 @@ public partial class PlayerResourcesComponent : UserControl
this.UpdateEnergyText();
}
private void UpdateRightSideBarsLayout()
{
var bars = 2;
if (this.Vanquishing)
{
bars += 1;
}
if (this.TitleActive)
{
bars += 1;
}
var marginsTotalSize = 4 * bars;
var finalBarSize = (BarsTotalSize - marginsTotalSize) / bars;
this.RightSideBarSize = finalBarSize;
}
private void UpdateGameData()
{
if (this.DataContext is not GameData gameData ||
gameData.MainPlayer is null ||
gameData.Session is null)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.User?.User is null ||
mainPlayerResourceContext.Session?.Session is null ||
mainPlayerResourceContext.Player?.PlayerInformation is null)
{
return;
}
this.CurrentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
this.NextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
this.TotalFoes = (int)(gameData.Session.FoesKilled + gameData.Session.FoesToKill);
this.Vanquishing = gameData.Session.FoesToKill + gameData.Session.FoesKilled > 0U;
this.TitleActive = gameData.MainPlayer.TitleInformation is not null && gameData.MainPlayer.TitleInformation.IsValid;
this.CurrentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(mainPlayerResourceContext.Player.PlayerInformation.Experience);
this.NextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(mainPlayerResourceContext.Player.PlayerInformation.Experience);
this.TotalFoes = (int)(mainPlayerResourceContext.Session.Session.FoesKilled + mainPlayerResourceContext.Session.Session.FoesToKill);
this.Vanquishing = mainPlayerResourceContext.Session.Session.FoesToKill + mainPlayerResourceContext.Session.Session.FoesKilled > 0U;
this.TitleActive = mainPlayerResourceContext.Player.PlayerInformation.TitleInformation is not null && mainPlayerResourceContext.Player.PlayerInformation.TitleInformation.IsValid;
if (gameData.MainPlayer.TitleInformation is TitleInformation titleInformation && titleInformation.IsValid)
if (mainPlayerResourceContext.Player.PlayerInformation.TitleInformation is TitleInformation titleInformation && titleInformation.IsValid)
{
if (titleInformation.MaxTierNumber == titleInformation.TierNumber)
{
@@ -325,8 +284,8 @@ public partial class PlayerResourcesComponent : UserControl
private void UpdateExperienceText()
{
if (this.DataContext is not GameData gameData ||
gameData.MainPlayer is null)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.Player?.PlayerInformation is null)
{
return;
}
@@ -334,22 +293,22 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.ExperienceDisplay)
{
case Configuration.FocusView.ExperienceDisplay.CurrentLevelCurrentAndCurrentLevelMax:
var currentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
var nextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
var currentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(mainPlayerResourceContext.Player.PlayerInformation.Experience);
var nextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(mainPlayerResourceContext.Player.PlayerInformation.Experience);
this.ExperienceBarText = $"{(int)currentExperienceInLevel} / {(int)nextLevelExperienceThreshold} XP";
break;
case Configuration.FocusView.ExperienceDisplay.TotalCurretAndTotalMax:
var currentTotalExperience = gameData.MainPlayer.Experience;
var currentTotalExperience = mainPlayerResourceContext.Player.PlayerInformation.Experience;
var requiredTotalExperience = this.experienceCalculator.GetTotalExperienceForNextLevel(currentTotalExperience);
this.ExperienceBarText = $"{(int)currentTotalExperience} / {(int)requiredTotalExperience} XP";
break;
case Configuration.FocusView.ExperienceDisplay.RemainingUntilNextLevel:
var remainingExperience = this.experienceCalculator.GetRemainingExperienceForNextLevel(gameData.MainPlayer.Experience);
var remainingExperience = this.experienceCalculator.GetRemainingExperienceForNextLevel(mainPlayerResourceContext.Player.PlayerInformation.Experience);
this.ExperienceBarText = $"Remaining {(int)remainingExperience} XP";
break;
case Configuration.FocusView.ExperienceDisplay.Percentage:
var currentExperienceInLevel2 = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
var nextLevelExperienceThreshold2 = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
var currentExperienceInLevel2 = this.experienceCalculator.GetExperienceForCurrentLevel(mainPlayerResourceContext.Player.PlayerInformation.Experience);
var nextLevelExperienceThreshold2 = this.experienceCalculator.GetNextExperienceThreshold(mainPlayerResourceContext.Player.PlayerInformation.Experience);
this.ExperienceBarText = $"{(int)((double)currentExperienceInLevel2 / (double)nextLevelExperienceThreshold2 * 100)}% XP";
break;
}
@@ -357,7 +316,8 @@ public partial class PlayerResourcesComponent : UserControl
private void UpdateLuxonText()
{
if (this.DataContext is not GameData gameData)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.User?.User is null)
{
return;
}
@@ -365,20 +325,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.LuxonPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.LuxonBarText = $"{gameData.User!.CurrentLuxonPoints} / {gameData.User.MaxLuxonPoints} Luxon Points";
this.LuxonBarText = $"{mainPlayerResourceContext.User.User.CurrentLuxonPoints} / {mainPlayerResourceContext.User.User.MaxLuxonPoints} Luxon Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.LuxonBarText = $"Remaining {gameData.User!.MaxLuxonPoints - gameData.User.CurrentLuxonPoints} Luxon Points";
this.LuxonBarText = $"Remaining {mainPlayerResourceContext.User.User.MaxLuxonPoints - mainPlayerResourceContext.User.User.CurrentLuxonPoints} Luxon Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.LuxonBarText = $"{(int)((double)gameData.User!.CurrentLuxonPoints / (double)gameData.User.MaxLuxonPoints * 100)}% Luxon Points";
this.LuxonBarText = $"{(int)((double)mainPlayerResourceContext.User.User.CurrentLuxonPoints / (double)mainPlayerResourceContext.User.User.MaxLuxonPoints * 100)}% Luxon Points";
break;
}
}
private void UpdateKurzickText()
{
if (this.DataContext is not GameData gameData)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.User?.User is null)
{
return;
}
@@ -386,20 +347,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.KurzickPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.KurzickBarText = $"{gameData.User!.CurrentKurzickPoints} / {gameData.User.MaxKurzickPoints} Kurzick Points";
this.KurzickBarText = $"{mainPlayerResourceContext.User.User.CurrentKurzickPoints} / {mainPlayerResourceContext.User.User.MaxKurzickPoints} Kurzick Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.KurzickBarText = $"Remaining {gameData.User!.MaxKurzickPoints - gameData.User.CurrentKurzickPoints} Kurzick Points";
this.KurzickBarText = $"Remaining {mainPlayerResourceContext.User.User!.MaxKurzickPoints - mainPlayerResourceContext.User.User.CurrentKurzickPoints} Kurzick Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.KurzickBarText = $"{(int)((double)gameData.User!.CurrentKurzickPoints / (double)gameData.User.MaxKurzickPoints * 100)}% Kurzick Points";
this.KurzickBarText = $"{(int)((double)mainPlayerResourceContext.User.User.CurrentKurzickPoints / (double)mainPlayerResourceContext.User.User.MaxKurzickPoints * 100)}% Kurzick Points";
break;
}
}
private void UpdateImperialText()
{
if (this.DataContext is not GameData gameData)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.User?.User is null)
{
return;
}
@@ -407,20 +369,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.ImperialPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.ImperialBarText = $"{gameData.User!.CurrentImperialPoints} / {gameData.User.MaxImperialPoints} Imperial Points";
this.ImperialBarText = $"{mainPlayerResourceContext.User.User!.CurrentImperialPoints} / {mainPlayerResourceContext.User.User.MaxImperialPoints} Imperial Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.ImperialBarText = $"Remaining {gameData.User!.MaxImperialPoints - gameData.User.CurrentImperialPoints} Imperial Points";
this.ImperialBarText = $"Remaining {mainPlayerResourceContext.User.User!.MaxImperialPoints - mainPlayerResourceContext.User.User.CurrentImperialPoints} Imperial Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.ImperialBarText = $"{(int)((double)gameData.User!.CurrentImperialPoints / (double)gameData.User.MaxImperialPoints * 100)}% Imperial Points";
this.ImperialBarText = $"{(int)((double)mainPlayerResourceContext.User.User!.CurrentImperialPoints / (double)mainPlayerResourceContext.User.User.MaxImperialPoints * 100)}% Imperial Points";
break;
}
}
private void UpdateBalthazarText()
{
if (this.DataContext is not GameData gameData)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.User?.User is null)
{
return;
}
@@ -428,21 +391,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.BalthazarPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.BalthazarBarText = $"{gameData.User!.CurrentBalthazarPoints} / {gameData.User.MaxBalthazarPoints} Balthazar Points";
this.BalthazarBarText = $"{mainPlayerResourceContext.User.User.CurrentBalthazarPoints} / {mainPlayerResourceContext.User.User.MaxBalthazarPoints} Balthazar Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.BalthazarBarText = $"Remaining {gameData.User!.MaxBalthazarPoints - gameData.User.CurrentBalthazarPoints} Balthazar Points";
this.BalthazarBarText = $"Remaining {mainPlayerResourceContext.User.User.MaxBalthazarPoints - mainPlayerResourceContext.User.User.CurrentBalthazarPoints} Balthazar Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.BalthazarBarText = $"{(int)((double)gameData.User!.CurrentBalthazarPoints / (double)gameData.User.MaxBalthazarPoints * 100)}% Balthazar Points";
this.BalthazarBarText = $"{(int)((double)mainPlayerResourceContext.User.User.CurrentBalthazarPoints / (double)mainPlayerResourceContext.User.User.MaxBalthazarPoints * 100)}% Balthazar Points";
break;
}
}
private void UpdateVanquishingText()
{
if (this.DataContext is not GameData gameData ||
gameData.Session is null)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.Session?.Session is null)
{
return;
}
@@ -450,21 +413,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.VanquishingDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.VanquishingText = $"{gameData.Session.FoesKilled} / {(int)this.TotalFoes} Foes Killed";
this.VanquishingText = $"{mainPlayerResourceContext.Session.Session.FoesKilled} / {(int)this.TotalFoes} Foes Killed";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.VanquishingText = $"Remaining {gameData.Session.FoesToKill} Foes";
this.VanquishingText = $"Remaining {mainPlayerResourceContext.Session.Session.FoesToKill} Foes";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.VanquishingText = $"{(int)((double)gameData.Session.FoesKilled / (double)this.TotalFoes * 100)}% Foes Killed";
this.VanquishingText = $"{(int)((double)mainPlayerResourceContext.Session.Session.FoesKilled / (double)this.TotalFoes * 100)}% Foes Killed";
break;
}
}
private void UpdateHealthText()
{
if (this.DataContext is not GameData gameData ||
gameData.MainPlayer is null)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.Player?.PlayerInformation is null)
{
return;
}
@@ -472,21 +435,21 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.HealthDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.HealthBarText = $"{(int)gameData.MainPlayer.CurrentHealth} / {(int)gameData.MainPlayer.MaxHealth} Health";
this.HealthBarText = $"{(int)mainPlayerResourceContext.Player.PlayerInformation.CurrentHealth} / {(int)mainPlayerResourceContext.Player.PlayerInformation.MaxHealth} Health";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.HealthBarText = $"Remaining {(int)gameData.MainPlayer.CurrentHealth} Health";
this.HealthBarText = $"Remaining {(int)mainPlayerResourceContext.Player.PlayerInformation.CurrentHealth} Health";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.HealthBarText = $"{(int)(gameData.MainPlayer.CurrentHealth / gameData.MainPlayer.MaxHealth * 100)}% Health";
this.HealthBarText = $"{(int)(mainPlayerResourceContext.Player.PlayerInformation.CurrentHealth / mainPlayerResourceContext.Player.PlayerInformation.MaxHealth * 100)}% Health";
break;
}
}
private void UpdateEnergyText()
{
if (this.DataContext is not GameData gameData ||
gameData.MainPlayer is null)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.Player?.PlayerInformation is null)
{
return;
}
@@ -494,31 +457,32 @@ public partial class PlayerResourcesComponent : UserControl
switch (this.liveOptions.Value.EnergyDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.EnergyBarText = $"{(int)gameData.MainPlayer.CurrentEnergy} / {(int)gameData.MainPlayer.MaxEnergy} Energy";
this.EnergyBarText = $"{(int)mainPlayerResourceContext.Player.PlayerInformation.CurrentEnergy} / {(int)mainPlayerResourceContext.Player.PlayerInformation.MaxEnergy} Energy";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.EnergyBarText = $"Remaining {(int)gameData.MainPlayer.CurrentEnergy} Energy";
this.EnergyBarText = $"Remaining {(int)mainPlayerResourceContext.Player.PlayerInformation.CurrentEnergy} Energy";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.EnergyBarText = $"{(int)(gameData.MainPlayer.CurrentEnergy / gameData.MainPlayer.MaxEnergy * 100)}% Energy";
this.EnergyBarText = $"{(int)(mainPlayerResourceContext.Player.PlayerInformation.CurrentEnergy / mainPlayerResourceContext.Player.PlayerInformation.MaxEnergy * 100)}% Energy";
break;
}
}
private void UpdateTitleText()
{
if (this.DataContext is not GameData gameData)
if (this.DataContext is not MainPlayerResourceContext mainPlayerResourceContext ||
mainPlayerResourceContext.Player?.PlayerInformation is null)
{
return;
}
if (gameData.MainPlayer?.TitleInformation?.IsPercentage is true)
if (mainPlayerResourceContext.Player.PlayerInformation.TitleInformation?.IsPercentage is true)
{
this.TitleText = $"{(double?)gameData.MainPlayer?.TitleInformation?.CurrentPoints / 10d}% Rank Progress";
this.TitleText = $"{(double?)mainPlayerResourceContext.Player.PlayerInformation.TitleInformation?.CurrentPoints / 10d}% Rank Progress";
}
else
{
this.TitleText = $"{gameData.MainPlayer?.TitleInformation?.CurrentPoints}/{gameData.MainPlayer?.TitleInformation?.PointsForNextRank} Rank Progress";
this.TitleText = $"{mainPlayerResourceContext.Player.PlayerInformation.TitleInformation?.CurrentPoints}/{mainPlayerResourceContext.Player.PlayerInformation.TitleInformation?.PointsForNextRank} Rank Progress";
}
}
}
@@ -167,7 +167,7 @@ public partial class GuildwarsMinimap : UserControl
return;
}
this.TargetEntityId = this.GameData.Session?.CurrentTargetId ?? 0;
this.TargetEntityId = this.GameData.CurrentTargetId ?? 0;
this.TargetEntityModelId = (int?)this.GameData.LivingEntities?.FirstOrDefault(e => e.Id == this.TargetEntityId)?.ModelType ?? 0;
var screenVirtualWidth = this.ActualWidth / this.Zoom;
var screenVirtualHeight = this.ActualHeight / this.Zoom;
+1 -1
View File
@@ -13,7 +13,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8.78</Version>
<Version>0.9.8.80</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+1 -2
View File
@@ -7,9 +7,8 @@ public sealed class GameData
public bool Valid { get; init; }
public MainPlayerInformation? MainPlayer { get; init; }
public List<PlayerInformation>? Party { get; init; }
public UserInformation? User { get; init; }
public SessionInformation? Session { get; init; }
public List<WorldPlayerInformation>? WorldPlayers { get; init; }
public List<LivingEntity>? LivingEntities { get; init; }
public List<MapIcon>? MapIcons { get; init; }
public int? CurrentTargetId { get; init; }
}
@@ -0,0 +1,9 @@
namespace Daybreak.Models.Guildwars;
public enum InstanceType
{
Undefined,
Outpost,
Explorable,
Loading
}
@@ -0,0 +1,6 @@
namespace Daybreak.Models.Guildwars;
public sealed class MainPlayerData
{
public MainPlayerInformation? PlayerInformation { get; init; }
}
+6
View File
@@ -0,0 +1,6 @@
namespace Daybreak.Models.Guildwars;
public sealed class SessionData
{
public SessionInformation? Session { get; init; }
}
@@ -5,6 +5,6 @@ public sealed class SessionInformation
public uint FoesKilled { get; init; }
public uint FoesToKill { get; init; }
public Map? CurrentMap { get; init; }
public int? CurrentTargetId { get; init; }
public uint InstanceTimer { get; init; }
public InstanceType InstanceType { get; init; }
}
+5
View File
@@ -0,0 +1,5 @@
namespace Daybreak.Models.Guildwars;
public sealed class UserData
{
public UserInformation? User { get; init; }
}
+32 -16
View File
@@ -5,47 +5,63 @@ namespace Daybreak.Models.Interop;
[StructLayout(LayoutKind.Explicit)]
public readonly struct EntityContext
{
public const uint EntityContextBaseOffset = 0x0014;
[FieldOffset(0x0000)]
[FieldOffset(0x0014)]
public readonly uint Timer;
[FieldOffset(0x0018)]
[FieldOffset(0x002C)]
public readonly uint AgentId;
[FieldOffset(0x001C)]
[FieldOffset(0x0030)]
public readonly uint ZCoords;
[FieldOffset(0x0044)]
[FieldOffset(0x0058)]
public readonly uint NameProperties;
[FieldOffset(0x0060)]
[FieldOffset(0x0074)]
public readonly GamePosition Position;
[FieldOffset(0x0088)]
[FieldOffset(0x009C)]
public readonly EntityType EntityType;
[FieldOffset(0x00E0)]
[FieldOffset(0x00F4)]
public readonly ushort EntityModelType;
[FieldOffset(0x00E2)]
[FieldOffset(0x00F6)]
public readonly ushort AgentModelType;
[FieldOffset(0x00F6)]
[FieldOffset(0x010A)]
public readonly byte PrimaryProfessionId;
[FieldOffset(0x00F7)]
[FieldOffset(0x010B)]
public readonly byte SecondaryProfessionId;
[FieldOffset(0x00F8)]
[FieldOffset(0x010C)]
public readonly byte Level;
[FieldOffset(0x00F9)]
[FieldOffset(0x010D)]
public readonly PvpTeam TeamId;
[FieldOffset(0x0144)]
[FieldOffset(0x0114)]
public readonly float EnergyRegen; // Only works for main player
[FieldOffset(0x011C)]
public readonly float CurrentEnergyPercentage; // Only works for main player
[FieldOffset(0x0120)]
public readonly uint MaxEnergy; // Only works for main player
[FieldOffset(0x0128)]
public readonly float HealthRegen; // Only works for main player
[FieldOffset(0x0130)]
public readonly float CurrentHealthPercentage; // Only works for main player
[FieldOffset(0x0134)]
public readonly uint MaxHealth; // Only works for main player
[FieldOffset(0x0158)]
public readonly EntityState State;
[FieldOffset(0x019D)]
[FieldOffset(0x01B1)]
public readonly EntityAllegiance Allegiance;
}
@@ -0,0 +1,9 @@
using Daybreak.Models.Guildwars;
namespace Daybreak.Models;
public sealed class MainPlayerResourceContext
{
public UserData? User { get; init; }
public SessionData? Session { get; init; }
public MainPlayerData? Player { get; init; }
}
@@ -19,6 +19,9 @@ public sealed class GuildwarsMemoryCache : IGuildwarsMemoryCache
private readonly CachedData<PathingData?> pathingDataCache = new();
private readonly CachedData<PathingMetadata?> pathingMetadataCache = new();
private readonly CachedData<WorldData?> worldDataCache = new();
private readonly CachedData<SessionData?> sessionDataCache = new();
private readonly CachedData<UserData?> userDataCache = new();
private readonly CachedData<MainPlayerData?> mainPlayerDataCache = new();
public GuildwarsMemoryCache(
IGuildwarsMemoryReader guildwarsMemoryReader,
@@ -58,6 +61,21 @@ public sealed class GuildwarsMemoryCache : IGuildwarsMemoryCache
return this.ReadDataInternal(this.worldDataCache, this.guildwarsMemoryReader.ReadWorldData, cancellationToken);
}
public Task<SessionData?> ReadSessionData(CancellationToken cancellationToken)
{
return this.ReadDataInternal(this.sessionDataCache, this.guildwarsMemoryReader.ReadSessionData, cancellationToken);
}
public Task<UserData?> ReadUserData(CancellationToken cancellationToken)
{
return this.ReadDataInternal(this.userDataCache, this.guildwarsMemoryReader.ReadUserData, cancellationToken);
}
public Task<MainPlayerData?> ReadMainPlayerData(CancellationToken cancellationToken)
{
return this.ReadDataInternal(this.mainPlayerDataCache, this.guildwarsMemoryReader.ReadMainPlayerData, cancellationToken);
}
private async Task<T?> ReadDataInternal<T>(CachedData<T?> cachedData, Func<CancellationToken, Task<T?>> task, CancellationToken cancellationToken)
{
if (DateTime.Now - cachedData.SetTime <= TimeSpan.FromMilliseconds(this.liveOptions.Value.MemoryReaderFrequency))
+247 -108
View File
@@ -1,5 +1,4 @@
using Daybreak.Models;
using Daybreak.Models.Builds;
using Daybreak.Models.Builds;
using Daybreak.Models.Guildwars;
using Daybreak.Models.Interop;
using Daybreak.Models.Metrics;
@@ -15,7 +14,6 @@ using System.Diagnostics.Metrics;
using System.Extensions;
using System.Linq;
using System.Logging;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -146,6 +144,36 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
return Task.Run(() => this.SafeReadGameMemory(this.ReadWorldDataInternal), cancellationToken);
}
public Task<UserData?> ReadUserData(CancellationToken cancellationToken)
{
if (this.memoryScanner.Scanning is false)
{
return Task.FromResult<UserData?>(default);
}
return Task.Run(() => this.SafeReadGameMemory(this.ReadUserDataInternal), cancellationToken);
}
public Task<SessionData?> ReadSessionData(CancellationToken cancellationToken)
{
if (this.memoryScanner.Scanning is false)
{
return Task.FromResult<SessionData?>(default);
}
return Task.Run(() => this.SafeReadGameMemory(this.ReadSessionDataInternal), cancellationToken);
}
public Task<MainPlayerData?> ReadMainPlayerData(CancellationToken cancellationToken)
{
if (this.memoryScanner.Scanning is false)
{
return Task.FromResult<MainPlayerData?>(default);
}
return Task.Run(() => this.SafeReadGameMemory(this.ReadMainPlayerDataInternal), cancellationToken);
}
private async Task InitializeSafe(Process process, ScopedLogger<GuildwarsMemoryReader> scopedLogger)
{
if (this.memoryScanner.Process is null ||
@@ -208,7 +236,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private LoginData? ReadLoginDataInternal()
{
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
@@ -226,26 +254,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private GameData? ReadGameDataInternal()
{
/*
* The following offsets were reverse-engineered using pointer scanning. All of the following ones seem to currently work.
* If any breaks, try the other ones from the list below.
* startAddress + 00629204 -> +0x18
* startAddress + 006296B4 -> +0x18
* startAddress + 00AA9CE0 -> +0xC -> +0xC
*/
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
{
return new GameData { Valid = false };
return default;
}
// GameContext struct is offset by 0x07C due to the memory layout of the structure.
var gameContext = this.memoryScanner.Read(globalContext.GameContext, GameContext.BaseOffset);
// UserContext struct is offset by 0x074 due to the memory layout of the structure.
var userContext = this.memoryScanner.Read(globalContext.UserContext, UserContext.BaseOffset);
// InstanceContext struct is offset by 0x01AC due to memory layout of the structure.
var instanceContext = this.memoryScanner.Read(globalContext.InstanceContext, InstanceContext.BaseOffset);
@@ -258,7 +276,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
gameContext.Titles.Size > 1000 ||
gameContext.TitlesTiers.Size > 10000)
{
return new GameData { Valid = false };
return default;
}
var mapEntities = this.memoryScanner.ReadArray(gameContext.MapEntities);
@@ -282,17 +300,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
}
var entityPointers = this.memoryScanner.ReadArray<uint>(entityPointersArray.Buffer, entityPointersArray.Size);
var entities = entityPointers.Select(ptr => this.memoryScanner.Read<EntityContext>(ptr + EntityContext.EntityContextBaseOffset)).ToArray();
var entities = entityPointers.Select(ptr => (ptr, this.memoryScanner.Read<EntityContext>(ptr))).ToArray();
return this.AggregateGameData(
gameContext,
instanceContext,
mapEntities,
entities,
entities.Select(e => e.Item2).ToArray(),
players,
professions,
quests,
userContext,
skills,
partyAttributes,
titles,
@@ -305,7 +322,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private PathingData? ReadPathingDataInternal()
{
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
var mapContext = this.memoryScanner.Read(globalContext.MapContext);
var pathingMapContext = this.memoryScanner.ReadPtrChain<PathingMapContext>(mapContext.PathingMapContextPtr, 0x0, 0x0);
@@ -363,7 +380,6 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
var adjacentTrapezoid = this.memoryScanner.Read<PathingTrapezoid>(adjacentAddress);
trapezoidAdjacencyList.Add((int)adjacentTrapezoid.Id);
}
}
}
@@ -382,7 +398,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private PathingMetadata? ReadPathingMetaDataInternal()
{
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
var mapContext = this.memoryScanner.Read(globalContext.MapContext);
var pathingMapContext = this.memoryScanner.ReadPtrChain<PathingMapContext>(mapContext.PathingMapContextPtr, 0x0, 0x0);
@@ -393,7 +409,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private InventoryData? ReadInventoryDataInternal()
{
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
@@ -424,8 +440,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private WorldData? ReadWorldDataInternal()
{
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
@@ -451,6 +466,139 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
};
}
private UserData? ReadUserDataInternal()
{
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
{
return default;
}
// GameContext struct is offset by 0x07C due to the memory layout of the structure.
var gameContext = this.memoryScanner.Read(globalContext.GameContext, GameContext.BaseOffset);
// UserContext struct is offset by 0x074 due to the memory layout of the structure.
var userContext = this.memoryScanner.Read(globalContext.UserContext, UserContext.BaseOffset);
var userInformation = new UserInformation
{
Email = userContext.PlayerEmailFirstChar + (userContext.PlayerEmailSecondChar + userContext.PlayerEmailRemaining),
CurrentKurzickPoints = gameContext.CurrentKurzick,
TotalKurzickPoints = gameContext.TotalKurzick,
MaxKurzickPoints = gameContext.MaxKurzick,
CurrentLuxonPoints = gameContext.CurrentLuxon,
TotalLuxonPoints = gameContext.TotalLuxon,
MaxLuxonPoints = gameContext.MaxLuxon,
CurrentImperialPoints = gameContext.CurrentImperial,
TotalImperialPoints = gameContext.TotalImperial,
MaxImperialPoints = gameContext.MaxImperial,
CurrentBalthazarPoints = gameContext.CurrentBalthazar,
TotalBalthazarPoints = gameContext.TotalBalthazar,
MaxBalthazarPoints = gameContext.MaxBalthazar,
CurrentSkillPoints = gameContext.CurrentSkillPoints,
TotalSkillPoints = gameContext.TotalSkillPoints
};
return new UserData { User = userInformation };
}
private SessionData? ReadSessionDataInternal()
{
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
{
return default;
}
// GameContext struct is offset by 0x07C due to the memory layout of the structure.
var gameContext = this.memoryScanner.Read(globalContext.GameContext, GameContext.BaseOffset);
// InstanceContext struct is offset by 0x01AC due to memory layout of the structure.
var instanceContext = this.memoryScanner.Read(globalContext.InstanceContext, InstanceContext.BaseOffset);
// UserContext struct is offset by 0x074 due to the memory layout of the structure.
var userContext = this.memoryScanner.Read(globalContext.UserContext, UserContext.BaseOffset);
var instanceInfo = this.memoryScanner.ReadPtrChain<InstanceInfoContext>(this.GetInstanceInfoPointer(), 0x0, 0x0);
_ = Map.TryParse((int)userContext.MapId, out var currentMap);
var sessionInformation = new SessionInformation
{
FoesKilled = gameContext.FoesKilled,
FoesToKill = gameContext.FoesToKill,
CurrentMap = currentMap,
InstanceTimer = instanceContext.Timer,
InstanceType = instanceInfo.InstanceType switch
{
Daybreak.Models.Interop.InstanceType.Outpost => Daybreak.Models.Guildwars.InstanceType.Outpost,
Daybreak.Models.Interop.InstanceType.Explorable => Daybreak.Models.Guildwars.InstanceType.Explorable,
Daybreak.Models.Interop.InstanceType.Loading => Daybreak.Models.Guildwars.InstanceType.Loading,
_ => Daybreak.Models.Guildwars.InstanceType.Undefined
}
};
return new SessionData { Session = sessionInformation };
}
private MainPlayerData? ReadMainPlayerDataInternal()
{
var globalContext = this.GetGlobalContext();
if (!globalContext.GameContext.IsValid() ||
!globalContext.UserContext.IsValid() ||
!globalContext.InstanceContext.IsValid())
{
return default;
}
// GameContext struct is offset by 0x07C due to the memory layout of the structure.
var gameContext = this.memoryScanner.Read(globalContext.GameContext, GameContext.BaseOffset);
// InstanceContext struct is offset by 0x01AC due to memory layout of the structure.
var instanceContext = this.memoryScanner.Read(globalContext.InstanceContext, InstanceContext.BaseOffset);
var playerEntityId = this.memoryScanner.ReadPtrChain<int>(this.GetPlayerIdPointer(), 0x0, 0x0);
var mapEntity = this.memoryScanner.ReadItemAtIndex(gameContext.MapEntities, playerEntityId);
var entityPointersArray = this.memoryScanner.ReadPtrChain<GenericGuildwarsArray>(this.GetEntityArrayPointer(), 0x0, 0x0);
var entity = this.memoryScanner.ReadPtrChain<EntityContext>((uint)(entityPointersArray.Buffer + (sizeof(uint) * playerEntityId)), 0x0, 0x0);
if (mapEntity.MaxHealth != entity.MaxHealth ||
mapEntity.MaxEnergy != entity.MaxEnergy ||
mapEntity.MaxHealth < 0 || mapEntity.MaxHealth > 10000 ||
mapEntity.MaxEnergy < 0 || mapEntity.MaxEnergy > 10000 ||
gameContext.Professions.Size == 0 ||
gameContext.Players.Size == 0 ||
gameContext.Skillbars.Size == 0 ||
gameContext.PartyAttributes.Size == 0 ||
gameContext.Titles.Size == 0 ||
gameContext.TitlesTiers.Size == 0)
{
return default;
}
var professions = this.memoryScanner.ReadArray(gameContext.Professions);
var players = this.memoryScanner.ReadArray(gameContext.Players);
var quests = this.memoryScanner.ReadArray(gameContext.QuestLog);
var skills = this.memoryScanner.ReadArray(gameContext.Skillbars);
var partyAttributes = this.memoryScanner.ReadArray(gameContext.PartyAttributes);
var titles = this.memoryScanner.ReadArray(gameContext.Titles);
var titleTiers = this.memoryScanner.ReadArray(gameContext.TitlesTiers);
var mainPlayerInfo = this.GetMainPlayerInformation(
gameContext,
instanceContext,
players.FirstOrDefault(p => p.AgentId == playerEntityId),
mapEntity,
professions.FirstOrDefault(p => p.AgentId == playerEntityId),
quests,
skills.FirstOrDefault(p => p.AgentId == playerEntityId),
partyAttributes.FirstOrDefault(p => p.AgentId == playerEntityId),
titles,
titleTiers,
entity);
return new MainPlayerData
{
PlayerInformation = mainPlayerInfo
};
}
private uint GetPlayerIdPointer()
{
if (this.playerIdPointer == 0)
@@ -568,6 +716,18 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
};
}
private GlobalContext GetGlobalContext()
{
/*
* The following offsets were reverse-engineered using pointer scanning. All of the following ones seem to currently work.
* If any breaks, try the other ones from the list below.
* startAddress + 00629204 -> +0x18
* startAddress + 006296B4 -> +0x18
* startAddress + 00AA9CE0 -> +0xC -> +0xC
*/
return this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
}
private unsafe GameData AggregateGameData(
GameContext gameContext,
InstanceContext instanceContext,
@@ -576,7 +736,6 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
PlayerContext[] players,
ProfessionsContext[] professions,
QuestContext[] quests,
UserContext userContext,
SkillbarContext[] skills,
PartyAttributesContext[] partyAttributes,
TitleContext[] titles,
@@ -586,11 +745,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
int mainPlayerEntityId,
int targetEntityId)
{
var name = userContext.PlayerName;
_ = Map.TryParse((int)userContext.MapId, out var currentMap);
var partyMembers = professions
.Where(p => p.AgentId != mainPlayerEntityId)
.Select(p => GetPlayerInformation((int)p.AgentId, instanceContext, mapEntities, professions, skills, partyAttributes, entities))
.Select(p => GetPlayerInformation(
(int)p.AgentId,
instanceContext,
mapEntities.Skip((int)p.AgentId).FirstOrDefault(),
professions.FirstOrDefault(prof => prof.AgentId == p.AgentId),
skills.FirstOrDefault(s => s.AgentId == p.AgentId),
partyAttributes.OfType<PartyAttributesContext>().FirstOrDefault(attr => attr.AgentId == p.AgentId),
entities.OfType<EntityContext>().FirstOrDefault(e => e.AgentId == p.AgentId)))
.ToList();
var parsedMapIcons = GetMapIcons(mapIcons);
@@ -599,18 +763,28 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
gameContext,
instanceContext,
players.FirstOrDefault(p => p.AgentId == mainPlayerEntityId),
mapEntities,
professions,
mapEntities.Skip(mainPlayerEntityId).FirstOrDefault(),
professions.FirstOrDefault(p => p.AgentId == mainPlayerEntityId),
quests,
skills,
partyAttributes,
skills.OfType<SkillbarContext>().FirstOrDefault(s => s.AgentId == mainPlayerEntityId),
partyAttributes.OfType<PartyAttributesContext>().FirstOrDefault(p => p.AgentId == mainPlayerEntityId),
titles,
titleTiers,
entities);
entities.FirstOrDefault(e => e.AgentId == mainPlayerEntityId));
var worldPlayers = players
.Where(p => p.AgentId != mainPlayerEntityId && p.AgentId != 0)
.Select(p => this.GetWorldPlayerInformation(p, instanceContext, mapEntities, professions, skills, partyAttributes, titles, titleTiers, entities))
.Select(p =>
this.GetWorldPlayerInformation(
p,
instanceContext,
mapEntities.Skip(p.AgentId).First(),
professions.FirstOrDefault(prof => prof.AgentId == p.AgentId),
skills.OfType<SkillbarContext>().FirstOrDefault(s => s.AgentId == p.AgentId),
partyAttributes.OfType<PartyAttributesContext>().FirstOrDefault(attr => attr.AgentId == p.AgentId),
titles,
titleTiers,
entities.FirstOrDefault(e => e.AgentId == p.AgentId)))
.ToList();
var remainingEntities = entities
@@ -619,44 +793,15 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
.ToArray();
var livingEntities = this.GetLivingEntities(remainingEntities, npcs);
var userInformation = new UserInformation
{
Email = userContext.PlayerEmailFirstChar + (userContext.PlayerEmailSecondChar + userContext.PlayerEmailRemaining),
CurrentKurzickPoints = gameContext.CurrentKurzick,
TotalKurzickPoints = gameContext.TotalKurzick,
MaxKurzickPoints = gameContext.MaxKurzick,
CurrentLuxonPoints = gameContext.CurrentLuxon,
TotalLuxonPoints = gameContext.TotalLuxon,
MaxLuxonPoints = gameContext.MaxLuxon,
CurrentImperialPoints = gameContext.CurrentImperial,
TotalImperialPoints = gameContext.TotalImperial,
MaxImperialPoints = gameContext.MaxImperial,
CurrentBalthazarPoints = gameContext.CurrentBalthazar,
TotalBalthazarPoints = gameContext.TotalBalthazar,
MaxBalthazarPoints = gameContext.MaxBalthazar,
CurrentSkillPoints = gameContext.CurrentSkillPoints,
TotalSkillPoints = gameContext.TotalSkillPoints
};
var sessionInformation = new SessionInformation
{
FoesKilled = gameContext.FoesKilled,
FoesToKill = gameContext.FoesToKill,
CurrentMap = currentMap,
InstanceTimer = instanceContext.Timer,
CurrentTargetId = targetEntityId
};
return new GameData
{
Party = partyMembers,
MainPlayer = mainPlayer,
Session = sessionInformation,
User = userInformation,
WorldPlayers = worldPlayers,
LivingEntities = livingEntities,
MapIcons = parsedMapIcons,
CurrentTargetId = targetEntityId,
Valid = mapEntities.Length > 0 || players.Length > 0 || professions.Length > 0 || partyAttributes.Length > 0 || entities.Length > 0
};
}
@@ -749,16 +894,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
GameContext gameContext,
InstanceContext instanceContext,
PlayerContext playerContext,
MapEntityContext[] mapEntities,
ProfessionsContext[] professions,
MapEntityContext mapEntity,
ProfessionsContext professions,
QuestContext[] quests,
SkillbarContext[] skillbars,
PartyAttributesContext[] partyAttributes,
SkillbarContext skillbar,
PartyAttributesContext partyAttributes,
TitleContext[] titles,
TitleTierContext[] titleTiers,
EntityContext[] entities)
EntityContext entity)
{
var playerInformation = this.GetWorldPlayerInformation(playerContext, instanceContext, mapEntities, professions, skillbars, partyAttributes, titles, titleTiers, entities);
var playerInformation = this.GetWorldPlayerInformation(playerContext, instanceContext, mapEntity, professions, skillbar, partyAttributes, titles, titleTiers, entity);
_ = Quest.TryParse((int)gameContext.QuestId, out var quest);
var questLog = quests
.Select(q =>
@@ -788,8 +933,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
SecondaryProfession = playerInformation.SecondaryProfession,
UnlockedProfession = playerInformation.UnlockedProfession,
HardModeUnlocked = gameContext.HardModeUnlocked == 1,
CurrentEnergy = playerInformation.CurrentEnergy,
CurrentHealth = playerInformation.CurrentHealth,
CurrentEnergy = playerInformation.MaxEnergy * entity.CurrentEnergyPercentage,
CurrentHealth = playerInformation.MaxHealth * entity.CurrentHealthPercentage,
MaxEnergy = playerInformation.MaxEnergy,
MaxHealth = playerInformation.MaxHealth,
EnergyRegen = playerInformation.EnergyRegen,
@@ -808,16 +953,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private WorldPlayerInformation GetWorldPlayerInformation(
PlayerContext playerContext,
InstanceContext instanceContext,
MapEntityContext[] mapEntities,
ProfessionsContext[] professions,
SkillbarContext[] skillbars,
PartyAttributesContext[] partyAttributes,
MapEntityContext mapEntity,
ProfessionsContext professions,
SkillbarContext skillbar,
PartyAttributesContext partyAttributes,
TitleContext[] titles,
TitleTierContext[] titleTiers,
EntityContext[] entities)
EntityContext entity)
{
var name = this.memoryScanner.Read(playerContext.NamePointer);
var playerInformation = GetPlayerInformation(playerContext.AgentId, instanceContext, mapEntities, professions, skillbars, partyAttributes, entities);
var playerInformation = GetPlayerInformation(playerContext.AgentId, instanceContext, mapEntity, professions, skillbar, partyAttributes, entity);
var maybeCurrentTitleContext = (TitleContext?)null;
var maybeCurrentTitle = (Title?)null;
for(var i = 0; i < titles.Length; i++)
@@ -878,14 +1023,12 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
private static PlayerInformation GetPlayerInformation(
int playerId,
InstanceContext instanceContext,
MapEntityContext[] mapEntities,
ProfessionsContext[] professions,
SkillbarContext[] skillbars,
PartyAttributesContext[] partyAttributes,
EntityContext[] entities)
MapEntityContext mapEntity,
ProfessionsContext professionContext,
SkillbarContext? skillbar,
PartyAttributesContext? partyAttributes,
EntityContext entity)
{
var mapEntityContext = mapEntities.Skip(playerId).FirstOrDefault();
var professionContext = professions.Where(p => p.AgentId == playerId).FirstOrDefault();
_ = Profession.TryParse((int)professionContext.CurrentPrimary, out var primaryProfession);
_ = Profession.TryParse((int)professionContext.CurrentSecondary, out var secondaryProfession);
var unlockedProfessions = Profession.Professions
@@ -894,11 +1037,10 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
.Where(p => p is not null && p != Profession.None)
.OrderBy(p => p.Id)
.ToList();
var maybeSkillbarContext = skillbars.Select(s => (SkillbarContext?) s).FirstOrDefault(s => s?.AgentId == playerId);
var maybePartyAttributesContext = partyAttributes.Select(p => (PartyAttributesContext?) p).FirstOrDefault(p => p?.AgentId == playerId);
Build? build = null;
if (maybeSkillbarContext is SkillbarContext skillbarContext &&
maybePartyAttributesContext is PartyAttributesContext attributesContext &&
if (skillbar is SkillbarContext skillbarContext &&
partyAttributes is PartyAttributesContext attributesContext &&
partyAttributes.Value.Attributes is not null &&
primaryProfession is not null &&
secondaryProfession is not null)
{
@@ -949,9 +1091,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
};
}
(var currentHp, var currentEnergy) = ApplyEnergyAndHealthRegen(instanceContext, mapEntityContext);
EntityContext? entityContext = entities.FirstOrDefault(e => e.AgentId == playerId);
if (!Npc.TryParse(entityContext?.EntityModelType ?? 0, out var npc))
(var currentHp, var currentEnergy) = ApplyEnergyAndHealthRegen(instanceContext, mapEntity);
if (!Npc.TryParse(entity.EntityModelType, out var npc))
{
npc = Npc.Unknown;
}
@@ -959,27 +1100,25 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
return new PlayerInformation
{
Id = playerId,
Timer = entityContext!.Value.Timer,
Level = entityContext.Value.Level,
Timer = entity.Timer,
Level = entity.Level,
NpcDefinition = npc,
ModelType = entityContext.Value.EntityModelType,
ModelType = entity.EntityModelType,
PrimaryProfession = primaryProfession,
SecondaryProfession = secondaryProfession,
UnlockedProfession = unlockedProfessions,
CurrentHealth = currentHp,
CurrentEnergy = currentEnergy,
MaxHealth = mapEntityContext.MaxHealth,
MaxEnergy = mapEntityContext.MaxEnergy,
HealthRegen = mapEntityContext.HealthRegen,
EnergyRegen = mapEntityContext.EnergyRegen,
MaxHealth = mapEntity.MaxHealth,
MaxEnergy = mapEntity.MaxEnergy,
HealthRegen = mapEntity.HealthRegen,
EnergyRegen = mapEntity.EnergyRegen,
CurrentBuild = build,
Position = entityContext is not null ?
new Position
{
X = entityContext.Value.Position.X,
Y = entityContext.Value.Position.Y
} :
default
Position = new Position
{
X = entity.Position.X,
Y = entity.Position.Y
}
};
}
@@ -12,4 +12,7 @@ public interface IGuildwarsMemoryCache
Task<PathingMetadata?> ReadPathingMetaData(CancellationToken cancellationToken);
Task<InventoryData?> ReadInventoryData(CancellationToken cancellationToken);
Task<WorldData?> ReadWorldData(CancellationToken cancellationToken);
Task<SessionData?> ReadSessionData(CancellationToken cancellationToken);
Task<UserData?> ReadUserData(CancellationToken cancellationToken);
Task<MainPlayerData?> ReadMainPlayerData(CancellationToken cancellationToken);
}
@@ -13,5 +13,8 @@ public interface IGuildwarsMemoryReader
Task<PathingMetadata?> ReadPathingMetaData(CancellationToken cancellationToken);
Task<InventoryData?> ReadInventoryData(CancellationToken cancellationToken);
Task<WorldData?> ReadWorldData(CancellationToken cancellationToken);
Task<UserData?> ReadUserData(CancellationToken cancellationToken);
Task<SessionData?> ReadSessionData(CancellationToken cancellationToken);
Task<MainPlayerData?> ReadMainPlayerData(CancellationToken cancellationToken);
void Stop();
}
@@ -1,5 +1,6 @@
using Daybreak.Models.Interop;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Daybreak.Services.Scanner;
@@ -18,6 +19,8 @@ public interface IMemoryScanner
T[] ReadArray<T>(uint address, uint size);
T[] ReadArray<T>(GuildwarsArray<T> guildwarsArray);
T[] ReadArray<T>(GuildwarsPointerArray<T> guildwarsPointerArray);
T ReadItemAtIndex<T>(uint address, int index);
T ReadItemAtIndex<T>(GuildwarsArray<T> guildwarsArray, int index);
byte[]? ReadBytes(uint address, uint size);
string ReadWString(uint address, uint maxsize);
T ReadPtrChain<T>(uint Base, uint finalPointerOffset = 0, params uint[] offsets);
@@ -179,6 +179,18 @@ public sealed class MemoryScanner : IMemoryScanner
{
return this.ReadArray<T>(guildwarsArray.Buffer.Address, guildwarsArray.Size);
}
public T ReadItemAtIndex<T>(uint address, int index)
{
var itemSize = Marshal.SizeOf(typeof(T));
var itemAtIndexAddress = address + (index * itemSize);
return this.Read<T>((uint)itemAtIndexAddress);
}
public T ReadItemAtIndex<T>(GuildwarsArray<T> guildwarsArray, int index)
{
return this.ReadItemAtIndex<T>(guildwarsArray.Buffer.Address, index);
}
public T[] ReadArray<T>(GuildwarsPointerArray<T> guildwarsPointerArray)
{
+7 -7
View File
@@ -34,7 +34,7 @@
<components:PlayerResourcesComponent Margin="5, 5, 5, 0"
Grid.ColumnSpan="2"
Grid.Row="1"
DataContext="{Binding ElementName=_this, Path=GameData, Mode=OneWay}"
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=MainPlayerDataValid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Grid Margin="5, 0, 5, 0"
Visibility="{Binding ElementName=_this, Path=MainPlayerDataValid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
@@ -47,26 +47,26 @@
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<components:MainPlayerInformationComponent Grid.Row="0"
DataContext="{Binding ElementName=_this, Path=GameData.MainPlayer, Mode=OneWay}"
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext.Player.PlayerInformation, Mode=OneWay}"
Margin="0, 0, 0, 10"
NavigateToClicked="Component_NavigateToClicked"/>
<components:TitleInformationComponent Grid.Row="1"
DataContext="{Binding ElementName=_this, Path=GameData.MainPlayer.TitleInformation, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=GameData.MainPlayer.TitleInformation.IsValid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext.Player.PlayerInformation.TitleInformation, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=MainPlayerResourceContext.Player.PlayerInformation.TitleInformation.IsValid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Margin="0, 0, 0, 10"
NavigateToClicked="Component_NavigateToClicked"/>
<components:CurrentMapComponent Grid.Row="2"
DataContext="{Binding ElementName=_this, Path=GameData.Session.CurrentMap, Mode=OneWay}"
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext.Session.Session.CurrentMap, Mode=OneWay}"
Margin="0, 0, 0, 10"
NavigateToClicked="Component_NavigateToClicked"/>
<components:CurrentQuestComponent Grid.Row="3"
Margin="0, 0, 0, 10"
DataContext="{Binding ElementName=_this, Path=GameData.MainPlayer.Quest, Mode=OneWay}"
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext.Player.PlayerInformation.Quest, Mode=OneWay}"
NavigateToClicked="Component_NavigateToClicked"/>
<components:QuestLogComponent Grid.Row="4"
Grid.RowSpan="2"
NavigateToClicked="Component_NavigateToClicked"
DataContext="{Binding ElementName=_this, Path=GameData.MainPlayer.QuestLog, Mode=OneWay}"></components:QuestLogComponent>
DataContext="{Binding ElementName=_this, Path=MainPlayerResourceContext.Player.PlayerInformation.QuestLog, Mode=OneWay}"></components:QuestLogComponent>
</Grid>
<Grid Grid.Column="1"
Margin="5, 0, 5, 0">
+73 -39
View File
@@ -45,6 +45,9 @@ public partial class FocusView : UserControl
[GenerateDependencyProperty]
private PathingData pathingData;
[GenerateDependencyProperty]
private MainPlayerResourceContext mainPlayerResourceContext;
[GenerateDependencyProperty]
private bool loadingPathingData;
@@ -113,6 +116,7 @@ public partial class FocusView : UserControl
if (pathingMeta?.TrapezoidCount == this.PathingData?.Trapezoids?.Count ||
this.cancellationTokenSource?.IsCancellationRequested is not false)
{
await this.Dispatcher.InvokeAsync(() => this.LoadingPathingData = false);
return;
}
@@ -132,43 +136,6 @@ public partial class FocusView : UserControl
});
}
private async Task UpdateGameData()
{
if (this.applicationLauncher.IsGuildwarsRunning is false)
{
this.logger.LogInformation($"Executable is not running. Returning to {nameof(LauncherView)}");
this.viewManager.ShowView<LauncherView>();
this.cancellationTokenSource?.Cancel();
return;
}
var maybeGameData = await this.guildwarsMemoryCache.ReadGameData(this.cancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(true);
if (maybeGameData is not GameData gameData)
{
this.MainPlayerDataValid = false;
this.Browser.Visibility = this.MainPlayerDataValid is true ? Visibility.Visible : Visibility.Collapsed;
return;
}
if (gameData.MainPlayer is null ||
gameData.User is null ||
gameData.Session is null ||
gameData.Valid is false)
{
this.MainPlayerDataValid = false;
this.Browser.Visibility = this.MainPlayerDataValid is true ? Visibility.Visible : Visibility.Collapsed;
return;
}
this.GameData = gameData;
this.MainPlayerDataValid = true;
this.Browser.Visibility = this.MainPlayerDataValid is true ?
this.minimapMaximized ?
Visibility.Hidden :
Visibility.Visible :
Visibility.Collapsed;
}
private async void PeriodicallyReadPathingData(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
@@ -203,10 +170,18 @@ public partial class FocusView : UserControl
return;
}
var readGameDataTask = this.guildwarsMemoryCache.ReadGameData(cancellationToken);
await Task.WhenAll(
this.UpdateGameData(),
readGameDataTask,
Task.Delay(16, cancellationToken)).ConfigureAwait(true);
var maybeGameData = await readGameDataTask;
if (maybeGameData is null)
{
continue;
}
this.GameData = maybeGameData;
}
catch (Exception ex)
{
@@ -246,6 +221,64 @@ public partial class FocusView : UserControl
}
}
private async void PeriodicallyReadMainPlayerContextData(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
if (this.applicationLauncher.IsGuildwarsRunning is false)
{
this.logger.LogInformation($"Executable is not running. Returning to {nameof(LauncherView)}");
this.viewManager.ShowView<LauncherView>();
this.cancellationTokenSource?.Cancel();
return;
}
var readUserDataTask = this.guildwarsMemoryCache.ReadUserData(cancellationToken);
var readSessionDataTask = this.guildwarsMemoryCache.ReadSessionData(cancellationToken);
var readMainPlayerDataTask = this.guildwarsMemoryCache.ReadMainPlayerData(cancellationToken);
await Task.WhenAll(
readUserDataTask,
readSessionDataTask,
readMainPlayerDataTask,
Task.Delay(16, cancellationToken)).ConfigureAwait(true);
var userData = await readUserDataTask;
var sessionData = await readSessionDataTask;
var mainPlayerData = await readMainPlayerDataTask;
if (userData?.User is null ||
sessionData?.Session is null ||
mainPlayerData?.PlayerInformation is null ||
sessionData.Session.InstanceType is InstanceType.Loading or InstanceType.Undefined)
{
this.MainPlayerDataValid = false;
this.Browser.Visibility = Visibility.Collapsed;
continue;
}
this.MainPlayerResourceContext = new MainPlayerResourceContext
{
Player = mainPlayerData,
User = userData,
Session = sessionData
};
this.MainPlayerDataValid = true;
this.Browser.Visibility = this.minimapMaximized ? Visibility.Hidden : Visibility.Visible;
}
catch (Exception ex)
{
this.logger.LogError(ex, "Encountered non-terminating exception. Silently continuing");
}
}
}
private void FocusView_Loaded(object _, RoutedEventArgs e)
{
this.BrowserAddress = this.liveUpdateableOptions.Value.BrowserUrl;
@@ -254,7 +287,7 @@ public partial class FocusView : UserControl
this.cancellationTokenSource?.Dispose();
this.cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = this.cancellationTokenSource.Token;
this.PeriodicallyReadGameData(cancellationToken);
this.PeriodicallyReadMainPlayerContextData(cancellationToken);
if (this.InventoryVisible)
{
this.PeriodicallyReadInventoryData(cancellationToken);
@@ -262,6 +295,7 @@ public partial class FocusView : UserControl
if (this.MinimapVisible)
{
this.PeriodicallyReadGameData(cancellationToken);
this.PeriodicallyReadPathingData(cancellationToken);
}
}