using Microsoft.AspNetCore.SignalR.Client; using Polly; using Refit; using Sledgemapper.Shared.Entities; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; namespace Sledgemapper { public class CommunicationManager { public IMapApi Api { get; private set; } public HubConnection Connection { get; private set; } public readonly Session SessionData; private readonly ChannelsQueue Queue = new(); private AuthenticateResponse _authenticateResponse; public CommunicationManager(Session sessionData) { SessionData = sessionData; Connection = new HubConnectionBuilder() .WithAutomaticReconnect() //.WithUrl("http://hub.michelescandura.com:5001/SledgemapperHub") //.WithUrl("http://localhost:5001/SledgemapperHub", options => .WithUrl("http://hub.michelescandura.com:5001/SledgemapperHub", options => { options.AccessTokenProvider = () => Task.FromResult(_authenticateResponse.Token); }) .Build(); Api = RestService.For( new HttpClient(new AuthenticatedHttpClientHandler(GetToken)) { BaseAddress = new Uri("http://hub.michelescandura.com:5001") // BaseAddress = new Uri("http://localhost:5001") } ); Connection.On("UpdateMap", (map) => { SessionData.Map = map.Map; SessionData.Walls = map.Walls; SessionData.Overlays = map.Overlays; }); Connection.On("PlayerUpdate", (player) => { var p = SessionData.Players.FirstOrDefault(m => m.UserId == player.UserId); if (p != null) { p.Position = player.Position; } else { SessionData.Players.Add(player); } }); Connection.On("DeleteTile", (tile) => { SessionData.Map.Remove(tile.ToString(), out var _); }); Connection.On("DeleteWall", (tile) => { SessionData.Walls.Remove(tile.ToString(), out var _); }); Connection.On("DeleteOverlay", (tile) => { SessionData.Overlays.Remove(tile.ToString(), out var _); }); Connection.On("NewTile", (tile) => { SessionData.Map.Remove(tile.ToString(), out var _); SessionData.Map.TryAdd(tile.ToString(), tile); }); Connection.On("RefreshPlayers", () => { if (!string.IsNullOrWhiteSpace(SessionData.SessionName)) { Connection?.SendAsync("UpdatePosition", SessionData.SessionName, SessionData.SessionId, SessionData.Players.First(p => p.UserId == int.Parse(_authenticateResponse.Id))); } }); Connection.On("NewWall", (tile) => { SessionData.Walls.Remove(tile.ToString(), out var _); SessionData.Walls.TryAdd(tile.ToString(), tile); }); Connection.On("NewOverlay", (tile) => { SessionData.Overlays.Remove(tile.ToString(), out var _); SessionData.Overlays.TryAdd(tile.ToString(), tile); }); Connection.On("NewPlayer", (player) => { var p = SessionData.Players.FirstOrDefault(m => m.UserId == player.UserId); if (p is null) { SessionData.Players.Add(player); } else { p.Color = player.Color; p.Position = player.Position; } }); } private Task GetToken() { return Task.FromResult(_authenticateResponse.Token); } public async Task Register(RegisterModel registerModel) { var result = await Api.Register(registerModel).ConfigureAwait(false); return result; } public async Task Login(AuthenticateModel authenticateModel) { _authenticateResponse = await Api.Authenticate(authenticateModel).ConfigureAwait(false); return _authenticateResponse; } private async Task Execute(Func call) { await Policy .Handle(ex => ex.StatusCode == HttpStatusCode.RequestTimeout) .RetryForeverAsync() //.RetryAsync(Polly.RetrySyntax., async (exception, retryCount) => await Task.Delay(500)) .ExecuteAsync(async () => await call().ConfigureAwait(false)) .ConfigureAwait(false); } public void Enqueue(BaseMapEntity entity, TileAction action) { switch (action) { case TileAction.Add: switch (entity) { case Tile tile: Queue.Enqueue(async () => await Execute(async () => await Api.NewTile(tile, SessionData.SessionName).ConfigureAwait(false))); break; case Overlay overlay: Queue.Enqueue(async () => await Execute(async () => await Api.NewOverlay(overlay, SessionData.SessionName).ConfigureAwait(false))); break; case Wall wall: Queue.Enqueue(async () => await Execute(async () => await Api.NewWall(wall, SessionData.SessionName).ConfigureAwait(false))); break; } break; case TileAction.Delete: switch (entity) { case Tile tile: Queue.Enqueue(async () => await Execute(async () => await Api.DeleteTile(tile, SessionData.SessionName).ConfigureAwait(false))); break; case Overlay overlay: Queue.Enqueue(async () => await Execute(async () => await Api.DeleteOverlay(overlay, SessionData.SessionName).ConfigureAwait(false))); break; case Wall wall: Queue.Enqueue(async () => await Execute(async () => await Api.DeleteWall(wall, SessionData.SessionName).ConfigureAwait(false))); break; } break; } } } class AuthenticatedHttpClientHandler : HttpClientHandler { private readonly Func> getToken; public AuthenticatedHttpClientHandler(Func> getToken) { if (getToken == null) throw new ArgumentNullException(nameof(getToken)); this.getToken = getToken; //if (myConfigurationService.VerifySslCertificate == false) //{ ServerCertificateCustomValidationCallback = (message, certificate, chain, sslPolicyErrors) => true; //} } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // See if the request has an authorize header var auth = request.Headers.Authorization; if (auth != null) { var token = await getToken().ConfigureAwait(false); request.Headers.Authorization = new AuthenticationHeaderValue(auth.Scheme, token); } return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } }