Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor fixes #463

Merged
merged 2 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Daybreak.Tests/Services/JsonLoggerProviderTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using Daybreak.Services.Logging;
using Daybreak.Configuration.Options;
using Daybreak.Services.Logging;
using FluentAssertions;
using LiteDB;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using NSubstitute.Extensions;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Logging;
Expand All @@ -21,7 +25,9 @@ public void InitializeProvider()
{
File.Delete("Daybreak.db");
this.liteDatabase = new LiteDatabase("Daybreak.db");
this.logsManager = new JsonLogsManager(this.liteDatabase.GetCollection<Daybreak.Models.Log>());
var options = Substitute.For<ILiveOptions<LauncherOptions>>();
options.Value.Returns(new LauncherOptions { PersistentLogging = true });
this.logsManager = new JsonLogsManager(this.liteDatabase.GetCollection<Daybreak.Models.Log>(), options);
this.loggerProvider = new CVLoggerProvider(this.logsManager);
}

Expand Down
5 changes: 5 additions & 0 deletions Daybreak/Configuration/Options/LauncherOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Daybreak.Views;
using Newtonsoft.Json;
using System;
using System.ComponentModel;

namespace Daybreak.Configuration.Options;

Expand Down Expand Up @@ -45,4 +46,8 @@ public sealed class LauncherOptions
[OptionName(Name = "Mod Startup Timeout", Description = "Amount of seconds that Daybreak will wait for each mod to start-up before cancelling the tasks")]
[OptionRange<double>(MinValue = 30, MaxValue = 300)]
public double ModStartupTimeout { get; set; } = 30;

