Skip to content

Commit

Permalink
- only write to disk on specific timer properties changing
Browse files Browse the repository at this point in the history
- tooltips, but they're kinda bad with full list refresh
- debugger logging
  • Loading branch information
pjmagee committed Jun 18, 2024
1 parent 16ea2e8 commit 737b5e3
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 102 deletions.
30 changes: 13 additions & 17 deletions src/Dota2Helper/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;

using Dota2Helper.ViewModels;
using Dota2Helper.Views;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
Expand All @@ -17,12 +15,13 @@
using Dota2Helper.Core.Gsi;
using Dota2Helper.Core.Listeners;
using Dota2Helper.Core.Timers;
using Microsoft.Extensions.Logging;
using ViewLocator = Dota2Helper.Core.Framework.ViewLocator;
using Hosting = Microsoft.Extensions.Hosting;

namespace Dota2Helper;

public partial class App : Application
public class App : Application
{
public IHost? Host { get; private set; }

Expand All @@ -34,21 +33,21 @@ public override void Initialize()
public override async void OnFrameworkInitializationCompleted()
{
Host = CreateHost();

if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = Host.Services.GetRequiredService<MainWindowViewModel>()
};

desktop.Exit += async (sender, args) =>
{
foreach (var listener in Host.Services.GetRequiredService<List<IDotaListener>>())
{
listener.Dispose();
}

await Host.StopAsync(TimeSpan.FromSeconds(5));
Host.Dispose();
Host = null;
Expand All @@ -64,25 +63,21 @@ public override async void OnFrameworkInitializationCompleted()
private static IHost CreateHost()
{
HostApplicationBuilder builder = Hosting.Host.CreateApplicationBuilder(Environment.GetCommandLineArgs());

builder.Services.AddSingleton<FakeDotaListener>();
builder.Services.AddSingleton<DotaListener>();

builder.Services.AddSingleton<List<IDotaListener>>(sp =>
[
sp.GetRequiredService<FakeDotaListener>(), sp.GetRequiredService<DotaListener>()
]);

builder.Services.AddSingleton<IListenerStrategy, DynamicListenerStrategy>();


builder.Logging.AddDebug();

builder.Services.Configure<Settings>(options =>
{
using (var stream = File.OpenRead("appsettings.json"))
{
using (var document = JsonDocument.Parse(stream))
{
var parsed = document.RootElement.Deserialize<AppSettings>(JsonContext.Default.Options);

if (parsed != null)
{
options.Timers = parsed.Settings.Timers;
Expand All @@ -97,17 +92,18 @@ private static IHost CreateHost()
builder.Services.AddSingleton<GameStateHolder>();

builder.Services.AddTransient<ViewLocator>();

builder.Services.AddSingleton<SettingsViewModel>();
builder.Services.AddSingleton<MainWindowViewModel>();
builder.Services.AddSingleton<TimersViewModel>();

builder.Services.AddView<MainWindowViewModel, MainWindow>();
builder.Services.AddView<TimersViewModel, TimersView>();
builder.Services.AddView<SettingsViewModel, SettingsView>();

builder.Services.AddHostedService<GameStateUpdater>();
builder.Services.AddHostedService<AudioPlayerService>();

return builder.Build();
}
}
2 changes: 0 additions & 2 deletions src/Dota2Helper/Core/Audio/AudioPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO.Compression;
using System.Speech.Synthesis;
using System.Threading.Tasks;
using Dota2Helper.Core.Timers;
using LibVLCSharp.Shared;

Expand Down
2 changes: 1 addition & 1 deletion src/Dota2Helper/Core/Configuration/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ namespace Dota2Helper.Core.Configuration;
public class AppSettings
{
[JsonPropertyName("Settings")]
public Settings Settings { get; set; }
public required Settings Settings { get; set; }
}
2 changes: 1 addition & 1 deletion src/Dota2Helper/Core/Configuration/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Dota2Helper.Core.Configuration;
public class Settings
{
[JsonPropertyName("Timers")]
public List<TimerOptions> Timers { get; set; }
public required List<TimerOptions> Timers { get; set; }

[JsonPropertyName("Address")]
public required Uri Address { get; set; } = new("http://localhost:4001/");
Expand Down
3 changes: 2 additions & 1 deletion src/Dota2Helper/Core/Gsi/GameStateUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
container.State = await listenerStrategy.Current.GetStateAsync();
var listener = await listenerStrategy.GetListener(stoppingToken);
container.State = await listener.GetStateAsync(stoppingToken);
timersViewModel.Update();
}
catch (Exception ex)
Expand Down
17 changes: 9 additions & 8 deletions src/Dota2Helper/Core/Listeners/DotaListener.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Net;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Dota2Helper.Core.Configuration;
using Dota2Helper.Core.Gsi;
Expand All @@ -17,17 +18,17 @@ public DotaListener(IOptions<Settings> settings)
_listener.Prefixes.Add(settings.Value.Address.AbsoluteUri);
}

public async Task<GameState?> GetStateAsync()
{
if (!_listener.IsListening)
{
_listener.Start();
}

public async Task<GameState?> GetStateAsync(CancellationToken cancellationToken)
{
try
{
if (!_listener.IsListening)
{
_listener.Start();
}

HttpListenerContext context = await _listener.GetContextAsync();
GameState? state = await JsonSerializer.DeserializeAsync<GameState>(context.Request.InputStream);
GameState? state = await JsonSerializer.DeserializeAsync<GameState>(context.Request.InputStream, cancellationToken: cancellationToken);

context.Response.StatusCode = 200;
context.Response.Close();
Expand Down
35 changes: 24 additions & 11 deletions src/Dota2Helper/Core/Listeners/DynamicListenerStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,40 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Microsoft.Extensions.Logging;

namespace Dota2Helper.Core.Listeners;

public class DynamicListenerStrategy(List<IDotaListener> listeners) : IListenerStrategy
public class DynamicListenerStrategy(
ILogger<DynamicListenerStrategy> logger,
FakeDotaListener fakeDotaListener,
DotaListener dotaListener) : IListenerStrategy
{
public IDotaListener Current
public async Task<IDotaListener> GetListener(CancellationToken cancellationToken)
{
get
if (Design.IsDesignMode)
{
if (Design.IsDesignMode)
{
return listeners.First(l => l.GetType() == typeof(FakeDotaListener));
}
return fakeDotaListener;
}

if (Process.GetProcessesByName("dota2").Any())
if (Process.GetProcessesByName("dota2").Any())
{
logger.LogInformation("Dota 2 is running, using DotaListener");

var state = await dotaListener.GetStateAsync(cancellationToken);

if (state != null)
{
return listeners.First(l => l.GetType() == typeof(DotaListener));
logger.LogInformation("DotaListener is working");
return dotaListener;
}

return listeners.First(l => l.GetType() == typeof(FakeDotaListener));
}

logger.LogInformation("Dota 2 is not running, using FakeDotaListener");

return fakeDotaListener;
}
}
24 changes: 18 additions & 6 deletions src/Dota2Helper/Core/Listeners/FakeDotaListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@
using System.Threading;
using System.Threading.Tasks;
using Dota2Helper.Core.Gsi;
using Microsoft.Extensions.Logging;

namespace Dota2Helper.Core.Listeners;

public class FakeDotaListener : IDotaListener, IAsyncDisposable
{
private readonly ITimer? _timer;
private readonly ILogger<FakeDotaListener> _logger = null!;
private readonly ITimer? _timer = null!;

private TimeSpan _gameTime = new(hours: 0, minutes: 0, seconds: 0);

public FakeDotaListener() : this(TimeProvider.System)
public FakeDotaListener(ILogger<FakeDotaListener> logger) : this(TimeProvider.System)
{

_logger = logger;
}

public FakeDotaListener(TimeProvider provider)
private FakeDotaListener(TimeProvider provider)
{
_timer = provider.CreateTimer(Update, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
}
Expand All @@ -26,7 +28,7 @@ private void Update(object? state)
_gameTime = _gameTime.Add(TimeSpan.FromSeconds(4 ));
}

public Task<GameState?> GetStateAsync()
public Task<GameState?> GetStateAsync(CancellationToken cancellationToken)
{
var fakeState = new GameState()
{
Expand All @@ -47,6 +49,16 @@ public void Dispose()

public async ValueTask DisposeAsync()
{
if (_timer != null) await _timer.DisposeAsync();
try
{
if (_timer != null)
{
await _timer.DisposeAsync();
}
}
catch (Exception e)
{
_logger.LogError(e, "Failed to dispose timer");
}
}
}
3 changes: 2 additions & 1 deletion src/Dota2Helper/Core/Listeners/IDotaListener.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Dota2Helper.Core.Gsi;

namespace Dota2Helper.Core.Listeners;

public interface IDotaListener : IDisposable
{
Task<GameState?> GetStateAsync();
Task<GameState?> GetStateAsync(CancellationToken cancellationToken);
}
7 changes: 5 additions & 2 deletions src/Dota2Helper/Core/Listeners/IListenerStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
namespace Dota2Helper.Core.Listeners;
using System.Threading;
using System.Threading.Tasks;

namespace Dota2Helper.Core.Listeners;

public interface IListenerStrategy
{
IDotaListener Current { get; }
Task<IDotaListener> GetListener(CancellationToken cancellationToken);
}
Loading

0 comments on commit 737b5e3

Please sign in to comment.