Skip to content

Commit

Permalink
Add option to speed up and remove range check for open world preset p…
Browse files Browse the repository at this point in the history
…lacement fixes #2
  • Loading branch information
sourpuh committed Jan 10, 2025
1 parent 7db0848 commit e193909
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 37 deletions.
4 changes: 4 additions & 0 deletions WaymarkStudio/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ public class Configuration : IPluginConfiguration
public bool CombineEquivalentDutyPresets = true;
public bool ReplaceNativeUi = true;
public bool ClearNativeWhenPlacing = false;
public bool DisableWorldPresetSafetyChecks = false;
public List<WaymarkPreset> SavedPresets { get; set; } = [];

public void Save()
{
Plugin.Interface.SavePluginConfig(this);
}

[JsonIgnore]
public int WaymarkPlacementFrequency => DisableWorldPresetSafetyChecks ? 1 : 60;
}
69 changes: 34 additions & 35 deletions WaymarkStudio/WaymarkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Lumina.Excel.Sheets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using WaymarkStudio.Guides;
Expand All @@ -18,7 +17,6 @@ namespace WaymarkStudio;
internal class WaymarkManager
{
private static readonly IReadOnlyDictionary<Waymark, Vector3> EmptyWaymarks = new Dictionary<Waymark, Vector3>();
private static readonly TimeSpan MinPlacementFrequency = TimeSpan.FromSeconds(0.25);

internal bool showGuide = false;
internal Guide guide;
Expand All @@ -28,8 +26,7 @@ internal class WaymarkManager
internal string mapName;
internal Dictionary<Waymark, Vector3> placeholders = new();
internal IReadOnlyDictionary<Waymark, Vector3> hoverPreviews = EmptyWaymarks;
private readonly Stopwatch lastPlacementTimer;
private List<(Waymark waymark, Vector3 wPos)> safePlaceQueue = new();
private Queue<(Waymark waymark, Vector3 wPos)> safePlaceQueue = new();

private unsafe delegate byte PlaceWaymark(MarkingController* markingController, uint marker, Vector3 wPos);
private readonly PlaceWaymark placeWaymarkFn;
Expand All @@ -55,8 +52,6 @@ internal class WaymarkManager

public WaymarkManager()
{
lastPlacementTimer = new();
lastPlacementTimer.Start();
placeWaymarkFn = Marshal.GetDelegateForFunctionPointer<PlaceWaymark>(Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? 84 C0 0F 85 ?? ?? ?? ?? EB 23"));
clearWaymarkFn = Marshal.GetDelegateForFunctionPointer<ClearWaymark>(Plugin.SigScanner.ScanText("E8 ?? ?? ?? ?? EB D8 83 FB 09"));
clearWaymarksFn = Marshal.GetDelegateForFunctionPointer<ClearWaymarks>(Plugin.SigScanner.ScanText("41 55 48 83 EC 50 4C 8B E9"));
Expand Down Expand Up @@ -152,8 +147,6 @@ internal PlacementUnsafeReason WaymarkPlacementStatus(Vector3 wPos)
return PlacementUnsafeReason.NotGrounded;
if (Vector3.Distance(wPos, Plugin.ClientState.LocalPlayer.Position) > 200)
return PlacementUnsafeReason.TooFar;
if (lastPlacementTimer.Elapsed < MinPlacementFrequency)
return PlacementUnsafeReason.TooFrequent;
return PlacementUnsafeReason.Safe;
}

Expand Down Expand Up @@ -183,21 +176,19 @@ internal void ClearWaymarkPlaceholder(Waymark waymark)
placeholders.Remove(waymark);
}

internal bool SafePlaceWaymark(Waymark waymark, Vector3 wPos, bool retryError = false)
public bool SafePlaceWaymark(Waymark waymark, Vector3 wPos)
{
var reason = WaymarkPlacementStatus(wPos);
if (reason == PlacementUnsafeReason.Safe)
if (reason is PlacementUnsafeReason.Safe)
{
lastPlacementTimer.Restart();
var status = UnsafeNativePlaceWaymark(waymark, wPos);
if (status == 0) return true;

// Too frequent
if (retryError && status == 2)
// Retry Too frequent
if (status == 2)
{
// TODO just place everything through the queue?
safePlaceQueue.Add((waymark, wPos));
processSafePlaceQueue(false);
safePlaceQueue.Enqueue((waymark, wPos));
processSafePlaceQueue(clearPlaceholder: false);
return true;
}
if (status != 2)
Expand All @@ -206,15 +197,14 @@ internal bool SafePlaceWaymark(Waymark waymark, Vector3 wPos, bool retryError =

return false;
}

internal unsafe bool IsWaymarksEnabled()
private unsafe byte UnsafeNativePlaceWaymark(Waymark waymark, Vector3 wPos)
{
return waymarkSafetyFn.Invoke() == 0;
return placeWaymarkFn?.Invoke(MarkingController.Instance(), (uint)waymark, wPos) ?? 69;
}

private unsafe byte UnsafeNativePlaceWaymark(Waymark waymark, Vector3 wPos)
internal unsafe bool IsWaymarksEnabled()
{
return placeWaymarkFn?.Invoke(MarkingController.Instance(), (uint)waymark, wPos) ?? 69;
return waymarkSafetyFn.Invoke() == 0;
}

internal bool IsPossibleToNativePlace()
Expand Down Expand Up @@ -262,14 +252,14 @@ public void AdjustPresetHeight(WaymarkPreset preset, float castHeight = 100000f)
}
}

public void SafePlacePreset(WaymarkPreset preset, bool clearPlaceholder = true, bool mergeNative = false)
public void SafePlacePreset(WaymarkPreset preset, bool clearPlaceholder = true, bool mergeExisting = false)
{
if (preset.MarkerPositions.Count == 0) return;
if (preset.TerritoryId != territoryId) return;
if (preset.PendingHeightAdjustment.IsAnySet()) return;
if (IsPossibleToNativePlace())
{
if (mergeNative)
if (mergeExisting)
foreach ((Waymark w, Vector3 p) in Plugin.WaymarkManager.Waymarks)
if (!preset.MarkerPositions.ContainsKey(w))
preset.MarkerPositions.Add(w, p);
Expand All @@ -279,6 +269,7 @@ public void SafePlacePreset(WaymarkPreset preset, bool clearPlaceholder = true,
}
else
{
if (!IsSafeToPlaceWaymarks()) return;
foreach (Waymark w in Enum.GetValues<Waymark>())
{
if (preset.MarkerPositions.TryGetValue(w, out var wPos))
Expand All @@ -289,38 +280,47 @@ public void SafePlacePreset(WaymarkPreset preset, bool clearPlaceholder = true,
placeholders.Remove(w);
continue;
}
safePlaceQueue.Add((w, wPos));
safePlaceQueue.Enqueue((w, wPos));
}
else if (!mergeExisting)
NativeClearWaymark(w);
}
processSafePlaceQueue(clearPlaceholder);
}
}

internal async void processSafePlaceQueue(bool clearPlaceholder = true)

private async void processSafePlaceQueue(bool clearPlaceholder = true)
{
await Plugin.Framework.Run(async () =>
{
var territoryId = this.territoryId;
int attempts = safePlaceQueue.Count + 2;
while (safePlaceQueue.Count > 0 && territoryId == this.territoryId && attempts-- > 0)
{
(Waymark waymark, Vector3 wPos) = safePlaceQueue[0];
safePlaceQueue.RemoveAt(0);
if (SafePlaceWaymark(waymark, wPos))
(Waymark waymark, Vector3 wPos) = safePlaceQueue.Dequeue();

var isSafe = Plugin.Config.DisableWorldPresetSafetyChecks || WaymarkPlacementStatus(wPos) is PlacementUnsafeReason.Safe;
if (!isSafe) continue;

var status = UnsafeNativePlaceWaymark(waymark, wPos);
if (status == 0)
{
if (clearPlaceholder && placeholders.GetValueOrDefault(waymark) == wPos)
placeholders.Remove(waymark);
}
else if (status == 2 || status == 3)
{
// requeue retriable error
await Plugin.Framework.DelayTicks(30);
safePlaceQueue.Enqueue((waymark, wPos));
}
else
{
// requeue failure in case it was retriable
// TODO actually differentiate between retriable and not
safePlaceQueue.Add((waymark, wPos));
Plugin.Chat.PrintError("Native placement failed with status " + status, Plugin.Tag);
}
await Plugin.Framework.DelayTicks(50);
await Plugin.Framework.DelayTicks(Plugin.Config.WaymarkPlacementFrequency);
}
safePlaceQueue.Clear();
// Plugin.Chat.Print("Placement complete");
});
}

Expand Down Expand Up @@ -348,7 +348,6 @@ internal enum PlacementUnsafeReason
NoWaymarksPlaced,
TooFar,
NotGrounded,
TooFrequent,
UnsupportedArea,
}
}
2 changes: 2 additions & 0 deletions WaymarkStudio/Windows/ConfigWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public override void Draw()
HoverTooltip("Clear already an placed waymark when beginning the place the same waymark.\nFor example, when you click the 'A' button to start placing it, the 'A' waymark will be removed if it's already been placed.\nThe game's default UI clears waymarks, but '/waymark' does not.");
needSave |= ImGui.Checkbox("Combine Equivalent Duty Presets (Criterion and Savage)", ref Configuration.CombineEquivalentDutyPresets);
HoverTooltip("Combine Criterion and Criterion Savage presets to use normal presets in savage and vice versa.\nUsing this setting will cause these presets to change which duty they belong to; this only matters if you disable the setting or share the preset to someone with it disabled.");
needSave |= ImGui.Checkbox("Disable World Preset Placement Safety Checks", ref Configuration.DisableWorldPresetSafetyChecks);
HoverTooltip("Disable distance, height, and frequency safety checks when placing presets in the open world / non-instance areas.\nWith this disabled, your preset placement will be obviously impossible; use at your own risk.");
if (needSave)
{
Configuration.Save();
Expand Down
4 changes: 2 additions & 2 deletions WaymarkStudio/Windows/StudioWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ internal void DrawDraftSection()
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.MapMarkedAlt, "Place Draft"))
{
Plugin.WaymarkManager.SafePlacePreset(Plugin.WaymarkManager.DraftPreset, mergeNative: true);
Plugin.WaymarkManager.SafePlacePreset(Plugin.WaymarkManager.DraftPreset, mergeExisting: true);
}
HoverTooltip("Replace draft markers with real markers");
}
Expand Down Expand Up @@ -536,7 +536,7 @@ internal void WaymarkButton(Waymark w)
break;
case PctOverlay.SelectionResult.Selected:
if (Plugin.Config.PlaceRealIfPossible
&& Plugin.WaymarkManager.SafePlaceWaymark(w, pos, retryError: true))
&& Plugin.WaymarkManager.SafePlaceWaymark(w, pos))
{
Plugin.WaymarkManager.ClearWaymarkPlaceholder(w);
break;
Expand Down

0 comments on commit e193909

Please sign in to comment.