sledgemapper/Sledgemapper/CommunicationManager.cs
Michele Scandura 85e2f699bf
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
general fixes
2021-09-24 10:21:44 +01:00

345 lines
13 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using Polly;
using Polly.Retry;
using Refit;
using SharpFontInternal;
using Sledgemapper.Messages;
using Sledgemapper.Shared.Entities;
using TinyMessenger;
namespace Sledgemapper
{
public class CommunicationManager
{
public TinyMessengerHub Messenger { get; }
public IMapApi Api { get; }
public HubConnection Connection { get; }
private readonly ChannelsQueue _queue;
private readonly AsyncRetryPolicy _retryPolicy;
public readonly Session SessionData;
private AuthenticateResponse _authenticateResponse;
public CommunicationManager(Session sessionData, TinyMessengerHub messenger)
{
Messenger = messenger;
_queue = new ChannelsQueue(Messenger);
#if DEBUG
var baseAddress = "http://localhost:5000";
//baseAddress = "http://hub.michelescandura.com:5001";
#else
var baseAddress = "http://hub.michelescandura.com:5001";
#endif
CheckLogin();
_retryPolicy = Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.Or<HttpRequestException>()
.RetryAsync(3);
//.RetryAsync(Polly.RetrySyntax., async (exception, retryCount) => await Task.Delay(500))
SessionData = sessionData;
Connection = new HubConnectionBuilder()
.WithAutomaticReconnect()
.WithUrl($"{baseAddress}/SledgemapperHub",
options => { options.AccessTokenProvider = () => Task.FromResult(_authenticateResponse.Token); })
.Build();
Api = RestService.For<IMapApi>(
new HttpClient(new AuthenticatedHttpClientHandler(GetToken))
{
BaseAddress = new Uri(baseAddress)
}
);
Connection.On<Session>("UpdateMap", map =>
{
SessionData.Map = map.Map;
SessionData.Walls = map.Walls;
SessionData.Overlays = map.Overlays;
});
Connection.On<Player>("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<Tile>("DeleteTile", tile => { SessionData.Map.Remove(tile.ToString(), out var _); });
Connection.On<Wall>("DeleteWall", tile => { SessionData.Walls.Remove(tile.ToString(), out var _); });
Connection.On<Note>("DeleteNote", tile => { SessionData.Notes.Remove(tile.ToString(), out var _); });
Connection.On<Overlay>("DeleteOverlay",
tile => { SessionData.Overlays.Remove(tile.ToString(), out var _); });
Connection.On<Tile>("NewTile", tile =>
{
SessionData.Map.Remove(tile.ToString(), out var _);
SessionData.Map.TryAdd(tile.ToString(), tile);
});
Connection.On<Room>("NewRoom", room =>
{
SessionData.Rooms.Remove(room.ToString(), out var _);
SessionData.Rooms.TryAdd(room.ToString(), room);
});
Connection.On<Line>("NewLine", line =>
{
SessionData.Lines.Remove(line.ToString(), out var _);
SessionData.Lines.TryAdd(line.ToString(), line);
});
Connection.On("RefreshPlayers", () =>
{
if (!string.IsNullOrWhiteSpace(State.Instance.MapName))
Connection?.SendAsync("UpdatePosition", State.Instance.MapName, State.Instance.MapId,
SessionData.Players.First(p => p.UserId == new Guid(_authenticateResponse.Id)));
});
Connection.On<Player>("RemovePlayer", player =>
{
var p = SessionData.Players.FirstOrDefault(m => m.UserId == player.UserId);
if (p != null) SessionData.Players.Remove(p);
});
Connection.On<Wall>("NewWall", tile =>
{
SessionData.Walls.Remove(tile.ToString(), out var _);
SessionData.Walls.TryAdd(tile.ToString(), tile);
});
Connection.On<Overlay>("NewOverlay", tile =>
{
SessionData.Overlays.Remove(tile.ToString(), out var _);
SessionData.Overlays.TryAdd(tile.ToString(), tile);
});
Connection.On<Note>("NewNote", note =>
{
//SessionData.Notes.Remove(note.ToString(), out var _);
SessionData.Notes.AddOrUpdate(note.ToString(), note, (key, oldnote) => note);
});
Connection.On<Player>("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;
}
});
Connection.On<Ping>("Ping", ping => { SessionData.Pings.TryAdd(Guid.NewGuid(), ping); });
}
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, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Overlay overlay:
_queue.Enqueue(async () => await Execute(async () =>
await Api.NewOverlay(overlay, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Wall wall:
_queue.Enqueue(async () => await Execute(async () =>
await Api.NewWall(wall, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Note note:
_queue.Enqueue(async () => await Execute(async () =>
await Api.NewNote(note, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Room room:
_queue.Enqueue(async () => await Execute(async () =>
await Api.NewRoom(room, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Line line:
_queue.Enqueue(async () => await Execute(async () =>
await Api.NewLine(line, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
}
break;
case TileAction.Delete:
switch (entity)
{
case Tile tile:
_queue.Enqueue(async () => await Execute(async () =>
await Api.DeleteTile(tile, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Overlay overlay:
_queue.Enqueue(async () => await Execute(async () =>
await Api.DeleteOverlay(overlay, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Wall wall:
_queue.Enqueue(async () => await Execute(async () =>
await Api.DeleteWall(wall, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
case Note note:
_queue.Enqueue(async () => await Execute(async () =>
await Api.DeleteNote(note, State.Instance.CampaignId, State.Instance.MapId)
.ConfigureAwait(false)));
break;
}
break;
}
}
private async Task Execute(Func<Task> call)
{
await Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.Or<HttpRequestException>()
.RetryForeverAsync()
//.RetryAsync(Polly.RetrySyntax., async (exception, retryCount) => await Task.Delay(500))
.ExecuteAsync(async () => await call().ConfigureAwait(false))
.ConfigureAwait(false);
}
private Task<string> GetToken()
{
return Task.FromResult(_authenticateResponse?.Token ?? "");
}
public async Task<List<Campaign>> GetCampaigns()
{
try
{
return await _retryPolicy.ExecuteAsync(Api.GetCampaigns);
}
catch (Exception e)
{
Sentry.SentrySdk.CaptureException(e);
Messenger.Publish(new ErrorMessage(this, "Error loading campaigns"));
}
return null;
}
public async Task<List<Session>> GetMaps(Guid campaignId)
{
try
{
return await _retryPolicy.ExecuteAsync(() => Api.GetMaps(campaignId));
}
catch (Exception e)
{
Sentry.SentrySdk.CaptureException(e);
Messenger.Publish(new ErrorMessage(this, "Error loading maps"));
}
return null;
}
public async Task<List<Player>> GetPlayers(Guid campaignId)
{
try
{
return await _retryPolicy.ExecuteAsync(() => Api.GetPlayers(campaignId));
}
catch (Exception e)
{
Sentry.SentrySdk.CaptureException(e);
Messenger.Publish(new ErrorMessage(this, "Error loading players"));
}
return null;
}
public async Task<AuthenticateResponse> Login(AuthenticateModel authenticateModel)
{
_authenticateResponse = await Policy
.Handle<ApiException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.Or<HttpRequestException>()
.RetryAsync(3)
//.RetryAsync(Polly.RetrySyntax., async (exception, retryCount) => await Task.Delay(500))
.ExecuteAsync(async () => await Api.Authenticate(authenticateModel))
.ConfigureAwait(false);
//_authenticateResponse = await Api.Authenticate(authenticateModel).ConfigureAwait(false);
var data = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(_authenticateResponse));
Program.helper.SaveUnencryptedTokenCache(JsonSerializer.SerializeToUtf8Bytes(_authenticateResponse));
await Connection.StopAsync();
State.Instance.CampaignId=Guid.Empty;
State.Instance.CampaignName = string.Empty;
State.Instance.SessionId = Guid.Empty;
State.Instance.MapId = Guid.Empty;
State.Instance.MapName = string.Empty;
SessionData.SessionId = Guid.Empty;
SessionData.SessionName = string.Empty;
SessionData.Players.Clear();
return _authenticateResponse;
}
internal async Task Ping(Tile location)
{
if (Connection is { State: HubConnectionState.Connected })
await Connection.InvokeAsync("Ping", SessionData.SessionId, location);
}
public async Task<IMapApi.AuthResult> Register(RegisterModel registerModel)
{
var result = await Api.Register(registerModel).ConfigureAwait(false);
return result;
}
public void CheckLogin()
{
var data = Program.helper.LoadUnencryptedTokenCache();
if (data != null && data.Any())
{
try
{
_authenticateResponse = JsonSerializer.Deserialize<AuthenticateResponse>(Encoding.UTF8.GetString(data));
Messenger.Publish(new LoginSuccesfulMessage(this) { UserName = _authenticateResponse.Username, Initials = _authenticateResponse.Initials });
}
catch
{ }
}
}
}
}