Skip to content

Commit

Permalink
automatically update the turtle session when a mark is detected (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
dit-zy authored Aug 11, 2024
1 parent 01731d5 commit b53dca2
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 38 deletions.
1 change: 1 addition & 0 deletions ScoutHelper/Config/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 1 addition & 10 deletions ScoutHelper/Localization/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions ScoutHelper/Localization/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,7 @@
<value> COLLAB </value>
</data>
<data name="TurtleCollabButtonTooltip" xml:space="preserve">
<value>start or join a collaborative scouting session on turtle.</value>
</data>
<data name="TurtleCollabButtonActiveTooltip" xml:space="preserve">
<value>click to leave the current session.</value>
<value>manage or join a collaborative turtle session.</value>
</data>
<data name="ConfigWindowTweaksSectionLabelInstances" xml:space="preserve">
<value>INSTANCES</value>
Expand Down
43 changes: 42 additions & 1 deletion ScoutHelper/Managers/HuntHelperManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,53 @@
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;

public class HuntHelperManager : IDisposable {
private const uint SupportedVersion = 1;

private readonly IPluginLog _log;
private readonly IChatGui _chat;
private readonly TurtleManager _turtleManager;
private readonly ICallGateSubscriber<uint> _cgGetVersion;
private readonly ICallGateSubscriber<uint, bool> _cgEnable;
private readonly ICallGateSubscriber<bool> _cgDisable;
private readonly ICallGateSubscriber<List<TrainMob>> _cgGetTrainList;
private readonly ICallGateSubscriber<TrainMob, bool> _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<uint>("HH.GetVersion");
_cgEnable = pluginInterface.GetIpcSubscriber<uint, bool>("HH.Enable");
_cgDisable = pluginInterface.GetIpcSubscriber<bool>("HH.Disable");
_cgGetTrainList = pluginInterface.GetIpcSubscriber<List<TrainMob>>("HH.GetTrainList");
_cgMarkSeen = pluginInterface.GetIpcSubscriber<TrainMob, bool>("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) {
Expand Down Expand Up @@ -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<List<TrainMob>, string> GetTrainList() {
if (!Available) {
return "Hunt Helper is not currently available ;-;";
Expand Down
22 changes: 18 additions & 4 deletions ScoutHelper/Managers/TurtleManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,26 +26,31 @@ namespace ScoutHelper.Managers;
public partial class TurtleManager {
[GeneratedRegex(@"(?:/scout)?/?(?<session>\w+)/(?<password>\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; }

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);
Expand All @@ -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<TurtleHttpStatus> UpdateCurrentSession(IList<TrainMob> train) {
var turtleSupportedMobs = train.Where(mob => MobIdToTurtleId.ContainsKey(mob.MobId)).AsList();
if (turtleSupportedMobs.IsEmpty())
Expand All @@ -75,6 +88,7 @@ public async Task<TurtleHttpStatus> UpdateCurrentSession(IList<TrainMob> train)
_log,
new TurtleTrainUpdateRequest(
_currentCollabPassword,
_clientState.PlayerTag().Where(_ => _conf.IncludeNameInTurtleSession),
turtleSupportedMobs.Select(
mob =>
(TerritoryIdToTurtleData[mob.TerritoryId].TurtleId,
Expand All @@ -83,7 +97,7 @@ public async Task<TurtleHttpStatus> UpdateCurrentSession(IList<TrainMob> train)
mob.Position)
)
),
requestContent => HttpClient.PutAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", requestContent)
requestContent => HttpClient.PatchAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", requestContent)
).TapError(
error => {
if (error.ErrorType == HttpErrorType.Timeout) {
Expand Down
6 changes: 6 additions & 0 deletions ScoutHelper/Models/Http/TurtleTrainUpdateRequest.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using CSharpFunctionalExtensions;
using Newtonsoft.Json;

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<TurtleTrainUpdateMark> Sightings { get; }

public TurtleTrainUpdateRequest(
string collaboratorPassword,
Maybe<string> 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();
Expand Down
14 changes: 10 additions & 4 deletions ScoutHelper/Utils/HttpUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Result<U, HttpError>> DoRequest<T, U>(
IPluginLog log,
T requestObject,
Expand All @@ -31,21 +37,21 @@ public static async Task<Result<string, HttpError>> DoRequest<T>(
Func<HttpContent, Task<HttpResponseMessage>> 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()
);

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);
Expand Down
12 changes: 12 additions & 0 deletions ScoutHelper/Utils/XivExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> 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
Expand Down
32 changes: 17 additions & 15 deletions ScoutHelper/Windows/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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...");
Expand All @@ -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
Expand All @@ -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 })) {
Expand Down Expand Up @@ -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}")
Expand Down

0 comments on commit b53dca2

Please sign in to comment.