345 lines
13 KiB
C#
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
|
|
{ }
|
|
}
|
|
}
|
|
}
|
|
} |