[Draft] Update 0.8 (#46)
* move stuff out into file transfer manager * obnoxious unsupported version text, adjustments to filetransfermanager * add back file upload transfer progress * restructure code * cleanup some more stuff I guess * downloadids by playername * individual anim/sound bs * fix migration stuff, finalize impl of individual sound/anim pause * fixes with logging stuff * move download manager to transient * rework dl ui first iteration * some refactoring and cleanup * more code cleanup * refactoring * switch to hostbuilder * some more rework I guess * more refactoring * clean up mediator calls and disposal * fun code cleanup * push error message when log level is set to anything but information in non-debug builds * remove notificationservice * move message to after login * add download bars to gameworld * fixes download progress bar * set gpose ui min and max size * remove unnecessary usings * adjustments to reconnection logic * add options to set visible/offline groups visibility * add impl of uploading display, transfer list in settings ui * attempt to fix issues with server selection * add back download status to compact ui * make dl bar fixed size based * some fixes for upload/download handling * adjust text from Syncing back to Uploading --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com> Co-authored-by: Stanley Dimant <stanley.dimant@varian.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public async Task PushCharacterData(CharacterData data, List<UserData> visibleCharacters)
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
|
||||
try
|
||||
{
|
||||
await PushCharacterDataInternal(data, visibleCharacters.ToList()).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Logger.LogDebug("Upload operation was cancelled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Error during upload of files");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UserAddPair(UserDto user)
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
await _mareHub!.SendAsync(nameof(UserAddPair), user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserDelete()
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(UserDelete)).ConfigureAwait(false);
|
||||
await CreateConnections().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<OnlineUserIdentDto>> UserGetOnlinePairs()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<OnlineUserIdentDto>>(nameof(UserGetOnlinePairs)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<UserPairDto>> UserGetPairedClients()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<UserPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserPushData(UserCharaDataMessageDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mareHub!.InvokeAsync(nameof(UserPushData), dto).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to Push character data");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UserRemovePair(UserDto userDto)
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
await _mareHub!.SendAsync(nameof(UserRemovePair), userDto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserSetPairPermissions(UserPermissionsDto userPermissions)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(UserSetPairPermissions), userPermissions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task PushCharacterDataInternal(CharacterData character, List<UserData> visibleCharacters)
|
||||
{
|
||||
Logger.LogInformation("Pushing character data for {hash} to {charas}", character.DataHash.Value, string.Join(", ", visibleCharacters.Select(c => c.AliasOrUID)));
|
||||
StringBuilder sb = new();
|
||||
foreach (var kvp in character.FileReplacements.ToList())
|
||||
{
|
||||
sb.AppendLine($"FileReplacements for {kvp.Key}: {kvp.Value.Count}");
|
||||
}
|
||||
foreach (var item in character.GlamourerData)
|
||||
{
|
||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||
}
|
||||
Logger.LogDebug("Chara data contained: {nl} {data}", Environment.NewLine, sb.ToString());
|
||||
await UserPushData(new(visibleCharacters, character)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Dto;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public Task Client_DownloadReady(Guid requestId)
|
||||
{
|
||||
Logger.LogDebug("Server sent {requestId} ready", requestId);
|
||||
Mediator.Publish(new DownloadReadyMessage(requestId));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupChangePermissions(GroupPermissionDto groupPermission)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupChangePermissions: {perm}", groupPermission);
|
||||
ExecuteSafely(() => _pairManager.SetGroupPermissions(groupPermission));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupDelete(GroupDto groupDto)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupDelete: {dto}", groupDto);
|
||||
ExecuteSafely(() => _pairManager.RemoveGroup(groupDto.Group));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupPairChangePermissions(GroupPairUserPermissionDto permissionDto)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupPairChangePermissions: {perm}", permissionDto);
|
||||
ExecuteSafely(() =>
|
||||
{
|
||||
if (string.Equals(permissionDto.UID, UID, StringComparison.Ordinal)) _pairManager.SetGroupUserPermissions(permissionDto);
|
||||
else _pairManager.SetGroupPairUserPermissions(permissionDto);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto userInfo)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupPairChangeUserInfo: {dto}", userInfo);
|
||||
ExecuteSafely(() =>
|
||||
{
|
||||
if (string.Equals(userInfo.UID, UID, StringComparison.Ordinal)) _pairManager.SetGroupStatusInfo(userInfo);
|
||||
else _pairManager.SetGroupPairStatusInfo(userInfo);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupPairJoined: {dto}", groupPairInfoDto);
|
||||
ExecuteSafely(() => _pairManager.AddGroupPair(groupPairInfoDto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupPairLeft(GroupPairDto groupPairDto)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupPairLeft: {dto}", groupPairDto);
|
||||
ExecuteSafely(() => _pairManager.RemoveGroupPair(groupPairDto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupSendFullInfo: {dto}", groupInfo);
|
||||
ExecuteSafely(() => _pairManager.AddGroup(groupInfo));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_GroupSendInfo(GroupInfoDto groupInfo)
|
||||
{
|
||||
Logger.LogTrace("Client_GroupSendInfo: {dto}", groupInfo);
|
||||
ExecuteSafely(() => _pairManager.SetGroupInfo(groupInfo));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message)
|
||||
{
|
||||
switch (messageSeverity)
|
||||
{
|
||||
case MessageSeverity.Error:
|
||||
Mediator.Publish(new NotificationMessage("Warning from " + _serverManager.CurrentServer!.ServerName, message, NotificationType.Error, 7500));
|
||||
break;
|
||||
|
||||
case MessageSeverity.Warning:
|
||||
Mediator.Publish(new NotificationMessage("Warning from " + _serverManager.CurrentServer!.ServerName, message, NotificationType.Warning, 7500));
|
||||
break;
|
||||
|
||||
case MessageSeverity.Information:
|
||||
if (_doNotNotifyOnNextInfo)
|
||||
{
|
||||
_doNotNotifyOnNextInfo = false;
|
||||
break;
|
||||
}
|
||||
Mediator.Publish(new NotificationMessage("Info from " + _serverManager.CurrentServer!.ServerName, message, NotificationType.Info, 5000));
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UpdateSystemInfo(SystemInfoDto systemInfo)
|
||||
{
|
||||
SystemInfoDto = systemInfo;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserAddClientPair(UserPairDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserAddClientPair: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.AddUserPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto)
|
||||
{
|
||||
Logger.LogTrace("Client_UserReceiveCharacterData: {user}", dataDto.User);
|
||||
ExecuteSafely(() => _pairManager.ReceiveCharaData(dataDto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserReceiveUploadStatus(UserDto dto)
|
||||
{
|
||||
Logger.LogTrace("Client_UserReceiveUploadStatus: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.ReceiveUploadStatus(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserRemoveClientPair(UserDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserRemoveClientPair: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.RemoveUserPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserSendOffline(UserDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserSendOffline: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.MarkPairOffline(dto.User));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserSendOnline(OnlineUserIdentDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserSendOnline: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.MarkPairOnline(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserUpdateOtherPairPermissions: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.UpdatePairPermissions(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
Logger.LogDebug("Client_UserUpdateSelfPairPermissions: {dto}", dto);
|
||||
ExecuteSafely(() => _pairManager.UpdateSelfPairPermissions(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnDownloadReady(Action<Guid> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_DownloadReady), act);
|
||||
}
|
||||
|
||||
public void OnGroupChangePermissions(Action<GroupPermissionDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupChangePermissions), act);
|
||||
}
|
||||
|
||||
public void OnGroupDelete(Action<GroupDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupDelete), act);
|
||||
}
|
||||
|
||||
public void OnGroupPairChangePermissions(Action<GroupPairUserPermissionDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairChangePermissions), act);
|
||||
}
|
||||
|
||||
public void OnGroupPairChangeUserInfo(Action<GroupPairUserInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairChangeUserInfo), act);
|
||||
}
|
||||
|
||||
public void OnGroupPairJoined(Action<GroupPairFullInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairJoined), act);
|
||||
}
|
||||
|
||||
public void OnGroupPairLeft(Action<GroupPairDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairLeft), act);
|
||||
}
|
||||
|
||||
public void OnGroupSendFullInfo(Action<GroupFullInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupSendFullInfo), act);
|
||||
}
|
||||
|
||||
public void OnGroupSendInfo(Action<GroupInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupSendInfo), act);
|
||||
}
|
||||
|
||||
public void OnReceiveServerMessage(Action<MessageSeverity, string> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_ReceiveServerMessage), act);
|
||||
}
|
||||
|
||||
public void OnUpdateSystemInfo(Action<SystemInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UpdateSystemInfo), act);
|
||||
}
|
||||
|
||||
public void OnUserAddClientPair(Action<UserPairDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserAddClientPair), act);
|
||||
}
|
||||
|
||||
public void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserReceiveCharacterData), act);
|
||||
}
|
||||
|
||||
public void OnUserReceiveUploadStatus(Action<UserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserReceiveUploadStatus), act);
|
||||
}
|
||||
|
||||
public void OnUserRemoveClientPair(Action<UserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserRemoveClientPair), act);
|
||||
}
|
||||
|
||||
public void OnUserSendOffline(Action<UserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserSendOffline), act);
|
||||
}
|
||||
|
||||
public void OnUserSendOnline(Action<OnlineUserIdentDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserSendOnline), act);
|
||||
}
|
||||
|
||||
public void OnUserUpdateOtherPairPermissions(Action<UserPermissionsDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserUpdateOtherPairPermissions), act);
|
||||
}
|
||||
|
||||
public void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserUpdateSelfPairPermissions), act);
|
||||
}
|
||||
|
||||
private void ExecuteSafely(Action act)
|
||||
{
|
||||
try
|
||||
{
|
||||
act();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogCritical(ex, "Error on executing safely");
|
||||
}
|
||||
}
|
||||
}
|
||||
115
MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs
Normal file
115
MareSynchronos/WebAPI/SignalR/ApiController.Functions.Groups.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public async Task GroupBanUser(GroupPairDto dto, string reason)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupBanUser), dto, reason).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangeGroupPermissionState(GroupPermissionDto dto)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeGroupPermissionState), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeIndividualPermissionState), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangeOwnership(GroupPairDto groupPair)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeOwnership), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> GroupChangePassword(GroupPasswordDto groupPassword)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupChangePassword), groupPassword).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupClear(GroupDto group)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupClear), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<GroupPasswordDto> GroupCreate()
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<GroupPasswordDto>(nameof(GroupCreate)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GroupCreateTempInvite(GroupDto group, int amount)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<string>>(nameof(GroupCreateTempInvite), group, amount).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupDelete(GroupDto group)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupDelete), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<BannedGroupUserDto>> GroupGetBannedUsers(GroupDto group)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<BannedGroupUserDto>>(nameof(GroupGetBannedUsers), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> GroupJoin(GroupPasswordDto passwordedGroup)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupJoin), passwordedGroup).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupLeave(GroupDto group)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupLeave), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupRemoveUser(GroupPairDto groupPair)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupRemoveUser), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupSetUserInfo(GroupPairUserInfoDto groupPair)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupSetUserInfo), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<GroupFullInfoDto>> GroupsGetAll()
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<GroupFullInfoDto>>(nameof(GroupsGetAll)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<GroupPairFullInfoDto>> GroupsGetUsersInGroup(GroupDto group)
|
||||
{
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<GroupPairFullInfoDto>>(nameof(GroupsGetUsersInGroup), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupUnbanUser(GroupPairDto groupPair)
|
||||
{
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupUnbanUser), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void CheckConnection()
|
||||
{
|
||||
if (ServerState is not (ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting)) throw new InvalidDataException("Not connected");
|
||||
}
|
||||
}
|
||||
350
MareSynchronos/WebAPI/SignalR/ApiController.cs
Normal file
350
MareSynchronos/WebAPI/SignalR/ApiController.cs
Normal file
@@ -0,0 +1,350 @@
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronos.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MareSynchronos.API.Dto;
|
||||
using MareSynchronos.API.SignalR;
|
||||
using Dalamud.Utility;
|
||||
using System.Reflection;
|
||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||
using MareSynchronos.WebAPI.SignalR;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.Services;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public sealed partial class ApiController : DisposableMediatorSubscriberBase, IMareHubClient
|
||||
{
|
||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||
public const string MainServiceUri = "wss://maresynchronos.com";
|
||||
|
||||
private readonly DalamudUtilService _dalamudUtil;
|
||||
private readonly HubFactory _hubFactory;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverManager;
|
||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||
private ConnectionDto? _connectionDto;
|
||||
private bool _doNotNotifyOnNextInfo = false;
|
||||
private CancellationTokenSource? _healthCheckTokenSource = new();
|
||||
private bool _initialized;
|
||||
private HubConnection? _mareHub;
|
||||
private ServerState _serverState;
|
||||
|
||||
public ApiController(ILogger<ApiController> logger, HubFactory hubFactory, DalamudUtilService dalamudUtil,
|
||||
PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator) : base(logger, mediator)
|
||||
{
|
||||
_hubFactory = hubFactory;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_pairManager = pairManager;
|
||||
_serverManager = serverManager;
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
||||
Mediator.Subscribe<DalamudLogoutMessage>(this, (_) => DalamudUtilOnLogOut());
|
||||
Mediator.Subscribe<HubClosedMessage>(this, (msg) => MareHubOnClosed(msg.Exception));
|
||||
Mediator.Subscribe<HubReconnectedMessage>(this, (msg) => _ = Task.Run(MareHubOnReconnected));
|
||||
Mediator.Subscribe<HubReconnectingMessage>(this, (msg) => MareHubOnReconnecting(msg.Exception));
|
||||
|
||||
ServerState = ServerState.Offline;
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
DalamudUtilOnLogIn();
|
||||
}
|
||||
}
|
||||
|
||||
public string AuthFailureMessage { get; private set; } = string.Empty;
|
||||
public Version CurrentClientVersion => _connectionDto?.CurrentClientVersion ?? new Version(0, 0, 0);
|
||||
public string DisplayName => _connectionDto?.User.AliasOrUID ?? string.Empty;
|
||||
public bool IsConnected => ServerState == ServerState.Connected;
|
||||
public bool IsCurrentVersion => (Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0, 0)) >= (_connectionDto?.CurrentClientVersion ?? new Version(0, 0, 0, 0));
|
||||
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
||||
public bool ServerAlive => ServerState is ServerState.Connected or ServerState.RateLimited or ServerState.Unauthorized or ServerState.Disconnected;
|
||||
public ServerInfo ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfo();
|
||||
|
||||
public ServerState ServerState
|
||||
{
|
||||
get => _serverState;
|
||||
private set
|
||||
{
|
||||
Logger.LogDebug("New ServerState: {value}, prev ServerState: {_serverState}", value, _serverState);
|
||||
_serverState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||
public string UID => _connectionDto?.User.UID ?? string.Empty;
|
||||
|
||||
public async Task<bool> CheckClientHealth()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(CheckClientHealth)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task CreateConnections(bool forceGetToken = false)
|
||||
{
|
||||
Logger.LogDebug("CreateConnections called");
|
||||
|
||||
if (_serverManager.CurrentServer?.FullPause ?? true)
|
||||
{
|
||||
Logger.LogInformation("Not recreating Connection, paused");
|
||||
_connectionDto = null;
|
||||
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||
_connectionCancellationTokenSource.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
var secretKey = _serverManager.GetSecretKey();
|
||||
if (secretKey.IsNullOrEmpty())
|
||||
{
|
||||
Logger.LogWarning("No secret key set for current character");
|
||||
_connectionDto = null;
|
||||
await StopConnection(ServerState.NoSecretKey).ConfigureAwait(false);
|
||||
_connectionCancellationTokenSource.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Recreating Connection");
|
||||
|
||||
_connectionCancellationTokenSource.Cancel();
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _connectionCancellationTokenSource.Token;
|
||||
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
||||
{
|
||||
AuthFailureMessage = string.Empty;
|
||||
|
||||
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||
ServerState = ServerState.Connecting;
|
||||
|
||||
try
|
||||
{
|
||||
Logger.LogDebug("Building connection");
|
||||
|
||||
if (_serverManager.GetToken() == null || forceGetToken)
|
||||
{
|
||||
Logger.LogDebug("Requesting new JWT");
|
||||
using HttpClient httpClient = new();
|
||||
var postUri = MareAuth.AuthFullPath(new Uri(_serverManager.CurrentApiUrl
|
||||
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
|
||||
var auth = secretKey.GetHash256();
|
||||
var result = await httpClient.PostAsync(postUri, new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("auth", auth),
|
||||
new KeyValuePair<string, string>("charaIdent", _dalamudUtil.PlayerNameHashed),
|
||||
})).ConfigureAwait(false);
|
||||
AuthFailureMessage = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
_serverManager.SaveToken(await result.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
Logger.LogDebug("JWT Success");
|
||||
}
|
||||
|
||||
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
|
||||
{
|
||||
Logger.LogDebug("Player not loaded in yet, waiting");
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
_mareHub = _hubFactory.GetOrCreate();
|
||||
|
||||
await _mareHub.StartAsync(token).ConfigureAwait(false);
|
||||
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
|
||||
ServerState = ServerState.Connected;
|
||||
|
||||
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
||||
{
|
||||
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "HttpRequestException on Connection");
|
||||
|
||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
await StopConnection(ServerState.Unauthorized).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ServerState = ServerState.Reconnecting;
|
||||
Logger.LogInformation("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Exception on Connection");
|
||||
|
||||
Logger.LogInformation("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ConnectionDto> GetConnectionDto()
|
||||
{
|
||||
var dto = await _mareHub!.InvokeAsync<ConnectionDto>(nameof(GetConnectionDto)).ConfigureAwait(false);
|
||||
Mediator.Publish(new ConnectedMessage(dto));
|
||||
return dto;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
Task.Run(async () => await StopConnection(ServerState.Disconnected).ConfigureAwait(false));
|
||||
_connectionCancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private async Task ClientHealthCheck(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested && _mareHub != null)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(30), ct).ConfigureAwait(false);
|
||||
_ = await CheckClientHealth().ConfigureAwait(false);
|
||||
Logger.LogDebug("Checked Client Health State");
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
{
|
||||
Task.Run(() => CreateConnections(forceGetToken: true));
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
Task.Run(async () => await StopConnection(ServerState.Disconnected).ConfigureAwait(false));
|
||||
ServerState = ServerState.Offline;
|
||||
}
|
||||
|
||||
private async Task InitializeData()
|
||||
{
|
||||
if (_mareHub == null) return;
|
||||
|
||||
Logger.LogDebug("Initializing data");
|
||||
OnDownloadReady((guid) => Client_DownloadReady(guid));
|
||||
OnReceiveServerMessage((sev, msg) => Client_ReceiveServerMessage(sev, msg));
|
||||
OnUpdateSystemInfo((dto) => Client_UpdateSystemInfo(dto));
|
||||
|
||||
OnUserSendOffline((dto) => Client_UserSendOffline(dto));
|
||||
OnUserAddClientPair((dto) => Client_UserAddClientPair(dto));
|
||||
OnUserReceiveCharacterData((dto) => Client_UserReceiveCharacterData(dto));
|
||||
OnUserRemoveClientPair(dto => Client_UserRemoveClientPair(dto));
|
||||
OnUserSendOnline(dto => Client_UserSendOnline(dto));
|
||||
OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto));
|
||||
OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto));
|
||||
OnUserReceiveUploadStatus(dto => Client_UserReceiveUploadStatus(dto));
|
||||
|
||||
OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto));
|
||||
OnGroupDelete((dto) => Client_GroupDelete(dto));
|
||||
OnGroupPairChangePermissions((dto) => Client_GroupPairChangePermissions(dto));
|
||||
OnGroupPairChangeUserInfo((dto) => Client_GroupPairChangeUserInfo(dto));
|
||||
OnGroupPairJoined((dto) => Client_GroupPairJoined(dto));
|
||||
OnGroupPairLeft((dto) => Client_GroupPairLeft(dto));
|
||||
OnGroupSendFullInfo((dto) => Client_GroupSendFullInfo(dto));
|
||||
OnGroupSendInfo((dto) => Client_GroupSendInfo(dto));
|
||||
|
||||
foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false))
|
||||
{
|
||||
Logger.LogDebug("Individual Pair: {userPair}", userPair);
|
||||
_pairManager.AddUserPair(userPair, addToLastAddedUser: false);
|
||||
}
|
||||
foreach (var entry in await GroupsGetAll().ConfigureAwait(false))
|
||||
{
|
||||
Logger.LogDebug("Group: {entry}", entry);
|
||||
_pairManager.AddGroup(entry);
|
||||
}
|
||||
foreach (var group in _pairManager.GroupPairs.Keys)
|
||||
{
|
||||
var users = await GroupsGetUsersInGroup(group).ConfigureAwait(false);
|
||||
foreach (var user in users)
|
||||
{
|
||||
Logger.LogDebug("Group Pair: {user}", user);
|
||||
_pairManager.AddGroupPair(user);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false))
|
||||
{
|
||||
_pairManager.MarkPairOnline(entry, sendNotif: false);
|
||||
}
|
||||
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
_healthCheckTokenSource?.Dispose();
|
||||
_healthCheckTokenSource = new CancellationTokenSource();
|
||||
_ = ClientHealthCheck(_healthCheckTokenSource.Token);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void MareHubOnClosed(Exception? arg)
|
||||
{
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
Mediator.Publish(new DisconnectedMessage());
|
||||
_pairManager.ClearPairs();
|
||||
ServerState = ServerState.Offline;
|
||||
if (arg != null)
|
||||
{
|
||||
Logger.LogWarning(arg, "Connection closed");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation("Connection closed");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MareHubOnReconnected()
|
||||
{
|
||||
ServerState = ServerState.Connecting;
|
||||
try
|
||||
{
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
||||
{
|
||||
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
ServerState = ServerState.Connected;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogCritical(ex, "Failure to obtain data after reconnection");
|
||||
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void MareHubOnReconnecting(Exception? arg)
|
||||
{
|
||||
_doNotNotifyOnNextInfo = true;
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
ServerState = ServerState.Reconnecting;
|
||||
Logger.LogWarning(arg, "Connection closed... Reconnecting");
|
||||
}
|
||||
|
||||
private async Task StopConnection(ServerState state)
|
||||
{
|
||||
ServerState = state;
|
||||
|
||||
if (_mareHub is not null)
|
||||
{
|
||||
_initialized = false;
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
Logger.LogInformation("Stopping existing connection");
|
||||
await _hubFactory.DisposeHubAsync().ConfigureAwait(false);
|
||||
Mediator.Publish(new DisconnectedMessage());
|
||||
_mareHub = null;
|
||||
_connectionDto = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
MareSynchronos/WebAPI/SignalR/HubFactory.cs
Normal file
117
MareSynchronos/WebAPI/SignalR/HubFactory.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using MareSynchronos.API.SignalR;
|
||||
using MareSynchronos.Interop;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||
using MessagePack;
|
||||
using MessagePack.Resolvers;
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.WebAPI.SignalR;
|
||||
|
||||
public class HubFactory : MediatorSubscriberBase
|
||||
{
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly MareConfigService _configService;
|
||||
private HubConnection? _instance;
|
||||
private bool _isDisposed = false;
|
||||
|
||||
public HubFactory(ILogger<HubFactory> logger, MareMediator mediator, ServerConfigurationManager serverConfigurationManager, MareConfigService configService) : base(logger, mediator)
|
||||
{
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
private HubConnection BuildHubConnection()
|
||||
{
|
||||
Logger.LogDebug("Building new HubConnection");
|
||||
|
||||
_instance = new HubConnectionBuilder()
|
||||
.WithUrl(_serverConfigurationManager.CurrentApiUrl + IMareHub.Path, options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", "Bearer " + _serverConfigurationManager.GetToken());
|
||||
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
||||
})
|
||||
.AddMessagePackProtocol(opt =>
|
||||
{
|
||||
var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance,
|
||||
BuiltinResolver.Instance,
|
||||
AttributeFormatterResolver.Instance,
|
||||
// replace enum resolver
|
||||
DynamicEnumAsStringResolver.Instance,
|
||||
DynamicGenericResolver.Instance,
|
||||
DynamicUnionResolver.Instance,
|
||||
DynamicObjectResolver.Instance,
|
||||
PrimitiveObjectResolver.Instance,
|
||||
// final fallback(last priority)
|
||||
StandardResolver.Instance);
|
||||
|
||||
opt.SerializerOptions =
|
||||
MessagePackSerializerOptions.Standard
|
||||
.WithCompression(MessagePackCompression.Lz4Block)
|
||||
.WithResolver(resolver);
|
||||
})
|
||||
.WithAutomaticReconnect(new ForeverRetryPolicy(Mediator))
|
||||
.ConfigureLogging(a =>
|
||||
{
|
||||
a.ClearProviders().AddProvider(new DalamudLoggingProvider(_configService));
|
||||
a.SetMinimumLevel(LogLevel.Information);
|
||||
})
|
||||
.Build();
|
||||
|
||||
_instance.Closed += HubOnClosed;
|
||||
_instance.Reconnecting += HubOnReconnecting;
|
||||
_instance.Reconnected += HubOnReconnected;
|
||||
|
||||
_isDisposed = false;
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
private Task HubOnReconnected(string? arg)
|
||||
{
|
||||
Mediator.Publish(new HubReconnectedMessage(arg));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task HubOnReconnecting(Exception? arg)
|
||||
{
|
||||
Mediator.Publish(new HubReconnectingMessage(arg));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task HubOnClosed(Exception? arg)
|
||||
{
|
||||
Mediator.Publish(new HubClosedMessage(arg));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public HubConnection GetOrCreate()
|
||||
{
|
||||
if (!_isDisposed && _instance != null) return _instance;
|
||||
|
||||
return BuildHubConnection();
|
||||
}
|
||||
|
||||
public async Task DisposeHubAsync()
|
||||
{
|
||||
if (_instance == null || _isDisposed) return;
|
||||
|
||||
Logger.LogDebug("Disposing current HubConnection");
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
_instance.Closed -= HubOnClosed;
|
||||
_instance.Reconnecting -= HubOnReconnecting;
|
||||
_instance.Reconnected -= HubOnReconnected;
|
||||
|
||||
await _instance.StopAsync().ConfigureAwait(false);
|
||||
await _instance.DisposeAsync().ConfigureAwait(false);
|
||||
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
39
MareSynchronos/WebAPI/SignalR/Utils/ForeverRetryPolicy.cs
Normal file
39
MareSynchronos/WebAPI/SignalR/Utils/ForeverRetryPolicy.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI.SignalR.Utils;
|
||||
|
||||
public class ForeverRetryPolicy : IRetryPolicy
|
||||
{
|
||||
private readonly MareMediator _mediator;
|
||||
private bool _sentDisconnected = false;
|
||||
|
||||
public ForeverRetryPolicy(MareMediator mediator)
|
||||
{
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public TimeSpan? NextRetryDelay(RetryContext retryContext)
|
||||
{
|
||||
TimeSpan timeToWait = TimeSpan.FromSeconds(new Random().Next(10, 20));
|
||||
if (retryContext.PreviousRetryCount == 0)
|
||||
{
|
||||
_sentDisconnected = false;
|
||||
timeToWait = TimeSpan.FromSeconds(3);
|
||||
}
|
||||
else if (retryContext.PreviousRetryCount == 1) timeToWait = TimeSpan.FromSeconds(5);
|
||||
else if (retryContext.PreviousRetryCount == 2) timeToWait = TimeSpan.FromSeconds(10);
|
||||
else
|
||||
{
|
||||
if (!_sentDisconnected)
|
||||
{
|
||||
_mediator.Publish(new NotificationMessage("Connection lost", "Connection lost to server", NotificationType.Warning, 5000));
|
||||
_mediator.Publish(new DisconnectedMessage());
|
||||
}
|
||||
_sentDisconnected = true;
|
||||
}
|
||||
|
||||
return timeToWait;
|
||||
}
|
||||
}
|
||||
14
MareSynchronos/WebAPI/SignalR/Utils/ServerState.cs
Normal file
14
MareSynchronos/WebAPI/SignalR/Utils/ServerState.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace MareSynchronos.WebAPI.SignalR.Utils;
|
||||
|
||||
public enum ServerState
|
||||
{
|
||||
Offline,
|
||||
Connecting,
|
||||
Reconnecting,
|
||||
Disconnected,
|
||||
Connected,
|
||||
Unauthorized,
|
||||
VersionMisMatch,
|
||||
RateLimited,
|
||||
NoSecretKey,
|
||||
}
|
||||
Reference in New Issue
Block a user