From 6f95fcde97b4ae612fc62efaa0e4b140754eb3d9 Mon Sep 17 00:00:00 2001 From: Nathan C Date: Sat, 15 Mar 2025 22:10:24 -0400 Subject: [PATCH] Update project --- QuestShare.Common/Common.cs | 2 +- QuestShare.Common/Errors.cs | 1 + QuestShare.Common/Objects.cs | 1 + .../Common/ConfigurationManager.cs | 20 +- QuestShare.Dalamud/Common/GameQuestManager.cs | 12 +- QuestShare.Dalamud/Plugin.cs | 7 +- QuestShare.Dalamud/QuestShare.csproj | 5 +- QuestShare.Dalamud/QuestShare.json | 8 +- QuestShare.Dalamud/Services/API/Authorize.cs | 32 ++ QuestShare.Dalamud/Services/API/GroupJoin.cs | 1 + .../Services/API/SessionStart.cs | 13 +- QuestShare.Dalamud/Services/ApiService.cs | 32 +- QuestShare.Dalamud/Services/HostService.cs | 10 + QuestShare.Dalamud/Services/ShareService.cs | 47 +-- QuestShare.Dalamud/Services/UiService.cs | 1 + .../Windows/MainWindow/MainWindow.cs | 369 +++++++++++------- QuestShare.Server/Hubs/Methods/Authorize.cs | 66 ++-- QuestShare.Server/Hubs/Methods/GroupJoin.cs | 32 ++ QuestShare.Server/Hubs/Methods/Update.cs | 25 +- QuestShare.Server/Managers/SessionManager.cs | 24 +- QuestShare.Server/Models/QuestShareContext.cs | 4 +- QuestShare.Server/Models/Session.cs | 2 +- QuestShare.Server/Models/SessionMember.cs | 4 +- QuestShare.Server/Program.cs | 2 +- .../Properties/launchSettings.json | 3 +- QuestShare.Server/QuestShare.Server.csproj | 11 +- plogonmaster.json | 8 +- 27 files changed, 494 insertions(+), 248 deletions(-) diff --git a/QuestShare.Common/Common.cs b/QuestShare.Common/Common.cs index d5317b5..12d475b 100644 --- a/QuestShare.Common/Common.cs +++ b/QuestShare.Common/Common.cs @@ -4,7 +4,7 @@ namespace QuestShare.Common { public class Constants { - public static readonly string Version = "3"; + public static readonly string Version = "1"; } public static class StringExtensions diff --git a/QuestShare.Common/Errors.cs b/QuestShare.Common/Errors.cs index 45356eb..52989da 100644 --- a/QuestShare.Common/Errors.cs +++ b/QuestShare.Common/Errors.cs @@ -24,5 +24,6 @@ namespace QuestShare.Common BannedTooManyBadRequests, AlreadyRegistered, AlreadyJoined, + ServerMaintenance, } } diff --git a/QuestShare.Common/Objects.cs b/QuestShare.Common/Objects.cs index e81c3cf..1bbabf4 100644 --- a/QuestShare.Common/Objects.cs +++ b/QuestShare.Common/Objects.cs @@ -21,6 +21,7 @@ namespace QuestShare.Common public required string OwnerCharacterId { get; set; } public int ActiveQuestId { get; set; } public byte ActiveQuestStep { get; set; } + public bool IsValid { get; set; } = true; } public record OwnedSession diff --git a/QuestShare.Dalamud/Common/ConfigurationManager.cs b/QuestShare.Dalamud/Common/ConfigurationManager.cs index baceba8..269bbd3 100644 --- a/QuestShare.Dalamud/Common/ConfigurationManager.cs +++ b/QuestShare.Dalamud/Common/ConfigurationManager.cs @@ -19,8 +19,8 @@ public class ConfigurationManager public List KnownShareCodes { get; set; } = []; public int ActiveQuestId { get; set; } = 0; public byte ActiveQuestStep { get; set; } = 0; - public Dictionary KnownCharacters { get; set; } = []; - public ApiConfiguration[] ApiConfigurations { get; set; } = [new ApiConfiguration { DisplayName = "Primary Server - US East", Url = "https://api.nathanc.tech/Hub", Active = false }]; + public Dictionary ShareCodeOwners { get; set; } = []; + public List ApiConfigurations { get; set; } = []; 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"; } @@ -44,14 +44,14 @@ public class ConfigurationManager if (ClientState.LocalContentId != 0) { LocalContentId = ClientState.LocalContentId; - Framework.Run(Load); + Load(); } } public void OnLogin() { LocalContentId = ClientState.LocalContentId; - Framework.Run(Load); + Load(); } public void Dispose() @@ -77,18 +77,12 @@ public class ConfigurationManager if (deserialized != null) { Instance = deserialized; -#if DEBUG - if (Instance.ApiConfigurations.All(x => x.DisplayName != "Local")) - Instance.ApiConfigurations.Append(new ApiConfiguration { DisplayName = "Local", Url = "http://localhost:8080/Hub", Active = true }); - foreach (var item in Instance.ApiConfigurations) - { - if (item.DisplayName != "Local") item.Active = false; - } -#endif } else { - Log.Error($"Failed to deserialize configuration for {LocalContentId}"); + Log.Error($"Failed to deserialize configuration for {LocalContentId}, using defaults."); + Instance = new Configuration(); + Save(); } } } diff --git a/QuestShare.Dalamud/Common/GameQuestManager.cs b/QuestShare.Dalamud/Common/GameQuestManager.cs index b9cb03c..5874435 100644 --- a/QuestShare.Dalamud/Common/GameQuestManager.cs +++ b/QuestShare.Dalamud/Common/GameQuestManager.cs @@ -54,11 +54,17 @@ namespace QuestShare.Common GameQuests.Add(new GameQuest(quest.RowId)); } } - if (activeQuest != null) + if (activeQuest != null && !GameQuests.Any(q => q.QuestId == activeQuest.QuestId)) + { + Log.Debug($"Active quest {activeQuest.QuestId} was not found in GameQuests, assuming completed."); + HostService.Update(0, 0); + return; + } + else if (activeQuest != null) { SetActiveFlag(activeQuest.QuestId); } - else if (ConfigurationManager.Instance.OwnedSession != null) + else if (ConfigurationManager.Instance.OwnedSession != null && ConfigurationManager.Instance.OwnedSession.ActiveQuestId != 0) { var quest = GetQuestById((uint)ConfigurationManager.Instance.OwnedSession.ActiveQuestId); SetActiveFlag(quest.QuestId); @@ -149,6 +155,8 @@ namespace QuestShare.Common Log.Debug("No next quest in chain"); GameQuests.Remove(q); LoadQuests(); + HostService.Update(0, 0); + return; } } HostService.Update((int)q.QuestId, q.CurrentStep); diff --git a/QuestShare.Dalamud/Plugin.cs b/QuestShare.Dalamud/Plugin.cs index 55b2f0e..a52212c 100644 --- a/QuestShare.Dalamud/Plugin.cs +++ b/QuestShare.Dalamud/Plugin.cs @@ -11,7 +11,7 @@ namespace QuestShare; public sealed class Plugin : IDalamudPlugin { public static string Name => "Quest Share"; - public static string Version => "1.0.0"; + public static string Version => "0.0.0.1"; public static string PluginDataPath { get; private set; } = null!; internal static ConfigurationManager Configuration { get; private set; } = null!; private static List Services = []; @@ -33,8 +33,10 @@ public sealed class Plugin : IDalamudPlugin ]; GameQuestManager.Initialize(); LogStream = new StringWriter(); +#if DEBUG Console.SetOut(LogStream); Console.SetError(LogStream); +#endif Framework.Update += OnFramework; Log.Debug($"Token: {ConfigurationManager.Instance.Token}"); foreach (var service in Services) @@ -65,6 +67,7 @@ public sealed class Plugin : IDalamudPlugin private void OnFramework(IFramework framework) { +#if DEBUG // check if there's logs to write if (LogStream != null && LogStream.ToString() != "") { @@ -72,5 +75,7 @@ public sealed class Plugin : IDalamudPlugin LogStream.GetStringBuilder().Clear(); Log.Write(LogEventLevel.Debug, null, toWrite); } +#endif } + } diff --git a/QuestShare.Dalamud/QuestShare.csproj b/QuestShare.Dalamud/QuestShare.csproj index a327205..22e8185 100644 --- a/QuestShare.Dalamud/QuestShare.csproj +++ b/QuestShare.Dalamud/QuestShare.csproj @@ -2,12 +2,13 @@ 0.0.0.1 - A sample plugin. - https://github.com/goatcorp/SamplePlugin + Share quests with your friends so they can follow you on your journey. + https://github.com/Era-FFXIV/QuestShare AGPL-3.0-or-later false enable net8.0-windows + https://github.com/Era-FFXIV/QuestShare diff --git a/QuestShare.Dalamud/QuestShare.json b/QuestShare.Dalamud/QuestShare.json index fe67cfd..2a77dc6 100644 --- a/QuestShare.Dalamud/QuestShare.json +++ b/QuestShare.Dalamud/QuestShare.json @@ -2,13 +2,13 @@ "Author": "Era", "Name": "Quest Share", "Punchline": "Share your quest progress with others.", - "Description": "blah", + "Description": "Follow your friends on their questing journey by seeing their progress through a shared quest. Provides markers for quest destinations and (soon) teleporting features.", "ApplicableVersion": "any", "Tags": [ "utility", - "vendor", - "shop" + "questing", + "coop" ], - "RepoUrl": "", + "RepoUrl": "https://github.com/Era-FFXIV/QuestShare", "AcceptsFeedback": false } diff --git a/QuestShare.Dalamud/Services/API/Authorize.cs b/QuestShare.Dalamud/Services/API/Authorize.cs index 7ce47bc..225519d 100644 --- a/QuestShare.Dalamud/Services/API/Authorize.cs +++ b/QuestShare.Dalamud/Services/API/Authorize.cs @@ -30,19 +30,51 @@ namespace QuestShare.Services.API ConfigurationManager.Instance.Token = response.Token; foreach(var session in response.Sessions) { + if (!session.IsValid) + { + Log.Warning("Session {ShareCode} is invalid.", session.ShareCode); + share.RemoveSession(session.ShareCode); + ShareService.RemoveKnownShareCode(session.ShareCode); + continue; + } share.AddSession(session); } if (response.OwnedSession != null) { ConfigurationManager.Instance.OwnedSession = response.OwnedSession; + if (response.OwnedSession.Session != null) + { + Log.Debug("Setting active quest to {QuestId} - {QuestStep}", response.OwnedSession.Session.ActiveQuestId, response.OwnedSession.Session.ActiveQuestStep); + GameQuestManager.SetActiveFlag((uint)response.OwnedSession.Session.ActiveQuestId); + } } ConfigurationManager.Save(); + ShareService.RecheckShareCodes(); } else { Log.Error("Failed to authorize: {Error}", response.Error); UiService.LastErrorMessage = $"Failed to authorize: {response.Error}"; _ = ((ApiService)Plugin.GetService()).Disconnect(); + if (response.Error == Error.InvalidVersion) + { + UiService.LastServerMessage = "Invalid version detected, please update the plugin."; + ((ApiService)Plugin.GetService()).IsLockedOut = true; + } + else if (response.Error == Error.InvalidToken) + { + UiService.LastServerMessage = "Invalid token detected, please reauthorize."; + ((ApiService)Plugin.GetService()).IsLockedOut = true; + } + else if (response.Error == Error.BannedTooManyBadRequests) + { + UiService.LastServerMessage = "You are temporarily banned due to too many bad requests."; + ((ApiService)Plugin.GetService()).IsLockedOut = true; + } + else if (response.Error == Error.ServerMaintenance) + { + UiService.LastServerMessage = "Server is currently undergoing maintenance. Please try again later."; + } } return Task.CompletedTask; } diff --git a/QuestShare.Dalamud/Services/API/GroupJoin.cs b/QuestShare.Dalamud/Services/API/GroupJoin.cs index 61eb391..c1e2daf 100644 --- a/QuestShare.Dalamud/Services/API/GroupJoin.cs +++ b/QuestShare.Dalamud/Services/API/GroupJoin.cs @@ -21,6 +21,7 @@ namespace QuestShare.Services.API var share = (ShareService)Plugin.GetService(); Log.Information("Successfully joined group."); share.AddSession(resp.Session); + ShareService.RecheckShareCodes(); var api = (ApiService)Plugin.GetService(); api.OnGroupJoin(new ApiService.GroupJoinEventArgs { Session = resp.Session, IsSuccess = true }); } diff --git a/QuestShare.Dalamud/Services/API/SessionStart.cs b/QuestShare.Dalamud/Services/API/SessionStart.cs index 42f5d16..4ba8587 100644 --- a/QuestShare.Dalamud/Services/API/SessionStart.cs +++ b/QuestShare.Dalamud/Services/API/SessionStart.cs @@ -26,8 +26,19 @@ namespace QuestShare.Services.API { Log.Information("Session started successfully"); ConfigurationManager.Instance.OwnedSession = response.Session; + // check if hashes match + if (response.Session!.OwnerCharacterId != ClientState.LocalContentId.ToString().SaltedHash(response.Session.ShareCode)) + { + Log.Error($"Mismatched owner character ID! {response.Session!.OwnerCharacterId} != {ClientState.LocalContentId.ToString().SaltedHash(response.Session.ShareCode)}"); + } ConfigurationManager.Save(); - HostService.UpdateParty(); + if (GameQuestManager.GetActiveQuest() != null) + { + HostService.Update(GameQuestManager.GetActiveQuest()!.QuestId, GameQuestManager.GetActiveQuest()!.CurrentStep); + } else + { + HostService.UpdateParty(); + } } else { diff --git a/QuestShare.Dalamud/Services/ApiService.cs b/QuestShare.Dalamud/Services/ApiService.cs index 9e23e18..d2d9d9a 100644 --- a/QuestShare.Dalamud/Services/ApiService.cs +++ b/QuestShare.Dalamud/Services/ApiService.cs @@ -11,18 +11,18 @@ 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 bool IsLockedOut { get; set; } = false; 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 apiHandlers = []; + private int retryCount = 0; public void Initialize() { - var builder = new HubConnectionBuilder().WithUrl(socketUrl).ConfigureLogging(logging => + var builder = new HubConnectionBuilder().WithUrl(ConfigurationManager.Instance.ApiUrl).ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Information).AddConsole(); }); @@ -31,13 +31,20 @@ namespace QuestShare.Services { 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(); + if (retryCount < 3) + { + retryCount++; + await Task.Delay(new Random().Next(0, 5) * 1000); + await ApiConnection.StartAsync(); + } else + { + Log.Error("Failed to reconnect after 3 attempts, giving up."); + } }; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously ApiConnection.Reconnected += async (error) => { + retryCount = 0; Log.Information("Connection reconnected"); }; #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously @@ -58,6 +65,10 @@ namespace QuestShare.Services ClientState.Login += OnLogin; ClientState.Logout += OnLogout; Framework.Update += SavePersistedConfig; + if (ConfigurationManager.Instance.ConnectOnStartup) + { + Task.Run(Connect); + } } public void Shutdown() { @@ -83,7 +94,6 @@ namespace QuestShare.Services public void OnLogout(int code, int type) { ConfigurationManager.Save(); - IsAuthorized = false; ApiConnection.StopAsync().ConfigureAwait(false); } @@ -92,8 +102,15 @@ namespace QuestShare.Services ConfigurationManager.Instance.Token = Token; } + public void OnUrlChange() + { + Shutdown(); + Initialize(); + } + public async Task Connect() { + Log.Debug($"Attempting to connect to {ConfigurationManager.Instance.ApiUrl}"); try { if (IsConnected) await ApiConnection.StopAsync(); @@ -107,6 +124,7 @@ namespace QuestShare.Services else { Log.Information("Connected to socket server"); + retryCount = 0; } }); } diff --git a/QuestShare.Dalamud/Services/HostService.cs b/QuestShare.Dalamud/Services/HostService.cs index 0487ece..8dad01d 100644 --- a/QuestShare.Dalamud/Services/HostService.cs +++ b/QuestShare.Dalamud/Services/HostService.cs @@ -1,4 +1,5 @@ using Dalamud.Plugin.Services; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; @@ -24,6 +25,7 @@ namespace QuestShare.Services { ClientState.Login += OnLogin; ClientState.Logout += OnLogout; + CharacterId = ClientState.LocalContentId; } public void Shutdown() @@ -51,6 +53,9 @@ namespace QuestShare.Services SkipPartyCheck = false, Session = session, }; + Log.Debug($"Hashing {CharacterId} with {shareCode} to get ${session.OwnerCharacterId}"); + Log.Debug(JsonConvert.SerializeObject(ownedSession)); + Log.Debug(JsonConvert.SerializeObject(session)); ApiService.DispatchSessionStart(ownedSession); } @@ -125,5 +130,10 @@ namespace QuestShare.Services var api = (ApiService)Plugin.GetService(); ApiService.DispatchCancel(); } + + internal static void Update(uint questId, byte currentStep) + { + Update((int)questId, currentStep); + } } } diff --git a/QuestShare.Dalamud/Services/ShareService.cs b/QuestShare.Dalamud/Services/ShareService.cs index 2c3a359..5d287ad 100644 --- a/QuestShare.Dalamud/Services/ShareService.cs +++ b/QuestShare.Dalamud/Services/ShareService.cs @@ -5,8 +5,8 @@ namespace QuestShare.Services internal class ShareService : IService { internal static List ShareCodes => ConfigurationManager.Instance.KnownShareCodes; + internal static Dictionary ShareCodeOwners => ConfigurationManager.Instance.ShareCodeOwners; internal List Sessions { get; private set; } = []; - internal Dictionary CharacterLookup { get; private set; } = []; public void Initialize() { @@ -21,38 +21,46 @@ namespace QuestShare.Services ClientState.Login -= OnLogin; ClientState.Logout -= OnLogout; Framework.Update -= OnFrameworkUpdate; - CharacterLookup.Clear(); Sessions.Clear(); } private void OnLogin() { - CharacterLookup.Clear(); - CharacterLookup.Add((long)ClientState.LocalContentId, ClientState.LocalPlayer!.Name.TextValue); - foreach (var character in ConfigurationManager.Instance.KnownCharacters) - { - CharacterLookup.Add(character.Key, character.Value); - } } private void OnLogout(int code, int state) { - ConfigurationManager.Save(); - CharacterLookup.Clear(); Sessions.Clear(); } private int pCount = 0; + private static bool RecheckPending = false; + + public static void RecheckShareCodes() + { + RecheckPending = true; + } private void OnFrameworkUpdate(IFramework framework) { - if (PartyList.Count != pCount) + if (PartyList.Count != pCount || RecheckPending) { + Log.Debug("Party list changed"); foreach (var partyMember in PartyList) { - AddKnownCharacter(partyMember.ContentId, partyMember.Name.TextValue); + Log.Debug($"Checking party member {partyMember.Name.TextValue} {partyMember.ContentId}"); + foreach (var session in Sessions) + { + Log.Debug($"Checking session {session.ShareCode} - {session.OwnerCharacterId} ==? {partyMember.ContentId.ToString().SaltedHash(session.ShareCode)}"); + if (session.OwnerCharacterId == partyMember.ContentId.ToString().SaltedHash(session.ShareCode)) + { + Log.Debug($"Setting share code owner for {session.ShareCode} to {partyMember.Name.TextValue}"); + ConfigurationManager.Instance.ShareCodeOwners[session.ShareCode] = partyMember.Name.TextValue; + } + } } pCount = PartyList.Count; + RecheckPending = false; } } @@ -111,20 +119,13 @@ namespace QuestShare.Services ConfigurationManager.Instance.KnownShareCodes.RemoveAll(sc => sc.Code == shareCode); } - public static void AddKnownCharacter(long contentId, string characterId) + public static string GetShareCodeOwner(string shareCode) { - if (ConfigurationManager.Instance.KnownCharacters.ContainsKey(contentId)) + if (ConfigurationManager.Instance.ShareCodeOwners.ContainsKey(shareCode)) { - return; + return ConfigurationManager.Instance.ShareCodeOwners[shareCode]; } - ConfigurationManager.Instance.KnownCharacters.Add(contentId, characterId); - ConfigurationManager.Save(); - } - - public static void RemoveKnownCharacter(long contentId) - { - ConfigurationManager.Instance.KnownCharacters.Remove(contentId); - ConfigurationManager.Save(); + return "Unknown"; } } diff --git a/QuestShare.Dalamud/Services/UiService.cs b/QuestShare.Dalamud/Services/UiService.cs index 1de3295..5000a61 100644 --- a/QuestShare.Dalamud/Services/UiService.cs +++ b/QuestShare.Dalamud/Services/UiService.cs @@ -8,6 +8,7 @@ namespace QuestShare.Services public static WindowSystem WindowSystem = new("QuestShare"); public static MainWindow MainWindow { get; private set; } = new(); public static string LastErrorMessage { get; set; } = string.Empty; + public static string LastServerMessage { get; set; } = string.Empty; public void Initialize() { diff --git a/QuestShare.Dalamud/Windows/MainWindow/MainWindow.cs b/QuestShare.Dalamud/Windows/MainWindow/MainWindow.cs index 3dbc0a5..de147c6 100644 --- a/QuestShare.Dalamud/Windows/MainWindow/MainWindow.cs +++ b/QuestShare.Dalamud/Windows/MainWindow/MainWindow.cs @@ -7,6 +7,7 @@ using Dalamud.Interface.Windowing; using ImGuiNET; using Microsoft.AspNetCore.SignalR.Client; using QuestShare.Services; +using static QuestShare.Common.ConfigurationManager; using static QuestShare.Services.ApiService; namespace QuestShare.Windows.MainWindow; @@ -47,6 +48,7 @@ public class MainWindow : Window, IDisposable ImGui.TextUnformatted("Server Status: "); ImGui.SameLine(); DrawConnectionState(); ImGui.SameLine(); + ImGui.BeginDisabled(ApiService.IsLockedOut); if (ApiService.IsConnected) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Unlink, ImGuiColors.DPSRed)) @@ -58,9 +60,12 @@ public class MainWindow : Window, IDisposable { if (ImGuiComponents.IconButton(FontAwesomeIcon.Link, ImGuiColors.DPSRed)) { + UiService.LastErrorMessage = ""; + UiService.LastServerMessage = ""; _ = ApiService.Connect(); } } + ImGui.EndDisabled(); // ImGui.SameLine(); ImGui.Separator(); using (ImRaii.TabBar("MainTabBar", ImGuiTabBarFlags.NoCloseWithMiddleMouseButton)) @@ -84,14 +89,19 @@ public class MainWindow : Window, IDisposable } } ImGui.Separator(); - if (UiService.LastErrorMessage != null) + if (UiService.LastErrorMessage != "") { - // ImGui.TextColored(ImGuiColors.DPSRed, UiService.LastErrorMessage); + ImGui.TextColored(ImGuiColors.DPSRed, UiService.LastErrorMessage); } } private void DrawConnectionState() { + if (UiService.LastServerMessage != "") + { + ImGui.TextColored(ImGuiColors.DalamudYellow, UiService.LastServerMessage); + return; + } switch (this.ApiService.ConnectionState) { case HubConnectionState.Connecting: @@ -116,135 +126,117 @@ public class MainWindow : Window, IDisposable 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.TextUnformatted("Share Code:"); ImGui.SameLine(); - ImGui.TextUnformatted("Enable Hosting"); - ImGui.Separator(); - if (isEnabled) + if (HostService.ActiveSession != null) { - ImGui.TextUnformatted("Share Code:"); - ImGui.SameLine(); - if (HostService.ActiveSession != null) + ImGui.TextColored(ImGuiColors.HealerGreen, HostService.ActiveSession.ShareCode); + if (ImGui.IsItemClicked()) { - ImGui.TextColored(ImGuiColors.HealerGreen, HostService.ActiveSession.ShareCode); - if (ImGui.IsItemClicked()) + ImGui.SetClipboardText(HostService.ActiveSession.ShareCode); + } + 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 (ToggleButtonWithHelpMarker("Allow new Joins", "Allows new players to join the group.", ref allowJoins)) + { + HostService.SetAllowJoins(allowJoins); + } + if (ToggleButtonWithHelpMarker("Skip Party Check", "Allows new players to join the group without being in your party first.", ref skipPartyCheck)) + { + HostService.SetSkipPartyCheck(skipPartyCheck); + } + if (ToggleButtonWithHelpMarker("Send Updates", "Sends quest updates to the server.", ref sendUpdates)) + { + HostService.SetIsActive(sendUpdates); + } + var track = Instance.TrackMSQ; + if (ToggleButtonWithHelpMarker("Track MSQ", "Automatically track the Main Scenario Quest.", ref track)) + { + Instance.TrackMSQ = track; + Save(); + } + ImGui.BeginDisabled(track); + using (var combo = ImRaii.Combo("##Quests", GameQuestManager.GetActiveQuest()?.QuestName ?? "---SELECT---", ImGuiComboFlags.HeightRegular)) + { + if (combo) { - ImGui.SetClipboardText(HostService.ActiveSession.ShareCode); - } - 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)) { - foreach (var quest in GameQuestManager.GameQuests.OrderBy(q => q.QuestName)) + if (ImGui.Selectable(quest.QuestName)) { - if (ImGui.Selectable(quest.QuestName)) - { - selectedQuest = quest; - GameQuestManager.SetActiveFlag(quest.QuestId); - HostService.Update((int)quest.QuestId, quest.CurrentStep); - ConfigurationManager.Save(); - } + selectedQuest = quest; + GameQuestManager.SetActiveFlag(quest.QuestId); + HostService.Update((int)quest.QuestId, quest.CurrentStep); + 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(); + } + ImGui.SameLine(); + if (ImGui.Button("Refresh")) + { + GameQuestManager.LoadQuests(); + } + ImGui.EndDisabled(); + + // add a line of space + ImGui.TextUnformatted(" "); + 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.Separator(); - var steps = selectedQuest.QuestSteps; - for (var i = 0; i < steps!.Count; i++) + var steps = selectedQuest.QuestSteps; + for (var i = 0; i < steps!.Count; i++) + { + var questText = Instance.HideFutureStepsHost && i + 1 > selectedQuest.CurrentStep ? "???" : steps[i]; + if (i + 1 == selectedQuest.CurrentStep || (selectedQuest.CurrentStep == 0 && i == 0) || (selectedQuest.CurrentStep == 0xFF && i + 1 == steps.Count)) { - - 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("???"); - } + ImGui.TextColored(ImGuiColors.HealerGreen, questText); } - - } else { - ImGui.BeginDisabled(generatePending); - if (ImGui.Button("Generate New")) + else if (i + 1 < selectedQuest.CurrentStep) { - ApiService.DispatchRegister(); + ImGui.TextColored(ImGuiColors.DalamudYellow, questText); + } + else + { + ImGui.TextUnformatted(questText); } - ImGui.EndDisabled(); } } + else + { + ImGui.BeginDisabled(generatePending); + if (ImGui.Button("Generate New")) + { + DispatchRegister(); + } + ImGui.EndDisabled(); + } } @@ -270,7 +262,7 @@ public class MainWindow : Window, IDisposable var payload = new Objects.ShareCode { CharacterId = ClientState.LocalContentId.ToString().SaltedHash(enteredShareCode), Code = enteredShareCode }; isJoining = true; ApiService.GroupJoined += OnGroupJoin; - ApiService.DispatchGroup(payload); + DispatchGroup(payload); } ImGui.EndDisabled(); ImGui.Separator(); @@ -289,7 +281,7 @@ public class MainWindow : Window, IDisposable DrawSessionDetails(session); if (ImGui.Button("Leave Group")) { - ApiService.DispatchUngroup(session); + DispatchUngroup(session); isLeaving = true; } } @@ -300,7 +292,7 @@ public class MainWindow : Window, IDisposable private void DrawSessionDetails(Objects.Session session) { - ImGui.TextUnformatted($"Owner: {session.OwnerCharacterId}"); + ImGui.TextUnformatted($"Owner: {ShareService.GetShareCodeOwner(session.ShareCode)}"); var activeQuest = session.ActiveQuestId; var activeStep = session.ActiveQuestStep; if (activeQuest != 0) @@ -309,21 +301,17 @@ public class MainWindow : Window, IDisposable 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++) { + var questText = Instance.HideFutureStepsMember && i + 1 > activeStep ? "???" : steps[i]; if (i + 1 == activeStep || (i + 1 == steps.Count && activeStep == 0xFF)) { - ImGui.TextColored(ImGuiColors.HealerGreen, steps[i]); + ImGui.TextColored(ImGuiColors.HealerGreen, questText); } else { - ImGui.TextUnformatted(steps[i]); + ImGui.TextUnformatted(questText); } } if (ImGui.Button("Get Marker")) @@ -350,31 +338,36 @@ public class MainWindow : Window, IDisposable }*/ } else { - ImGui.TextUnformatted("No active quest or host is offline."); + ImGui.TextUnformatted("No active quest."); } } + string newServerDisplayName = ""; + string newServerUrl = ""; + private void DrawSettingsTab() { - var selectedApiServer = ConfigurationManager.Instance.ApiDisplayName; + var selectedApiServer = Instance.ApiDisplayName; + ImGui.BeginDisabled(Instance.ApiConfigurations.Count < 1); if (ImGui.BeginCombo("API Server", selectedApiServer)) { - foreach (var server in ConfigurationManager.Instance.ApiConfigurations) + foreach (var server in 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) + var index = Array.FindIndex([.. Instance.ApiConfigurations], x => x.DisplayName == server.DisplayName); + Instance.ApiConfigurations[index].Active = true; + foreach (var config in Instance.ApiConfigurations) { if (config.DisplayName != server.DisplayName) { config.Active = false; } } - ConfigurationManager.Save(); + Save(); + Framework.Run(ApiService.OnUrlChange); } if (isSelected) { @@ -383,29 +376,117 @@ public class MainWindow : Window, IDisposable } ImGui.EndCombo(); } + ImGui.EndDisabled(); ImGui.SameLine(); if (ImGui.Button("Add")) { - // does nothing yet + // pop up a dialog to add a new server + ImGui.OpenPopup("Add Server"); } - var connectOnStartup = ConfigurationManager.Instance.ConnectOnStartup; - if (ImGui.Checkbox("Connect on Startup", ref connectOnStartup)) + ImGui.SameLine(); + ImGui.BeginDisabled(Instance.ApiConfigurations.Count < 1); + if (ImGui.Button("Delete")) { - ConfigurationManager.Instance.ConnectOnStartup = connectOnStartup; - ConfigurationManager.Save(); + // pop up a dialog to delete the selected server + ImGui.OpenPopup("Delete?"); } - var autoShareMsq = ConfigurationManager.Instance.AutoShareMsq; - if (ImGui.Checkbox("Auto Share MSQ", ref autoShareMsq)) + ImGui.EndDisabled(); + var center = ImGui.GetMainViewport().GetCenter(); + ImGui.SetNextWindowPos(center, ImGuiCond.Appearing, new Vector2(0.5f, 0.5f)); + var open = true; + using (var addServer = ImRaii.PopupModal("Add Server", ref open, ImGuiWindowFlags.AlwaysAutoResize)) { - ConfigurationManager.Instance.AutoShareMsq = autoShareMsq; - ConfigurationManager.Save(); + if (addServer) + { + ImGui.TextUnformatted("Add a new API server configuration."); + ImGui.InputText("Display Name", ref newServerDisplayName, 64); + ImGui.InputText("URL", ref newServerUrl, 200); + ImGui.TextUnformatted("Note: The URL should be the full URL to the hub endpoint and MUST be ws:// or wss:// (preferred)"); + bool isValid = Uri.TryCreate(newServerUrl, UriKind.Absolute, out var uriResult) && (uriResult.Scheme == Uri.UriSchemeWs || uriResult.Scheme == Uri.UriSchemeWss); + ImGui.BeginDisabled(!isValid || string.IsNullOrEmpty(newServerDisplayName) || string.IsNullOrEmpty(newServerUrl)); + if (ImGui.Button("Save")) + { + if (Instance.ApiConfigurations.Count == 0) + { + // add default server first + Instance.ApiConfigurations.Add(new ApiConfiguration { DisplayName = ConfigurationManager.Instance.ApiDisplayName, Url = ConfigurationManager.Instance.ApiUrl, Active = false }); + } + Instance.ApiConfigurations.Add(new ApiConfiguration { DisplayName = newServerDisplayName, Url = newServerUrl, Active = true }); + Save(); + ImGui.CloseCurrentPopup(); + newServerUrl = ""; + newServerDisplayName = ""; + } + ImGui.EndDisabled(); + ImGui.SameLine(); + if (ImGui.Button("Cancel")) + { + ImGui.CloseCurrentPopup(); + newServerUrl = ""; + newServerDisplayName = ""; + } + } } - var autoShareNewQuests = ConfigurationManager.Instance.AutoShareNewQuests; - if (ImGui.Checkbox("Auto Share New Quests", ref autoShareNewQuests)) + var deleteServer = false; + using (var delServer = ImRaii.PopupModal("Delete?", ref deleteServer, ImGuiWindowFlags.AlwaysAutoResize)) { - ConfigurationManager.Instance.AutoShareNewQuests = autoShareNewQuests; - ConfigurationManager.Save(); + if (delServer) + { + ImGui.TextUnformatted("Are you sure you want to delete this server?"); + if (ImGui.Button("Yes")) + { + var index = Array.FindIndex([.. Instance.ApiConfigurations], x => x.DisplayName == selectedApiServer); + Instance.ApiConfigurations.RemoveAt(index); + Save(); + ImGui.CloseCurrentPopup(); + Framework.Run(ApiService.OnUrlChange); + } + ImGui.SameLine(); + if (ImGui.Button("No")) + { + ImGui.CloseCurrentPopup(); + } + } } + var connectOnStartup = Instance.ConnectOnStartup; + if (ToggleButtonWithHelpMarker("Connect on Startup", "Automatically connect to the selected API server when the game is started.", ref connectOnStartup)) + { + Instance.ConnectOnStartup = connectOnStartup; + Save(); + } + var autoShareMsq = Instance.AutoShareMsq; + if (ToggleButtonWithHelpMarker("Auto Share MSQ", "Automatically share the Main Scenario Quest with your group when it is accepted.", ref autoShareMsq)) + { + Instance.AutoShareMsq = autoShareMsq; + Save(); + } + // TODO: Implement this feature + /*var autoShareNewQuests = Instance.AutoShareNewQuests; + if (ToggleButtonWithHelpMarker("Auto Share New Quests", "Automatically share new quests with your group when they are accepted.", ref autoShareNewQuests)) + { + Instance.AutoShareNewQuests = autoShareNewQuests; + Save(); + }*/ + var hideFutureStepsHost = Instance.HideFutureStepsHost; + if (ToggleButtonWithHelpMarker("Hide Future Steps (Host)", "Hides future steps of the quest from the UI when viewing your hosted quest. This does not affect the quest sharing process.", ref hideFutureStepsHost)) + { + Instance.HideFutureStepsHost = hideFutureStepsHost; + Save(); + } + var hideFutureStepsMember = Instance.HideFutureStepsMember; + if (ToggleButtonWithHelpMarker("Hide Future Steps (Member)", "Hides future steps of the quest from the UI when viewing a shared quest. This does not affect the quest sharing process.", ref hideFutureStepsMember)) + { + Instance.HideFutureStepsMember = hideFutureStepsMember; + Save(); + } + } + private static bool ToggleButtonWithHelpMarker(string label, string helpText, ref bool v) + { + ImGui.TextUnformatted(label); + ImGui.SameLine(); + var result = ImGuiComponents.ToggleButton(label, ref v); + ImGuiComponents.HelpMarker(helpText); + return result; } } diff --git a/QuestShare.Server/Hubs/Methods/Authorize.cs b/QuestShare.Server/Hubs/Methods/Authorize.cs index 8021869..5bf8ad2 100644 --- a/QuestShare.Server/Hubs/Methods/Authorize.cs +++ b/QuestShare.Server/Hubs/Methods/Authorize.cs @@ -49,41 +49,61 @@ namespace QuestShare.Server.Hubs var sessions = new List(); Objects.OwnedSession? ownedSession = null; - foreach(var share in request.ShareCodes) + 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) { - if (session.OwnerCharacterId == share.CharacterId && client.ClientId == session.Owner.ClientId && ownedSession == null) + ownedSession = new Objects.OwnedSession { - 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 + Session = new Objects.Session { OwnerCharacterId = session.OwnerCharacterId, + ShareCode = session.ShareCode, ActiveQuestId = session.SharedQuestId, - ActiveQuestStep = session.SharedQuestStep, - ShareCode = share.Code, - }); - } + ActiveQuestStep = session.SharedQuestStep + }, + SkipPartyCheck = session.SkipPartyCheck, + IsActive = session.IsActive, + AllowJoins = session.AllowJoins, + }; + } + else if (members.Any(m => m.Client.ClientId == client.ClientId)) + { + sessions.Add(new Objects.Session + { + OwnerCharacterId = session.OwnerCharacterId, + ActiveQuestId = session.SharedQuestId, + ActiveQuestStep = session.SharedQuestStep, + ShareCode = share.Code, + }); await Groups.AddToGroupAsync(Context.ConnectionId, session.ShareCode); } + else + { + sessions.Add(new Objects.Session + { + OwnerCharacterId = "", + ActiveQuestId = 0, + ActiveQuestStep = 0, + ShareCode = share.Code, + IsValid = false, + }); + } + } + else + { + sessions.Add(new Objects.Session + { + OwnerCharacterId = "", + ActiveQuestId = 0, + ActiveQuestStep = 0, + ShareCode = share.Code, + IsValid = false, + }); } } await Clients.Caller.SendAsync(nameof(Authorize), new Authorize.Response diff --git a/QuestShare.Server/Hubs/Methods/GroupJoin.cs b/QuestShare.Server/Hubs/Methods/GroupJoin.cs index 07cbc27..0b9a4a5 100644 --- a/QuestShare.Server/Hubs/Methods/GroupJoin.cs +++ b/QuestShare.Server/Hubs/Methods/GroupJoin.cs @@ -36,6 +36,36 @@ namespace QuestShare.Server.Hubs }); return; } + if (!session.AllowJoins) + { + Log.Warning($"[GroupJoin] Session {request.SessionInfo.Code} does not allow joins."); + await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response + { + Success = false, + Error = Error.InvalidParty, + }); + return; + } + if (session.Owner.ClientId == client!.ClientId) + { + Log.Warning($"[GroupJoin] Client {client} is the owner of session {session.ShareCode}"); + await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response + { + Success = false, + Error = Error.InvalidParty, + }); + return; + } + if (!session.SkipPartyCheck && !session.PartyMembers.Contains(request.SessionInfo.CharacterId)) + { + Log.Warning($"[GroupJoin] Client {client} is not joined to party hosted by {session.OwnerCharacterId}."); + await Clients.Caller.SendAsync(nameof(GroupJoin), new GroupJoin.Response + { + Success = false, + Error = Error.InvalidParty, + }); + return; + } await ClientManager.AddClientSession(client!.ClientId, session.SessionId); await Groups.AddToGroupAsync(Context.ConnectionId, session.ShareCode.ToString()); await ClientManager.AddKnownShareCode(client!, session.ShareCode); @@ -46,6 +76,8 @@ namespace QuestShare.Server.Hubs { OwnerCharacterId = session.OwnerCharacterId, ShareCode = session.ShareCode, + ActiveQuestId = session.SharedQuestId, + ActiveQuestStep = session.SharedQuestStep }, }); await Clients.GroupExcept(Context.ConnectionId, session.ShareCode.ToString()).SendAsync(nameof(GroupJoin.GroupJoinBroadcast), new GroupJoin.GroupJoinBroadcast diff --git a/QuestShare.Server/Hubs/Methods/Update.cs b/QuestShare.Server/Hubs/Methods/Update.cs index 3080a29..d5859b7 100644 --- a/QuestShare.Server/Hubs/Methods/Update.cs +++ b/QuestShare.Server/Hubs/Methods/Update.cs @@ -36,18 +36,25 @@ namespace QuestShare.Server.Hubs }); return; } - await SessionManager.UpdateActiveQuest(request.Session.ShareCode, request.Session.ActiveQuestId, request.Session.ActiveQuestStep); await SessionManager.SetPartyMembers(session!.ShareCode, [.. request.PartyMembers]); - await Clients.Caller.SendAsync(nameof(Update), new Update.Response + + if (request.IsQuestUpdate) { - Success = true, - Error = Error.None, - }); - // Broadcast to party - await Clients.GroupExcept(session.ShareCode.ToString(), Context.ConnectionId).SendAsync(nameof(Update.UpdateBroadcast), new Update.UpdateBroadcast + await SessionManager.UpdateActiveQuest(request.Session.ShareCode, request.Session.ActiveQuestId, request.Session.ActiveQuestStep); + // Broadcast to party + await Clients.GroupExcept(session.ShareCode.ToString(), Context.ConnectionId).SendAsync(nameof(Update.UpdateBroadcast), new Update.UpdateBroadcast + { + Session = request.Session.Session, + }); + } else { - Session = request.Session.Session, - }); + await SessionManager.SetSessionSettings(session.ShareCode, request.Session); + await Clients.Caller.SendAsync(nameof(Update), new Update.Response + { + Success = true, + Error = Error.None, + }); + } } } } diff --git a/QuestShare.Server/Managers/SessionManager.cs b/QuestShare.Server/Managers/SessionManager.cs index caba8c5..4b7c6d2 100644 --- a/QuestShare.Server/Managers/SessionManager.cs +++ b/QuestShare.Server/Managers/SessionManager.cs @@ -9,12 +9,12 @@ namespace QuestShare.Server.Managers public static async Task GetSession(Client client) { using var context = new QuestShareContext(); - return await context.Sessions.Where(s => s.Owner.ClientId == client.ClientId).FirstOrDefaultAsync(); + return await context.Sessions.Where(s => s.Owner.ClientId == client.ClientId).Include(session => session.Owner).FirstOrDefaultAsync(); } public static async Task GetSession(string ShareCode) { using var context = new QuestShareContext(); - var session = await context.Sessions.Where(s => s.ShareCode == ShareCode).FirstOrDefaultAsync(); + var session = await context.Sessions.Where(s => s.ShareCode == ShareCode).Include(session => session.Owner).FirstOrDefaultAsync(); return session; } @@ -47,6 +47,7 @@ namespace QuestShare.Server.Managers s.AllowJoins = session.AllowJoins; s.SkipPartyCheck = session.SkipPartyCheck; await context.SaveChangesAsync(); + await AddMemberToSession(s, s.OwnerCharacterId); return s; } @@ -92,7 +93,7 @@ namespace QuestShare.Server.Managers public static async Task> 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(); + var s = await context.SessionMembers.Where(s => s.Session.SessionId == session.SessionId).Include(sm => sm.Session).Include(sm => sm.Client).ToListAsync(); return s; } @@ -111,5 +112,22 @@ namespace QuestShare.Server.Managers var records = await context.SaveChangesAsync(); Log.Debug($"[UPDATE] Updated {records} quests for session {shareCode}"); } + + public static async Task SetSessionSettings(string shareCode, Objects.OwnedSession sessionObj) + { + 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 settings for session {shareCode}"); + return; + } + session.IsActive = sessionObj.IsActive; + session.AllowJoins = sessionObj.AllowJoins; + session.SkipPartyCheck = sessionObj.SkipPartyCheck; + var records = await context.SaveChangesAsync(); + Log.Debug($"[UPDATE] Updated {records} settings for session {shareCode}"); + } } } diff --git a/QuestShare.Server/Models/QuestShareContext.cs b/QuestShare.Server/Models/QuestShareContext.cs index f876fa3..8ea98fc 100644 --- a/QuestShare.Server/Models/QuestShareContext.cs +++ b/QuestShare.Server/Models/QuestShareContext.cs @@ -21,7 +21,9 @@ namespace QuestShare.Server.Models protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseNpgsql(Environment.GetEnvironmentVariable("QUESTSHARE_DATABASE")); + optionsBuilder + .UseLazyLoadingProxies() + .UseNpgsql(Environment.GetEnvironmentVariable("QUESTSHARE_DATABASE")); } diff --git a/QuestShare.Server/Models/Session.cs b/QuestShare.Server/Models/Session.cs index 8ee089d..60515cb 100644 --- a/QuestShare.Server/Models/Session.cs +++ b/QuestShare.Server/Models/Session.cs @@ -13,7 +13,7 @@ namespace QuestShare.Server.Models [Key] public Guid SessionId { get; set; } public string OwnerCharacterId { get; set; } = ""; - public required Client Owner { get; set; } + public virtual required Client Owner { get; set; } public required string ShareCode { get; set; } public required string ReservedConnectionId { get; set; } public int SharedQuestId { get; set; } = 0; diff --git a/QuestShare.Server/Models/SessionMember.cs b/QuestShare.Server/Models/SessionMember.cs index 66f1282..e55c6b8 100644 --- a/QuestShare.Server/Models/SessionMember.cs +++ b/QuestShare.Server/Models/SessionMember.cs @@ -9,8 +9,8 @@ namespace QuestShare.Server.Models public class SessionMember { public Guid ClientSessionId { get; set; } - public required Client Client { get; set; } - public required Session Session { get; set; } + public virtual required Client Client { get; set; } + public virtual required Session Session { get; set; } // public required string CharacterId { get; set; } public DateTime Created { get; set; } public DateTime LastUpdated { get; set; } diff --git a/QuestShare.Server/Program.cs b/QuestShare.Server/Program.cs index 60b5acd..473bf15 100644 --- a/QuestShare.Server/Program.cs +++ b/QuestShare.Server/Program.cs @@ -12,7 +12,7 @@ namespace QuestShare.Server { public class Program { - public static async Task Main(string[] args) + public static void Main(string[] args) { var log = new LoggerConfiguration() .WriteTo.Console() diff --git a/QuestShare.Server/Properties/launchSettings.json b/QuestShare.Server/Properties/launchSettings.json index 07637e7..a5e61a4 100644 --- a/QuestShare.Server/Properties/launchSettings.json +++ b/QuestShare.Server/Properties/launchSettings.json @@ -3,7 +3,8 @@ "http": { "commandName": "Project", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "QUESTSHARE_DATABASE": "Host=sol.nate.lan;User ID=dalamud;Password=dalamud1meteor;Database=questshare" }, "dotnetRunMessages": true, "applicationUrl": "http://localhost:5087" diff --git a/QuestShare.Server/QuestShare.Server.csproj b/QuestShare.Server/QuestShare.Server.csproj index bd37534..856c79e 100644 --- a/QuestShare.Server/QuestShare.Server.csproj +++ b/QuestShare.Server/QuestShare.Server.csproj @@ -10,16 +10,17 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + - + diff --git a/plogonmaster.json b/plogonmaster.json index 58051c3..fa9fa22 100644 --- a/plogonmaster.json +++ b/plogonmaster.json @@ -12,9 +12,9 @@ ], "RepoUrl": "https://git.nathanc.tech/nate/QuestShare/", "AcceptsFeedback": false, - "DownloadLinkInstall": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-4/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-4/latest.zip", + "DownloadLinkInstall": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-5/latest.zip", + "DownloadLinkTesting": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-5/latest.zip", + "DownloadLinkUpdate": "https://git.nathanc.tech/nate/QuestShare/releases/download/alpha-5/latest.zip", "DownloadCount": 1, "LastUpdate": "1739859999", "IsHide": false, @@ -22,7 +22,7 @@ "IconUrl": "", "DalamudApiLevel": 11, "InternalName": "QuestShare", - "AssemblyVersion": "1.0.1.0" + "AssemblyVersion": "1.0.2.0" } ]