rework and changes
This commit is contained in:
parent
ef35c59d13
commit
5e75451638
27
QuestShare.Common/API/AuthRequest.cs
Normal file
27
QuestShare.Common/API/AuthRequest.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace QuestShare.Common.API
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
public sealed class AuthRequest : IAPIEndpoint<AuthRequest.Request, AuthRequest.Response>
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
{
|
||||||
|
[Obsolete("This constructor is for serialization only. Do not use it in code.")]
|
||||||
|
public class Request : IRequest
|
||||||
|
{
|
||||||
|
public Request() { }
|
||||||
|
public required string Version { get; set; }
|
||||||
|
public required string Token { get; set; }
|
||||||
|
}
|
||||||
|
public class Response : IResponse
|
||||||
|
{
|
||||||
|
public Response() { }
|
||||||
|
public Error Error { get; set; } = Error.None;
|
||||||
|
public bool Success { get; set; } = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,14 +6,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace QuestShare.Common.API
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public class Authorize : IAPIEndpoint<Authorize.Request, Authorize.Response>
|
public sealed class Authorize : IAPIEndpoint<Authorize.Request, Authorize.Response>
|
||||||
{
|
{
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public string Version { get; set; } = null!;
|
public string Version { get; set; } = null!;
|
||||||
public string Token { get; set; } = null!;
|
public string Token { get; set; } = null!;
|
||||||
public ulong CharacterId { get; set; } = 0;
|
public List<Objects.ShareCode> ShareCodes { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
@ -22,11 +22,8 @@ namespace QuestShare.Common.API
|
|||||||
public Error Error { get; set; } = Error.None;
|
public Error Error { get; set; } = Error.None;
|
||||||
public bool Success { get; set; }
|
public bool Success { get; set; }
|
||||||
public string Token { get; set; } = null!;
|
public string Token { get; set; } = null!;
|
||||||
}
|
public List<Objects.Session> Sessions { get; set; } = [];
|
||||||
|
public Objects.OwnedSession? OwnedSession { get; set; }
|
||||||
public class AuthBroadcast
|
|
||||||
{
|
|
||||||
public AuthBroadcast() { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,17 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public class Cancel : IAPIEndpoint<Cancel.Request, Cancel.Response>
|
public sealed class Cancel : IAPIEndpoint<Cancel.Request, Cancel.Response>
|
||||||
{
|
{
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public string Version { get; set; } = null!;
|
public required string Version { get; set; }
|
||||||
public string Token { get; set; } = null!;
|
public required string Token { get; set; }
|
||||||
public string ShareCode { get; set; } = null!;
|
public required string ShareCode { get; set; }
|
||||||
|
public required string OwnerCharacterId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
@ -1,31 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common.API
|
|
||||||
{
|
|
||||||
public enum Error
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
InvalidToken,
|
|
||||||
InvalidShareCode,
|
|
||||||
InvalidVersion,
|
|
||||||
InvalidQuests,
|
|
||||||
ShareNotFound,
|
|
||||||
Unauthorized,
|
|
||||||
UnknownError,
|
|
||||||
InternalServerError,
|
|
||||||
InvalidCharacterName,
|
|
||||||
InvalidNotifyType,
|
|
||||||
InvalidParty,
|
|
||||||
InvalidMember,
|
|
||||||
InvalidQuest,
|
|
||||||
InvalidCharacterId,
|
|
||||||
BannedTooManyBadRequests,
|
|
||||||
AlreadyRegistered,
|
|
||||||
AlreadyJoined,
|
|
||||||
InvalidHostClient,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,25 +1,19 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using QuestShare.Common.API;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public class Register : IAPIEndpoint<Register.Request, Register.Response>
|
public sealed class GroupJoin : IAPIEndpoint<GroupJoin.Request, GroupJoin.Response>
|
||||||
{
|
{
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public required string Token { get; set; }
|
public required string Token { get; set; }
|
||||||
public required ulong CharacterId { get; set; }
|
|
||||||
public required string Version { get; set; }
|
public required string Version { get; set; }
|
||||||
public uint SharedQuestId { get; set; }
|
public required Objects.ShareCode SessionInfo { get; set; }
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
public bool BroadcastParty { get; set; } = false;
|
|
||||||
public List<ulong> PartyMembers { get; set; } = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
@ -27,8 +21,13 @@ namespace QuestShare.Common.API.Share
|
|||||||
public Response() { }
|
public Response() { }
|
||||||
public Error Error { get; set; } = Error.None;
|
public Error Error { get; set; } = Error.None;
|
||||||
public bool Success { get; set; } = false;
|
public bool Success { get; set; } = false;
|
||||||
public required string ShareCode { get; set; }
|
public Objects.Session? Session { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GroupJoinBroadcast
|
||||||
|
{
|
||||||
|
public GroupJoinBroadcast() { }
|
||||||
|
public required Objects.Session Session { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public sealed class GroupLeave : IAPIEndpoint<GroupLeave.Request, GroupLeave.Response>
|
public sealed class GroupLeave : IAPIEndpoint<GroupLeave.Request, GroupLeave.Response>
|
||||||
{
|
{
|
||||||
@ -12,14 +12,21 @@ namespace QuestShare.Common.API.Share
|
|||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public string Version { get; set; } = null!;
|
public string Version { get; set; } = null!;
|
||||||
public string ShareCode { get; set; } = null!;
|
|
||||||
public string Token { get; set; } = null!;
|
public string Token { get; set; } = null!;
|
||||||
|
public required Objects.Session Session { get; set; }
|
||||||
}
|
}
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
{
|
{
|
||||||
public Response() { }
|
public Response() { }
|
||||||
public Error Error { get; set; } = Error.None;
|
public Error Error { get; set; } = Error.None;
|
||||||
public bool Success { get; set; } = false;
|
public bool Success { get; set; } = false;
|
||||||
|
public Objects.Session? Session { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GroupLeaveBroadcast
|
||||||
|
{
|
||||||
|
public GroupLeaveBroadcast() { }
|
||||||
|
public required Objects.Session Session { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common.API
|
|
||||||
{
|
|
||||||
public interface IRequest
|
|
||||||
{
|
|
||||||
string Version { get; set; }
|
|
||||||
string Token { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IResponse
|
|
||||||
{
|
|
||||||
Error Error { get; set; }
|
|
||||||
bool Success { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IAPIEndpoint<T1, T2> where T1 : IRequest where T2 : IResponse;
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
namespace QuestShare.Common.API.Party
|
|
||||||
{
|
|
||||||
public class PartyCheck : IAPIEndpoint<PartyCheck.Request, PartyCheck.Response>
|
|
||||||
{
|
|
||||||
public class Request : IRequest
|
|
||||||
{
|
|
||||||
public Request() { }
|
|
||||||
public string Version { get; set; } = null!;
|
|
||||||
public string Token { get; set; } = null!;
|
|
||||||
public required ulong CharacterId { get; set; } = 0;
|
|
||||||
public required List<ulong> PartyMembers { get; set; }
|
|
||||||
}
|
|
||||||
public class Response : IResponse
|
|
||||||
{
|
|
||||||
public Response() { }
|
|
||||||
public Error Error { get; set; } = Error.None;
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public string ShareCode { get; set; } = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +1,19 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public sealed class GetShareInfo : IAPIEndpoint<GetShareInfo.Request, GetShareInfo.Response>
|
public sealed class Register : IAPIEndpoint<Register.Request, Register.Response>
|
||||||
{
|
{
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public required string Version { get; set; }
|
|
||||||
public required string Token { get; set; }
|
public required string Token { get; set; }
|
||||||
public required string ShareCode { get; set; }
|
public required string Version { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
@ -21,9 +21,7 @@ namespace QuestShare.Common.API.Share
|
|||||||
public Response() { }
|
public Response() { }
|
||||||
public Error Error { get; set; } = Error.None;
|
public Error Error { get; set; } = Error.None;
|
||||||
public bool Success { get; set; } = false;
|
public bool Success { get; set; } = false;
|
||||||
public uint SharedQuestId { get; set; }
|
public required string ShareCode { get; set; }
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
public required List<ulong> Members { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,33 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
|
||||||
{
|
|
||||||
public class GroupJoin : IAPIEndpoint<GroupJoin.Request, GroupJoin.Response>
|
|
||||||
{
|
|
||||||
public class Request : IRequest
|
|
||||||
{
|
|
||||||
public Request() { }
|
|
||||||
public required string Token { get; set; }
|
|
||||||
public required string Version { get; set; }
|
|
||||||
public string ShareCode { get; set; } = "";
|
|
||||||
public required ulong CharacterId { get; set; }
|
|
||||||
public bool RequestPartyQuickJoin { get; set; } = false;
|
|
||||||
public ulong QuickJoinCharacterId { get; set; } = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Response : IResponse
|
|
||||||
{
|
|
||||||
public Response() { }
|
|
||||||
public Error Error { get; set; } = Error.None;
|
|
||||||
public bool Success { get; set; } = false;
|
|
||||||
public uint SharedQuestId { get; set; }
|
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
public string ShareCode { get; set; } = null!;
|
|
||||||
public List<ulong> Members { get; set; } = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
|
||||||
{
|
|
||||||
public sealed class GroupNotify
|
|
||||||
{
|
|
||||||
public class GroupNotifyBroadcast
|
|
||||||
{
|
|
||||||
public GroupNotifyBroadcast() { }
|
|
||||||
public required string ShareCode { get; set; }
|
|
||||||
public required ulong CharacterId { get; set; }
|
|
||||||
public required NotifyType NotifyType { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum NotifyType
|
|
||||||
{
|
|
||||||
Join,
|
|
||||||
Leave,
|
|
||||||
JoinViaParty,
|
|
||||||
Rejoin,
|
|
||||||
Disconnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
|
||||||
{
|
|
||||||
public class Resume : IAPIEndpoint<Resume.Request, Resume.Response>
|
|
||||||
{
|
|
||||||
public class Request : IRequest
|
|
||||||
{
|
|
||||||
public Request() { }
|
|
||||||
public required string Version { get; set; }
|
|
||||||
public required string Token { get; set; }
|
|
||||||
public string ShareCode { get; set; } = "";
|
|
||||||
public List<ulong>? Members { get; set; } = [];
|
|
||||||
}
|
|
||||||
public class Response : IResponse
|
|
||||||
{
|
|
||||||
public Response() { }
|
|
||||||
public Error Error { get; set; } = Error.None;
|
|
||||||
public bool Success { get; set; } = false;
|
|
||||||
public uint SharedQuestId { get; set; }
|
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
public string ShareCode { get; set; } = "";
|
|
||||||
public List<ulong>? Members { get; set; } = [];
|
|
||||||
public bool IsGroup { get; set; } = false;
|
|
||||||
public bool IsHost { get; set; } = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -4,20 +4,18 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace QuestShare.Common.API.Share
|
namespace QuestShare.Common.API
|
||||||
{
|
{
|
||||||
public class Update : IAPIEndpoint<Update.Request, Update.Response>
|
public sealed class Update : IAPIEndpoint<Update.Request, Update.Response>
|
||||||
{
|
{
|
||||||
public class Request : IRequest
|
public class Request : IRequest
|
||||||
{
|
{
|
||||||
public Request() { }
|
public Request() { }
|
||||||
public required string Version { get; set; }
|
public required string Version { get; set; }
|
||||||
public required string Token { get; set; }
|
public required string Token { get; set; }
|
||||||
public uint SharedQuestId { get; set; }
|
public required Objects.OwnedSession Session { get; set; }
|
||||||
public byte SharedQuestStep { get; set; }
|
public List<string> PartyMembers { get; set; } = [];
|
||||||
public required bool BroadcastParty { get; set; }
|
public bool IsQuestUpdate { get; set; } = false;
|
||||||
public List<ulong> PartyMembers { get; set; } = [];
|
|
||||||
public bool IsPartyChange { get; set; } = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Response : IResponse
|
public class Response : IResponse
|
||||||
@ -30,9 +28,7 @@ namespace QuestShare.Common.API.Share
|
|||||||
public class UpdateBroadcast
|
public class UpdateBroadcast
|
||||||
{
|
{
|
||||||
public UpdateBroadcast() { }
|
public UpdateBroadcast() { }
|
||||||
public string ShareCode { get; set; } = null!;
|
public required Objects.Session Session { get; set; }
|
||||||
public uint SharedQuestId { get; set; }
|
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,35 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace QuestShare.Common
|
namespace QuestShare.Common
|
||||||
{
|
{
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public static readonly string Version = "1";
|
public static readonly string Version = "3";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringExtensions
|
||||||
|
{
|
||||||
|
public static string ToBase64(this string str)
|
||||||
|
{
|
||||||
|
return Convert.ToBase64String(Encoding.UTF8.GetBytes(str));
|
||||||
|
}
|
||||||
|
public static string FromBase64(this string str)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetString(Convert.FromBase64String(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToSHA256(this string str)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(str);
|
||||||
|
// 10 reps of SHA256
|
||||||
|
for (var i = 0; i < 10; i++)
|
||||||
|
bytes = System.Security.Cryptography.SHA256.HashData(bytes);
|
||||||
|
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SaltedHash(this string str, string salt)
|
||||||
|
{
|
||||||
|
return (str + salt).ToSHA256();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
using Dalamud.Game.Addon.Lifecycle;
|
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Addons
|
|
||||||
{
|
|
||||||
internal class AddonPartyList
|
|
||||||
{
|
|
||||||
public static int MemberCount { get; private set; }
|
|
||||||
public static event EventHandler? OnMemberCountChanged;
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
AddonLifecycle.RegisterListener(AddonEvent.PostRefresh, OnPostRefresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Dispose()
|
|
||||||
{
|
|
||||||
AddonLifecycle.UnregisterListener(AddonEvent.PostRefresh, OnPostRefresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnPostRefresh(AddonEvent e, AddonArgs args)
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var addon = (FFXIVClientStructs.FFXIV.Client.UI.AddonPartyList*)args.Addon;
|
|
||||||
if (addon == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int count = addon->MemberCount;
|
|
||||||
if (count != MemberCount)
|
|
||||||
{
|
|
||||||
MemberCount = addon->MemberCount;
|
|
||||||
OnMemberCountChanged?.Invoke(null, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using QuestShare.Services;
|
using QuestShare.Services;
|
||||||
|
|
||||||
@ -9,42 +8,55 @@ public class ConfigurationManager
|
|||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
public string Token { get; set; } = string.Empty;
|
public string Token { get; set; } = string.Empty;
|
||||||
public string ShareCode { get; set; } = string.Empty;
|
|
||||||
public string LastShareCode { get; set; } = string.Empty;
|
|
||||||
public ShareMode LastShareMode { get; set; } = ShareMode.None;
|
|
||||||
public bool ConnectOnStartup { get; set; } = false;
|
public bool ConnectOnStartup { get; set; } = false;
|
||||||
public bool ResumeOnStartup { get; set; } = false;
|
|
||||||
public bool LastConnectionState { get; set; } = false;
|
|
||||||
public bool AutoShareMsq { get; set; } = false;
|
public bool AutoShareMsq { get; set; } = false;
|
||||||
public bool AutoShareNewQuests { get; set; } = false;
|
public bool AutoShareNewQuests { get; set; } = false;
|
||||||
public bool BroadcastToParty { get; set; } = false;
|
|
||||||
public bool TrackMSQ { get; set; } = false;
|
public bool TrackMSQ { get; set; } = false;
|
||||||
|
public bool HideFutureStepsHost { get; set; } = true;
|
||||||
|
public bool HideFutureStepsMember { get; set; } = false;
|
||||||
|
public bool EnableHosting { get; set; } = false;
|
||||||
|
public Objects.OwnedSession? OwnedSession { get; set; }
|
||||||
|
public List<Objects.ShareCode> KnownShareCodes { get; set; } = [];
|
||||||
|
public int ActiveQuestId { get; set; } = 0;
|
||||||
|
public byte ActiveQuestStep { get; set; } = 0;
|
||||||
|
public Dictionary<long, string> KnownCharacters { get; set; } = [];
|
||||||
|
public ApiConfiguration[] ApiConfigurations { get; set; } = [new ApiConfiguration { DisplayName = "Primary Server - US East", Url = "https://api.nathanc.tech/Hub", Active = false }];
|
||||||
|
public string ApiUrl => ApiConfigurations.FirstOrDefault(x => x.Active)?.Url ?? "https://api.nathanc.tech/Hub";
|
||||||
|
public string ApiDisplayName => ApiConfigurations.FirstOrDefault(x => x.Active)?.DisplayName ?? "Primary Server - US East";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Configuration Instance { get; private set; } = new Configuration();
|
public class ApiConfiguration
|
||||||
private ulong localContentId = 0;
|
{
|
||||||
|
public required string DisplayName;
|
||||||
|
public required string Url;
|
||||||
|
public bool Active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Configuration Instance { get; private set; } = new Configuration();
|
||||||
|
private static ulong LocalContentId = 0;
|
||||||
|
|
||||||
public ConfigurationManager()
|
public ConfigurationManager()
|
||||||
{
|
{
|
||||||
Log.Debug("ConfigurationManager constructor");
|
Log.Debug("ConfigurationManager constructor");
|
||||||
|
|
||||||
ClientState.Login += OnLogin;
|
ClientState.Login += OnLogin;
|
||||||
ClientState.Logout += OnLogout;
|
ClientState.Logout += OnLogout;
|
||||||
if (ClientState.LocalContentId != 0)
|
if (ClientState.LocalContentId != 0)
|
||||||
{
|
{
|
||||||
localContentId = ClientState.LocalContentId;
|
LocalContentId = ClientState.LocalContentId;
|
||||||
Framework.Run(Load);
|
Framework.Run(Load);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnLogin()
|
public void OnLogin()
|
||||||
{
|
{
|
||||||
localContentId = ClientState.LocalContentId;
|
LocalContentId = ClientState.LocalContentId;
|
||||||
Framework.Run(Load);
|
Framework.Run(Load);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Framework.Run(Save);
|
Save();
|
||||||
ClientState.Login -= OnLogin;
|
ClientState.Login -= OnLogin;
|
||||||
ClientState.Logout -= OnLogout;
|
ClientState.Logout -= OnLogout;
|
||||||
}
|
}
|
||||||
@ -52,30 +64,31 @@ public class ConfigurationManager
|
|||||||
public void OnLogout(int code, int state)
|
public void OnLogout(int code, int state)
|
||||||
{
|
{
|
||||||
Framework.RunOnTick(Save);
|
Framework.RunOnTick(Save);
|
||||||
ShareService.SetShareCode(string.Empty);
|
|
||||||
ShareService.SetHostedShareCode(string.Empty);
|
|
||||||
ShareService.Token = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load()
|
public void Load()
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{localContentId}.json")))
|
if (File.Exists(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{LocalContentId}.json")))
|
||||||
{
|
{
|
||||||
var config = File.ReadAllText(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{localContentId}.json"));
|
var config = File.ReadAllText(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{LocalContentId}.json"));
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
var deserialized = JsonConvert.DeserializeObject<Configuration>(config);
|
var deserialized = JsonConvert.DeserializeObject<Configuration>(config);
|
||||||
if (deserialized != null)
|
if (deserialized != null)
|
||||||
{
|
{
|
||||||
Instance = deserialized;
|
Instance = deserialized;
|
||||||
|
#if DEBUG
|
||||||
ShareService.SetShareCode(Instance.LastShareCode);
|
if (Instance.ApiConfigurations.All(x => x.DisplayName != "Local"))
|
||||||
ShareService.Token = Instance.Token;
|
Instance.ApiConfigurations.Append(new ApiConfiguration { DisplayName = "Local", Url = "http://localhost:8080/Hub", Active = true });
|
||||||
Log.Warning($"Writing token {Instance.Token} to ShareService");
|
foreach (var item in Instance.ApiConfigurations)
|
||||||
|
{
|
||||||
|
if (item.DisplayName != "Local") item.Active = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Error($"Failed to deserialize configuration for {localContentId}");
|
Log.Error($"Failed to deserialize configuration for {LocalContentId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,10 +98,11 @@ public class ConfigurationManager
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void Save()
|
public static void Save()
|
||||||
{
|
{
|
||||||
Log.Debug("Saving configuration");
|
Log.Debug("Saving configuration");
|
||||||
File.WriteAllText(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{localContentId}.json"), JsonConvert.SerializeObject(Instance));
|
File.WriteAllText(Path.Join(PluginInterface.ConfigDirectory.FullName, $"{LocalContentId}.json"), JsonConvert.SerializeObject(Instance));
|
||||||
|
Log.Debug($"Wrote config: {JsonConvert.SerializeObject(Instance)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
namespace QuestShare.Common
|
|
||||||
{
|
|
||||||
internal static class GameEventManager
|
|
||||||
{
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public static void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,6 +2,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
@ -57,6 +58,11 @@ namespace QuestShare.Common
|
|||||||
{
|
{
|
||||||
SetActiveFlag(activeQuest.QuestId);
|
SetActiveFlag(activeQuest.QuestId);
|
||||||
}
|
}
|
||||||
|
else if (ConfigurationManager.Instance.OwnedSession != null)
|
||||||
|
{
|
||||||
|
var quest = GetQuestById((uint)ConfigurationManager.Instance.OwnedSession.ActiveQuestId);
|
||||||
|
SetActiveFlag(quest.QuestId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnLogin()
|
private static void OnLogin()
|
||||||
@ -72,7 +78,9 @@ namespace QuestShare.Common
|
|||||||
|
|
||||||
public unsafe static bool TrackMsq()
|
public unsafe static bool TrackMsq()
|
||||||
{
|
{
|
||||||
if (ShareService.IsMsqTracking && ShareService.IsHost)
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
if (ConfigurationManager.Instance.TrackMSQ)
|
||||||
{
|
{
|
||||||
var questId = (uint)AgentScenarioTree.Instance()->Data->CurrentScenarioQuest;
|
var questId = (uint)AgentScenarioTree.Instance()->Data->CurrentScenarioQuest;
|
||||||
if (questId == 0) return false;
|
if (questId == 0) return false;
|
||||||
@ -85,13 +93,13 @@ namespace QuestShare.Common
|
|||||||
if (GameQuests.Contains(quest) && GetActiveQuest()?.QuestId != questId)
|
if (GameQuests.Contains(quest) && GetActiveQuest()?.QuestId != questId)
|
||||||
{
|
{
|
||||||
SetActiveFlag(questId);
|
SetActiveFlag(questId);
|
||||||
SocketClientService.DispatchUpdate(false);
|
HostService.Update((int)questId, quest.CurrentStep);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GameQuests.Add(quest);
|
GameQuests.Add(quest);
|
||||||
SetActiveFlag(questId);
|
SetActiveFlag(questId);
|
||||||
SocketClientService.DispatchUpdate(false);
|
HostService.Update((int)questId, quest.CurrentStep);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -108,6 +116,7 @@ namespace QuestShare.Common
|
|||||||
if (ClientState.LocalContentId == 0) return;
|
if (ClientState.LocalContentId == 0) return;
|
||||||
TrackMsq();
|
TrackMsq();
|
||||||
var q = GetActiveQuest();
|
var q = GetActiveQuest();
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
if (q != null && q.QuestId == LastQuestId)
|
if (q != null && q.QuestId == LastQuestId)
|
||||||
{
|
{
|
||||||
var step = QuestManager.GetQuestSequence(q.QuestId);
|
var step = QuestManager.GetQuestSequence(q.QuestId);
|
||||||
@ -124,6 +133,7 @@ namespace QuestShare.Common
|
|||||||
{
|
{
|
||||||
Log.Debug("No previous quest in chain");
|
Log.Debug("No previous quest in chain");
|
||||||
GameQuests.Remove(q);
|
GameQuests.Remove(q);
|
||||||
|
LoadQuests();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.Debug($"Previous quest in chain was {prevQuest.RowId}");
|
Log.Debug($"Previous quest in chain was {prevQuest.RowId}");
|
||||||
@ -138,16 +148,16 @@ namespace QuestShare.Common
|
|||||||
{
|
{
|
||||||
Log.Debug("No next quest in chain");
|
Log.Debug("No next quest in chain");
|
||||||
GameQuests.Remove(q);
|
GameQuests.Remove(q);
|
||||||
|
LoadQuests();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SocketClientService.DispatchUpdate(false);
|
HostService.Update((int)q.QuestId, q.CurrentStep);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LastStep = step;
|
LastStep = step;
|
||||||
Log.Debug($"Quest step changed to {step}");
|
Log.Debug($"Quest step changed to {step}");
|
||||||
SocketClientService.DispatchUpdate(false);
|
HostService.Update((int)q.QuestId, q.CurrentStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,7 +232,7 @@ namespace QuestShare.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Map GetMapLocation()
|
public Lumina.Excel.Sheets.Map GetMapLocation()
|
||||||
{
|
{
|
||||||
return QuestData.TodoParams.FirstOrDefault(param => param.ToDoCompleteSeq == CurrentStep)
|
return QuestData.TodoParams.FirstOrDefault(param => param.ToDoCompleteSeq == CurrentStep)
|
||||||
.ToDoLocation.FirstOrDefault(location => location is not { RowId: 0 }).Value.Map.Value;
|
.ToDoLocation.FirstOrDefault(location => location is not { RowId: 0 }).Value.Map.Value;
|
||||||
@ -231,17 +241,18 @@ namespace QuestShare.Common
|
|||||||
public MapLinkPayload GetMapLink(byte step)
|
public MapLinkPayload GetMapLink(byte step)
|
||||||
{
|
{
|
||||||
var toDoData = QuestData.TodoParams.Select(t => t.ToDoLocation).ToList();
|
var toDoData = QuestData.TodoParams.Select(t => t.ToDoLocation).ToList();
|
||||||
var data = toDoData.ElementAt(step == 0xFF ? QuestSteps.Count-1 : step).FirstOrDefault();
|
Log.Debug($"Step: {step}/{0xFF} - Total steps: {QuestSteps.Count} - ToDoParams Count: {toDoData.Count}");
|
||||||
|
var data = toDoData.ElementAt(step > QuestSteps.Count ? QuestSteps.Count-1 : step).FirstOrDefault();
|
||||||
|
if (!data.IsValid)
|
||||||
|
{
|
||||||
|
Log.Error("Invalid ToDoLocation data");
|
||||||
|
return new MapLinkPayload(0, 0, 0, 0);
|
||||||
|
}
|
||||||
var coords = MapUtil.WorldToMap(new Vector3(data.Value.X, data.Value.Y, data.Value.Z), data.Value.Map.Value.OffsetX, data.Value.Map.Value.OffsetY, 0, data.Value.Map.Value.SizeFactor);
|
var coords = MapUtil.WorldToMap(new Vector3(data.Value.X, data.Value.Y, data.Value.Z), data.Value.Map.Value.OffsetX, data.Value.Map.Value.OffsetY, 0, data.Value.Map.Value.SizeFactor);
|
||||||
var mapLink = new MapLinkPayload(data.Value.Territory.Value.RowId, data.Value.Map.Value.RowId, coords.X, coords.Y);
|
var mapLink = new MapLinkPayload(data.Value.Territory.Value.RowId, data.Value.Map.Value.RowId, coords.X, coords.Y);
|
||||||
return mapLink;
|
return mapLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetQuestDetail()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ExcelSheet<QuestDialogue> TextSheetForQuest(Lumina.Excel.Sheets.Quest q)
|
internal static ExcelSheet<QuestDialogue> TextSheetForQuest(Lumina.Excel.Sheets.Quest q)
|
||||||
{
|
{
|
||||||
var qid = q.Id.ToString();
|
var qid = q.Id.ToString();
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Common
|
|
||||||
{
|
|
||||||
internal static class Maps
|
|
||||||
{
|
|
||||||
public static int MarkerToMap(double coord, double scale)
|
|
||||||
=> (int)(2 * coord / scale + 100.9);
|
|
||||||
|
|
||||||
public static int NodeToMap(double coord, double scale)
|
|
||||||
=> (int)(2 * coord + 2048 / scale + 100.9);
|
|
||||||
|
|
||||||
public static int IntegerToInternal(int coord, double scale)
|
|
||||||
=> (int)(coord - 100 - 2048 / scale) / 2;
|
|
||||||
|
|
||||||
public static unsafe void SetFlagMarker(AgentMap* instance, Location location, uint iconId = 60561U)
|
|
||||||
{
|
|
||||||
instance->IsFlagMarkerSet = 0;
|
|
||||||
var x = IntegerToInternal(location.XCoord, location.SizeFactor);
|
|
||||||
var y = IntegerToInternal(location.YCoord, location.SizeFactor);
|
|
||||||
instance->SetFlagMapMarker(location.Id, location.Data.Map.RowId, x, y, iconId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class Location(int X, int Y)
|
|
||||||
{
|
|
||||||
public uint Id { get; init; }
|
|
||||||
public float SizeFactor => (Data.Map.ValueNullable?.SizeFactor ?? 100f) / 100f;
|
|
||||||
|
|
||||||
public int XCoord { get; init; } = X;
|
|
||||||
public int YCoord { get; init; } = Y;
|
|
||||||
public TerritoryType Data { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
global using static QuestShare.Service;
|
global using static QuestShare.Service;
|
||||||
global using QuestShare.Common;
|
global using QuestShare.Common;
|
||||||
|
global using QuestShare.Common.API;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using QuestShare.Services;
|
using QuestShare.Services;
|
||||||
using QuestShare.Addons;
|
|
||||||
|
|
||||||
namespace QuestShare;
|
namespace QuestShare;
|
||||||
|
|
||||||
@ -14,8 +14,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
public static string Version => "1.0.0";
|
public static string Version => "1.0.0";
|
||||||
public static string PluginDataPath { get; private set; } = null!;
|
public static string PluginDataPath { get; private set; } = null!;
|
||||||
internal static ConfigurationManager Configuration { get; private set; } = null!;
|
internal static ConfigurationManager Configuration { get; private set; } = null!;
|
||||||
internal static SocketClientService SocketClient { get; private set; } = null!;
|
private static List<IService> Services = [];
|
||||||
|
|
||||||
internal static StringWriter LogStream { get; private set; } = null!;
|
internal static StringWriter LogStream { get; private set; } = null!;
|
||||||
|
|
||||||
public Plugin(IDalamudPluginInterface pluginInterface)
|
public Plugin(IDalamudPluginInterface pluginInterface)
|
||||||
@ -23,33 +22,45 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
pluginInterface.Create<Service>(pluginInterface);
|
pluginInterface.Create<Service>(pluginInterface);
|
||||||
// redirect console output to plugin log
|
// redirect console output to plugin log
|
||||||
Configuration = new ConfigurationManager();
|
Configuration = new ConfigurationManager();
|
||||||
WindowManager.Initialize();
|
Services =
|
||||||
GameEventManager.Initialize();
|
[
|
||||||
Commands.Initialize();
|
new ApiService(),
|
||||||
|
new CommandService(),
|
||||||
|
new ShareService(),
|
||||||
|
new PartyService(),
|
||||||
|
new UiService(),
|
||||||
|
new HostService()
|
||||||
|
];
|
||||||
GameQuestManager.Initialize();
|
GameQuestManager.Initialize();
|
||||||
AddonPartyList.Initialize();
|
|
||||||
LogStream = new StringWriter();
|
LogStream = new StringWriter();
|
||||||
Console.SetOut(LogStream);
|
Console.SetOut(LogStream);
|
||||||
Console.SetError(LogStream);
|
Console.SetError(LogStream);
|
||||||
Service.Framework.Update += OnFramework;
|
Framework.Update += OnFramework;
|
||||||
Log.Debug($"Share Code: {Configuration.Instance.ShareCode} - Token: {Configuration.Instance.Token}");
|
Log.Debug($"Token: {ConfigurationManager.Instance.Token}");
|
||||||
SocketClient = new SocketClientService();
|
foreach (var service in Services)
|
||||||
|
{
|
||||||
|
Log.Debug($"Initializing {service.GetType().Name}");
|
||||||
|
service.Initialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
WindowManager.Dispose();
|
|
||||||
GameEventManager.Dispose();
|
|
||||||
Commands.Dispose();
|
|
||||||
Configuration.Save();
|
|
||||||
LogStream.Dispose();
|
LogStream.Dispose();
|
||||||
AddonPartyList.Dispose();
|
foreach (var service in Services)
|
||||||
Service.Framework.Update -= OnFramework;
|
{
|
||||||
SocketClient.Dispose();
|
service.Shutdown();
|
||||||
Configuration.Dispose();
|
}
|
||||||
ShareService.Dispose();
|
ConfigurationManager.Save();
|
||||||
ClientState.Login -= Configuration.OnLogin;
|
ClientState.Login -= Configuration.OnLogin;
|
||||||
ClientState.Logout -= Configuration.OnLogout;
|
ClientState.Logout -= Configuration.OnLogout;
|
||||||
|
Framework.Update -= OnFramework;
|
||||||
|
Configuration.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IService GetService<T>() where T : IService
|
||||||
|
{
|
||||||
|
return Services.FirstOrDefault(s => s is T)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFramework(IFramework framework)
|
private void OnFramework(IFramework framework)
|
||||||
|
|||||||
@ -9,9 +9,6 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Windows\MainWindow\MainWindow.cs_" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="QuestShare.json" />
|
<Content Include="QuestShare.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -23,4 +20,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Addons\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
13
QuestShare.Dalamud/Services/API/AuthRequest.cs
Normal file
13
QuestShare.Dalamud/Services/API/AuthRequest.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class AuthRequest_Client
|
||||||
|
{
|
||||||
|
|
||||||
|
public static Task HandleResponse(AuthRequest.Response response)
|
||||||
|
{
|
||||||
|
var api = ((ApiService)Plugin.GetService<ApiService>());
|
||||||
|
ApiService.DispatchAuthorize();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
QuestShare.Dalamud/Services/API/Authorize.cs
Normal file
50
QuestShare.Dalamud/Services/API/Authorize.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class Authorize_Client
|
||||||
|
{
|
||||||
|
public static void HandleDispatch()
|
||||||
|
{
|
||||||
|
if (ClientState.LocalContentId == 0)
|
||||||
|
{
|
||||||
|
_ = ((ApiService)Plugin.GetService<ApiService>()).Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var knownCodes = ShareService.ShareCodes.Select(s => s).ToList();
|
||||||
|
if (HostService.ActiveSession != null)
|
||||||
|
{
|
||||||
|
knownCodes.Add(new Objects.ShareCode { CharacterId = HostService.ActiveSession.OwnerCharacterId, Code = HostService.ActiveSession.ShareCode });
|
||||||
|
}
|
||||||
|
var request = new Authorize.Request()
|
||||||
|
{
|
||||||
|
Token = ApiService.Token,
|
||||||
|
Version = Constants.Version,
|
||||||
|
ShareCodes = knownCodes,
|
||||||
|
};
|
||||||
|
((ApiService)Plugin.GetService<ApiService>()).Invoke(nameof(Authorize), request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public static Task HandleResponse(Authorize.Response response)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.Token = response.Token;
|
||||||
|
foreach(var session in response.Sessions)
|
||||||
|
{
|
||||||
|
share.AddSession(session);
|
||||||
|
}
|
||||||
|
if (response.OwnedSession != null)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.OwnedSession = response.OwnedSession;
|
||||||
|
}
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("Failed to authorize: {Error}", response.Error);
|
||||||
|
UiService.LastErrorMessage = $"Failed to authorize: {response.Error}";
|
||||||
|
_ = ((ApiService)Plugin.GetService<ApiService>()).Disconnect();
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
QuestShare.Dalamud/Services/API/Cancel.cs
Normal file
41
QuestShare.Dalamud/Services/API/Cancel.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class Cancel_Client
|
||||||
|
{
|
||||||
|
public static void HandleDispatch()
|
||||||
|
{
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
var party = (PartyService)Plugin.GetService<PartyService>();
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
var request = new Cancel.Request
|
||||||
|
{
|
||||||
|
Token = ApiService.Token,
|
||||||
|
Version = Constants.Version,
|
||||||
|
ShareCode = HostService.ActiveSession!.ShareCode,
|
||||||
|
OwnerCharacterId = HostService.ActiveSession!.OwnerCharacterId
|
||||||
|
};
|
||||||
|
_ = api.Invoke(nameof(Cancel), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleResponse(Cancel.Response cancelResponse)
|
||||||
|
{
|
||||||
|
if (cancelResponse.Success)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
ConfigurationManager.Instance.OwnedSession = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UiService.LastErrorMessage = "Failed to cancel the party.";
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleBroadcast(Cancel.CancelBroadcast cancelBroadcast)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
share.RemoveSession(cancelBroadcast.ShareCode);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
QuestShare.Dalamud/Services/API/GroupJoin.cs
Normal file
44
QuestShare.Dalamud/Services/API/GroupJoin.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class GroupJoin_Client
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void HandleDispatch(Objects.ShareCode shareCode) {
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
var request = new GroupJoin.Request()
|
||||||
|
{
|
||||||
|
Token = ApiService.Token,
|
||||||
|
Version = Constants.Version,
|
||||||
|
SessionInfo = shareCode
|
||||||
|
};
|
||||||
|
api.Invoke(nameof(GroupJoin), request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleResponse(GroupJoin.Response resp)
|
||||||
|
{
|
||||||
|
if (resp.Success && resp.Session != null)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
Log.Information("Successfully joined group.");
|
||||||
|
share.AddSession(resp.Session);
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
api.OnGroupJoin(new ApiService.GroupJoinEventArgs { Session = resp.Session, IsSuccess = true });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("Failed to join group: {Error}", resp.Error);
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
api.OnGroupJoin(new ApiService.GroupJoinEventArgs { Session = null, IsSuccess = false });
|
||||||
|
UiService.LastErrorMessage = $"Failed to join group. {resp.Error}";
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleBroadcast(GroupJoin.GroupJoinBroadcast broadcast)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
share.AddSession(broadcast.Session);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
QuestShare.Dalamud/Services/API/GroupLeave.cs
Normal file
36
QuestShare.Dalamud/Services/API/GroupLeave.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class GroupLeave_Client
|
||||||
|
{
|
||||||
|
public static void HandleDispatch(Objects.Session session)
|
||||||
|
{
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
var request = new GroupLeave.Request
|
||||||
|
{
|
||||||
|
Token = ApiService.Token,
|
||||||
|
Version = Constants.Version,
|
||||||
|
Session = session
|
||||||
|
};
|
||||||
|
api.Invoke(nameof(GroupLeave), request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleResponse(GroupLeave.Response response)
|
||||||
|
{
|
||||||
|
if (response.Success && response.Session != null)
|
||||||
|
{
|
||||||
|
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
share.RemoveSession(response.Session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UiService.LastErrorMessage = "Failed to leave the party.";
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleBroadcast(GroupLeave.GroupLeaveBroadcast broadcast)
|
||||||
|
{
|
||||||
|
Log.Debug($"[GroupLeave] {broadcast.Session.OwnerCharacterId} left the party.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
QuestShare.Dalamud/Services/API/IAPIHandler.cs
Normal file
14
QuestShare.Dalamud/Services/API/IAPIHandler.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal interface IAPIHandler
|
||||||
|
{
|
||||||
|
string Method => GetType().Name;
|
||||||
|
void HandleDispatch();
|
||||||
|
void HandleDispatch(dynamic? data);
|
||||||
|
Task HandleResponse(IResponse response);
|
||||||
|
void InvokeHandler(IAPIHandler handler, IRequest request)
|
||||||
|
{
|
||||||
|
_ = ((ApiService)Plugin.GetService<ApiService>()).Invoke(Method, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
QuestShare.Dalamud/Services/API/Register.cs
Normal file
36
QuestShare.Dalamud/Services/API/Register.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class Register_Client
|
||||||
|
{
|
||||||
|
public static void HandleDispatch()
|
||||||
|
{
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
_ = api.Invoke(nameof(Register), new Common.API.Register.Request
|
||||||
|
{
|
||||||
|
Version = Constants.Version,
|
||||||
|
Token = ApiService.Token,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleDispatch(dynamic? data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleResponse(Register.Response response)
|
||||||
|
{
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
var host = (HostService)Plugin.GetService<HostService>();
|
||||||
|
host.Start(response.ShareCode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("Failed to register as host: {0}", response.Error);
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
QuestShare.Dalamud/Services/API/Update.cs
Normal file
43
QuestShare.Dalamud/Services/API/Update.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace QuestShare.Services.API
|
||||||
|
{
|
||||||
|
internal class Update_Client
|
||||||
|
{
|
||||||
|
public static void HandleDispatch(Objects.OwnedSession session, List<string> partyMembers, bool broadcast = true)
|
||||||
|
{
|
||||||
|
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
_ = api.Invoke(nameof(Update), new Update.Request
|
||||||
|
{
|
||||||
|
Token = ApiService.Token,
|
||||||
|
Version = Constants.Version,
|
||||||
|
Session = session,
|
||||||
|
PartyMembers = partyMembers,
|
||||||
|
IsQuestUpdate = broadcast
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task HandleResponse(Update.Response response)
|
||||||
|
{
|
||||||
|
if (response.Success)
|
||||||
|
{
|
||||||
|
Log.Debug("Successfully updated quest status.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Error("Failed to update quest status: {0}", response.Error);
|
||||||
|
UiService.LastErrorMessage = $"Failed to update quest status. {response.Error}";
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class UpdateBroadcast_Client
|
||||||
|
{
|
||||||
|
public static Task HandleResponse(Update.UpdateBroadcast response)
|
||||||
|
{
|
||||||
|
((ShareService)Plugin.GetService<ShareService>()).UpdateSession(response.Session);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
QuestShare.Dalamud/Services/ApiService.cs
Normal file
162
QuestShare.Dalamud/Services/ApiService.cs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using QuestShare.Services.API;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace QuestShare.Services
|
||||||
|
{
|
||||||
|
|
||||||
|
internal class ApiService : IService
|
||||||
|
{
|
||||||
|
private readonly string socketUrl = ConfigurationManager.Instance.ApiUrl;
|
||||||
|
private HubConnection ApiConnection { get; set; } = null!;
|
||||||
|
internal bool IsConnected => ApiConnection.State == HubConnectionState.Connected;
|
||||||
|
internal HubConnectionState ConnectionState => ApiConnection.State;
|
||||||
|
internal bool IsAuthorized { get; private set; } = false;
|
||||||
|
private bool isDisposing = false;
|
||||||
|
internal static string Token => ConfigurationManager.Instance.Token;
|
||||||
|
private readonly List<IAPIHandler> apiHandlers = [];
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var builder = new HubConnectionBuilder().WithUrl(socketUrl).ConfigureLogging(logging =>
|
||||||
|
{
|
||||||
|
logging.SetMinimumLevel(LogLevel.Information).AddConsole();
|
||||||
|
});
|
||||||
|
ApiConnection = builder.Build();
|
||||||
|
ApiConnection.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 ApiConnection.StartAsync();
|
||||||
|
};
|
||||||
|
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
ApiConnection.Reconnected += async (error) =>
|
||||||
|
{
|
||||||
|
Log.Information("Connection reconnected");
|
||||||
|
};
|
||||||
|
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||||
|
|
||||||
|
ApiConnection.On<AuthRequest.Response>(nameof(AuthRequest), AuthRequest_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Authorize.Response>(nameof(Authorize), Authorize_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Register.Response>(nameof(Register), Register_Client.HandleResponse);
|
||||||
|
ApiConnection.On<GroupJoin.Response>(nameof(GroupJoin), GroupJoin_Client.HandleResponse);
|
||||||
|
ApiConnection.On<GroupLeave.Response>(nameof(GroupLeave), GroupLeave_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Cancel.Response>(nameof(Cancel), Cancel_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Update.Response>(nameof(Update), Update_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Update.UpdateBroadcast>(nameof(Update.UpdateBroadcast), UpdateBroadcast_Client.HandleResponse);
|
||||||
|
ApiConnection.On<Cancel.CancelBroadcast>(nameof(Cancel.CancelBroadcast), Cancel_Client.HandleBroadcast);
|
||||||
|
ApiConnection.On<GroupJoin.GroupJoinBroadcast>(nameof(GroupJoin.GroupJoinBroadcast), GroupJoin_Client.HandleBroadcast);
|
||||||
|
ApiConnection.On<GroupLeave.GroupLeaveBroadcast>(nameof(GroupLeave.GroupLeaveBroadcast), GroupLeave_Client.HandleBroadcast);
|
||||||
|
ApiConnection.On<SessionStart.Response>(nameof(SessionStart), SessionStart_Client.HandleResponse);
|
||||||
|
|
||||||
|
ClientState.Login += OnLogin;
|
||||||
|
ClientState.Logout += OnLogout;
|
||||||
|
Framework.Update += SavePersistedConfig;
|
||||||
|
}
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
isDisposing = true;
|
||||||
|
if (IsConnected)
|
||||||
|
{
|
||||||
|
ApiConnection.StopAsync();
|
||||||
|
}
|
||||||
|
ApiConnection.DisposeAsync();
|
||||||
|
ClientState.Login -= OnLogin;
|
||||||
|
ClientState.Logout -= OnLogout;
|
||||||
|
Framework.Update -= SavePersistedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLogin()
|
||||||
|
{
|
||||||
|
if (Token != "" && ConfigurationManager.Instance.ConnectOnStartup)
|
||||||
|
{
|
||||||
|
Task.Run(Connect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLogout(int code, int type)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
IsAuthorized = false;
|
||||||
|
ApiConnection.StopAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SavePersistedConfig(IFramework _)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.Token = Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Connect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (IsConnected) await ApiConnection.StopAsync();
|
||||||
|
isDisposing = false;
|
||||||
|
await ApiConnection.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 ApiConnection.StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal 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 ApiConnection.InvokeAsync(methodName, request).ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (t.IsFaulted)
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to invoke {methodName}: {t.Exception}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DispatchAuthorize() => Authorize_Client.HandleDispatch();
|
||||||
|
public static void DispatchRegister() => Register_Client.HandleDispatch();
|
||||||
|
public static void DispatchGroup(Objects.ShareCode shareCode) => GroupJoin_Client.HandleDispatch(shareCode);
|
||||||
|
public static void DispatchUngroup(Objects.Session session) => GroupLeave_Client.HandleDispatch(session);
|
||||||
|
public static void DispatchCancel() => Cancel_Client.HandleDispatch();
|
||||||
|
public static void DispatchUpdate(Objects.OwnedSession session, List<string> partyMembers) => Update_Client.HandleDispatch(session, partyMembers, true);
|
||||||
|
public static void DispatchConfigChange(Objects.OwnedSession session, List<string> partyMembers) => Update_Client.HandleDispatch(session, partyMembers, false);
|
||||||
|
public static void DispatchSessionStart(Objects.OwnedSession session) => SessionStart_Client.HandleDispatch(session);
|
||||||
|
|
||||||
|
public event EventHandler<GroupJoinEventArgs>? GroupJoined;
|
||||||
|
|
||||||
|
internal void OnGroupJoin(GroupJoinEventArgs e)
|
||||||
|
{
|
||||||
|
GroupJoined?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GroupJoinEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public bool IsSuccess { get; set; }
|
||||||
|
public Objects.Session? Session { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +1,26 @@
|
|||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
|
|
||||||
namespace QuestShare.Common
|
namespace QuestShare.Services
|
||||||
{
|
{
|
||||||
internal static class Commands
|
internal class CommandService : IService
|
||||||
{
|
{
|
||||||
public static void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
CommandManager.AddHandler("/questshare", new CommandInfo(OnCommand)
|
CommandManager.AddHandler("/questshare", new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Open the Quest Share window."
|
HelpMessage = "Open the Quest Share window."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public static void Dispose()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
CommandManager.RemoveHandler("/questshare");
|
CommandManager.RemoveHandler("/questshare");
|
||||||
}
|
}
|
||||||
private static void OnCommand(string command, string args)
|
private static void OnCommand(string command, string args)
|
||||||
{
|
{
|
||||||
Log.Information("Command received: {command} {args}");
|
Log.Information($"Command received: {command} {args}");
|
||||||
if (command == "/questshare")
|
if (command == "/questshare")
|
||||||
{
|
{
|
||||||
WindowManager.ToggleMainUI();
|
UiService.ToggleMainUI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
QuestShare.Dalamud/Services/IService.cs
Normal file
8
QuestShare.Dalamud/Services/IService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace QuestShare.Services
|
||||||
|
{
|
||||||
|
internal interface IService
|
||||||
|
{
|
||||||
|
void Initialize();
|
||||||
|
void Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
QuestShare.Dalamud/Services/PartyService.cs
Normal file
66
QuestShare.Dalamud/Services/PartyService.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
namespace QuestShare.Services
|
||||||
|
{
|
||||||
|
internal class PartyService : IService
|
||||||
|
{
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
Framework.Update += OnFramework;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
Framework.Update -= OnFramework;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long PartyId { get; private set; }
|
||||||
|
private List<long> PartyMembers { get; set; } = [];
|
||||||
|
|
||||||
|
private void OnFramework(IFramework framework)
|
||||||
|
{
|
||||||
|
if (PartyList.Length > 0 && PartyId == 0)
|
||||||
|
{
|
||||||
|
PartyId = PartyList.PartyId;
|
||||||
|
Log.Debug($"Joined party {PartyId}");
|
||||||
|
PartyMembers.Clear();
|
||||||
|
foreach (var member in PartyList)
|
||||||
|
{
|
||||||
|
Log.Debug($"Party member {member.Name.TextValue} - {member.ContentId}");
|
||||||
|
PartyMembers.Add(member.ContentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PartyList.Length == 0 && PartyId != 0)
|
||||||
|
{
|
||||||
|
PartyId = 0;
|
||||||
|
Log.Debug($"Left party");
|
||||||
|
PartyMembers.Clear();
|
||||||
|
}
|
||||||
|
else if (PartyList.Length != PartyMembers.Count)
|
||||||
|
{
|
||||||
|
var newMembers = PartyList.Where(x => !PartyMembers.Contains(x.ContentId)).ToList();
|
||||||
|
var leftMembers = PartyMembers.Where(x => !PartyList.Any(y => y.ContentId == x)).ToList();
|
||||||
|
foreach (var member in newMembers)
|
||||||
|
{
|
||||||
|
Log.Debug($"Party member {member.Name.TextValue} - {member.ContentId}");
|
||||||
|
PartyMembers.Add(member.ContentId);
|
||||||
|
}
|
||||||
|
foreach (var member in leftMembers)
|
||||||
|
{
|
||||||
|
Log.Debug($"Party member left {member}");
|
||||||
|
PartyMembers.Remove(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetPartyMembers(Objects.Session session)
|
||||||
|
{
|
||||||
|
var members = new List<string>();
|
||||||
|
foreach (var member in PartyList)
|
||||||
|
{
|
||||||
|
members.Add(member.ContentId.ToString().SaltedHash(session.ShareCode));
|
||||||
|
}
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,133 +1,130 @@
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using Dalamud.Plugin.Services;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using QuestShare.Common.API;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace QuestShare.Services
|
namespace QuestShare.Services
|
||||||
{
|
{
|
||||||
internal class ShareService
|
internal class ShareService : IService
|
||||||
{
|
{
|
||||||
internal static uint ActiveQuestId { get; set; } = 0;
|
internal static List<Objects.ShareCode> ShareCodes => ConfigurationManager.Instance.KnownShareCodes;
|
||||||
internal static byte ActiveQuestStep { get; set; } = 0;
|
internal List<Objects.Session> Sessions { get; private set; } = [];
|
||||||
internal static List<SharedMember> SharedMembers = [];
|
internal Dictionary<long, string> CharacterLookup { get; private set; } = [];
|
||||||
internal static List<ulong> PartyMembers = [];
|
|
||||||
internal static bool IsGrouped { get; set; } = false;
|
|
||||||
internal static bool IsHost { get; set; } = false;
|
|
||||||
internal static bool IsRegistered { get; set; } = false;
|
|
||||||
internal static bool IsMsqTracking { get; set; } = false;
|
|
||||||
internal static string ShareCode { get; private set; } = "";
|
|
||||||
internal static string HostedShareCode { get; private set; } = "";
|
|
||||||
internal static string Token { get; set; } = "";
|
|
||||||
|
|
||||||
public static void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
Addons.AddonPartyList.OnMemberCountChanged += OnPartyChanged;
|
|
||||||
ClientState.Login += OnLogin;
|
ClientState.Login += OnLogin;
|
||||||
ClientState.Logout += OnLogout;
|
ClientState.Logout += OnLogout;
|
||||||
|
Framework.Update += OnFrameworkUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Dispose()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
OnLogout(0,0);
|
OnLogout(0, 0);
|
||||||
Addons.AddonPartyList.OnMemberCountChanged -= OnPartyChanged;
|
|
||||||
ClientState.Login -= OnLogin;
|
ClientState.Login -= OnLogin;
|
||||||
ClientState.Logout -= OnLogout;
|
ClientState.Logout -= OnLogout;
|
||||||
|
Framework.Update -= OnFrameworkUpdate;
|
||||||
|
CharacterLookup.Clear();
|
||||||
|
Sessions.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnLogin()
|
private void OnLogin()
|
||||||
{
|
{
|
||||||
ShareCode = Plugin.Configuration.Instance.LastShareCode;
|
CharacterLookup.Clear();
|
||||||
Token = Plugin.Configuration.Instance.Token;
|
CharacterLookup.Add((long)ClientState.LocalContentId, ClientState.LocalPlayer!.Name.TextValue);
|
||||||
|
foreach (var character in ConfigurationManager.Instance.KnownCharacters)
|
||||||
|
{
|
||||||
|
CharacterLookup.Add(character.Key, character.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnLogout(int code, int state)
|
private void OnLogout(int code, int state)
|
||||||
{
|
{
|
||||||
Plugin.Configuration.Instance.Token = Token;
|
ConfigurationManager.Save();
|
||||||
Plugin.Configuration.Instance.LastShareCode = ShareCode;
|
CharacterLookup.Clear();
|
||||||
Plugin.Configuration.Save();
|
Sessions.Clear();
|
||||||
SharedMembers = [];
|
|
||||||
PartyMembers = [];
|
|
||||||
IsGrouped = false;
|
|
||||||
IsHost = false;
|
|
||||||
IsRegistered = false;
|
|
||||||
ShareCode = "";
|
|
||||||
HostedShareCode = "";
|
|
||||||
Token = "";
|
|
||||||
ActiveQuestId = 0;
|
|
||||||
ActiveQuestStep = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetShareCode(string shareCode)
|
private int pCount = 0;
|
||||||
{
|
|
||||||
ShareCode = shareCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetHostedShareCode(string shareCode)
|
private void OnFrameworkUpdate(IFramework framework)
|
||||||
{
|
{
|
||||||
HostedShareCode = shareCode;
|
if (PartyList.Count != pCount)
|
||||||
|
{
|
||||||
|
foreach (var partyMember in PartyList)
|
||||||
|
{
|
||||||
|
AddKnownCharacter(partyMember.ContentId, partyMember.Name.TextValue);
|
||||||
|
}
|
||||||
|
pCount = PartyList.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetActiveQuest(uint questId, byte questStep)
|
public static void SetActiveQuest(uint questId, byte questStep)
|
||||||
{
|
{
|
||||||
Log.Debug($"Setting active quest to {questId} - {questStep}");
|
Log.Debug($"Setting active quest to {questId} - {questStep}");
|
||||||
ActiveQuestId = questId;
|
HostService.Update((int)questId, questStep);
|
||||||
ActiveQuestStep = questStep;
|
|
||||||
}
|
|
||||||
private static void OnPartyChanged(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
var members = new List<ulong>();
|
|
||||||
foreach (var member in PartyList)
|
|
||||||
{
|
|
||||||
members.Add((ulong)member.ContentId);
|
|
||||||
}
|
|
||||||
PartyMembers = members;
|
|
||||||
SocketClientService.DispatchUpdate(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SharedMember
|
public void AddSession(Objects.Session session)
|
||||||
{
|
{
|
||||||
public required ulong CharacterId { get; set; }
|
if (Sessions.Any(s => s.ShareCode == session.ShareCode))
|
||||||
public string CharacterName => ResolveCharacterName(CharacterId);
|
{
|
||||||
public bool IsHost { get; set; } = false;
|
return;
|
||||||
public bool IsMe { get; set; } = false;
|
}
|
||||||
|
Sessions.Add(session);
|
||||||
|
AddKnownShareCode(new Objects.ShareCode() { Code = session.ShareCode, CharacterId = ClientState.LocalContentId.ToString().SaltedHash(session.ShareCode) });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ResolveCharacterName(ulong characterId)
|
public void RemoveSession(Objects.Session session)
|
||||||
{
|
{
|
||||||
var member = SharedMembers.FirstOrDefault(m => m.CharacterId == characterId);
|
Sessions.Remove(session);
|
||||||
if (member != null && member.CharacterName == "")
|
RemoveKnownShareCode(session.ShareCode);
|
||||||
{
|
|
||||||
if (File.Exists(Path.Join(Plugin.PluginDataPath, "CharacterNames.json")))
|
|
||||||
{
|
|
||||||
var characterNames = JsonConvert.DeserializeObject<Dictionary<ulong, string>>(File.ReadAllText(Path.Join(Plugin.PluginDataPath, "CharacterNames.json")));
|
|
||||||
if (characterNames != null && characterNames.ContainsKey(member.CharacterId))
|
|
||||||
{
|
|
||||||
return characterNames[member.CharacterId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return member.CharacterName;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SaveCharacterName(ulong characterId, string characterName)
|
public void RemoveSession(string shareCode)
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Join(Plugin.PluginDataPath, "CharacterNames.json")))
|
var session = Sessions.FirstOrDefault(s => s.ShareCode == shareCode);
|
||||||
|
if (session != null)
|
||||||
{
|
{
|
||||||
var characterNames = JsonConvert.DeserializeObject<Dictionary<ulong, string>>(File.ReadAllText(Path.Join(Plugin.PluginDataPath, "CharacterNames.json")));
|
RemoveSession(session);
|
||||||
characterNames ??= [];
|
|
||||||
characterNames[characterId] = characterName;
|
|
||||||
File.WriteAllText(Path.Join(Plugin.PluginDataPath, "CharacterNames.json"), JsonConvert.SerializeObject(characterNames));
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
public void UpdateSession(Objects.Session session)
|
||||||
|
{
|
||||||
|
var existing = Sessions.FirstOrDefault(s => s.ShareCode == session.ShareCode);
|
||||||
|
if (existing != null)
|
||||||
{
|
{
|
||||||
File.WriteAllText(Path.Join(Plugin.PluginDataPath, "CharacterNames.json"), JsonConvert.SerializeObject(new Dictionary<ulong, string> { { characterId, characterName } }));
|
Sessions.Remove(existing);
|
||||||
}
|
}
|
||||||
|
Sessions.Add(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddKnownShareCode(Objects.ShareCode shareCode)
|
||||||
|
{
|
||||||
|
if (ConfigurationManager.Instance.KnownShareCodes.Any(sc => sc.Code == shareCode.Code))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConfigurationManager.Instance.KnownShareCodes.Add(shareCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveKnownShareCode(string shareCode)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.KnownShareCodes.RemoveAll(sc => sc.Code == shareCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddKnownCharacter(long contentId, string characterId)
|
||||||
|
{
|
||||||
|
if (ConfigurationManager.Instance.KnownCharacters.ContainsKey(contentId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ConfigurationManager.Instance.KnownCharacters.Add(contentId, characterId);
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveKnownCharacter(long contentId)
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.KnownCharacters.Remove(contentId);
|
||||||
|
ConfigurationManager.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,475 +0,0 @@
|
|||||||
|
|
||||||
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 = "https://api.nathanc.tech/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 static 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)
|
|
||||||
{
|
|
||||||
Log.Error("Not registered or grouped");
|
|
||||||
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;
|
|
||||||
ShareService.IsRegistered = true;
|
|
||||||
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;
|
|
||||||
Plugin.Configuration.Instance.LastShareCode = "";
|
|
||||||
ShareService.SetShareCode("");
|
|
||||||
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.IsRegistered = true;
|
|
||||||
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);
|
|
||||||
ShareService.SetShareCode(response.ShareCode);
|
|
||||||
}
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +1,16 @@
|
|||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using QuestShare.Windows.ConfigWindow;
|
|
||||||
using QuestShare.Windows.MainWindow;
|
using QuestShare.Windows.MainWindow;
|
||||||
|
|
||||||
namespace QuestShare.Common
|
namespace QuestShare.Services
|
||||||
{
|
{
|
||||||
internal static class WindowManager
|
internal class UiService : IService
|
||||||
{
|
{
|
||||||
public static WindowSystem WindowSystem = new("SamplePlugin");
|
public static WindowSystem WindowSystem = new("QuestShare");
|
||||||
public static ConfigWindow ConfigWindow { get; private set; } = new();
|
|
||||||
public static MainWindow MainWindow { get; private set; } = new();
|
public static MainWindow MainWindow { get; private set; } = new();
|
||||||
|
public static string LastErrorMessage { get; set; } = string.Empty;
|
||||||
|
|
||||||
public static void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
WindowSystem.AddWindow(ConfigWindow);
|
|
||||||
WindowSystem.AddWindow(MainWindow);
|
WindowSystem.AddWindow(MainWindow);
|
||||||
|
|
||||||
PluginInterface.UiBuilder.Draw += DrawUI;
|
PluginInterface.UiBuilder.Draw += DrawUI;
|
||||||
@ -24,18 +22,17 @@ namespace QuestShare.Common
|
|||||||
// Adds another button that is doing the same but for the main ui of the plugin
|
// Adds another button that is doing the same but for the main ui of the plugin
|
||||||
PluginInterface.UiBuilder.OpenMainUi += ToggleMainUI;
|
PluginInterface.UiBuilder.OpenMainUi += ToggleMainUI;
|
||||||
}
|
}
|
||||||
public static void Dispose()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
WindowSystem.RemoveAllWindows();
|
WindowSystem.RemoveAllWindows();
|
||||||
PluginInterface.UiBuilder.Draw -= DrawUI;
|
PluginInterface.UiBuilder.Draw -= DrawUI;
|
||||||
PluginInterface.UiBuilder.OpenConfigUi -= ToggleConfigUI;
|
PluginInterface.UiBuilder.OpenConfigUi -= ToggleConfigUI;
|
||||||
PluginInterface.UiBuilder.OpenMainUi -= ToggleMainUI;
|
PluginInterface.UiBuilder.OpenMainUi -= ToggleMainUI;
|
||||||
MainWindow.Dispose();
|
MainWindow.Dispose();
|
||||||
ConfigWindow.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawUI() => WindowSystem.Draw();
|
private static void DrawUI() => WindowSystem.Draw();
|
||||||
public static void ToggleConfigUI() => ConfigWindow.Toggle();
|
public static void ToggleConfigUI() => MainWindow.Toggle();
|
||||||
public static void ToggleMainUI() => MainWindow.Toggle();
|
public static void ToggleMainUI() => MainWindow.Toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,54 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using ImGuiNET;
|
|
||||||
using QuestShare;
|
|
||||||
using QuestShare.Common;
|
|
||||||
|
|
||||||
namespace QuestShare.Windows.ConfigWindow;
|
|
||||||
|
|
||||||
public class ConfigWindow : Window, IDisposable
|
|
||||||
{
|
|
||||||
private ConfigurationManager Configuration { get; set; }
|
|
||||||
|
|
||||||
// We give this window a constant ID using ###
|
|
||||||
// This allows for labels being dynamic, like "{FPS Counter}fps###XYZ counter window",
|
|
||||||
// and the window ID will always be "###XYZ counter window" for ImGui
|
|
||||||
public ConfigWindow() : base(Plugin.Name + " Config###ConfigWindow")
|
|
||||||
{
|
|
||||||
Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar |
|
|
||||||
ImGuiWindowFlags.NoScrollWithMouse;
|
|
||||||
|
|
||||||
Size = new Vector2(232, 90);
|
|
||||||
SizeCondition = ImGuiCond.Always;
|
|
||||||
|
|
||||||
Configuration = Plugin.Configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() { }
|
|
||||||
|
|
||||||
public override void PreDraw()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
var connectOnStartup = Configuration.Instance.ConnectOnStartup;
|
|
||||||
var resumeOnStartup = Configuration.Instance.ResumeOnStartup;
|
|
||||||
var autoShareMsq = Configuration.Instance.AutoShareMsq;
|
|
||||||
var autoShareNewQuests = Configuration.Instance.AutoShareNewQuests;
|
|
||||||
var BroadcastToParty = Configuration.Instance.BroadcastToParty;
|
|
||||||
|
|
||||||
ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.5f);
|
|
||||||
ImGui.Checkbox("Connect on startup", ref connectOnStartup);
|
|
||||||
ImGui.Checkbox("Resume on startup", ref resumeOnStartup);
|
|
||||||
ImGui.Checkbox("Auto share MSQ", ref autoShareMsq);
|
|
||||||
ImGui.Checkbox("Auto share new quests", ref autoShareNewQuests);
|
|
||||||
ImGui.Checkbox("Require party", ref BroadcastToParty);
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.SetTooltip("Require joiners to be in your party. You do not have to be partied after others join your group.");
|
|
||||||
}
|
|
||||||
ImGui.PopItemWidth();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using System;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace QuestShare.Windows
|
namespace QuestShare.Windows
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Utility;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using QuestShare.Common;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
using QuestShare.Services;
|
using QuestShare.Services;
|
||||||
|
using static QuestShare.Services.ApiService;
|
||||||
|
|
||||||
namespace QuestShare.Windows.MainWindow;
|
namespace QuestShare.Windows.MainWindow;
|
||||||
|
|
||||||
@ -29,257 +29,383 @@ public class MainWindow : Window, IDisposable
|
|||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
private ApiService ApiService => (ApiService)Plugin.GetService<ApiService>();
|
||||||
|
private ShareService ShareService => (ShareService)Plugin.GetService<ShareService>();
|
||||||
|
private PartyService PartyService => (PartyService)Plugin.GetService<PartyService>();
|
||||||
|
private HostService HostService => (HostService)Plugin.GetService<HostService>();
|
||||||
|
|
||||||
|
|
||||||
private string enteredShareCode = Plugin.Configuration.Instance.LastShareCode;
|
|
||||||
private bool unsavedChanges = false;
|
|
||||||
private GameQuest? selectedQuest = GameQuestManager.GetActiveQuest();
|
private GameQuest? selectedQuest = GameQuestManager.GetActiveQuest();
|
||||||
private ShareMode shareMode = Plugin.Configuration.Instance.LastShareMode;
|
|
||||||
private bool isConnecting = false;
|
private enum ActiveTab
|
||||||
|
{
|
||||||
|
Host,
|
||||||
|
Join,
|
||||||
|
Settings
|
||||||
|
}
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
var connected = SocketClientService.IsConnected;
|
|
||||||
var token = ShareService.Token;
|
|
||||||
|
|
||||||
ImGui.TextUnformatted("Server Status: "); ImGui.SameLine();
|
ImGui.TextUnformatted("Server Status: "); ImGui.SameLine();
|
||||||
if (isConnecting)
|
DrawConnectionState();
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ApiService.IsConnected)
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudYellow, "Connecting...");
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Unlink, ImGuiColors.DPSRed))
|
||||||
|
{
|
||||||
|
_ = ApiService.Disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui.TextColored(connected ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed, connected ? "Connected" : "Disconnected");
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Link, ImGuiColors.DPSRed))
|
||||||
}
|
{
|
||||||
ImGui.SameLine();
|
_ = ApiService.Connect();
|
||||||
ImGui.TextUnformatted($"Share Mode: {shareMode.ToString()}");
|
}
|
||||||
ImGui.Separator();
|
|
||||||
if (ImGui.Button("Connect"))
|
|
||||||
{
|
|
||||||
Plugin.SocketClient.Connect().ConfigureAwait(false);
|
|
||||||
isConnecting = true;
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Disconnect"))
|
|
||||||
{
|
|
||||||
SocketClientService.Disconnect().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
// ImGui.SameLine();
|
// ImGui.SameLine();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (!SocketClientService.IsConnected)
|
using (ImRaii.TabBar("MainTabBar", ImGuiTabBarFlags.NoCloseWithMiddleMouseButton))
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DPSRed, "Not connected to server.");
|
ImGui.BeginDisabled(!ApiService.IsConnected);
|
||||||
return;
|
if (ImGui.BeginTabItem("Host Group"))
|
||||||
}
|
|
||||||
isConnecting = false;
|
|
||||||
ImGui.TextUnformatted("Share Mode:");
|
|
||||||
// ImGui.BeginDisabled(ShareService.IsHost || ShareService.IsGrouped || ShareService.IsRegistered);
|
|
||||||
/*if (ShareService.IsHost && SocketClientService.IsConnected)
|
|
||||||
{
|
|
||||||
shareMode = ShareMode.Host;
|
|
||||||
} else if (ShareService.IsGrouped && SocketClientService.IsConnected)
|
|
||||||
{
|
|
||||||
shareMode = ShareMode.Member;
|
|
||||||
} else if (SocketClientService.IsConnected)
|
|
||||||
{
|
|
||||||
shareMode = ShareMode.None;
|
|
||||||
} */ // TODO: Fix this
|
|
||||||
if (ImGui.RadioButton("Host", shareMode == ShareMode.Host))
|
|
||||||
{
|
|
||||||
shareMode = ShareMode.Host;
|
|
||||||
Plugin.Configuration.Instance.LastShareMode = ShareMode.Host;
|
|
||||||
Plugin.Configuration.Save();
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.RadioButton("Receive", shareMode == ShareMode.Member))
|
|
||||||
{
|
|
||||||
shareMode = ShareMode.Member;
|
|
||||||
Plugin.Configuration.Instance.LastShareMode = ShareMode.Member;
|
|
||||||
Plugin.Configuration.Save();
|
|
||||||
}
|
|
||||||
// ImGui.EndDisabled();
|
|
||||||
ImGui.Separator();
|
|
||||||
if (shareMode == ShareMode.Host)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("Registered: "); ImGui.SameLine(); ImGui.TextColored(ShareService.IsHost ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed, ShareService.IsHost.ToString());
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (!ShareService.IsHost)
|
|
||||||
{
|
{
|
||||||
ImGui.BeginDisabled(!connected);
|
DrawHostTab();
|
||||||
if (ImGui.Button("Register"))
|
ImGui.EndTabItem();
|
||||||
{
|
|
||||||
SocketClientService.DispatchRegister();
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
}
|
||||||
else
|
if (ImGui.BeginTabItem("Join Group"))
|
||||||
{
|
{
|
||||||
ImGui.BeginDisabled(!connected);
|
DrawJoinTab();
|
||||||
if (ImGui.Button("Cancel"))
|
ImGui.EndTabItem();
|
||||||
{
|
|
||||||
SocketClientService.DispatchCancel();
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(" ");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted($"Share Code:");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
|
||||||
ImGui.TextUnformatted(ShareService.HostedShareCode);
|
|
||||||
if (ImGui.IsItemClicked())
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText(ShareService.HostedShareCode);
|
|
||||||
}
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.SetTooltip("Click to copy to clipboard");
|
|
||||||
}
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.BeginDisabled(ShareService.IsMsqTracking);
|
|
||||||
using (var combo = ImRaii.Combo("##Quests", GameQuestManager.GetActiveQuest()?.QuestName ?? "---SELECT---", ImGuiComboFlags.HeightRegular))
|
|
||||||
{
|
|
||||||
if (combo)
|
|
||||||
{
|
|
||||||
foreach (var quest in GameQuestManager.GameQuests.OrderBy(q => q.QuestName))
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(quest.QuestName))
|
|
||||||
{
|
|
||||||
selectedQuest = quest;
|
|
||||||
GameQuestManager.SetActiveFlag(quest.QuestId);
|
|
||||||
SocketClientService.DispatchUpdate(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Refresh"))
|
|
||||||
{
|
|
||||||
GameQuestManager.LoadQuests();
|
|
||||||
}
|
}
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
ImGui.SameLine();
|
if (ImGui.BeginTabItem("Settings"))
|
||||||
var track = Plugin.Configuration.Instance.TrackMSQ;
|
|
||||||
if (ImGui.Checkbox("Track MSQ", ref track))
|
|
||||||
{
|
{
|
||||||
Plugin.Configuration.Instance.TrackMSQ = track;
|
DrawSettingsTab();
|
||||||
ShareService.IsMsqTracking = track;
|
ImGui.EndTabItem();
|
||||||
Plugin.Configuration.Save();
|
|
||||||
}
|
|
||||||
if (selectedQuest == null && GameQuestManager.GetActiveQuest() == null)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("No quest selected.");
|
|
||||||
return;
|
|
||||||
} else if (GameQuestManager.GetActiveQuest() != null)
|
|
||||||
{
|
|
||||||
selectedQuest = GameQuestManager.GetActiveQuest();
|
|
||||||
}
|
|
||||||
ImGui.TextUnformatted("Active Quest:");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(selectedQuest!.QuestName);
|
|
||||||
ImGui.TextUnformatted("Current Step:");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(selectedQuest.CurrentStep.ToString());
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.TextUnformatted("Quest Steps:");
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
var steps = selectedQuest.QuestSteps;
|
|
||||||
for (var i = 0; i < steps!.Count; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (i + 1 == selectedQuest.CurrentStep || (selectedQuest.CurrentStep == 0 && i == 0) || (selectedQuest.CurrentStep == 0xFF && i + 1 == steps.Count))
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]);
|
|
||||||
}
|
|
||||||
else if (i+1 < selectedQuest.CurrentStep)
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudYellow, steps[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("???");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (shareMode == ShareMode.Member)
|
ImGui.Separator();
|
||||||
|
if (UiService.LastErrorMessage != null)
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(100);
|
// ImGui.TextColored(ImGuiColors.DPSRed, UiService.LastErrorMessage);
|
||||||
ImGui.InputTextWithHint("##ShareCode", "Share Code", ref enteredShareCode, 10);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawConnectionState()
|
||||||
|
{
|
||||||
|
switch (this.ApiService.ConnectionState)
|
||||||
|
{
|
||||||
|
case HubConnectionState.Connecting:
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudYellow, "Connecting...");
|
||||||
|
break;
|
||||||
|
case HubConnectionState.Connected:
|
||||||
|
ImGui.TextColored(ImGuiColors.HealerGreen, "Connected");
|
||||||
|
break;
|
||||||
|
case HubConnectionState.Disconnected:
|
||||||
|
ImGui.TextColored(ImGuiColors.DPSRed, "Disconnected");
|
||||||
|
break;
|
||||||
|
case HubConnectionState.Reconnecting:
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudYellow, "Reconnecting...");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool generatePending = false;
|
||||||
|
|
||||||
|
private void DrawHostTab()
|
||||||
|
{
|
||||||
|
if (HostService.ActiveSession != null && HostService.ActiveSession.ShareCode != null) generatePending = false;
|
||||||
|
var isEnabled = ConfigurationManager.Instance.EnableHosting;
|
||||||
|
if (ImGuiComponents.ToggleButton(Namespace + "/Enable Hosting", ref isEnabled))
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.EnableHosting = isEnabled;
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted("Enable Hosting");
|
||||||
|
ImGui.Separator();
|
||||||
|
if (isEnabled)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("Share Code:");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (!ShareService.IsGrouped)
|
if (HostService.ActiveSession != null)
|
||||||
{
|
{
|
||||||
ImGui.BeginDisabled(enteredShareCode.Length < 1);
|
ImGui.TextColored(ImGuiColors.HealerGreen, HostService.ActiveSession.ShareCode);
|
||||||
if (ImGui.Button("Join Group"))
|
if (ImGui.IsItemClicked())
|
||||||
{
|
{
|
||||||
Plugin.Configuration.Instance.LastShareCode = enteredShareCode;
|
ImGui.SetClipboardText(HostService.ActiveSession.ShareCode);
|
||||||
SocketClientService.DispatchGroup(enteredShareCode);
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Click to copy to clipboard.");
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Cancel"))
|
||||||
|
{
|
||||||
|
DispatchCancel();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Cancel the current session. This will permanently remove the share code and all connected clients.");
|
||||||
|
}
|
||||||
|
var allowJoins = HostService.AllowJoins;
|
||||||
|
var skipPartyCheck = HostService.SkipPartyCheck;
|
||||||
|
var sendUpdates = HostService.IsActive;
|
||||||
|
if (ImGui.Checkbox("Allow new Joins", ref allowJoins))
|
||||||
|
{
|
||||||
|
HostService.SetAllowJoins(allowJoins);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Checkbox("Skip Party Check", ref skipPartyCheck))
|
||||||
|
{
|
||||||
|
HostService.SetSkipPartyCheck(skipPartyCheck);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Checkbox("Send Updates", ref sendUpdates))
|
||||||
|
{
|
||||||
|
HostService.SetIsActive(sendUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
var shareMsq = ConfigurationManager.Instance.TrackMSQ;
|
||||||
|
ImGui.BeginDisabled(shareMsq);
|
||||||
|
using (var combo = ImRaii.Combo("##Quests", GameQuestManager.GetActiveQuest()?.QuestName ?? "---SELECT---", ImGuiComboFlags.HeightRegular))
|
||||||
|
{
|
||||||
|
if (combo)
|
||||||
|
{
|
||||||
|
foreach (var quest in GameQuestManager.GameQuests.OrderBy(q => q.QuestName))
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(quest.QuestName))
|
||||||
|
{
|
||||||
|
selectedQuest = quest;
|
||||||
|
GameQuestManager.SetActiveFlag(quest.QuestId);
|
||||||
|
HostService.Update((int)quest.QuestId, quest.CurrentStep);
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Refresh"))
|
||||||
|
{
|
||||||
|
GameQuestManager.LoadQuests();
|
||||||
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
ImGui.SameLine();
|
||||||
|
var track = ConfigurationManager.Instance.TrackMSQ;
|
||||||
|
if (ImGui.Checkbox("Track MSQ", ref track))
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.TrackMSQ = track;
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
if (selectedQuest == null && GameQuestManager.GetActiveQuest() == null)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("No quest selected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (GameQuestManager.GetActiveQuest() != null)
|
||||||
|
{
|
||||||
|
selectedQuest = GameQuestManager.GetActiveQuest();
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted("Active Quest:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(selectedQuest!.QuestName);
|
||||||
|
ImGui.TextUnformatted("Current Step:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(selectedQuest.CurrentStep.ToString());
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextUnformatted("Quest Steps:");
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var steps = selectedQuest.QuestSteps;
|
||||||
|
for (var i = 0; i < steps!.Count; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (i + 1 == selectedQuest.CurrentStep || (selectedQuest.CurrentStep == 0 && i == 0) || (selectedQuest.CurrentStep == 0xFF && i + 1 == steps.Count))
|
||||||
|
{
|
||||||
|
ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]);
|
||||||
|
}
|
||||||
|
else if (i + 1 < selectedQuest.CurrentStep)
|
||||||
|
{
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudYellow, steps[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("???");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ImGui.BeginDisabled(generatePending);
|
||||||
|
if (ImGui.Button("Generate New"))
|
||||||
|
{
|
||||||
|
ApiService.DispatchRegister();
|
||||||
}
|
}
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ImGui.Button("Leave Group"))
|
|
||||||
{
|
|
||||||
SocketClientService.DispatchUngroup();
|
|
||||||
}
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.TextUnformatted("Host Active Quest:");
|
|
||||||
ImGui.SameLine();
|
|
||||||
var activeQuest = ShareService.ActiveQuestId;
|
|
||||||
var activeStep = ShareService.ActiveQuestStep;
|
|
||||||
if (activeQuest != 0)
|
|
||||||
{
|
|
||||||
var questInfo = GameQuestManager.GetQuestById(activeQuest);
|
|
||||||
ImGui.TextUnformatted(questInfo.QuestData.Name.ExtractText());
|
|
||||||
ImGui.TextUnformatted("Current Step:");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(activeStep.ToString());
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.TextUnformatted("Quest Steps:");
|
|
||||||
ImGui.Separator();
|
|
||||||
var steps = questInfo.QuestSteps;
|
|
||||||
for (var i = 0; i < steps.Count; i++)
|
|
||||||
{
|
|
||||||
if (i+1 == activeStep || (i+1 == steps.Count && activeStep == 0xFF))
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(steps[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ImGui.Button("Get Marker"))
|
|
||||||
{
|
|
||||||
var marker = questInfo.GetMapLink((byte)(activeStep - 1));
|
|
||||||
if (marker != null)
|
|
||||||
GameGui.OpenMapWithMapLink(marker);
|
|
||||||
else
|
|
||||||
Log.Error("No map link available for this quest.");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
}
|
||||||
ImGui.SetTooltip("Generates a map marker for the current step's destination.");
|
|
||||||
}
|
private string enteredShareCode = "";
|
||||||
ImGui.SameLine();
|
private bool isJoining = false;
|
||||||
/*if (ImGui.Button("Teleport"))
|
private bool isLeaving = false;
|
||||||
{
|
private void OnGroupJoin(object? sender, GroupJoinEventArgs args)
|
||||||
// attempt to generate a path to the next step
|
{
|
||||||
}
|
isJoining = false;
|
||||||
if (ImGui.IsItemHovered())
|
ApiService.GroupJoined -= OnGroupJoin;
|
||||||
{
|
}
|
||||||
ImGui.SetTooltip("Teleports to nearest aetheryte of quest destination.");
|
private void DrawJoinTab()
|
||||||
}*/
|
{
|
||||||
}
|
ImGui.TextUnformatted("Enter Share Code:");
|
||||||
else
|
ImGui.SameLine();
|
||||||
|
ImGui.BeginDisabled(isJoining);
|
||||||
|
ImGui.InputText("##ShareCode", ref enteredShareCode, 8);
|
||||||
|
ImGui.SameLine();
|
||||||
|
var btn = "Join";
|
||||||
|
if (isJoining) btn = "Joining...";
|
||||||
|
if (ImGui.Button(btn))
|
||||||
|
{
|
||||||
|
var payload = new Objects.ShareCode { CharacterId = ClientState.LocalContentId.ToString().SaltedHash(enteredShareCode), Code = enteredShareCode };
|
||||||
|
isJoining = true;
|
||||||
|
ApiService.GroupJoined += OnGroupJoin;
|
||||||
|
ApiService.DispatchGroup(payload);
|
||||||
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextUnformatted("Currently Joined Groups");
|
||||||
|
if (ShareService.Sessions.Count == 0)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("No groups joined.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var session in ShareService.Sessions)
|
||||||
|
{
|
||||||
|
using var tree = ImRaii.TreeNode($"Session: {session.ShareCode}");
|
||||||
|
if (tree)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted("No active quest.");
|
DrawSessionDetails(session);
|
||||||
|
if (ImGui.Button("Leave Group"))
|
||||||
|
{
|
||||||
|
ApiService.DispatchUngroup(session);
|
||||||
|
isLeaving = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawSessionDetails(Objects.Session session)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted($"Owner: {session.OwnerCharacterId}");
|
||||||
|
var activeQuest = session.ActiveQuestId;
|
||||||
|
var activeStep = session.ActiveQuestStep;
|
||||||
|
if (activeQuest != 0)
|
||||||
|
{
|
||||||
|
var questInfo = GameQuestManager.GetQuestById((uint)activeQuest);
|
||||||
|
var steps = questInfo.QuestSteps;
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(questInfo.QuestData.Name.ExtractText());
|
||||||
|
ImGui.TextUnformatted("Current Step:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(activeStep.ToString());
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextUnformatted("Quest Steps:");
|
||||||
|
ImGui.Separator();
|
||||||
|
for (var i = 0; i < steps.Count; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 == activeStep || (i + 1 == steps.Count && activeStep == 0xFF))
|
||||||
|
{
|
||||||
|
ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(steps[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui.Button("Get Marker"))
|
||||||
|
{
|
||||||
|
var marker = questInfo.GetMapLink((byte)(activeStep - 1));
|
||||||
|
if (marker != null)
|
||||||
|
GameGui.OpenMapWithMapLink(marker);
|
||||||
|
else
|
||||||
|
Log.Error("No map link available for this quest.");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Generates a map marker for the current step's destination.");
|
||||||
|
}
|
||||||
|
// ImGui.SameLine();
|
||||||
|
/*if (ImGui.Button("Teleport"))
|
||||||
|
{
|
||||||
|
// attempt to generate a path to the next step
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Teleports to nearest aetheryte of quest destination.");
|
||||||
|
}*/
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("No active quest or host is offline.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSettingsTab()
|
||||||
|
{
|
||||||
|
var selectedApiServer = ConfigurationManager.Instance.ApiDisplayName;
|
||||||
|
if (ImGui.BeginCombo("API Server", selectedApiServer))
|
||||||
|
{
|
||||||
|
foreach (var server in ConfigurationManager.Instance.ApiConfigurations)
|
||||||
|
{
|
||||||
|
var isSelected = selectedApiServer == server.DisplayName;
|
||||||
|
if (ImGui.Selectable(server.DisplayName, isSelected))
|
||||||
|
{
|
||||||
|
var index = Array.FindIndex(ConfigurationManager.Instance.ApiConfigurations, x => x.DisplayName == server.DisplayName);
|
||||||
|
ConfigurationManager.Instance.ApiConfigurations[index].Active = true;
|
||||||
|
foreach (var config in ConfigurationManager.Instance.ApiConfigurations)
|
||||||
|
{
|
||||||
|
if (config.DisplayName != server.DisplayName)
|
||||||
|
{
|
||||||
|
config.Active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
if (isSelected)
|
||||||
|
{
|
||||||
|
ImGui.SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.EndCombo();
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Add"))
|
||||||
|
{
|
||||||
|
// does nothing yet
|
||||||
|
}
|
||||||
|
var connectOnStartup = ConfigurationManager.Instance.ConnectOnStartup;
|
||||||
|
if (ImGui.Checkbox("Connect on Startup", ref connectOnStartup))
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.ConnectOnStartup = connectOnStartup;
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
var autoShareMsq = ConfigurationManager.Instance.AutoShareMsq;
|
||||||
|
if (ImGui.Checkbox("Auto Share MSQ", ref autoShareMsq))
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.AutoShareMsq = autoShareMsq;
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
var autoShareNewQuests = ConfigurationManager.Instance.AutoShareNewQuests;
|
||||||
|
if (ImGui.Checkbox("Auto Share New Quests", ref autoShareNewQuests))
|
||||||
|
{
|
||||||
|
ConfigurationManager.Instance.AutoShareNewQuests = autoShareNewQuests;
|
||||||
|
ConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,178 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Interface.Windowing;
|
|
||||||
using ImGuiNET;
|
|
||||||
using QuestShare.Common;
|
|
||||||
using QuestShare.Services;
|
|
||||||
|
|
||||||
namespace QuestShare.Windows.MainWindow;
|
|
||||||
|
|
||||||
public class MainWindow : Window, IDisposable
|
|
||||||
{
|
|
||||||
public MainWindow()
|
|
||||||
: base(Plugin.Name + "###Main", ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
|
|
||||||
{
|
|
||||||
SizeConstraints = new WindowSizeConstraints
|
|
||||||
{
|
|
||||||
MinimumSize = new Vector2(600, 650),
|
|
||||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnOpen()
|
|
||||||
{
|
|
||||||
var questId = 69286;
|
|
||||||
// find the quest by id
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() { }
|
|
||||||
|
|
||||||
private string enteredShareCode = Plugin.Configuration.LastShareCode;
|
|
||||||
private bool unsavedChanges = false;
|
|
||||||
private uint activeQuestId = 0;
|
|
||||||
private byte activeQuestStep = 0;
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted($"Token: {ShareService.Token}");
|
|
||||||
ImGui.TextUnformatted($"Share Code: {ShareService.ShareCode}");
|
|
||||||
ImGui.TextUnformatted($"Socket Status: {SocketClientService.IsConnected}");
|
|
||||||
ImGui.TextUnformatted($"Is Hosting: {ShareService.IsHost} - Is Grouped: {ShareService.IsGrouped}");
|
|
||||||
ImGui.Separator();
|
|
||||||
if (ImGui.Button("Connect"))
|
|
||||||
{
|
|
||||||
Plugin.SocketClient.Connect().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Disconnect"))
|
|
||||||
{
|
|
||||||
SocketClientService.connection.StopAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
if (ImGui.Button("Register"))
|
|
||||||
{
|
|
||||||
SocketClientService.DispatchRegister();
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.BeginDisabled(!ShareService.IsGrouped && !ShareService.IsHost);
|
|
||||||
if (ImGui.Button("Cancel"))
|
|
||||||
{
|
|
||||||
SocketClientService.DispatchCancel();
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.SetNextItemWidth(100);
|
|
||||||
ImGui.InputTextWithHint("##ShareCode", "Share Code", ref enteredShareCode, 10);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.BeginDisabled(enteredShareCode.Length < 1);
|
|
||||||
if (ImGui.Button("Pair"))
|
|
||||||
{
|
|
||||||
SocketClientService.DispatchGroup(enteredShareCode);
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.Separator();
|
|
||||||
if (ShareService.IsHost)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("Host Controls:");
|
|
||||||
ImGui.TextUnformatted("Share Code: ");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, ShareService.ShareCode);
|
|
||||||
ImGui.TextUnformatted("Quests:");
|
|
||||||
ImGui.BeginChild("##Quests", new Vector2(0, ImGui.GetTextLineHeightWithSpacing() * 10), true);
|
|
||||||
ImGui.BeginTable("##QuestTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.Resizable | ImGuiTableFlags.SizingFixedFit, new Vector2(0, -ImGui.GetTextLineHeightWithSpacing()));
|
|
||||||
ImGui.TableSetupColumn("Sync", ImGuiTableColumnFlags.WidthFixed, 50);
|
|
||||||
ImGui.TableSetupColumn("Active", ImGuiTableColumnFlags.WidthFixed, 50);
|
|
||||||
ImGui.TableSetupColumn("Quest", ImGuiTableColumnFlags.WidthStretch);
|
|
||||||
ImGui.TableSetupScrollFreeze(0, 1);
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
foreach (var quest in GameQuestManager.GameQuests)
|
|
||||||
{
|
|
||||||
ImGui.TableNextRow();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var isSynced = quest.IsSynced;
|
|
||||||
ImGui.BeginDisabled(quest.IsActive);
|
|
||||||
if (ImGui.Checkbox($"##{quest.QuestId}_Sync", ref isSynced))
|
|
||||||
{
|
|
||||||
GameQuestManager.SetSyncFlag(quest.QuestId, isSynced);
|
|
||||||
unsavedChanges = true;
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
var isActive = quest.IsActive;
|
|
||||||
ImGui.BeginDisabled(!isSynced);
|
|
||||||
if (ImGui.Checkbox($"##{quest.QuestId}_Active", ref isActive))
|
|
||||||
{
|
|
||||||
GameQuestManager.SetActiveFlag(quest.QuestId, isActive);
|
|
||||||
unsavedChanges = true;
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextColored(isActive ? ImGuiColors.HealerGreen : ImGuiColors.DalamudGrey, quest.QuestData.Name.ExtractText());
|
|
||||||
if (isActive)
|
|
||||||
{
|
|
||||||
activeQuestId = quest.QuestId;
|
|
||||||
activeQuestStep = quest.CurrentStep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.EndTable();
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.EndChild();
|
|
||||||
ImGui.BeginDisabled(!unsavedChanges);
|
|
||||||
if (ImGui.Button("Save"))
|
|
||||||
{
|
|
||||||
SocketClientService.DispatchUpdate(false);
|
|
||||||
unsavedChanges = false;
|
|
||||||
}
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("Click Register to host a share session, or enter a share code to join.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ShareService.IsGrouped || ShareService.IsHost)
|
|
||||||
{
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.TextUnformatted("Members:");
|
|
||||||
ImGui.BeginChild("##Members", new Vector2(0, ImGui.GetTextLineHeightWithSpacing() * 5), true);
|
|
||||||
foreach (var member in ShareService.SharedMembers)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(member.CharacterName);
|
|
||||||
}
|
|
||||||
ImGui.EndChild();
|
|
||||||
}
|
|
||||||
ImGui.TextUnformatted("Current Active Quest Steps:");
|
|
||||||
if (activeQuestId != 0)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted($"Quest: {GameQuestManager.GameQuests.FirstOrDefault(q => q.QuestId == activeQuestId)?.QuestData.Name.ExtractText()}");
|
|
||||||
ImGui.TextUnformatted($"Step: {activeQuestStep}");
|
|
||||||
ImGui.TextUnformatted("Steps:");
|
|
||||||
ImGui.BeginChild("##QuestSteps", new Vector2(0, ImGui.GetTextLineHeightWithSpacing()*5), true);
|
|
||||||
var steps = GameQuestManager.GameQuests.FirstOrDefault(q => q.QuestId == activeQuestId)?.QuestSteps;
|
|
||||||
var counter = 0;
|
|
||||||
if (steps != null)
|
|
||||||
{
|
|
||||||
foreach (var step in steps)
|
|
||||||
{
|
|
||||||
if (counter+1 == activeQuestStep || (activeQuestStep == 0xFF && steps.Count == counter+1))
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, step);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(step);
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("No steps found.");
|
|
||||||
}
|
|
||||||
ImGui.EndChild();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("No active quest.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using QuestShare.Common;
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
namespace QuestShare.Server.Hubs
|
||||||
@ -16,7 +18,7 @@ namespace QuestShare.Server.Hubs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.CharacterId == 0) error = Error.InvalidCharacterId;
|
Log.Debug($"[AUTHORIZE] Client {Context.ConnectionId} attempting to authorize. {JsonConvert.SerializeObject(request)}");
|
||||||
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
@ -29,54 +31,69 @@ namespace QuestShare.Server.Hubs
|
|||||||
Context.Abort();
|
Context.Abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId, request.Token);
|
var client = await ClientManager.GetClient(Context.ConnectionId, request.Token);
|
||||||
var clientCharacterId = ClientManager.GetClient(request.CharacterId);
|
if (client == null)
|
||||||
if (client == null && clientCharacterId == null)
|
|
||||||
{
|
{
|
||||||
// create new client
|
// create new client
|
||||||
var token = ClientManager.AddClient(Context.ConnectionId, request.CharacterId);
|
client = await ClientManager.AddClient(Context.ConnectionId);
|
||||||
Context.Items.Add("Token", token);
|
|
||||||
Log.Information($"[AUTHORIZE] Client {Context.ConnectionId} authorized with token {token}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
Token = token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (client == null && clientCharacterId != null)
|
|
||||||
{
|
|
||||||
error = Error.Unauthorized;
|
|
||||||
if (BanManager.CheckBadRequests(Context, nameof(Authorize)))
|
|
||||||
{
|
|
||||||
error = Error.BannedTooManyBadRequests;
|
|
||||||
}
|
|
||||||
Log.Warning($"[AUTHORIZE] MISMATCH Client {Context.ConnectionId} failed authorization with error {error}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = error,
|
|
||||||
});
|
|
||||||
Context.Abort();
|
|
||||||
}
|
|
||||||
else if (client != null && clientCharacterId != null)
|
|
||||||
{
|
|
||||||
Log.Information($"[AUTHORIZE] Client {Context.ConnectionId} reauthorized with token {client.Token}.");
|
|
||||||
Context.Items.Add("Token", client.Token);
|
Context.Items.Add("Token", client.Token);
|
||||||
ClientManager.ChangeClientConnectionId(clientCharacterId.ConnectionId, Context.ConnectionId);
|
Log.Information($"[AUTHORIZE] Client {Context.ConnectionId} authorized with token {client.Token}.");
|
||||||
await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
Token = client.Token,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response
|
Log.Information($"[AUTHORIZE] Client {Context.ConnectionId} reauthorized with token {client.Token}.");
|
||||||
{
|
Context.Items.Add("Token", client.Token);
|
||||||
Success = false,
|
await ClientManager.ChangeClientConnectionId(client.ConnectionId, Context.ConnectionId);
|
||||||
Error = Error.Unauthorized,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
var sessions = new List<Objects.Session>();
|
||||||
|
Objects.OwnedSession? ownedSession = null;
|
||||||
|
|
||||||
|
foreach(var share in request.ShareCodes)
|
||||||
|
{
|
||||||
|
var session = await SessionManager.GetSession(share.Code);
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
var members = await ClientManager.GetClientsInSession(session);
|
||||||
|
if (members.Any(m => m.Client.ClientId == client.ClientId))
|
||||||
|
{
|
||||||
|
if (session.OwnerCharacterId == share.CharacterId && client.ClientId == session.Owner.ClientId && ownedSession == null)
|
||||||
|
{
|
||||||
|
ownedSession = new Objects.OwnedSession
|
||||||
|
{
|
||||||
|
Session = new Objects.Session {
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
ShareCode = session.ShareCode,
|
||||||
|
ActiveQuestId = session.SharedQuestId,
|
||||||
|
ActiveQuestStep = session.SharedQuestStep
|
||||||
|
},
|
||||||
|
SkipPartyCheck = session.SkipPartyCheck,
|
||||||
|
IsActive = session.IsActive,
|
||||||
|
AllowJoins = session.AllowJoins,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sessions.Add(new Objects.Session
|
||||||
|
{
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
ActiveQuestId = session.SharedQuestId,
|
||||||
|
ActiveQuestStep = session.SharedQuestStep,
|
||||||
|
ShareCode = share.Code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId, session.ShareCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Error = Error.None,
|
||||||
|
Token = client.Token,
|
||||||
|
Sessions = sessions,
|
||||||
|
OwnedSession = ownedSession,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using QuestShare.Common;
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
namespace QuestShare.Server.Hubs
|
||||||
{
|
{
|
||||||
|
|
||||||
public partial class ShareHub : Hub
|
public partial class ShareHub : Hub
|
||||||
{
|
{
|
||||||
[HubMethodName(nameof(Cancel))]
|
[HubMethodName(nameof(Cancel))]
|
||||||
@ -14,7 +15,7 @@ namespace QuestShare.Server.Hubs
|
|||||||
Context.Abort();
|
Context.Abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId);
|
var client = await ClientManager.GetClient(Context.ConnectionId);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
||||||
@ -27,8 +28,10 @@ namespace QuestShare.Server.Hubs
|
|||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.Token == "") error = Error.InvalidToken;
|
if (request.Token == "") error = Error.InvalidToken;
|
||||||
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
||||||
var share = ShareManager.GetShare(client);
|
var session = await SessionManager.GetSession(request.ShareCode);
|
||||||
if (share == null) error = Error.Unauthorized;
|
if (session == null) error = Error.Unauthorized;
|
||||||
|
if (session != null && session.OwnerCharacterId != request.OwnerCharacterId) error = Error.InvalidSession;
|
||||||
|
if (session != null && session.Owner.ClientId != client.ClientId) error = Error.Unauthorized;
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
||||||
@ -38,23 +41,22 @@ namespace QuestShare.Server.Hubs
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShareManager.RemoveShare(share!.ShareCode);
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId, session!.ShareCode.ToString());
|
||||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, share.ShareCode);
|
if (session.PartyMembers.Count > 0)
|
||||||
// remove all members from the group
|
|
||||||
var members = await ShareManager.GetShareMembers(share);
|
|
||||||
if (members != null && members.Count > 0)
|
|
||||||
{
|
{
|
||||||
// broadcast to party
|
// broadcast to party
|
||||||
await Clients.GroupExcept(share.ShareCode, Context.ConnectionId).SendAsync(nameof(Cancel), new Cancel.CancelBroadcast
|
await Clients.GroupExcept(session.SessionId.ToString(), Context.ConnectionId).SendAsync(nameof(Cancel.CancelBroadcast), new Cancel.CancelBroadcast
|
||||||
{
|
{
|
||||||
ShareCode = share.ShareCode,
|
ShareCode = request.ShareCode
|
||||||
});
|
});
|
||||||
|
var members = await ClientManager.GetClientsInSession(session);
|
||||||
foreach (var member in members)
|
foreach (var member in members)
|
||||||
{
|
{
|
||||||
await Groups.RemoveFromGroupAsync(member.ConnectionId, share.ShareCode);
|
await Groups.RemoveFromGroupAsync(member.Client.ConnectionId, session.ShareCode.ToString());
|
||||||
await ShareManager.RemoveGroupMember(share, member);
|
await ClientManager.RemoveClientSession(member.Client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await SessionManager.RemoveSession(session!.ShareCode);
|
||||||
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
await Clients.Caller.SendAsync(nameof(Cancel), new Cancel.Response
|
||||||
{
|
{
|
||||||
Success = true,
|
Success = true,
|
||||||
|
|||||||
@ -1,55 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using QuestShare.Server.Managers;
|
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
|
||||||
{
|
|
||||||
public partial class ShareHub : Hub
|
|
||||||
{
|
|
||||||
[HubMethodName(nameof(GetShareInfo))]
|
|
||||||
public async Task Server_GetShareInfo(GetShareInfo.Request request)
|
|
||||||
{
|
|
||||||
if (BanManager.IsBanned(Context))
|
|
||||||
{
|
|
||||||
Context.Abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var error = Error.None;
|
|
||||||
if (request.ShareCode == "") error = Error.InvalidShareCode;
|
|
||||||
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId, request.Token);
|
|
||||||
var share = ShareManager.GetShare(request.ShareCode);
|
|
||||||
if (client == null) error = Error.Unauthorized;
|
|
||||||
if (share == null) error = Error.ShareNotFound;
|
|
||||||
if (error != Error.None)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(GetShareInfo), new GetShareInfo.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = error,
|
|
||||||
Members = [],
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(GetShareInfo), new GetShareInfo.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.ShareNotFound,
|
|
||||||
SharedQuestId = share!.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
Members = [],
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var members = await ShareManager.GetShareMembers(share);
|
|
||||||
await Clients.Caller.SendAsync(nameof(GetShareInfo), new GetShareInfo.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
SharedQuestId = share.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
Members = members.Select(m => m.CharacterId).ToList(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using QuestShare.Common;
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
using QuestShare.Server.Models;
|
using QuestShare.Server.Models;
|
||||||
|
|
||||||
@ -11,11 +12,9 @@ namespace QuestShare.Server.Hubs
|
|||||||
public async Task Server_GroupJoin(GroupJoin.Request request)
|
public async Task Server_GroupJoin(GroupJoin.Request request)
|
||||||
{
|
{
|
||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.ShareCode == "") error = Error.InvalidShareCode;
|
|
||||||
if (request.Token == "") error = Error.InvalidToken;
|
if (request.Token == "") error = Error.InvalidToken;
|
||||||
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
||||||
if (request.CharacterId == 0) error = Error.InvalidCharacterId;
|
var client = await ClientManager.GetClient(Context.ConnectionId, request.Token);
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId, request.Token);
|
|
||||||
if (client == null) error = Error.Unauthorized;
|
if (client == null) error = Error.Unauthorized;
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
@ -26,80 +25,38 @@ namespace QuestShare.Server.Hubs
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.RequestPartyQuickJoin)
|
var session = await SessionManager.GetSession(request.SessionInfo.Code);
|
||||||
|
if (session == null)
|
||||||
{
|
{
|
||||||
if (ShareManager.HasBroadcastParty(request.QuickJoinCharacterId))
|
Log.Warning($"[GroupJoin] Session {request.SessionInfo.Code} not found.");
|
||||||
{
|
|
||||||
var host = ClientManager.GetClient(request.QuickJoinCharacterId);
|
|
||||||
if (host == null)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.InvalidHostClient,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var share = ShareManager.GetShare(host);
|
|
||||||
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.ShareNotFound,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var members = await ShareManager.GetShareMembers(share);
|
|
||||||
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
Members = members.Select(m => m.CharacterId).ToList(),
|
|
||||||
SharedQuestId = share.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
});
|
|
||||||
await Clients.GroupExcept(share.ShareCode, Context.ConnectionId).SendAsync(nameof(GroupNotify), new GroupNotify.GroupNotifyBroadcast
|
|
||||||
{
|
|
||||||
CharacterId = request.CharacterId,
|
|
||||||
NotifyType = NotifyType.JoinViaParty,
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var share = ShareManager.GetShare(request.ShareCode);
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.ShareNotFound,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Groups.AddToGroupAsync(Context.ConnectionId, share.ShareCode);
|
|
||||||
await ShareManager.AddGroupMember(share, client!);
|
|
||||||
var members = await ShareManager.GetShareMembers(share);
|
|
||||||
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
||||||
{
|
{
|
||||||
Success = true,
|
Success = false,
|
||||||
ShareCode = share.ShareCode,
|
Error = Error.InvalidParty,
|
||||||
Members = members.Select(m => m.CharacterId).ToList(),
|
|
||||||
SharedQuestId = share.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
});
|
|
||||||
await Clients.GroupExcept(share.ShareCode, Context.ConnectionId).SendAsync(nameof(GroupNotify.GroupNotifyBroadcast), new GroupNotify.GroupNotifyBroadcast
|
|
||||||
{
|
|
||||||
CharacterId = request.CharacterId,
|
|
||||||
NotifyType = NotifyType.Join,
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
await ClientManager.AddClientSession(client!.ClientId, session.SessionId);
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId, session.ShareCode.ToString());
|
||||||
|
await ClientManager.AddKnownShareCode(client!, session.ShareCode);
|
||||||
|
await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Session = new Objects.Session
|
||||||
|
{
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
ShareCode = session.ShareCode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await Clients.GroupExcept(Context.ConnectionId, session.ShareCode.ToString()).SendAsync(nameof(GroupJoin.GroupJoinBroadcast), new GroupJoin.GroupJoinBroadcast
|
||||||
|
{
|
||||||
|
Session = new Objects.Session
|
||||||
|
{
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
ShareCode = session.ShareCode,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Log.Debug($"[GroupJoin] {client} joined session {request.SessionInfo.Code}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using QuestShare.Common;
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
namespace QuestShare.Server.Hubs
|
||||||
@ -18,10 +19,8 @@ namespace QuestShare.Server.Hubs
|
|||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.Token == "") error = Error.InvalidToken;
|
if (request.Token == "") error = Error.InvalidToken;
|
||||||
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId);
|
var client = await ClientManager.GetClient(Context.ConnectionId);
|
||||||
if (client == null) error = Error.Unauthorized;
|
if (client == null) error = Error.Unauthorized;
|
||||||
var share = ShareManager.GetShare(request.ShareCode);
|
|
||||||
if (share == null) error = Error.ShareNotFound;
|
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
||||||
@ -31,17 +30,46 @@ namespace QuestShare.Server.Hubs
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await ShareManager.RemoveGroupMember(share!, client!);
|
var session = await SessionManager.GetSession(request.Session.ShareCode);
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = Error.InvalidSession,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var smember = await SessionManager.GetMembersInSession(session);
|
||||||
|
if (!smember.Any(s => s.Client.ClientId == client!.ClientId))
|
||||||
|
{
|
||||||
|
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = Error.InvalidMember,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await SessionManager.RemoveMemberFromSession(session, client!);
|
||||||
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId, session.ShareCode.ToString());
|
||||||
|
await ClientManager.RemoveKnownShareCode(client!, session.ShareCode);
|
||||||
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
await Clients.Caller.SendAsync(nameof(GroupLeave), new GroupLeave.Response
|
||||||
{
|
{
|
||||||
Success = true,
|
Success = true,
|
||||||
|
Session = new Objects.Session
|
||||||
|
{
|
||||||
|
ShareCode = session.ShareCode,
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// broadcast to party
|
// broadcast to party
|
||||||
await Clients.GroupExcept(share!.ShareCode, Context.ConnectionId).SendAsync(nameof(GroupNotify), new GroupNotify.GroupNotifyBroadcast
|
await Clients.GroupExcept(session.SessionId.ToString(), Context.ConnectionId).SendAsync(nameof(GroupLeave.GroupLeaveBroadcast), new GroupLeave.GroupLeaveBroadcast
|
||||||
{
|
{
|
||||||
ShareCode = share.ShareCode,
|
Session = new Objects.Session
|
||||||
CharacterId = client!.CharacterId,
|
{
|
||||||
NotifyType = NotifyType.Leave,
|
ShareCode = session.ShareCode,
|
||||||
|
OwnerCharacterId = session.OwnerCharacterId,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using QuestShare.Common.API.Party;
|
|
||||||
using QuestShare.Server.Managers;
|
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
|
||||||
{
|
|
||||||
public partial class ShareHub : Hub
|
|
||||||
{
|
|
||||||
[HubMethodName(nameof(PartyCheck))]
|
|
||||||
public async Task Server_PartyCheck(PartyCheck.Request request)
|
|
||||||
{
|
|
||||||
if (BanManager.IsBanned(Context))
|
|
||||||
{
|
|
||||||
Context.Abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var error = Error.None;
|
|
||||||
if (request.CharacterId == 0) error = Error.InvalidCharacterId;
|
|
||||||
if (request.Token == "") error = Error.InvalidToken;
|
|
||||||
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId, request.Token);
|
|
||||||
if (client == null)
|
|
||||||
{
|
|
||||||
error = Error.Unauthorized;
|
|
||||||
if (BanManager.CheckBadRequests(Context, nameof(PartyCheck)))
|
|
||||||
{
|
|
||||||
error = Error.BannedTooManyBadRequests;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error != Error.None)
|
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(PartyCheck), new PartyCheck.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = error,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using QuestShare.Common.API.Share;
|
using QuestShare.Common;
|
||||||
using QuestShare.Server.Hubs;
|
using QuestShare.Server.Hubs;
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
using QuestShare.Server.Models;
|
using QuestShare.Server.Models;
|
||||||
@ -21,63 +21,23 @@ namespace QuestShare.Server.Hubs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.CharacterId == 0) error = Error.InvalidCharacterId;
|
if (request.Version != Constants.Version) error = Error.InvalidVersion;
|
||||||
if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
var client = await ClientManager.GetClient(Context.ConnectionId, request.Token);
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId);
|
if (client == null) error = Error.InvalidToken;
|
||||||
if (client == null || client.Token != request.Token)
|
if (client != null)
|
||||||
{
|
{
|
||||||
error = Error.Unauthorized;
|
var existingSession = await SessionManager.GetSession(client);
|
||||||
if (BanManager.CheckBadRequests(Context, nameof(Register)))
|
if (existingSession != null) error = Error.AlreadyRegistered;
|
||||||
{
|
|
||||||
error = Error.BannedTooManyBadRequests;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
Log.Warning($"[REGISTER] Client {Context.ConnectionId} failed registration with error {error}.");
|
Log.Warning($"[REGISTER] Client {Context.ConnectionId} failed to register: {error}");
|
||||||
await Clients.Caller.SendAsync(nameof(Register), new Register.Response
|
await Clients.Caller.SendAsync(nameof(Register), new Register.Response { Error = error, Success = false, ShareCode = "" });
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = error,
|
|
||||||
ShareCode = "",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.BroadcastParty && request.PartyMembers.Count > 0)
|
var session = await SessionManager.GenerateSession(Context.ConnectionId, client!);
|
||||||
{
|
await Clients.Caller.SendAsync(nameof(Register), new Register.Response { Error = Error.None, Success = true, ShareCode = session });
|
||||||
foreach (var partyMember in request.PartyMembers)
|
Log.Information($"[REGISTER] Client {Context.ConnectionId} generated share code {session}.");
|
||||||
{
|
|
||||||
ShareManager.AddBroadcastPartyMember(request.CharacterId, partyMember);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ShareManager.GetShare(client!) != null)
|
|
||||||
{
|
|
||||||
Log.Warning($"[REGISTER] Client {Context.ConnectionId} already registered.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Register), new Register.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.AlreadyRegistered,
|
|
||||||
ShareCode = ""
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var share = new Share
|
|
||||||
{
|
|
||||||
ShareCode = ShareManager.GenerateShareCode(),
|
|
||||||
ShareHost = client!,
|
|
||||||
BroadcastParty = request.BroadcastParty,
|
|
||||||
SharedQuestId = request.SharedQuestId,
|
|
||||||
SharedQuestStep = request.SharedQuestStep,
|
|
||||||
};
|
|
||||||
var newShare = await ShareManager.AddShare(share);
|
|
||||||
Log.Information($"[REGISTER] Client {Context.ConnectionId} registered share {share.ShareCode}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Register), new Register.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
Error = Error.None,
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using QuestShare.Server.Managers;
|
|
||||||
|
|
||||||
namespace QuestShare.Server.Hubs
|
|
||||||
{
|
|
||||||
public partial class ShareHub : Hub
|
|
||||||
{
|
|
||||||
[HubMethodName(nameof(Resume))]
|
|
||||||
public async Task Server_Resume(Resume.Request request)
|
|
||||||
{
|
|
||||||
if (BanManager.IsBanned(Context))
|
|
||||||
{
|
|
||||||
Log.Error($"[RESUME] Client {Context.ConnectionId} is banned.");
|
|
||||||
Context.Abort();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var error = Error.None;
|
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId, request.Token);
|
|
||||||
if (client == null)
|
|
||||||
{
|
|
||||||
error = Error.Unauthorized;
|
|
||||||
if (BanManager.CheckBadRequests(Context, nameof(Resume)))
|
|
||||||
{
|
|
||||||
error = Error.BannedTooManyBadRequests;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.Token == "") error = Error.InvalidToken;
|
|
||||||
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
|
||||||
if (error != Error.None)
|
|
||||||
{
|
|
||||||
Log.Warning($"[RESUME] Client {Context.ConnectionId} failed resume with error {error}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Resume), new Resume.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = error,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var share = ShareManager.GetShare(client!);
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
if (request.ShareCode == "")
|
|
||||||
{
|
|
||||||
Log.Warning($"[RESUME] Client {Context.ConnectionId} failed resume with error {error}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Resume), new Resume.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.ShareNotFound,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
share = ShareManager.GetShare(request.ShareCode);
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
Log.Warning($"[RESUME] Client {Context.ConnectionId} failed resume with error {error}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Resume), new Resume.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.ShareNotFound,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await Groups.AddToGroupAsync(Context.ConnectionId, share.ShareCode);
|
|
||||||
var members = await ShareManager.GetShareMembers(share);
|
|
||||||
await Clients.Caller.SendAsync(nameof(Resume), new Resume.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
SharedQuestId = share.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
Members = members.Select(m => m.CharacterId).ToList(),
|
|
||||||
IsGroup = true,
|
|
||||||
ShareCode = share.ShareCode
|
|
||||||
});
|
|
||||||
await Clients.GroupExcept(share.ShareCode, Context.ConnectionId).SendAsync(nameof(Resume), new GroupNotify.GroupNotifyBroadcast
|
|
||||||
{
|
|
||||||
CharacterId = client!.CharacterId,
|
|
||||||
NotifyType = NotifyType.Rejoin,
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
});
|
|
||||||
Log.Information($"[RESUME] Client {Context.ConnectionId} resumed share {share.ShareCode}. (Member)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Information($"[RESUME] Client {Context.ConnectionId} resumed share {share.ShareCode}. (Host)");
|
|
||||||
List<ulong> members = [];
|
|
||||||
if (share.BroadcastParty)
|
|
||||||
{
|
|
||||||
if (share.ShareHost.CharacterId == client!.CharacterId && ShareManager.HasBroadcastParty(share.ShareHost.CharacterId))
|
|
||||||
{
|
|
||||||
members = request.Members ?? [];
|
|
||||||
ShareManager.SetBroadcastPartyMembers(client!.CharacterId, members);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
members = ShareManager.GetBroadcastPartyMembers(share.ShareHost.CharacterId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Clients.Caller.SendAsync(nameof(Resume), new Resume.Response
|
|
||||||
{
|
|
||||||
Success = true,
|
|
||||||
SharedQuestId = share.SharedQuestId,
|
|
||||||
SharedQuestStep = share.SharedQuestStep,
|
|
||||||
Members = members ?? [],
|
|
||||||
IsHost = true,
|
|
||||||
ShareCode = share.ShareCode
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using QuestShare.Common.API;
|
using QuestShare.Common;
|
||||||
using QuestShare.Common.API.Share;
|
|
||||||
using QuestShare.Server.Managers;
|
using QuestShare.Server.Managers;
|
||||||
using QuestShare.Server.Models;
|
using QuestShare.Server.Models;
|
||||||
|
|
||||||
@ -20,22 +19,13 @@ namespace QuestShare.Server.Hubs
|
|||||||
Context.Abort();
|
Context.Abort();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ClientManager.GetClient(Context.ConnectionId) == null)
|
var client = await ClientManager.GetClient(Context.ConnectionId);
|
||||||
{
|
|
||||||
await Clients.Caller.SendAsync(nameof(Update), new Update.Response
|
|
||||||
{
|
|
||||||
Success = false,
|
|
||||||
Error = Error.Unauthorized,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var client = ClientManager.GetClient(Context.ConnectionId);
|
|
||||||
var error = Error.None;
|
var error = Error.None;
|
||||||
if (request.Token == "") error = Error.InvalidToken;
|
if (request.Token == "" || request.Token != client!.Token) error = Error.InvalidToken;
|
||||||
if (client == null) error = Error.Unauthorized;
|
if (client == null) error = Error.Unauthorized;
|
||||||
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
else if (request.Version != Common.Constants.Version) error = Error.InvalidVersion;
|
||||||
var share = ShareManager.GetShare(client!);
|
var session = await SessionManager.GetSession(request.Session.ShareCode);
|
||||||
if (share == null) error = Error.ShareNotFound;
|
if (session == null) error = Error.InvalidSession;
|
||||||
if (error != Error.None)
|
if (error != Error.None)
|
||||||
{
|
{
|
||||||
Log.Warning($"[UPDATE] Client {Context.ConnectionId} failed update with error {error}.");
|
Log.Warning($"[UPDATE] Client {Context.ConnectionId} failed update with error {error}.");
|
||||||
@ -46,25 +36,17 @@ namespace QuestShare.Server.Hubs
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.IsPartyChange)
|
await SessionManager.UpdateActiveQuest(request.Session.ShareCode, request.Session.ActiveQuestId, request.Session.ActiveQuestStep);
|
||||||
{
|
await SessionManager.SetPartyMembers(session!.ShareCode, [.. request.PartyMembers]);
|
||||||
ShareManager.SetBroadcastPartyMembers(share!.ShareHost.CharacterId, request.PartyMembers);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
await ShareManager.UpdateActiveQuest(share!.ShareCode, request.SharedQuestId, request.SharedQuestStep);
|
|
||||||
// broadcast to party
|
|
||||||
Log.Debug($"[UPDATE] Broadcasting quests to party members for share {share.ShareCode}, excluding {Context.ConnectionId}");
|
|
||||||
await Clients.GroupExcept(share.ShareCode, Context.ConnectionId).SendAsync(nameof(Update.UpdateBroadcast), new Update.UpdateBroadcast
|
|
||||||
{
|
|
||||||
ShareCode = share.ShareCode,
|
|
||||||
SharedQuestId = request.SharedQuestId,
|
|
||||||
SharedQuestStep = request.SharedQuestStep,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Log.Debug($"[UPDATE] Client {Context.ConnectionId} updated quest for share {share.ShareCode}.");
|
|
||||||
await Clients.Caller.SendAsync(nameof(Update), new Update.Response
|
await Clients.Caller.SendAsync(nameof(Update), new Update.Response
|
||||||
{
|
{
|
||||||
Success = true,
|
Success = true,
|
||||||
|
Error = Error.None,
|
||||||
|
});
|
||||||
|
// Broadcast to party
|
||||||
|
await Clients.GroupExcept(session.ShareCode.ToString(), Context.ConnectionId).SendAsync(nameof(Update.UpdateBroadcast), new Update.UpdateBroadcast
|
||||||
|
{
|
||||||
|
Session = request.Session.Session,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace QuestShare.Server.Hubs
|
|||||||
Log.Warning($"Client {Context.ConnectionId} is banned.");
|
Log.Warning($"Client {Context.ConnectionId} is banned.");
|
||||||
Context.Abort();
|
Context.Abort();
|
||||||
}
|
}
|
||||||
Clients.Caller.SendAsync(nameof(Authorize.AuthBroadcast), new Authorize.AuthBroadcast
|
Clients.Caller.SendAsync(nameof(AuthRequest), new AuthRequest.Response
|
||||||
{
|
{
|
||||||
});
|
});
|
||||||
return base.OnConnectedAsync();
|
return base.OnConnectedAsync();
|
||||||
@ -28,12 +28,6 @@ namespace QuestShare.Server.Hubs
|
|||||||
{
|
{
|
||||||
Log.Error(exception, "Exception occurred on disconnect.");
|
Log.Error(exception, "Exception occurred on disconnect.");
|
||||||
}
|
}
|
||||||
if (Context.Items["Token"] != null)
|
|
||||||
{
|
|
||||||
using var db = new QuestShareContext();
|
|
||||||
var client = db.Clients.FirstOrDefault(c => c.Token == Context.Items["Token"]!.ToString());
|
|
||||||
if (client != null) db.Clients.Remove(client);
|
|
||||||
}
|
|
||||||
return base.OnDisconnectedAsync(exception);
|
return base.OnDisconnectedAsync(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,9 @@ namespace QuestShare.Server.Managers
|
|||||||
private static void CleanupTask(object? timerState)
|
private static void CleanupTask(object? timerState)
|
||||||
{
|
{
|
||||||
using var shareContext = new QuestShareContext();
|
using var shareContext = new QuestShareContext();
|
||||||
var toDelete = shareContext.Shares.Where(s => s.LastUpdated < DateTime.Now.AddMinutes(-CleanupTimeMinutes)).ToList();
|
var toDelete = shareContext.Sessions.Where(s => s.LastUpdated < DateTime.Now.AddMinutes(-CleanupTimeMinutes)).ToList();
|
||||||
Console.WriteLine($"Deleting {toDelete.Count} old shares.");
|
Console.WriteLine($"Deleting {toDelete.Count} old shares.");
|
||||||
shareContext.Shares.RemoveRange(toDelete);
|
shareContext.Sessions.RemoveRange(toDelete);
|
||||||
shareContext.SaveChanges();
|
shareContext.SaveChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using QuestShare.Server.Models;
|
using QuestShare.Server.Models;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
@ -7,20 +8,19 @@ namespace QuestShare.Server.Managers
|
|||||||
{
|
{
|
||||||
public static class ClientManager
|
public static class ClientManager
|
||||||
{
|
{
|
||||||
public static string AddClient(string connectionId, ulong characterId)
|
public static async Task<Client> AddClient(string connectionId)
|
||||||
{
|
{
|
||||||
using var context = new QuestShareContext();
|
using var context = new QuestShareContext();
|
||||||
var token = GenerateToken();
|
var token = GenerateToken();
|
||||||
context.Clients.Add(new Client
|
var c = context.Clients.Add(new Client
|
||||||
{
|
{
|
||||||
ConnectionId = connectionId,
|
ConnectionId = connectionId,
|
||||||
CharacterId = characterId,
|
|
||||||
Token = token
|
Token = token
|
||||||
});
|
});
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
return token;
|
return c.Entity;
|
||||||
}
|
}
|
||||||
public static void RemoveClient(string connectionId)
|
public static async Task RemoveClient(string connectionId)
|
||||||
{
|
{
|
||||||
using var context = new QuestShareContext();
|
using var context = new QuestShareContext();
|
||||||
var client = context.Clients.FirstOrDefault(c => c.ConnectionId == connectionId);
|
var client = context.Clients.FirstOrDefault(c => c.ConnectionId == connectionId);
|
||||||
@ -28,12 +28,12 @@ namespace QuestShare.Server.Managers
|
|||||||
{
|
{
|
||||||
context.Clients.Remove(client);
|
context.Clients.Remove(client);
|
||||||
}
|
}
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
public static Client? GetClient(string connectionId, string token = "")
|
public static async Task<Client?> GetClient(string connectionId, string token = "")
|
||||||
{
|
{
|
||||||
using var context = new QuestShareContext();
|
using var context = new QuestShareContext();
|
||||||
var client = context.Clients.FirstOrDefault(c => c.ConnectionId == connectionId || c.Token == token);
|
var client = await context.Clients.Where(c => c.ConnectionId == connectionId || c.Token == token).FirstOrDefaultAsync();
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
if (token != "" && client.Token != token)
|
if (token != "" && client.Token != token)
|
||||||
@ -44,7 +44,7 @@ namespace QuestShare.Server.Managers
|
|||||||
if (client.ConnectionId != connectionId)
|
if (client.ConnectionId != connectionId)
|
||||||
{
|
{
|
||||||
Log.Information($"[ClientManager] Changing connection ID from {connectionId} to {client.ConnectionId} for token {token}");
|
Log.Information($"[ClientManager] Changing connection ID from {connectionId} to {client.ConnectionId} for token {token}");
|
||||||
ChangeClientConnectionId(client.ConnectionId, connectionId);
|
await ChangeClientConnectionId(client.ConnectionId, connectionId);
|
||||||
}
|
}
|
||||||
return client;
|
return client;
|
||||||
} else
|
} else
|
||||||
@ -53,32 +53,81 @@ namespace QuestShare.Server.Managers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static Client? GetClient(ulong characterId)
|
|
||||||
|
public static async Task<List<SessionMember>> GetClientsInSession(Session session)
|
||||||
{
|
{
|
||||||
using var context = new QuestShareContext();
|
using var context = new QuestShareContext();
|
||||||
var client = context.Clients.FirstOrDefault(c => c.CharacterId == characterId);
|
var clients = await context.SessionMembers.Where(c => c.Session == session).Include(s => s.Session).Include(sm => sm.Client).ToListAsync();
|
||||||
if (client != null)
|
return clients;
|
||||||
{
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ChangeClientConnectionId(string oldConnectionId, string newConnectionId)
|
public static async Task RemoveClientSession(Client client)
|
||||||
{
|
{
|
||||||
using var context = new QuestShareContext();
|
using var context = new QuestShareContext();
|
||||||
var client = context.Clients.FirstOrDefault(c => c.ConnectionId == oldConnectionId);
|
var cs = await context.SessionMembers.Where(cs => cs.Client.ClientId == client.ClientId).FirstOrDefaultAsync();
|
||||||
|
if (cs == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"[ClientManager] Unable to find client session for {client.ClientId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.SessionMembers.Remove(cs);
|
||||||
|
Log.Debug($"[ClientManager] Removing client {client.ClientId} from session");
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task AddClientSession(Guid ClientId, Guid SessionId)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var client = await context.Clients.Where(c => c.ClientId == ClientId).FirstOrDefaultAsync();
|
||||||
|
var session = await context.Sessions.Where(s => s.SessionId == SessionId).FirstOrDefaultAsync();
|
||||||
|
if (client == null || session == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"[ClientManager] Unable to find client {ClientId} or session {SessionId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await context.SessionMembers.AddAsync(new SessionMember
|
||||||
|
{
|
||||||
|
Client = client,
|
||||||
|
Session = session
|
||||||
|
});
|
||||||
|
Log.Debug($"[ClientManager] Adding client {client.ClientId} to session {session.SessionId}");
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ChangeClientConnectionId(string oldConnectionId, string newConnectionId)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var client = await context.Clients.Where(c => c.ConnectionId == oldConnectionId).FirstOrDefaultAsync();
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
client.ConnectionId = newConnectionId;
|
client.ConnectionId = newConnectionId;
|
||||||
context.SaveChanges();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateToken()
|
public static async Task AddKnownShareCode(Client client, string shareCode)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var c = await context.Clients.Where(c => c.ClientId == client.ClientId).FirstOrDefaultAsync();
|
||||||
|
if (c != null)
|
||||||
|
{
|
||||||
|
c.KnownShareCodes.Add(shareCode);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RemoveKnownShareCode(Client client, string shareCode)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var c = await context.Clients.Where(c => c.ClientId == client.ClientId).FirstOrDefaultAsync();
|
||||||
|
if (c != null)
|
||||||
|
{
|
||||||
|
c.KnownShareCodes.Remove(shareCode);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateToken()
|
||||||
{
|
{
|
||||||
var random = RandomNumberGenerator.GetHexString(32, true);
|
var random = RandomNumberGenerator.GetHexString(32, true);
|
||||||
return random;
|
return random;
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
namespace QuestShare.Server.Managers
|
|
||||||
{
|
|
||||||
public class PartyManager
|
|
||||||
{
|
|
||||||
private static List<Party> Parties = [];
|
|
||||||
|
|
||||||
public static void CreateParty(string host, List<string> members)
|
|
||||||
{
|
|
||||||
members.Add(host);
|
|
||||||
Parties.Add(new Party
|
|
||||||
{
|
|
||||||
Host = host,
|
|
||||||
Members = members
|
|
||||||
});
|
|
||||||
}
|
|
||||||
public static void JoinParty(string host, string member)
|
|
||||||
{
|
|
||||||
var party = Parties.Find(p => p.Host == host);
|
|
||||||
party?.Members.Add(member);
|
|
||||||
}
|
|
||||||
public static void LeaveParty(string host, string member)
|
|
||||||
{
|
|
||||||
var party = Parties.Find(p => p.Host == host);
|
|
||||||
party?.Members.Remove(member);
|
|
||||||
}
|
|
||||||
public static void DeleteParty(string host)
|
|
||||||
{
|
|
||||||
Parties.RemoveAll(p => p.Host == host);
|
|
||||||
}
|
|
||||||
public static List<string> GetPartyMembers(string host)
|
|
||||||
{
|
|
||||||
var party = Parties.Find(p => p.Host == host);
|
|
||||||
return party?.Members ?? new List<string>();
|
|
||||||
}
|
|
||||||
public static bool IsInParty(string member, string shareCode)
|
|
||||||
{
|
|
||||||
var share = Parties.Find(p => p.HostShareCode == shareCode);
|
|
||||||
return share?.Members.Contains(member) ?? false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed record Party
|
|
||||||
{
|
|
||||||
public string Host { get; set; } = null!;
|
|
||||||
public string HostShareCode { get; set; } = null!;
|
|
||||||
public List<string> Members { get; set; } = null!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
115
QuestShare.Server/Managers/SessionManager.cs
Normal file
115
QuestShare.Server/Managers/SessionManager.cs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using QuestShare.Common;
|
||||||
|
using QuestShare.Server.Models;
|
||||||
|
|
||||||
|
namespace QuestShare.Server.Managers
|
||||||
|
{
|
||||||
|
public class SessionManager
|
||||||
|
{
|
||||||
|
public static async Task<Session?> GetSession(Client client)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
return await context.Sessions.Where(s => s.Owner.ClientId == client.ClientId).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
public static async Task<Session?> GetSession(string ShareCode)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var session = await context.Sessions.Where(s => s.ShareCode == ShareCode).FirstOrDefaultAsync();
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> GenerateSession(string connectionId, Client client)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var token = ClientManager.GenerateToken();
|
||||||
|
var c = await context.Clients.Where(c => c.ClientId == client.ClientId).FirstOrDefaultAsync();
|
||||||
|
var s = context.Sessions.Add(new Session
|
||||||
|
{
|
||||||
|
ShareCode = token[..8].ToUpperInvariant(),
|
||||||
|
ReservedConnectionId = connectionId,
|
||||||
|
Owner = c!,
|
||||||
|
});
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
return token[..8].ToUpperInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Session?> CreateSession(Client owner, string connectionId, Objects.OwnedSession session)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var s = await context.Sessions.Where(s => s.ShareCode == session.Session.ShareCode && s.ReservedConnectionId == connectionId).FirstOrDefaultAsync();
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
Log.Error($"[SessionManager] Failed to create session for {session.Session.OwnerCharacterId} with share code {session.Session.ShareCode}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
s.OwnerCharacterId = session.Session.OwnerCharacterId;
|
||||||
|
s.IsActive = session.IsActive;
|
||||||
|
s.AllowJoins = session.AllowJoins;
|
||||||
|
s.SkipPartyCheck = session.SkipPartyCheck;
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RemoveSession(string shareCode)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var session = await context.Sessions.Where(s => s.ShareCode == shareCode).FirstOrDefaultAsync();
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
context.Sessions.Remove(session);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SetPartyMembers(string shareCode, List<string> partyMembers)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var s = await context.Sessions.Where(s => s.ShareCode == shareCode).FirstOrDefaultAsync();
|
||||||
|
if (s != null)
|
||||||
|
{
|
||||||
|
s.PartyMembers = partyMembers;
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task AddMemberToSession(Session session, string member)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var s = await context.Sessions.Where(s => s.SessionId == session.SessionId).FirstOrDefaultAsync();
|
||||||
|
if (s != null)
|
||||||
|
{
|
||||||
|
s.AddMember(member);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RemoveMemberFromSession(Session session, Client client)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
await context.SessionMembers.Where(s => s.Session.SessionId == session.SessionId && s.Client.ClientId == client.ClientId).ExecuteDeleteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<SessionMember>> GetMembersInSession(Session session)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var s = await context.SessionMembers.Where(s => s.Session.SessionId == session.SessionId).Include("Sessions").Include("Clients").ToListAsync();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UpdateActiveQuest(string shareCode, int questId, byte questStep)
|
||||||
|
{
|
||||||
|
using var context = new QuestShareContext();
|
||||||
|
var session = await context.Sessions.Where(s => s.ShareCode == shareCode).FirstOrDefaultAsync();
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
// log error
|
||||||
|
Console.Error.WriteLine($"Failed to update quests for session {shareCode}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session.SharedQuestStep = questStep;
|
||||||
|
session.SharedQuestId = questId;
|
||||||
|
var records = await context.SaveChangesAsync();
|
||||||
|
Log.Debug($"[UPDATE] Updated {records} quests for session {shareCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,134 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using QuestShare.Server.Models;
|
|
||||||
|
|
||||||
namespace QuestShare.Server.Managers
|
|
||||||
{
|
|
||||||
public class ShareManager
|
|
||||||
{
|
|
||||||
|
|
||||||
// party members are only stored in memory, so they will be lost when the server restarts
|
|
||||||
private static readonly Dictionary<ulong, List<ulong>> PartyMembers = [];
|
|
||||||
public static Share? GetShare(string shareCode)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
return context.Shares.Where(s => s.ShareCode == shareCode).FirstOrDefault();
|
|
||||||
}
|
|
||||||
public static Share? GetShare(Client host)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
return context.Shares.Where(s => s.ShareHost == host).FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<Share> AddShare(Share share)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
context.Entry(share.ShareHost).State = EntityState.Unchanged;
|
|
||||||
var s = context.Shares.Add(share);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
return s.Entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveShare(string shareCode)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
var share = context.Shares.Where(s => s.ShareCode == shareCode).FirstOrDefault();
|
|
||||||
if (share != null)
|
|
||||||
{
|
|
||||||
context.Shares.Remove(share);
|
|
||||||
// remove all share group members
|
|
||||||
var shareMembers = context.Clients.Where(c => c.ConnectedShareCode == shareCode);
|
|
||||||
foreach (var member in shareMembers)
|
|
||||||
{
|
|
||||||
member.ConnectedShareCode = "";
|
|
||||||
}
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task UpdateActiveQuest(string shareCode, uint questId, byte questStep)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
var share = await context.Shares.Where(s => s.ShareCode == shareCode).FirstOrDefaultAsync();
|
|
||||||
if (share == null)
|
|
||||||
{
|
|
||||||
// log error
|
|
||||||
Console.Error.WriteLine($"Failed to update quests for share {shareCode}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
share.SharedQuestStep = questStep;
|
|
||||||
share.SharedQuestId = questId;
|
|
||||||
var records = await context.SaveChangesAsync();
|
|
||||||
Log.Debug($"[UPDATE] Updated {records} quests for share {shareCode}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task AddGroupMember(Share share, Client client)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
var clientDb = await context.Clients.Where(c => c.CharacterId == client.CharacterId).FirstAsync();
|
|
||||||
clientDb.ConnectedShareCode = share.ShareCode;
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task RemoveGroupMember(Share share, Client client)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
var clientDb = await context.Clients.Where(c => c.CharacterId == client.CharacterId).FirstOrDefaultAsync();
|
|
||||||
if (clientDb != null)
|
|
||||||
{
|
|
||||||
clientDb.ConnectedShareCode = "";
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<Client>> GetShareMembers(Share share)
|
|
||||||
{
|
|
||||||
using var context = new QuestShareContext();
|
|
||||||
var members = await context.Clients.Where(c => c.ConnectedShareCode == share.ShareCode).ToListAsync();
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddBroadcastPartyMember(ulong hostCharacterId, ulong characterId)
|
|
||||||
{
|
|
||||||
if (!PartyMembers.TryGetValue(hostCharacterId, out var value))
|
|
||||||
{
|
|
||||||
value = ([]);
|
|
||||||
PartyMembers[hostCharacterId] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Add(characterId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveBroadcastPartyMember(ulong hostCharacterId, ulong characterId)
|
|
||||||
{
|
|
||||||
if (PartyMembers.TryGetValue(hostCharacterId, out var value))
|
|
||||||
{
|
|
||||||
value.Remove(characterId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetBroadcastPartyMembers(ulong hostCharacterId, List<ulong> characterIds)
|
|
||||||
{
|
|
||||||
PartyMembers[hostCharacterId] = characterIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DisbandBroadcastParty(ulong hostCharacterId)
|
|
||||||
{
|
|
||||||
PartyMembers.Remove(hostCharacterId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasBroadcastParty(ulong hostCharacterId)
|
|
||||||
{
|
|
||||||
return PartyMembers.ContainsKey(hostCharacterId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ulong> GetBroadcastPartyMembers(ulong hostCharacterId)
|
|
||||||
{
|
|
||||||
return PartyMembers.GetValueOrDefault(hostCharacterId, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GenerateShareCode()
|
|
||||||
{
|
|
||||||
return Guid.NewGuid().ToString().Substring(0, 8).ToUpper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -60,13 +60,6 @@ namespace QuestShare.Server.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<decimal>("CharacterId")
|
|
||||||
.HasColumnType("numeric(20,0)");
|
|
||||||
|
|
||||||
b.Property<string>("ConnectedShareCode")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ConnectionId")
|
b.Property<string>("ConnectionId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@ -76,31 +69,102 @@ namespace QuestShare.Server.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasDefaultValueSql("current_timestamp");
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
|
b.Property<string>("KnownShareCodes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdated")
|
b.Property<DateTime>("LastUpdated")
|
||||||
.ValueGeneratedOnAddOrUpdate()
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasDefaultValueSql("current_timestamp");
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
|
b.Property<Guid?>("SessionMemberClientSessionId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<string>("Token")
|
b.Property<string>("Token")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.HasKey("ClientId");
|
b.HasKey("ClientId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionMemberClientSessionId");
|
||||||
|
|
||||||
b.ToTable("Clients", (string)null);
|
b.ToTable("Clients", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("QuestShare.Server.Models.Share", b =>
|
modelBuilder.Entity("QuestShare.Server.Models.Session", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("ShareId")
|
b.Property<Guid>("SessionId")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<bool>("BroadcastParty")
|
b.Property<bool>("AllowJoins")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.Property<DateTime>("Created")
|
b.Property<DateTime>("Created")
|
||||||
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastUpdated")
|
||||||
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
|
b.Property<string>("OwnerCharacterId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("OwnerClientId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PartyMembers")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ReservedConnectionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("SessionMemberClientSessionId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("ShareCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SharedQuestId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<byte>("SharedQuestStep")
|
||||||
|
.HasColumnType("smallint");
|
||||||
|
|
||||||
|
b.Property<bool>("SkipPartyCheck")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.HasKey("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerClientId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionMemberClientSessionId");
|
||||||
|
|
||||||
|
b.ToTable("Sessions", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("QuestShare.Server.Models.SessionMember", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("ClientSessionId")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("ClientId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasDefaultValueSql("current_timestamp");
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
@ -109,35 +173,57 @@ namespace QuestShare.Server.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasDefaultValueSql("current_timestamp");
|
.HasDefaultValueSql("current_timestamp");
|
||||||
|
|
||||||
b.Property<string>("ShareCode")
|
b.Property<Guid>("SessionId")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<Guid>("ShareHostClientId")
|
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<long>("SharedQuestId")
|
b.HasKey("ClientSessionId");
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<byte>("SharedQuestStep")
|
b.HasIndex("ClientId");
|
||||||
.HasColumnType("smallint");
|
|
||||||
|
|
||||||
b.HasKey("ShareId");
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
b.HasIndex("ShareHostClientId");
|
b.ToTable("SessionMembers", (string)null);
|
||||||
|
|
||||||
b.ToTable("QuestShares", (string)null);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("QuestShare.Server.Models.Share", b =>
|
modelBuilder.Entity("QuestShare.Server.Models.Client", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("QuestShare.Server.Models.Client", "ShareHost")
|
b.HasOne("QuestShare.Server.Models.SessionMember", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ShareHostClientId")
|
.HasForeignKey("SessionMemberClientSessionId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("QuestShare.Server.Models.Session", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("QuestShare.Server.Models.Client", "Owner")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("OwnerClientId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("ShareHost");
|
b.HasOne("QuestShare.Server.Models.SessionMember", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SessionMemberClientSessionId");
|
||||||
|
|
||||||
|
b.Navigation("Owner");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("QuestShare.Server.Models.SessionMember", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("QuestShare.Server.Models.Client", "Client")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("QuestShare.Server.Models.Session", "Session")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Client");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,9 @@ namespace QuestShare.Server.Models
|
|||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public Guid ClientId { get; set; }
|
public Guid ClientId { get; set; }
|
||||||
public required ulong CharacterId { get; set; }
|
|
||||||
public required string ConnectionId { get; set; } = null!;
|
public required string ConnectionId { get; set; } = null!;
|
||||||
public required string Token { get; set; } = null!;
|
public required string Token { get; set; } = null!;
|
||||||
public string ConnectedShareCode { get; set; } = "";
|
public List<string> KnownShareCodes { get; set; } = [];
|
||||||
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
|
|||||||
@ -14,9 +14,10 @@ namespace QuestShare.Server.Models
|
|||||||
{
|
{
|
||||||
// Database.EnsureCreated();
|
// Database.EnsureCreated();
|
||||||
}
|
}
|
||||||
public DbSet<Share> Shares { get; set; } = null!;
|
public DbSet<Session> Sessions { get; set; } = null!;
|
||||||
public DbSet<Client> Clients { get; set; } = null!;
|
public DbSet<Client> Clients { get; set; } = null!;
|
||||||
public DbSet<Ban> Bans { get; set; } = null!;
|
public DbSet<Ban> Bans { get; set; } = null!;
|
||||||
|
public DbSet<SessionMember> SessionMembers { get; set; } = null!;
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
@ -29,13 +30,12 @@ namespace QuestShare.Server.Models
|
|||||||
modelBuilder.Entity<Client>().ToTable("Clients");
|
modelBuilder.Entity<Client>().ToTable("Clients");
|
||||||
modelBuilder.Entity<Client>().Property(a => a.Created).HasDefaultValueSql("current_timestamp");
|
modelBuilder.Entity<Client>().Property(a => a.Created).HasDefaultValueSql("current_timestamp");
|
||||||
modelBuilder.Entity<Client>().Property(a => a.LastUpdated).HasDefaultValueSql("current_timestamp").ValueGeneratedOnAddOrUpdate();
|
modelBuilder.Entity<Client>().Property(a => a.LastUpdated).HasDefaultValueSql("current_timestamp").ValueGeneratedOnAddOrUpdate();
|
||||||
modelBuilder.Entity<Share>().ToTable("QuestShares");
|
modelBuilder.Entity<Client>().Property(a => a.KnownShareCodes).HasConversion(
|
||||||
modelBuilder.Entity<Share>().Property(a => a.Created).HasDefaultValueSql("current_timestamp");
|
v => string.Join(',', v),
|
||||||
modelBuilder.Entity<Share>().Property(a => a.LastUpdated).HasDefaultValueSql("current_timestamp").ValueGeneratedOnAddOrUpdate();
|
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList()
|
||||||
modelBuilder.Entity<Share>(x =>
|
);
|
||||||
{
|
new SessionsConfiguration().Configure(modelBuilder.Entity<Session>());
|
||||||
x.HasOne(a => a.ShareHost).WithMany();
|
new ClientSessionConfiguration().Configure(modelBuilder.Entity<SessionMember>());
|
||||||
});
|
|
||||||
modelBuilder.Entity<Ban>().ToTable("Bans");
|
modelBuilder.Entity<Ban>().ToTable("Bans");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
56
QuestShare.Server/Models/Session.cs
Normal file
56
QuestShare.Server/Models/Session.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using QuestShare.Common;
|
||||||
|
|
||||||
|
namespace QuestShare.Server.Models
|
||||||
|
{
|
||||||
|
public class Session
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public Guid SessionId { get; set; }
|
||||||
|
public string OwnerCharacterId { get; set; } = "";
|
||||||
|
public required Client Owner { get; set; }
|
||||||
|
public required string ShareCode { get; set; }
|
||||||
|
public required string ReservedConnectionId { get; set; }
|
||||||
|
public int SharedQuestId { get; set; } = 0;
|
||||||
|
public byte SharedQuestStep { get; set; } = 0;
|
||||||
|
public List<string> PartyMembers { get; set; } = [];
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public bool SkipPartyCheck { get; set; } = false;
|
||||||
|
public bool AllowJoins { get; set; } = true;
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
public DateTime LastUpdated { get; set; }
|
||||||
|
|
||||||
|
public bool IsMember(string characterId)
|
||||||
|
{
|
||||||
|
return PartyMembers.Contains(characterId);
|
||||||
|
}
|
||||||
|
public void AddMember(string characterId)
|
||||||
|
{
|
||||||
|
PartyMembers.Add(characterId);
|
||||||
|
}
|
||||||
|
public void RemoveMember(string characterId)
|
||||||
|
{
|
||||||
|
PartyMembers.Remove(characterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SessionsConfiguration : IEntityTypeConfiguration<Session>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<Session> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("Sessions");
|
||||||
|
builder.Property(s => s.Created).HasDefaultValueSql("current_timestamp").ValueGeneratedOnAddOrUpdate(); ;
|
||||||
|
builder.Property(s => s.LastUpdated).HasDefaultValueSql("current_timestamp").ValueGeneratedOnAddOrUpdate();
|
||||||
|
builder.Property(s => s.PartyMembers).HasConversion(
|
||||||
|
v => JsonConvert.SerializeObject(v),
|
||||||
|
v => JsonConvert.DeserializeObject<List<string>>(v ?? "")!
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,22 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace QuestShare.Server.Models
|
|
||||||
{
|
|
||||||
public class Share
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public Guid ShareId { get; set; }
|
|
||||||
public virtual required Client ShareHost { get; set; }
|
|
||||||
public required string ShareCode { get; set; }
|
|
||||||
public uint SharedQuestId { get; set; }
|
|
||||||
public byte SharedQuestStep { get; set; }
|
|
||||||
|
|
||||||
[DefaultValue(false)]
|
|
||||||
public bool BroadcastParty { get; set; }
|
|
||||||
public DateTime Created { get; set; }
|
|
||||||
public DateTime LastUpdated { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +1,18 @@
|
|||||||
global using QuestShare.Common.API.Share;
|
|
||||||
global using QuestShare.Common.API;
|
global using QuestShare.Common.API;
|
||||||
global using Serilog;
|
global using Serilog;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using QuestShare.Common;
|
||||||
|
using QuestShare.Server.Models;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace QuestShare.Server
|
namespace QuestShare.Server
|
||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
var log = new LoggerConfiguration()
|
var log = new LoggerConfiguration()
|
||||||
.WriteTo.Console()
|
.WriteTo.Console()
|
||||||
@ -52,20 +55,20 @@ namespace QuestShare.Server
|
|||||||
logging.AddDebug();
|
logging.AddDebug();
|
||||||
logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Information);
|
logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Information);
|
||||||
logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Information);
|
logging.AddFilter("Microsoft.AspNetCore.Http.Connections", LogLevel.Information);
|
||||||
}).UseSerilog() ;
|
}).UseSerilog();
|
||||||
var secretKey = Environment.GetEnvironmentVariable("QUESTSHARE_SECRET");
|
|
||||||
if (string.IsNullOrEmpty(secretKey))
|
|
||||||
{
|
|
||||||
Console.WriteLine("Please set the QUESTSHARE_SECRET environment variable. A hardcoded default is being used.");
|
|
||||||
Environment.SetEnvironmentVariable("QUESTSHARE_SECRET", "A1B2C3D4E5F6G7H8I9J0");
|
|
||||||
}
|
|
||||||
var database = Environment.GetEnvironmentVariable("QUESTSHARE_DATABASE");
|
var database = Environment.GetEnvironmentVariable("QUESTSHARE_DATABASE");
|
||||||
if (string.IsNullOrEmpty(database))
|
if (string.IsNullOrEmpty(database))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Please set the QUESTSHARE_DATABASE environment variable. A hardcoded default is being used.");
|
Console.WriteLine("Please set the QUESTSHARE_DATABASE environment variable.");
|
||||||
Environment.SetEnvironmentVariable("QUESTSHARE_DATABASE", "Host=sol.nate.lan;User ID=dalamud;Password=dalamud1meteor;Database=questshare");
|
Environment.Exit(-1);
|
||||||
}
|
}
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
Log.Information($"Starting QuestShare Server - API Version {Constants.Version}");
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var context = scope.ServiceProvider.GetRequiredService<QuestShareContext>();
|
||||||
|
await context.Database.MigrateAsync();
|
||||||
|
}
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.1" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
||||||
@ -26,6 +26,8 @@
|
|||||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -12,17 +12,17 @@
|
|||||||
],
|
],
|
||||||
"RepoUrl": "https://git.nathanc.tech/nate/QuestShare/",
|
"RepoUrl": "https://git.nathanc.tech/nate/QuestShare/",
|
||||||
"AcceptsFeedback": false,
|
"AcceptsFeedback": false,
|
||||||
"DownloadLinkInstall": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-3/latest.zip",
|
"DownloadLinkInstall": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-4/latest.zip",
|
||||||
"DownloadLinkTesting": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-3/latest.zip",
|
"DownloadLinkTesting": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-4/latest.zip",
|
||||||
"DownloadLinkUpdate": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-3/latest.zip",
|
"DownloadLinkUpdate": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-4/latest.zip",
|
||||||
"DownloadCount": 1,
|
"DownloadCount": 1,
|
||||||
"LastUpdate": "1739849999",
|
"LastUpdate": "1739859999",
|
||||||
"IsHide": false,
|
"IsHide": false,
|
||||||
"IsTestingExclusive": false,
|
"IsTestingExclusive": false,
|
||||||
"IconUrl": "",
|
"IconUrl": "",
|
||||||
"DalamudApiLevel": 11,
|
"DalamudApiLevel": 11,
|
||||||
"InternalName": "QuestShare",
|
"InternalName": "QuestShare",
|
||||||
"AssemblyVersion": "1.0.0.5"
|
"AssemblyVersion": "1.0.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user