diff --git a/ScoutHelper/Config/Configuration.cs b/ScoutHelper/Config/Configuration.cs
index 031d3c0..b33747e 100644
--- a/ScoutHelper/Config/Configuration.cs
+++ b/ScoutHelper/Config/Configuration.cs
@@ -29,6 +29,7 @@ public class Configuration : IPluginConfiguration {
public string TurtleApiBaseUrl = "https://scout.wobbuffet.net";
public string TurtleApiTrainPath = "/api/v1/scout";
public TimeSpan TurtleApiTimeout = TimeSpan.FromSeconds(5);
+ public bool IncludeNameInTurtleSession = true;
public string CopyTemplate = Constants.DefaultCopyTemplate;
public bool IsCopyModeFullText = false;
diff --git a/ScoutHelper/Localization/Strings.Designer.cs b/ScoutHelper/Localization/Strings.Designer.cs
index 3866f07..99b8cf0 100644
--- a/ScoutHelper/Localization/Strings.Designer.cs
+++ b/ScoutHelper/Localization/Strings.Designer.cs
@@ -358,16 +358,7 @@ internal static string TurtleCollabButton {
}
///
- /// Looks up a localized string similar to click to leave the current session..
- ///
- internal static string TurtleCollabButtonActiveTooltip {
- get {
- return ResourceManager.GetString("TurtleCollabButtonActiveTooltip", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to start or join a collaborative scouting session on turtle..
+ /// Looks up a localized string similar to manage or join a collaborative turtle session..
///
internal static string TurtleCollabButtonTooltip {
get {
diff --git a/ScoutHelper/Localization/Strings.resx b/ScoutHelper/Localization/Strings.resx
index a174d14..1b46a65 100644
--- a/ScoutHelper/Localization/Strings.resx
+++ b/ScoutHelper/Localization/Strings.resx
@@ -112,10 +112,7 @@
COLLAB
- start or join a collaborative scouting session on turtle.
-
-
- click to leave the current session.
+ manage or join a collaborative turtle session.
INSTANCES
diff --git a/ScoutHelper/Managers/HuntHelperManager.cs b/ScoutHelper/Managers/HuntHelperManager.cs
index b7f3757..a6c545b 100644
--- a/ScoutHelper/Managers/HuntHelperManager.cs
+++ b/ScoutHelper/Managers/HuntHelperManager.cs
@@ -4,8 +4,10 @@
using ScoutHelper.Models;
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
+using ScoutHelper.Utils;
namespace ScoutHelper.Managers;
@@ -13,29 +15,42 @@ public class HuntHelperManager : IDisposable {
private const uint SupportedVersion = 1;
private readonly IPluginLog _log;
+ private readonly IChatGui _chat;
+ private readonly TurtleManager _turtleManager;
private readonly ICallGateSubscriber _cgGetVersion;
private readonly ICallGateSubscriber _cgEnable;
private readonly ICallGateSubscriber _cgDisable;
private readonly ICallGateSubscriber> _cgGetTrainList;
+ private readonly ICallGateSubscriber _cgMarkSeen;
public bool Available { get; private set; } = false;
- public HuntHelperManager(IDalamudPluginInterface pluginInterface, IPluginLog log) {
+ public HuntHelperManager(
+ IDalamudPluginInterface pluginInterface,
+ IPluginLog log,
+ IChatGui chat,
+ TurtleManager turtleManager
+ ) {
_log = log;
+ _chat = chat;
+ _turtleManager = turtleManager;
_cgGetVersion = pluginInterface.GetIpcSubscriber("HH.GetVersion");
_cgEnable = pluginInterface.GetIpcSubscriber("HH.Enable");
_cgDisable = pluginInterface.GetIpcSubscriber("HH.Disable");
_cgGetTrainList = pluginInterface.GetIpcSubscriber>("HH.GetTrainList");
+ _cgMarkSeen = pluginInterface.GetIpcSubscriber("HH.channel.MarkSeen");
CheckVersion();
_cgEnable.Subscribe(OnEnable);
_cgDisable.Subscribe(OnDisable);
+ _cgMarkSeen.Subscribe(OnMarkSeen);
}
public void Dispose() {
_cgEnable.Unsubscribe(OnEnable);
_cgDisable.Unsubscribe(OnDisable);
+ _cgMarkSeen.Unsubscribe(OnMarkSeen);
}
private void OnEnable(uint version) {
@@ -67,6 +82,32 @@ private void CheckVersion(uint? version = null) {
}
}
+ private void OnMarkSeen(TrainMob mark) {
+ if (!_turtleManager.IsTurtleCollabbing) return;
+
+ _turtleManager.UpdateCurrentSession(mark.AsSingletonList())
+ .ContinueWith(
+ task => {
+ switch (task.Result) {
+ case TurtleHttpStatus.Success:
+ _chat.TaggedPrint($"added {mark.Name} to the turtle session.");
+ break;
+ case TurtleHttpStatus.NoSupportedMobs:
+ _chat.TaggedPrint($"{mark.Name} was seen, but is not supported by turtle and will not be added to the session.");
+ break;
+ case TurtleHttpStatus.HttpError:
+ _chat.TaggedPrintError($"something went wrong when adding {mark.Name} to the turtle session ;-;.");
+ break;
+ }
+ },
+ TaskContinuationOptions.OnlyOnRanToCompletion
+ )
+ .ContinueWith(
+ task => _log.Error(task.Exception, "failed to update turtle session"),
+ TaskContinuationOptions.OnlyOnFaulted
+ );
+ }
+
public Result, string> GetTrainList() {
if (!Available) {
return "Hunt Helper is not currently available ;-;";
diff --git a/ScoutHelper/Managers/TurtleManager.cs b/ScoutHelper/Managers/TurtleManager.cs
index 1349394..73a025e 100644
--- a/ScoutHelper/Managers/TurtleManager.cs
+++ b/ScoutHelper/Managers/TurtleManager.cs
@@ -3,12 +3,11 @@
using System.IO;
using System.Linq;
using System.Net.Http;
-using System.Numerics;
-using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CSharpFunctionalExtensions;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Newtonsoft.Json;
using ScoutHelper.Config;
using ScoutHelper.Models;
@@ -27,10 +26,11 @@ namespace ScoutHelper.Managers;
public partial class TurtleManager {
[GeneratedRegex(@"(?:/scout)?/?(?\w+)/(?\w+)/?\s*$")]
private static partial Regex CollabLinkRegex();
+ private static HttpClient HttpClient { get; } = new();
private readonly IPluginLog _log;
private readonly Configuration _conf;
- private static HttpClient HttpClient { get; } = new();
+ private readonly IClientState _clientState;
private MobDict MobIdToTurtleId { get; }
private TerritoryDict TerritoryIdToTurtleData { get; }
@@ -38,15 +38,19 @@ public partial class TurtleManager {
private string _currentCollabSession = "";
private string _currentCollabPassword = "";
+ public bool IsTurtleCollabbing { get; private set; } = false;
+
public TurtleManager(
IPluginLog log,
Configuration conf,
+ IClientState clientState,
ScoutHelperOptions options,
TerritoryManager territoryManager,
MobManager mobManager
) {
_log = log;
_conf = conf;
+ _clientState = clientState;
HttpClient.BaseAddress = new Uri(_conf.TurtleApiBaseUrl);
HttpClient.DefaultRequestHeaders.UserAgent.Add(Constants.UserAgent);
@@ -62,9 +66,18 @@ MobManager mobManager
_currentCollabSession = match.Groups["session"].Value;
_currentCollabPassword = match.Groups["password"].Value;
+ IsTurtleCollabbing = true;
return (_currentCollabSession, _currentCollabPassword);
}
+ public void RejoinLastCollabSession() {
+ if (_currentCollabSession.IsNullOrEmpty() || _currentCollabPassword.IsNullOrEmpty())
+ throw new Exception("cannot rejoin the last turtle collab session as there is no last session.");
+ IsTurtleCollabbing = true;
+ }
+
+ public void LeaveCollabSession() => IsTurtleCollabbing = false;
+
public async Task UpdateCurrentSession(IList train) {
var turtleSupportedMobs = train.Where(mob => MobIdToTurtleId.ContainsKey(mob.MobId)).AsList();
if (turtleSupportedMobs.IsEmpty())
@@ -75,6 +88,7 @@ public async Task UpdateCurrentSession(IList train)
_log,
new TurtleTrainUpdateRequest(
_currentCollabPassword,
+ _clientState.PlayerTag().Where(_ => _conf.IncludeNameInTurtleSession),
turtleSupportedMobs.Select(
mob =>
(TerritoryIdToTurtleData[mob.TerritoryId].TurtleId,
@@ -83,7 +97,7 @@ public async Task UpdateCurrentSession(IList train)
mob.Position)
)
),
- requestContent => HttpClient.PutAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", requestContent)
+ requestContent => HttpClient.PatchAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", requestContent)
).TapError(
error => {
if (error.ErrorType == HttpErrorType.Timeout) {
diff --git a/ScoutHelper/Models/Http/TurtleTrainUpdateRequest.cs b/ScoutHelper/Models/Http/TurtleTrainUpdateRequest.cs
index 1ac8dc5..8780f51 100644
--- a/ScoutHelper/Models/Http/TurtleTrainUpdateRequest.cs
+++ b/ScoutHelper/Models/Http/TurtleTrainUpdateRequest.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
+using CSharpFunctionalExtensions;
using Newtonsoft.Json;
namespace ScoutHelper.Models.Http;
@@ -8,14 +9,19 @@ namespace ScoutHelper.Models.Http;
public record struct TurtleTrainUpdateRequest {
[JsonProperty("collaborator_password")]
public string CollaboratorPassword { get; }
+
+ [JsonProperty("update_user")]
+ public string? UpdateUser { get; }
[JsonProperty("sightings")] public IList Sightings { get; }
public TurtleTrainUpdateRequest(
string collaboratorPassword,
+ Maybe updateUser,
IEnumerable<(uint zoneId, uint instance, uint mobId, Vector2 position)> marks
) {
CollaboratorPassword = collaboratorPassword;
+ UpdateUser = updateUser.GetValueOrDefault();
Sightings = marks
.Select(mark => new TurtleTrainUpdateMark(mark.zoneId, mark.instance, mark.mobId, mark.position))
.AsList();
diff --git a/ScoutHelper/Utils/HttpUtils.cs b/ScoutHelper/Utils/HttpUtils.cs
index 6d073c7..91ec9b2 100644
--- a/ScoutHelper/Utils/HttpUtils.cs
+++ b/ScoutHelper/Utils/HttpUtils.cs
@@ -10,6 +10,12 @@
namespace ScoutHelper.Utils;
public static class HttpUtils {
+ private static readonly JsonSerializerSettings JsonSerializerSettings = new() {
+ DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK",
+ DateTimeZoneHandling = DateTimeZoneHandling.Utc,
+ NullValueHandling = NullValueHandling.Ignore,
+ };
+
public static Task> DoRequest(
IPluginLog log,
T requestObject,
@@ -31,13 +37,13 @@ public static async Task> DoRequest(
Func> requestAction
) {
try {
- var requestPayload = JsonConvert.SerializeObject(requestObject);
- log.Debug("Request body: {0}", requestPayload);
+ var requestPayload = JsonConvert.SerializeObject(requestObject, JsonSerializerSettings);
+ log.Debug("Request body: {0:l}", requestPayload);
var requestContent = new StringContent(requestPayload, Encoding.UTF8, Constants.MediaTypeJson);
var response = await requestAction(requestContent);
log.Debug(
- "Request: {0}\n\nResponse: {1}",
+ "Request: {0:l}\n\nResponse: {1:l}",
response.RequestMessage!.ToString(),
response.ToString()
);
@@ -45,7 +51,7 @@ Func> requestAction
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
- log.Debug("Response body: {0}", responseJson);
+ log.Debug("Response body: {0:l}", responseJson);
return responseJson;
} catch (TimeoutException) {
return new HttpError(HttpErrorType.Timeout);
diff --git a/ScoutHelper/Utils/XivExtensions.cs b/ScoutHelper/Utils/XivExtensions.cs
index bc79651..1ff3aa2 100644
--- a/ScoutHelper/Utils/XivExtensions.cs
+++ b/ScoutHelper/Utils/XivExtensions.cs
@@ -15,6 +15,18 @@ public static partial class XivExtensions {
public static string WorldName(this IClientState clientState) =>
clientState.LocalPlayer?.CurrentWorld.GameData?.Name.ToString() ?? "Not Found";
+ public static Maybe PlayerTag(this IClientState clientState) =>
+ clientState
+ .LocalPlayer
+ .AsMaybe()
+ .Select(
+ player => {
+ var playerName = player.Name.TextValue;
+ var worldName = player.HomeWorld.GameData?.Name?.RawString ?? "Unknown World";
+ return $"{playerName}@{worldName}";
+ }
+ );
+
public static string PluginFilePath(this IDalamudPluginInterface pluginInterface, string dataFilename) => Path.Combine(
pluginInterface.AssemblyLocation.Directory?.FullName!,
dataFilename
diff --git a/ScoutHelper/Windows/MainWindow.cs b/ScoutHelper/Windows/MainWindow.cs
index ed61c1e..4592132 100644
--- a/ScoutHelper/Windows/MainWindow.cs
+++ b/ScoutHelper/Windows/MainWindow.cs
@@ -5,6 +5,7 @@
using System.Threading.Tasks;
using CSharpFunctionalExtensions;
using Dalamud.Interface;
+using Dalamud.Interface.Components;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
@@ -60,7 +61,6 @@ public class MainWindow : Window, IDisposable {
private Vector4 _noticeAckButtonColor = DangerFgColor;
// turtle stuff
- private bool _isTurtleCollabbing = false;
private string _collabInput = "";
private string _collabLink = "";
private bool _closeTurtleCollabPopup = false;
@@ -348,10 +348,10 @@ private unsafe void DrawTurtleButtons() {
);
if (ImGui.IsItemHovered())
ImGuiPlus.CreateTooltip(
- _isTurtleCollabbing ? Strings.TurtleButtonActiveCollabTooltip : Strings.TurtleButtonTooltip
+ _turtleManager.IsTurtleCollabbing ? Strings.TurtleButtonActiveCollabTooltip : Strings.TurtleButtonTooltip
);
if (turtlePressed) {
- if (_isTurtleCollabbing) {
+ if (_turtleManager.IsTurtleCollabbing) {
PushLatestMobsToTurtle();
} else {
_chat.TaggedPrint("Generating Turtle link...");
@@ -368,7 +368,7 @@ private unsafe void DrawTurtleButtons() {
ImGui.SameLine();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() - itemSpacing.X + itemSpacing.Y);
- var collabColor = _isTurtleCollabbing
+ var collabColor = _turtleManager.IsTurtleCollabbing
? *ImGui.GetStyleColorVec4(ImGuiCol.ButtonActive)
: *ImGui.GetStyleColorVec4(ImGuiCol.Button);
var turtleCollabPressed = ImGuiPlus
@@ -381,22 +381,25 @@ private unsafe void DrawTurtleButtons() {
ImDrawFlags.RoundCornersRight
)
);
- if (ImGui.IsItemHovered())
- ImGuiPlus.CreateTooltip(
- _isTurtleCollabbing ? Strings.TurtleCollabButtonActiveTooltip : Strings.TurtleCollabButtonTooltip
- );
- if (turtleCollabPressed) {
- if (_isTurtleCollabbing) _isTurtleCollabbing = false;
- else {
- ImGui.OpenPopup("turtle collab popup");
- }
- }
+ if (ImGui.IsItemHovered()) ImGuiPlus.CreateTooltip(Strings.TurtleCollabButtonTooltip);
+ if (turtleCollabPressed) ImGui.OpenPopup("turtle collab popup");
}
private void DrawTurtleCollabPopup() {
var contentWidth = 1.5f * _buttonSize.Value.X;
ImGui.PushTextWrapPos(contentWidth);
+ ImGuiPlus.Heading("SESSION", centered: true);
+
+ ImGui.Checkbox("include name", ref _conf.IncludeNameInTurtleSession);
+ ImGui.SameLine();
+ ImGuiComponents.HelpMarker("share your character name in the turtle session, so others can see that you contributed.");
+
+ ImGui.BeginDisabled(!_turtleManager.IsTurtleCollabbing);
+ if (ImGui.Button("LEAVE SESSION")) _turtleManager.LeaveCollabSession();
+ ImGui.EndDisabled();
+
+ ImGuiPlus.Separator();
ImGuiPlus.Heading("NEW", centered: true);
ImGui.TextWrapped("start a new scout session on turtle for other scouters to join and contribute to.");
if (ImGui.Button("START NEW SESSION", _buttonSize.Value with { X = contentWidth })) {
@@ -448,7 +451,6 @@ private bool JoinTurtleCollabSession(string collabLink) {
sessionInfo => {
_collabLink = $"{_conf.TurtleBaseUrl}{_conf.TurtleTrainPath}/{sessionInfo.slug}/{sessionInfo.password}";
_chat.TaggedPrint($"joined turtle session: {_collabLink}");
- _isTurtleCollabbing = true;
_alreadyContributedMobs.Clear();
},
() => _chat.TaggedPrintError($"failed to parse collab link. please ensure it is a valid link.\n{collabLink}")