Statistics view

This commit is contained in:
Liza 2022-10-26 20:43:24 +02:00
parent c5d3c90b85
commit 4bd37bdefd
8 changed files with 213 additions and 24 deletions

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>9.0</LangVersion>
<Version>1.4.0.0</Version>
<Version>1.5.0.0</Version>
</PropertyGroup>
<PropertyGroup>
@ -29,6 +29,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pal.Common\Pal.Common.csproj" />
<ProjectReference Include="..\vendor\ECommons\ECommons\ECommons.csproj" />
</ItemGroup>

View File

@ -7,7 +7,9 @@ using Dalamud.Plugin;
using ECommons;
using ECommons.Schedulers;
using ECommons.SplatoonAPI;
using Grpc.Core;
using ImGuiNET;
using Pal.Client.Windows;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -63,6 +65,12 @@ namespace Pal.Client
Service.WindowSystem.AddWindow(configWindow);
}
var statisticsWindow = pluginInterface.Create<StatisticsWindow>();
if (statisticsWindow is not null)
{
Service.WindowSystem.AddWindow(statisticsWindow);
}
pluginInterface.UiBuilder.Draw += Service.WindowSystem.Draw;
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
Service.Framework.Update += OnFrameworkUpdate;
@ -93,7 +101,16 @@ namespace Pal.Client
return;
}
switch (arguments)
{
case "stats":
Task.Run(async () => await FetchFloorStatistics());
break;
default:
Service.WindowSystem.GetWindow<ConfigWindow>()?.Toggle();
break;
}
}
#region IDisposable Support
@ -341,6 +358,38 @@ namespace Pal.Client
}
}
private async Task FetchFloorStatistics()
{
if (Service.Configuration.Mode != Configuration.EMode.Online)
{
Service.Chat.Print($"[Palace Pal] You can view statistics for the floor you're currently on by opening the 'Debug' tab in the configuration window.");
return;
}
try
{
var (success, floorStatistics) = await Service.RemoteApi.FetchStatistics();
if (success)
{
var statisticsWindow = Service.WindowSystem.GetWindow<StatisticsWindow>();
statisticsWindow.SetFloorData(floorStatistics);
statisticsWindow.IsOpen = true;
}
else
{
Service.Chat.PrintError("[Palace Pal] Unable to fetch statistics.");
}
}
catch (RpcException e) when (e.StatusCode == StatusCode.PermissionDenied)
{
Service.Chat.Print($"[Palace Pal] You can view statistics for the floor you're currently on by opening the 'Debug' tab in the configuration window.");
}
catch (Exception e)
{
Service.Chat.PrintError($"[Palace Pal] {e}");
}
}
private void HandleRemoteDownloads()
{
while (_remoteDownloads.TryDequeue(out var download))

View File

@ -20,8 +20,7 @@ namespace Pal.Client
private const string remoteUrl = "https://pal.μ.tv";
#endif
private GrpcChannel _channel;
private string _authToken;
private DateTime _tokenExpiresAt;
private LoginReply _lastLoginReply;
private async Task<bool> Connect(CancellationToken cancellationToken, bool retry = true)
{
@ -67,18 +66,12 @@ namespace Pal.Client
if (string.IsNullOrEmpty(accountId))
return false;
if (string.IsNullOrEmpty(_authToken) || _tokenExpiresAt < DateTime.Now)
if (_lastLoginReply == null || string.IsNullOrEmpty(_lastLoginReply.AuthToken) || _lastLoginReply.ExpiresAt.ToDateTime().ToLocalTime() < DateTime.Now)
{
var loginReply = await accountClient.LoginAsync(new LoginRequest { AccountId = accountId }, deadline: DateTime.UtcNow.AddSeconds(10), cancellationToken: cancellationToken);
if (loginReply.Success)
_lastLoginReply = await accountClient.LoginAsync(new LoginRequest { AccountId = accountId }, deadline: DateTime.UtcNow.AddSeconds(10), cancellationToken: cancellationToken);
if (!_lastLoginReply.Success)
{
_authToken = loginReply.AuthToken;
_tokenExpiresAt = loginReply.ExpiresAt.ToDateTime().ToLocalTime();
}
else
{
_authToken = null;
if (loginReply.Error == LoginError.InvalidAccountId)
if (_lastLoginReply.Error == LoginError.InvalidAccountId)
{
accountId = null;
#if DEBUG
@ -95,7 +88,7 @@ namespace Pal.Client
}
}
return !string.IsNullOrEmpty(_authToken);
return !string.IsNullOrEmpty(_lastLoginReply?.AuthToken);
}
public async Task<string> VerifyConnection(CancellationToken cancellationToken = default)
@ -142,9 +135,19 @@ namespace Pal.Client
return uploadReply.Success;
}
public async Task<(bool, List<FloorStatistics>)> FetchStatistics(CancellationToken cancellationToken = default)
{
if (!await Connect(cancellationToken))
return new(false, new List<FloorStatistics>());
var palaceClient = new PalaceService.PalaceServiceClient(_channel);
var statisticsReply = await palaceClient.FetchStatisticsAsync(new StatisticsRequest(), headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(30), cancellationToken: cancellationToken);
return (statisticsReply.Success, statisticsReply.FloorStatistics.ToList());
}
private Metadata AuthorizedHeaders() => new Metadata
{
{ "Authorization", $"Bearer {_authToken}" },
{ "Authorization", $"Bearer {_lastLoginReply?.AuthToken}" },
};
public void Dispose()

View File

@ -4,7 +4,7 @@ using ECommons;
using ImGuiNET;
using System.Numerics;
namespace Pal.Client
namespace Pal.Client.Windows
{
internal class AgreementWindow : Window
{

View File

@ -8,7 +8,7 @@ using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
namespace Pal.Client
namespace Pal.Client.Windows
{
internal class ConfigWindow : Window
{

View File

@ -0,0 +1,84 @@
using Dalamud.Interface.Windowing;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Pal.Common;
using Palace;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace Pal.Client.Windows
{
internal class StatisticsWindow : Window
{
private SortedDictionary<ETerritoryType, TerritoryStatistics> _territoryStatistics = new();
public StatisticsWindow() : base("Palace Pal - Statistics###PalacePalStats")
{
Size = new Vector2(500, 500);
SizeCondition = ImGuiCond.FirstUseEver;
foreach (ETerritoryType territory in typeof(ETerritoryType).GetEnumValues())
{
_territoryStatistics[territory] = new TerritoryStatistics { TerritoryName = territory.ToString() };
}
}
public override void Draw()
{
if (ImGui.CollapsingHeader("Discovered Traps & Coffers per Instance", ImGuiTreeNodeFlags.DefaultOpen))
{
if (ImGui.BeginTable("TrapHoardStatistics", 3, ImGuiTableFlags.Borders))
{
ImGui.TableSetupColumn("Instance", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Traps", ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableSetupColumn("Hoard", ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableHeadersRow();
foreach (var (territoryType, stats) in _territoryStatistics)
{
ImGui.TableNextRow();
if (ImGui.TableNextColumn())
ImGui.Text(stats.TerritoryName);
if (ImGui.TableNextColumn())
ImGui.Text(stats.TrapCount?.ToString() ?? "-");
if (ImGui.TableNextColumn())
ImGui.Text(stats.HoardCofferCount?.ToString() ?? "-");
}
ImGui.EndTable();
}
}
}
internal void SetFloorData(IEnumerable<FloorStatistics> floorStatistics)
{
foreach (var territoryStatistics in _territoryStatistics.Values)
{
territoryStatistics.TrapCount = null;
territoryStatistics.HoardCofferCount = null;
}
foreach (var floor in floorStatistics)
{
if (_territoryStatistics.TryGetValue((ETerritoryType)floor.TerritoryType, out TerritoryStatistics territoryStatistics))
{
territoryStatistics.TrapCount = floor.TrapCount;
territoryStatistics.HoardCofferCount = floor.HoardCount;
}
}
}
private class TerritoryStatistics
{
public string TerritoryName { get; set; }
public uint? TrapCount { get; set; }
public uint? HoardCofferCount { get; set; }
}
}
}

View File

@ -0,0 +1,37 @@
namespace Pal.Common
{
public enum ETerritoryType : ushort
{
Palace_1_10 = 561,
Palace_11_20,
Palace_21_30,
Palace_31_40,
Palace_41_50,
Palace_51_60 = 593,
Palace_61_70,
Palace_71_80,
Palace_81_90,
Palace_91_100,
Palace_101_110,
Palace_111_120,
Palace_121_130,
Palace_131_140,
Palace_141_150,
Palace_151_160,
Palace_161_170,
Palace_171_180,
Palace_181_190,
Palace_191_200,
HeavenOnHigh_1_10 = 770,
HeavenOnHigh_11_20,
HeavenOnHigh_21_30,
HeavenOnHigh_31_40,
HeavenOnHigh_41_50,
HeavenOnHigh_51_60,
HeavenOnHigh_61_70 = 782,
HeavenOnHigh_71_80,
HeavenOnHigh_81_90,
HeavenOnHigh_91_100
}
}

View File

@ -5,6 +5,7 @@ package palace;
service PalaceService {
rpc DownloadFloors(DownloadFloorsRequest) returns (DownloadFloorsReply);
rpc UploadFloors(UploadFloorsRequest) returns (UploadFloorsReply);
rpc FetchStatistics(StatisticsRequest) returns (StatisticsReply);
}
message DownloadFloorsRequest {
@ -25,6 +26,20 @@ message UploadFloorsReply {
bool success = 1;
}
message StatisticsRequest {
}
message StatisticsReply {
bool success = 1;
repeated FloorStatistics floorStatistics = 2;
}
message FloorStatistics {
uint32 territoryType = 1;
uint32 trapCount = 2;
uint32 hoardCount = 3;
}
message PalaceObject {
ObjectType type = 1;
float x = 2;