Update project
This commit is contained in:
parent
93231e5419
commit
6f95fcde97
@ -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
|
||||
|
||||
@ -24,5 +24,6 @@ namespace QuestShare.Common
|
||||
BannedTooManyBadRequests,
|
||||
AlreadyRegistered,
|
||||
AlreadyJoined,
|
||||
ServerMaintenance,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -19,8 +19,8 @@ public class ConfigurationManager
|
||||
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 Dictionary<string, string> ShareCodeOwners { get; set; } = [];
|
||||
public List<ApiConfiguration> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<IService> 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,12 +2,13 @@
|
||||
<Project Sdk="Dalamud.NET.SDK/11.2.0">
|
||||
<PropertyGroup>
|
||||
<Version>0.0.0.1</Version>
|
||||
<Description>A sample plugin.</Description>
|
||||
<PackageProjectUrl>https://github.com/goatcorp/SamplePlugin</PackageProjectUrl>
|
||||
<Description>Share quests with your friends so they can follow you on your journey.</Description>
|
||||
<PackageProjectUrl>https://github.com/Era-FFXIV/QuestShare</PackageProjectUrl>
|
||||
<PackageLicenseExpression>AGPL-3.0-or-later</PackageLicenseExpression>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RepositoryUrl>https://github.com/Era-FFXIV/QuestShare</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="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
|
||||
}
|
||||
|
||||
@ -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<ApiService>()).Disconnect();
|
||||
if (response.Error == Error.InvalidVersion)
|
||||
{
|
||||
UiService.LastServerMessage = "Invalid version detected, please update the plugin.";
|
||||
((ApiService)Plugin.GetService<ApiService>()).IsLockedOut = true;
|
||||
}
|
||||
else if (response.Error == Error.InvalidToken)
|
||||
{
|
||||
UiService.LastServerMessage = "Invalid token detected, please reauthorize.";
|
||||
((ApiService)Plugin.GetService<ApiService>()).IsLockedOut = true;
|
||||
}
|
||||
else if (response.Error == Error.BannedTooManyBadRequests)
|
||||
{
|
||||
UiService.LastServerMessage = "You are temporarily banned due to too many bad requests.";
|
||||
((ApiService)Plugin.GetService<ApiService>()).IsLockedOut = true;
|
||||
}
|
||||
else if (response.Error == Error.ServerMaintenance)
|
||||
{
|
||||
UiService.LastServerMessage = "Server is currently undergoing maintenance. Please try again later.";
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ namespace QuestShare.Services.API
|
||||
var share = (ShareService)Plugin.GetService<ShareService>();
|
||||
Log.Information("Successfully joined group.");
|
||||
share.AddSession(resp.Session);
|
||||
ShareService.RecheckShareCodes();
|
||||
var api = (ApiService)Plugin.GetService<ApiService>();
|
||||
api.OnGroupJoin(new ApiService.GroupJoinEventArgs { Session = resp.Session, IsSuccess = true });
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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<IAPIHandler> 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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>();
|
||||
ApiService.DispatchCancel();
|
||||
}
|
||||
|
||||
internal static void Update(uint questId, byte currentStep)
|
||||
{
|
||||
Update((int)questId, currentStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@ namespace QuestShare.Services
|
||||
internal class ShareService : IService
|
||||
{
|
||||
internal static List<Objects.ShareCode> ShareCodes => ConfigurationManager.Instance.KnownShareCodes;
|
||||
internal static Dictionary<string, string> ShareCodeOwners => ConfigurationManager.Instance.ShareCodeOwners;
|
||||
internal List<Objects.Session> Sessions { get; private set; } = [];
|
||||
internal Dictionary<long, string> 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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
||||
@ -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<ApiConfiguration>([.. 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<ApiConfiguration>([.. 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,41 +49,61 @@ namespace QuestShare.Server.Hubs
|
||||
var sessions = new List<Objects.Session>();
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,12 +9,12 @@ namespace QuestShare.Server.Managers
|
||||
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();
|
||||
return await context.Sessions.Where(s => s.Owner.ClientId == client.ClientId).Include(session => session.Owner).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();
|
||||
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<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();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -10,16 +10,17 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||
<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.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user