Revert "Add db for backup and memory happiness"
This reverts commit 9003d2c4dd.
This commit is contained in:
@@ -1,105 +0,0 @@
|
||||
namespace JoshHeaps.Net.Services.Implementations;
|
||||
|
||||
public class AutoIpUpdateService(
|
||||
IConfiguration config,
|
||||
ILogger<AutoIpUpdateService> log)
|
||||
: BackgroundService
|
||||
{
|
||||
private static readonly TimeSpan CheckInterval = TimeSpan.FromMinutes(5);
|
||||
private static readonly HttpClient httpClient = new();
|
||||
public static bool IsEnabled { get; private set; } = false;
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stop)
|
||||
{
|
||||
IsEnabled = true;
|
||||
var timer = new PeriodicTimer(CheckInterval);
|
||||
AAAARecord dnsRecord = await GetDnsRecordAsync();
|
||||
string lastKnownIp = dnsRecord.Content;
|
||||
|
||||
while (await timer.WaitForNextTickAsync(stop))
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentIp = await GetPublicIpAsync() ?? "";
|
||||
|
||||
if (lastKnownIp != currentIp)
|
||||
{
|
||||
await UpdateDnsIpAsync(config, dnsRecord, currentIp);
|
||||
|
||||
lastKnownIp = currentIp;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { /* shutting down */ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError(ex, "Error while attempting ip update");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GetPublicIpAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await httpClient.GetStringAsync(@"https://api.ipify.org/");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
Console.WriteLine("Reattempting to grab public ip");
|
||||
|
||||
return await GetPublicIpAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<AAAARecord> GetDnsRecordAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpClient cfClient = new();
|
||||
cfClient.DefaultRequestHeaders.Add("X-Auth-Email", config["cfEmail"]);
|
||||
cfClient.DefaultRequestHeaders.Add("X-Auth-Key", config["cfKey"]);
|
||||
var result = await cfClient.GetAsync(@$"https://api.cloudflare.com/client/v4/zones/{config["zoneId"]}/dns_records");
|
||||
Console.WriteLine(await result.Content.ReadAsStringAsync());
|
||||
var records = System.Text.Json.JsonSerializer.Deserialize<RecordList>(await result.Content.ReadAsStringAsync());
|
||||
return records!.Result[0];
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
Console.WriteLine("Reattempting to grab current ip");
|
||||
|
||||
return await GetDnsRecordAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task UpdateDnsIpAsync(IConfiguration config, AAAARecord record, string ip)
|
||||
{
|
||||
HttpClient cfClient = new();
|
||||
cfClient.DefaultRequestHeaders.Add("X-Auth-Email", config["cfEmail"]);
|
||||
cfClient.DefaultRequestHeaders.Add("X-Auth-Key", config["cfKey"]);
|
||||
object content = new
|
||||
{
|
||||
comment = "Update as needed",
|
||||
content = ip,
|
||||
name = "@",
|
||||
proxied = true,
|
||||
ttl = 3600,
|
||||
type = "AAAA"
|
||||
};
|
||||
|
||||
var result = await cfClient.PutAsJsonAsync(@$"https://api.cloudflare.com/client/v4/zones/{config["zoneId"]}/dns_records{record.Id}", content);
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
Console.WriteLine("Reattempting to update ip");
|
||||
|
||||
await UpdateDnsIpAsync(config, record, ip);
|
||||
}
|
||||
}
|
||||
|
||||
record AAAARecord(string Comment, string Content, string Name, string Id);
|
||||
|
||||
record RecordList(List<AAAARecord> Result);
|
||||
}
|
||||
@@ -4,21 +4,18 @@ using System.Threading.Channels;
|
||||
|
||||
namespace JoshHeaps.Net.Services.Implementations;
|
||||
|
||||
public class BackgroundTaskQueue
|
||||
public class BackgroundTaskQueue : IBackgroundTaskQueue
|
||||
{
|
||||
private readonly ConcurrentDictionary<Guid, Task> _runningTasks = new();
|
||||
private readonly ConcurrentDictionary<int, Task> _runningTasks = new();
|
||||
|
||||
public void Queue(Func<Task> workItem, Guid workId)
|
||||
public void Queue(Func<Task> workItem)
|
||||
{
|
||||
if (_runningTasks.ContainsKey(workId))
|
||||
return;
|
||||
|
||||
var task = Task.Run(workItem);
|
||||
_runningTasks.TryAdd(workId, task);
|
||||
_runningTasks.TryAdd(task.Id, task);
|
||||
|
||||
task.ContinueWith(t => _runningTasks.TryRemove(workId, out _), TaskScheduler.Default);
|
||||
task.ContinueWith(t => _runningTasks.TryRemove(t.Id, out _), TaskScheduler.Default);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Guid, Task> Running => _runningTasks;
|
||||
public Task WhenAllDone() => Task.WhenAll(Running.Values);
|
||||
public IReadOnlyCollection<Task> Running => [.. _runningTasks.Values];
|
||||
public Task WhenAllDone() => Task.WhenAll(Running);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using JoshHeaps.Net.DAL;
|
||||
using JoshHeaps.Net.Models;
|
||||
using JoshHeaps.Net.Models;
|
||||
using JoshHeaps.Net.Services.Interfaces;
|
||||
|
||||
namespace JoshHeaps.Net.Services.Implementations;
|
||||
|
||||
public class ChessService(ChessDbAccess dbAccess) : IChessService
|
||||
public class ChessService : IChessService
|
||||
{
|
||||
public GameState CreateNewGame()
|
||||
{
|
||||
@@ -123,7 +122,7 @@ public class ChessService(ChessDbAccess dbAccess) : IChessService
|
||||
return legalMoves;
|
||||
}
|
||||
|
||||
public async Task<MoveResultDto> MakeMove(GameState gameState, MoveDto moveDto)
|
||||
public MoveResultDto MakeMove(GameState gameState, MoveDto moveDto)
|
||||
{
|
||||
var piece = gameState.Pieces.FirstOrDefault(p => p.Id == moveDto.PieceId);
|
||||
|
||||
@@ -146,12 +145,6 @@ public class ChessService(ChessDbAccess dbAccess) : IChessService
|
||||
var notation = $"{piece.Id}:{piece.Position}->{targetPos}";
|
||||
gameState.MoveHistory.Add(notation);
|
||||
|
||||
gameState.CurrentPlayer = gameState.CurrentPlayer == PieceColor.White
|
||||
? PieceColor.Black
|
||||
: PieceColor.White;
|
||||
|
||||
await dbAccess.SaveAsync(gameState);
|
||||
|
||||
return new MoveResultDto
|
||||
{
|
||||
Success = true,
|
||||
@@ -173,8 +166,8 @@ public class ChessService(ChessDbAccess dbAccess) : IChessService
|
||||
piece.Position = targetPos;
|
||||
gs.Board[targetPos.Row, targetPos.Col] = piece;
|
||||
|
||||
if (captured is not null && captured != piece)
|
||||
captured.Position = new Position(-1, -1); // Remove captured piece from board
|
||||
if (captured != null && captured != piece)
|
||||
captured.Position = new Position(-1, -1);
|
||||
|
||||
bool wasFirstMove = !piece.HasMoved;
|
||||
piece.HasMoved = true;
|
||||
@@ -186,6 +179,10 @@ public class ChessService(ChessDbAccess dbAccess) : IChessService
|
||||
HandlePawnPromotionIfNeeded(piece, moveDto);
|
||||
|
||||
UpdateCastlingRights(gs, piece, oldPos);
|
||||
|
||||
gs.CurrentPlayer = gs.CurrentPlayer == PieceColor.White
|
||||
? PieceColor.Black
|
||||
: PieceColor.White;
|
||||
}
|
||||
|
||||
private static void HandleEnPassantIfNeeded(GameState gs, ChessPiece piece, Position targetPos, ref ChessPiece? captured)
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
using JoshHeaps.Net.DAL;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JoshHeaps.Net.Services.Implementations;
|
||||
|
||||
public sealed class GameCleanupService(
|
||||
IDbContextFactory<ChessDbContext> dbFactory,
|
||||
ILogger<GameCleanupService> log)
|
||||
: BackgroundService
|
||||
{
|
||||
private static readonly TimeSpan CheckInterval = TimeSpan.FromMinutes(5);
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stop)
|
||||
{
|
||||
var timer = new PeriodicTimer(CheckInterval);
|
||||
|
||||
while (await timer.WaitForNextTickAsync(stop))
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(stop);
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var expired = await db.Games
|
||||
.Where(g => g.LastMoveUtc.AddDays(7) < DateTime.UtcNow)
|
||||
.ToListAsync(stop);
|
||||
|
||||
if (expired.Count == 0) continue;
|
||||
|
||||
db.Games.RemoveRange(expired);
|
||||
await db.SaveChangesAsync(stop);
|
||||
|
||||
log.LogInformation("🗑️ Removed {Count} expired games", expired.Count);
|
||||
}
|
||||
catch (OperationCanceledException) { /* shutting down */ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError(ex, "Error while purging old games");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using JoshHeaps.Net.DAL;
|
||||
using JoshHeaps.Net.Hubs;
|
||||
using JoshHeaps.Net.Hubs;
|
||||
using JoshHeaps.Net.Models;
|
||||
using JoshHeaps.Net.Services.Interfaces;
|
||||
using JoshHeaps.Net.Utilities;
|
||||
using Microsoft.AspNetCore.Rewrite;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -18,12 +16,6 @@ public sealed class Stockfish : IAsyncDisposable
|
||||
private readonly Channel<string> _stdout = Channel.CreateUnbounded<string>();
|
||||
private readonly int _skill;
|
||||
|
||||
private static readonly SemaphoreSlim _mutex = new(1, 1);
|
||||
private static readonly List<Guid> _statesRunning = [];
|
||||
|
||||
public bool IsRunning => _p is not null && !_p.HasExited;
|
||||
public bool InUse { get; set; }
|
||||
|
||||
public Stockfish(int skill = 20, int hash = 256)
|
||||
{
|
||||
_skill = skill;
|
||||
@@ -52,7 +44,7 @@ public sealed class Stockfish : IAsyncDisposable
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,6 +70,7 @@ public sealed class Stockfish : IAsyncDisposable
|
||||
Send("uci");
|
||||
WaitFor("uciok").GetAwaiter().GetResult();
|
||||
|
||||
Send($"setoption name Skill Level value {skill}");
|
||||
Send($"setoption name Hash value {hash}");
|
||||
Send("isready");
|
||||
WaitFor("readyok").GetAwaiter().GetResult();
|
||||
@@ -116,15 +109,8 @@ public sealed class Stockfish : IAsyncDisposable
|
||||
_p.Dispose();
|
||||
}
|
||||
|
||||
public async Task<bool> MakeMove(GameState state, IHubContext<ChessHub> chessHub, IChessService chessService, ChessDbAccess dbAccess)
|
||||
public async Task MakeMove(GameState state, IHubContext<ChessHub> chessHub, IChessService chessService)
|
||||
{
|
||||
if (state.ComputerColor != state.CurrentPlayer)
|
||||
return false;
|
||||
|
||||
Send($"setoption name Skill Level value {state.ComputerDifficulty}");
|
||||
Send("isready");
|
||||
await WaitFor("readyok");
|
||||
|
||||
var move = await GetBestMoveAsync(state.ToFen());
|
||||
|
||||
var moveDto = move.ToMoveDto(
|
||||
@@ -133,10 +119,8 @@ public sealed class Stockfish : IAsyncDisposable
|
||||
? state.WhitePlayerId
|
||||
: state.BlackPlayerId);
|
||||
|
||||
var result = await chessService.MakeMove(state, moveDto);
|
||||
var result = chessService.MakeMove(state, moveDto);
|
||||
|
||||
await chessHub.Clients.Group(state.GameId.ToString()).SendAsync("ReceiveMoveUpdate", state.GameId.ToString(), moveDto, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using JoshHeaps.Net.DAL;
|
||||
using JoshHeaps.Net.Hubs;
|
||||
using JoshHeaps.Net.Models;
|
||||
using JoshHeaps.Net.Services.Interfaces;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace JoshHeaps.Net.Services.Implementations;
|
||||
|
||||
public class StockfishManager(IHubContext<ChessHub> chessHub, IChessService chessService, ChessDbAccess dbAccess)
|
||||
{
|
||||
private readonly List<Stockfish> _workers = [];
|
||||
|
||||
public async Task<bool> Run(GameState state)
|
||||
{
|
||||
if (!state.IsVsComputer)
|
||||
return true;
|
||||
|
||||
if (!_workers.Any(x => x.InUse) && _workers.Count < 5)
|
||||
_workers.Add(new Stockfish(state.ComputerDifficulty));
|
||||
|
||||
var worker = _workers.First(x => !x.InUse);
|
||||
|
||||
worker.InUse = true;
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(500));
|
||||
return await worker.MakeMove(state, chessHub, chessService, dbAccess);
|
||||
}
|
||||
finally
|
||||
{
|
||||
worker.InUse = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<KeyValuePair<Guid, GameState>>> RunDeliquents(ConcurrentDictionary<Guid, GameState> deliquents)
|
||||
{
|
||||
ConcurrentDictionary<Guid, GameState> continuedDeliquents = [];
|
||||
List<Task> tasks = [];
|
||||
|
||||
foreach (var deliquent in deliquents)
|
||||
{
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
var state = deliquent.Value;
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (state.IsVsComputer && state.ComputerColor == state.CurrentPlayer)
|
||||
result = !(await Run(state));
|
||||
|
||||
if (result)
|
||||
continuedDeliquents[deliquent.Key] = deliquent.Value;
|
||||
}));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
return continuedDeliquents;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user