[JsonProperty(nameof(PersistentLogging))]
[OptionName(Name = "Persistent Logging", Description = "If true, the launcher will save logs in the local database. Otherwise, the launcher will only keep logs in a memory cache")]
public bool PersistentLogging { get; set; } = false;
}
1 change: 1 addition & 0 deletions Daybreak/Configuration/ProjectConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public override void RegisterServices(IServiceCollection services)
services.AddSingleton<ViewManager>();
services.AddSingleton<ProcessorUsageMonitor>();
services.AddSingleton<MemoryUsageMonitor>();
services.AddSingleton<DiskUsageMonitor>();
services.AddSingleton<IViewManager, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<IViewProducer, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<PostUpdateActionManager>();
Expand Down
2 changes: 1 addition & 1 deletion Daybreak/Daybreak.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8.136</Version>
<Version>0.9.8.137</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
Expand Down
9 changes: 8 additions & 1 deletion Daybreak/Services/BuildTemplates/BuildTemplateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,17 @@ private Result<Build, Exception> DecodeTemplateInner(string template)
for(int i = 0; i < buildMetadata.AttributeCount; i++)
{
var attributeId = buildMetadata.AttributesIds[i];
if (attributeId == 0)
{
continue;
}

var maybeAttribute = build.Attributes.FirstOrDefault(a => a.Attribute!.Id == attributeId);
if (maybeAttribute is null)
{
this.logger.LogError($"Failed to parse attribute with id {attributeId} for professions {primaryProfession.Name}/{secondaryProfession.Name}");
var msg = $"Failed to parse attribute with id {attributeId} for professions {primaryProfession.Name}/{secondaryProfession.Name}";
this.logger.LogError(msg);
return new InvalidOperationException(msg);
}

maybeAttribute!.Points = buildMetadata.AttributePoints[i];
Expand Down
45 changes: 40 additions & 5 deletions Daybreak/Services/Logging/JsonLogsManager.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
using LiteDB;
using Daybreak.Configuration.Options;
using LiteDB;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Linq.Expressions;
using System.Logging;

namespace Daybreak.Services.Logging;

public sealed class JsonLogsManager : ILogsManager
{
private const int MemoryCacheMaxSize = 5000;

private readonly List<Models.Log> memoryCache = new();
private readonly ILiteCollection<Models.Log> collection;
private readonly ILiveOptions<LauncherOptions> liveOptions;

public event EventHandler<Models.Log>? ReceivedLog;

public JsonLogsManager(ILiteCollection<Models.Log> collection)
public JsonLogsManager(
ILiteCollection<Models.Log> collection,
ILiveOptions<LauncherOptions> liveOptions)
{
this.collection = collection.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
}

public IEnumerable<Models.Log> GetLogs(Expression<Func<Models.Log, bool>> filter)
{
return this.collection.Find(filter);
return this.collection.Find(filter).Concat(this.memoryCache.Where(filter.Compile()));
}
public IEnumerable<Models.Log> GetLogs()
{
return this.collection.FindAll();
return this.collection.FindAll().Concat(this.memoryCache);
}
public void WriteLog(Log log)
{
Expand All @@ -39,7 +49,32 @@ public void WriteLog(Log log)
CorrelationVector = log.CorrelationVector
};

this.collection.Insert(dbLog);
if (this.liveOptions.Value.PersistentLogging)
{
lock (this.memoryCache)
{
if (this.memoryCache.Count > 0)
{
this.collection.InsertBulk(this.memoryCache, this.memoryCache.Count);
this.memoryCache.Clear();
}
}

this.collection.Insert(dbLog);
}
else
{
lock(this.memoryCache)
{
if (this.memoryCache.Count >= MemoryCacheMaxSize)
{
this.memoryCache.RemoveAt(this.memoryCache.Count - 1);
}

this.memoryCache.Add(dbLog);
}
}

this.ReceivedLog?.Invoke(this, dbLog);
}
public int DeleteLogs()
Expand Down
68 changes: 68 additions & 0 deletions Daybreak/Services/Monitoring/DiskUsageMonitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Daybreak.Services.Metrics;
using System.Diagnostics.Metrics;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Extensions.Services;
using System.Core.Extensions;
using System.Extensions;
using System.Threading;

namespace Daybreak.Services.Monitoring;

public sealed class DiskUsageMonitor : IApplicationLifetimeService
{
private const string WriteDiskUsage = "Write Disk Usage";
private const string WriteDiskUsageUnit = "MBs/s";
private const string WriteDiskUsageDescription = "MBs/s written by Daybreak";
private const string ReadDiskUsage = "Read Disk Usage";
private const string ReadDiskUsageUnit = "MBs/s";
private const string ReadDiskUsageDescription = "MBs/s read by Daybreak";

private readonly Histogram<double> writeDiskUsageHistogram;
private readonly Histogram<double> readDiskUsageHistogram;
private readonly CancellationTokenSource cancellationTokenSource = new();

private PerformanceCounter? readPerformanceCounter;
private PerformanceCounter? writePerformanceCounter;

public DiskUsageMonitor(
IMetricsService metricsService)
{
this.writeDiskUsageHistogram = metricsService.ThrowIfNull().CreateHistogram<double>(WriteDiskUsage, WriteDiskUsageUnit, WriteDiskUsageDescription, Models.Metrics.AggregationTypes.NoAggregate);
this.readDiskUsageHistogram = metricsService.ThrowIfNull().CreateHistogram<double>(ReadDiskUsage, ReadDiskUsageUnit, ReadDiskUsageDescription, Models.Metrics.AggregationTypes.NoAggregate);
}

public void OnClosing()
{
this.cancellationTokenSource.Cancel();
}

public void OnStartup()
{
_ = Task.Run(this.StartupAndPeriodicallyReadDiskUsage, this.cancellationTokenSource.Token);
}

private async Task StartupAndPeriodicallyReadDiskUsage()
{
var processName = Process.GetCurrentProcess().ProcessName;
this.readPerformanceCounter = new PerformanceCounter("Process", "IO Read Bytes/sec", processName);
this.writePerformanceCounter = new PerformanceCounter("Process", "IO Write Bytes/sec", processName);
while (!this.cancellationTokenSource.Token.IsCancellationRequested)
{
try
{
this.PeriodicallyCheckMemoryUsagePerfCounterBased();
await Task.Delay(1000, this.cancellationTokenSource.Token);
}
catch
{
}
}
}

private void PeriodicallyCheckMemoryUsagePerfCounterBased()
{
this.readDiskUsageHistogram.Record(this.readPerformanceCounter?.NextValue() / 1024 / 1024 ?? 0);
this.writeDiskUsageHistogram.Record(this.writePerformanceCounter?.NextValue() / 1024 / 1024 ?? 0);
}
}
29 changes: 18 additions & 11 deletions Daybreak/Services/Monitoring/MemoryUsageMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
using System.Windows.Extensions.Services;
using System.Core.Extensions;
using System.Extensions;
using System.Threading;

namespace Daybreak.Services.Monitoring;

public sealed class MemoryUsageMonitor : IApplicationLifetimeService
{
private const string MemoryUsage = "Memory Usage";
private const string MemoryUsageUnit = "MBs";
private const string MemoryUsageDescription = "MBs used by the launcher";
private const string MemoryUsageDescription = "MBs used by Daybreak";

private readonly Histogram<long> memoryUsageHistogram;
private readonly Process currentProcess;
private readonly CancellationTokenSource cancellationTokenSource = new();

private PerformanceCounter? memoryPerformanceCounter;

Expand All @@ -28,11 +30,12 @@ public MemoryUsageMonitor(

public void OnClosing()
{
this.cancellationTokenSource.Cancel();
}

public void OnStartup()
{
_ = Task.Run(this.StartupAndPeriodicallyReadMemoryUsage);
_ = Task.Run(this.StartupAndPeriodicallyReadMemoryUsage, this.cancellationTokenSource.Token);
}

private async Task StartupAndPeriodicallyReadMemoryUsage()
Expand All @@ -51,20 +54,24 @@ private async Task StartupAndPeriodicallyReadMemoryUsage()

private async Task PeriodicallyCheckMemoryUsagePerfCounterBased()
{
if (this.memoryPerformanceCounter is not null)
while (!this.cancellationTokenSource.IsCancellationRequested)
{
this.memoryUsageHistogram.Record(this.memoryPerformanceCounter.RawValue / 1024);
}
if (this.memoryPerformanceCounter is not null)
{
this.memoryUsageHistogram.Record(this.memoryPerformanceCounter.RawValue / 1024);
}

await Task.Delay(1000);
_ = Task.Run(this.PeriodicallyCheckMemoryUsagePerfCounterBased);
await Task.Delay(1000);
}
}

private async Task PeriodicallyCheckMemoryUsageProcessBased()
{
this.currentProcess.Refresh();
this.memoryUsageHistogram.Record(this.currentProcess.PrivateMemorySize64 / 1000000);
await Task.Delay(1000);
_ = Task.Run(this.PeriodicallyCheckMemoryUsageProcessBased);
while (!this.cancellationTokenSource.IsCancellationRequested)
{
this.currentProcess.Refresh();
this.memoryUsageHistogram.Record(this.currentProcess.PrivateMemorySize64 / 1000000);
await Task.Delay(1000);
}
}
}
29 changes: 17 additions & 12 deletions Daybreak/Services/Monitoring/ProcessorUsageMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Core.Extensions;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Extensions.Services;

Expand All @@ -12,11 +13,12 @@ public sealed class ProcessorUsageMonitor : IApplicationLifetimeService
{
private const string ProcessorTime = "Processor Usage";
private const string ProcessorTimeUnit = "% CPU";
private const string ProcessorTimeDescription = "Percentage of CPU used by the launcher";
private const string ProcessorTimeDescription = "Percentage of CPU used by Daybreak";

private readonly Histogram<double> processorTimeHistogram;
private readonly Process currentProcess;
private readonly int processorCount;
private readonly CancellationTokenSource cancellationTokenSource = new();

public ProcessorUsageMonitor(
IMetricsService metricsService)
Expand All @@ -28,24 +30,27 @@ public ProcessorUsageMonitor(

public void OnClosing()
{
this.cancellationTokenSource.Cancel();
}

public void OnStartup()
{
_ = Task.Run(this.PeriodicallyCheckCPU);
_ = Task.Run(this.PeriodicallyCheckCPU, this.cancellationTokenSource.Token);
}

private async Task PeriodicallyCheckCPU()
{
var stopwatch = Stopwatch.StartNew();
var startCpuUsage = this.currentProcess.TotalProcessorTime;
await Task.Delay(1000);

var endCpuUsage = this.currentProcess.TotalProcessorTime;
var elapsedTicks = stopwatch.ElapsedTicks;
var usage = (double)(endCpuUsage - startCpuUsage).Ticks / (double)elapsedTicks / this.processorCount * 100d;

this.processorTimeHistogram.Record(usage);
_ = Task.Run(this.PeriodicallyCheckCPU);
while (!this.cancellationTokenSource.IsCancellationRequested)
{
var stopwatch = Stopwatch.StartNew();
var startCpuUsage = this.currentProcess.TotalProcessorTime;
await Task.Delay(1000, this.cancellationTokenSource.Token);

var endCpuUsage = this.currentProcess.TotalProcessorTime;
var elapsedTicks = stopwatch.ElapsedTicks;
var usage = (double)(endCpuUsage - startCpuUsage).Ticks / (double)elapsedTicks / this.processorCount * 100d;

this.processorTimeHistogram.Record(usage);
}
}
}
5 changes: 5 additions & 0 deletions Daybreak/Services/Scanner/GWCAMemoryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,11 @@ private static PlayerInformation ParsePayload(PartyPlayerPayload partyPlayerPayl
return default;
}

if (a.ActualLevel == 64)
{
return default;
}

return new AttributeEntry
{
Attribute = attribute,
Expand Down
3 changes: 2 additions & 1 deletion Daybreak/Views/LauncherView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,13 @@ private async void SplitButton_Click(object sender, RoutedEventArgs e)
if (this.applicationLauncher.GetGuildwarsProcess(this.LatestConfiguration) is GuildWarsApplicationLaunchContext context)
{
// Detected already running guildwars process
await this.Dispatcher.InvokeAsync(() => this.Loading = false);
if (this.focusViewOptions.Value.Enabled)
{
this.menuService.CloseMenu();
this.viewManager.ShowView<FocusView>(context);
}

await this.Dispatcher.InvokeAsync(() => this.Loading = false);
return;
}

Expand Down
Loading