470 lines
19 KiB
C#
470 lines
19 KiB
C#
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Newtonsoft.Json;
|
|
using QuestShare.Common.API;
|
|
using QuestShare.Common.API.Share;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.Json.Nodes;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace QuestShare.Services
|
|
{
|
|
|
|
internal class SocketClientService
|
|
{
|
|
private readonly string socketUrl = "http://localhost:8080/Hub";
|
|
internal static HubConnection connection { get; private set; } = null!;
|
|
internal static bool IsConnected => connection.State == HubConnectionState.Connected;
|
|
internal static bool IsAuthorized { get; private set; } = false;
|
|
private static bool IsDisposing = false;
|
|
|
|
|
|
public event EventHandler<SocketEventArgs> OnCancelEvent = delegate { };
|
|
public event EventHandler<SocketEventArgs> OnGroupEvent = delegate { };
|
|
public event EventHandler<SocketEventArgs> OnUngroupEvent = delegate { };
|
|
public event EventHandler<SocketEventArgs> OnGetEvent = delegate { };
|
|
public event EventHandler<SocketEventArgs> OnRegisterEvent = delegate { };
|
|
public event EventHandler<SocketEventArgs> OnUpdateEvent = delegate { };
|
|
public event EventHandler<TokenCheckEventArgs> OnTokenCheckEvent = delegate { };
|
|
|
|
public SocketClientService()
|
|
{
|
|
var builder = new HubConnectionBuilder().WithUrl(socketUrl).ConfigureLogging(logging =>
|
|
{
|
|
logging.SetMinimumLevel(LogLevel.Information).AddConsole();
|
|
});
|
|
connection = builder.Build();
|
|
connection.Closed += async (error) =>
|
|
{
|
|
if (IsDisposing) return;
|
|
Log.Warning($"Connection closed... {error}");
|
|
Log.Warning($"Connection closed, retrying... {error}");
|
|
await Task.Delay(new Random().Next(0, 5) * 1000);
|
|
await connection.StartAsync();
|
|
};
|
|
connection.Reconnected += async (error) =>
|
|
{
|
|
Log.Information("Connection reconnected");
|
|
};
|
|
connection.On<Register.Response>(nameof(Register), Client_Register);
|
|
connection.On<Update.Response>(nameof(Update), Client_Update);
|
|
connection.On<Update.UpdateBroadcast>(nameof(Update.UpdateBroadcast), Client_UpdateBroadcast);
|
|
connection.On<GetShareInfo.Response>(nameof(GetShareInfo), Client_GetShareInfo);
|
|
connection.On<Cancel.Response>(nameof(Cancel), Client_Cancel);
|
|
connection.On<Cancel.CancelBroadcast>(nameof(Cancel.CancelBroadcast), Client_CancelBroadast);
|
|
connection.On<GroupJoin.Response>(nameof(GroupJoin), Client_GroupJoin);
|
|
connection.On<GroupLeave.Response>(nameof(GroupLeave), Client_GroupLeave);
|
|
connection.On<Authorize.Response>(nameof(Authorize), Client_Authorize);
|
|
connection.On<GroupNotify.GroupNotifyBroadcast>(nameof(GroupNotify.GroupNotifyBroadcast), Client_GroupNotify);
|
|
connection.On<Resume.Response>(nameof(Resume), Client_Resume);
|
|
connection.On<Authorize.AuthBroadcast>(nameof(Authorize.AuthBroadcast), Client_AuthRequest);
|
|
ClientState.Login += OnLogin;
|
|
ClientState.Logout += OnLogout;
|
|
}
|
|
public void Dispose()
|
|
{
|
|
IsDisposing = true;
|
|
if (IsConnected)
|
|
{
|
|
connection.StopAsync();
|
|
}
|
|
connection.DisposeAsync();
|
|
ClientState.Login -= OnLogin;
|
|
ClientState.Logout -= OnLogout;
|
|
}
|
|
|
|
public void OnLogin()
|
|
{
|
|
if (Plugin.Configuration.Instance.Token != "")
|
|
{
|
|
ShareService.Token = Plugin.Configuration.Instance.Token;
|
|
DispatchAuthorize();
|
|
}
|
|
}
|
|
|
|
public void OnLogout(int code, int type)
|
|
{
|
|
ShareService.Token = "";
|
|
Plugin.Configuration.Save();
|
|
IsAuthorized = false;
|
|
connection.StopAsync().ConfigureAwait(false);
|
|
}
|
|
|
|
public async Task Connect()
|
|
{
|
|
try
|
|
{
|
|
if (IsConnected) await connection.StopAsync();
|
|
IsDisposing = false;
|
|
await connection.StartAsync().ContinueWith(task =>
|
|
{
|
|
if (task.IsFaulted)
|
|
{
|
|
Log.Error("Failed to connect to socket server");
|
|
}
|
|
else
|
|
{
|
|
Log.Information("Connected to socket server");
|
|
}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex.Message);
|
|
}
|
|
}
|
|
|
|
public async Task Disconnect()
|
|
{
|
|
IsDisposing = true;
|
|
await connection.StopAsync();
|
|
}
|
|
|
|
private async Task Invoke(string methodName, object request)
|
|
{
|
|
if (!IsConnected) await Connect();
|
|
Log.Debug($"Invoking {methodName} with {JsonConvert.SerializeObject(request)}");
|
|
var s = new StackTrace();
|
|
Log.Debug(s.ToString());
|
|
await connection.InvokeAsync(methodName, request);
|
|
}
|
|
|
|
public static void DispatchAuthorize()
|
|
{
|
|
Plugin.SocketClient.Invoke(nameof(Authorize), new Authorize.Request
|
|
{
|
|
Version = Constants.Version,
|
|
Token = ShareService.Token,
|
|
CharacterId = ClientState.LocalContentId
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchRegister()
|
|
{
|
|
if (!IsAuthorized)
|
|
{
|
|
Log.Warning("Not authorized to register");
|
|
return;
|
|
}
|
|
var activeQuest = GameQuestManager.GetActiveQuest();
|
|
Plugin.SocketClient.Invoke(nameof(Register), new Register.Request
|
|
{
|
|
Version = Constants.Version,
|
|
Token = ShareService.Token,
|
|
CharacterId = ClientState.LocalContentId,
|
|
SharedQuestId = activeQuest != null ? activeQuest.QuestId : 0,
|
|
SharedQuestStep = activeQuest != null ? activeQuest.CurrentStep : (byte)0,
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchGroup(string shareCode)
|
|
{
|
|
Plugin.SocketClient.Invoke(nameof(GroupJoin), new GroupJoin.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
ShareCode = shareCode,
|
|
Version = Constants.Version,
|
|
CharacterId = ClientState.LocalContentId
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchUngroup()
|
|
{
|
|
Plugin.SocketClient.Invoke(nameof(GroupLeave), new GroupLeave.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
ShareCode = ShareService.ShareCode,
|
|
Version = Constants.Version,
|
|
}).ConfigureAwait(false);
|
|
}
|
|
|
|
public static void DispatchResume()
|
|
{
|
|
var members = new List<ulong>();
|
|
foreach (var member in PartyList)
|
|
{
|
|
members.Add((ulong)member.ContentId);
|
|
}
|
|
Plugin.SocketClient.Invoke(nameof(Resume), new Resume.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
Version = Constants.Version,
|
|
Members = members,
|
|
ShareCode = Plugin.Configuration.Instance.LastShareCode,
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchGetShareInfo()
|
|
{
|
|
Plugin.SocketClient.Invoke(nameof(GetShareInfo), new GetShareInfo.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
Version = Constants.Version,
|
|
ShareCode = ShareService.ShareCode
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchCancel()
|
|
{
|
|
Plugin.SocketClient.Invoke(nameof(Cancel), new Cancel.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
Version = Constants.Version,
|
|
ShareCode = ShareService.HostedShareCode
|
|
}).ConfigureAwait(false);
|
|
}
|
|
public static void DispatchUpdate(bool partyUpdate)
|
|
{
|
|
if (!ShareService.IsRegistered || !ShareService.IsGrouped)
|
|
{
|
|
return;
|
|
}
|
|
var activeQuest = GameQuestManager.GetActiveQuest();
|
|
Plugin.SocketClient.Invoke(nameof(Update), new Update.Request
|
|
{
|
|
Token = ShareService.Token,
|
|
Version = Constants.Version,
|
|
SharedQuestId = activeQuest != null ? activeQuest.QuestId : 0,
|
|
SharedQuestStep = activeQuest != null ? activeQuest.CurrentStep : (byte)0,
|
|
BroadcastParty = Plugin.Configuration.Instance.BroadcastToParty,
|
|
PartyMembers = ShareService.IsHost ? ShareService.PartyMembers : [],
|
|
IsPartyChange = partyUpdate
|
|
}).ConfigureAwait(false);
|
|
}
|
|
|
|
|
|
#region Server Methods
|
|
private Task Client_Register(Register.Response response)
|
|
{
|
|
Log.Debug($"Client_Register({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
ShareService.SetHostedShareCode(response.ShareCode);
|
|
ShareService.IsHost = true;
|
|
ShareService.IsGrouped = false;
|
|
OnRegisterEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
}
|
|
else
|
|
{
|
|
OnRegisterEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_Update(Update.Response response)
|
|
{
|
|
Log.Debug($"Client_Update({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
OnUpdateEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
}
|
|
else
|
|
{
|
|
OnUpdateEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_UpdateBroadcast(Update.UpdateBroadcast broadcast)
|
|
{
|
|
Log.Debug($"Client_UpdateBroadcast({JsonConvert.SerializeObject(broadcast)})");
|
|
ShareService.SetActiveQuest(broadcast.SharedQuestId, broadcast.SharedQuestStep);
|
|
Log.Debug($"Updated quest: {broadcast.SharedQuestId} - {broadcast.SharedQuestStep}");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_GroupJoin(GroupJoin.Response response)
|
|
{
|
|
Log.Debug($"Client_GroupJoin({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
Plugin.Configuration.Instance.LastShareCode = response.ShareCode;
|
|
Plugin.Configuration.Save();
|
|
ShareService.IsHost = false;
|
|
ShareService.IsGrouped = true;
|
|
ShareService.SetActiveQuest(response.SharedQuestId, response.SharedQuestStep);
|
|
ShareService.SetShareCode(response.ShareCode);
|
|
OnGroupEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug($"Joined group: {response.ShareCode}");
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Failed to join group: {response.Error}");
|
|
OnGroupEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_GroupLeave(GroupLeave.Response response)
|
|
{
|
|
Log.Debug($"Client_GroupLeave({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
ShareService.IsGrouped = false;
|
|
ShareService.IsHost = false;
|
|
ShareService.ActiveQuestId = 0;
|
|
ShareService.ActiveQuestStep = 0;
|
|
OnUngroupEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug("Left group");
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Failed to leave group: {response.Error}");
|
|
OnUngroupEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_Resume(Resume.Response response)
|
|
{
|
|
Log.Debug($"Client_Resume({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
if (response.IsHost)
|
|
{
|
|
ShareService.PartyMembers = response.Members ?? [];
|
|
ShareService.IsHost = response.IsHost;
|
|
ShareService.IsGrouped = false;
|
|
ShareService.SetHostedShareCode(response.ShareCode);
|
|
GameQuestManager.SetActiveFlag(response.SharedQuestId);
|
|
} else
|
|
{
|
|
ShareService.PartyMembers = response.Members ?? [];
|
|
ShareService.IsHost = false;
|
|
ShareService.IsGrouped = true;
|
|
ShareService.SetActiveQuest(response.SharedQuestId, response.SharedQuestStep);
|
|
}
|
|
OnGetEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug($"Resumed share: {response.ShareCode}");
|
|
DispatchUpdate(false);
|
|
}
|
|
else
|
|
{
|
|
Log.Warning($"Failed to resume share: {response.Error}");
|
|
ShareService.ActiveQuestId = 0;
|
|
ShareService.ActiveQuestStep = 0;
|
|
ShareService.SetShareCode("");
|
|
ShareService.IsGrouped = false;
|
|
ShareService.IsHost = false;
|
|
ShareService.PartyMembers = [];
|
|
OnGetEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_Authorize(Authorize.Response response)
|
|
{
|
|
Log.Debug($"Client_Authorize({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
ShareService.Token = response.Token;
|
|
Plugin.Configuration.Instance.Token = response.Token;
|
|
Plugin.Configuration.Save();
|
|
Log.Debug("Logged in");
|
|
IsAuthorized = true;
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Failed to authorize: {response.Error}");
|
|
}
|
|
DispatchResume();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_AuthRequest(Authorize.AuthBroadcast _)
|
|
{
|
|
Log.Debug("Received auth request");
|
|
DispatchAuthorize();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_Cancel(Cancel.Response response)
|
|
{
|
|
Log.Debug($"Client_Cancel({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
ShareService.SetActiveQuest(0, 0);
|
|
ShareService.SetShareCode("");
|
|
ShareService.IsGrouped = false;
|
|
ShareService.IsHost = false;
|
|
OnCancelEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug("Cancelled share");
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Failed to cancel share: {response.Error}");
|
|
ShareService.SetActiveQuest(0, 0);
|
|
ShareService.SetShareCode("");
|
|
ShareService.IsGrouped = false;
|
|
ShareService.IsHost = false;
|
|
OnCancelEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_CancelBroadast(Cancel.CancelBroadcast broadcast)
|
|
{
|
|
Log.Debug($"Client_CancelBroadcast({JsonConvert.SerializeObject(broadcast)})");
|
|
ShareService.SetActiveQuest(0, 0);
|
|
ShareService.SetShareCode("");
|
|
ShareService.IsGrouped = false;
|
|
ShareService.IsHost = false;
|
|
OnCancelEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug("Cancelled share");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_GetShareInfo(GetShareInfo.Response response)
|
|
{
|
|
Log.Debug($"Client_GetShareInfo({JsonConvert.SerializeObject(response)})");
|
|
if (response.Success)
|
|
{
|
|
ShareService.SetActiveQuest(response.SharedQuestId, response.SharedQuestStep);
|
|
ShareService.PartyMembers = response.Members ?? [];
|
|
OnGetEvent.Invoke(this, new SocketEventArgs { Success = true });
|
|
Log.Debug($"Received share info: {response}");
|
|
}
|
|
else
|
|
{
|
|
Log.Error($"Failed to get share info: {response.Error}");
|
|
OnGetEvent.Invoke(this, new SocketEventArgs { Success = false, Message = response.Error.ToString() });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task Client_GroupNotify(GroupNotify.GroupNotifyBroadcast broadcast)
|
|
{
|
|
Log.Debug($"Client_GroupNotify({JsonConvert.SerializeObject(broadcast)})");
|
|
if (broadcast.NotifyType == NotifyType.Join)
|
|
{
|
|
ShareService.SharedMembers.Add(new ShareService.SharedMember { CharacterId = broadcast.CharacterId });
|
|
}
|
|
else if (broadcast.NotifyType == NotifyType.Leave)
|
|
{
|
|
ShareService.SharedMembers.RemoveAll(m => m.CharacterId == broadcast.CharacterId);
|
|
}
|
|
else if (broadcast.NotifyType == NotifyType.JoinViaParty)
|
|
{
|
|
ShareService.SharedMembers.Add(new ShareService.SharedMember { CharacterId = broadcast.CharacterId });
|
|
}
|
|
else if (broadcast.NotifyType == NotifyType.Rejoin)
|
|
{
|
|
ShareService.SharedMembers.Add(new ShareService.SharedMember { CharacterId = broadcast.CharacterId });
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
#endregion
|
|
public class SocketEventArgs : EventArgs
|
|
{
|
|
public bool Success { get; set; }
|
|
public string Message { get; set; } = "";
|
|
}
|
|
public class TokenCheckEventArgs : EventArgs
|
|
{
|
|
public bool TokenValid { get; set; }
|
|
public bool ShareCodeValid { get; set; }
|
|
}
|
|
}
|
|
}
|