refactor: use a combined queue
This commit is contained in:
parent
5cea1924bd
commit
689984ee3e
@ -39,8 +39,13 @@ internal class Messages : IDisposable {
|
|||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
|
|
||||||
private SemaphoreSlim CurrentMutex { get; } = new(1, 1);
|
private SemaphoreSlim CurrentMutex { get; } = new(1, 1);
|
||||||
private Dictionary<Guid, Message> Current { get; } = new();
|
private Dictionary<Guid, Message> Current { get; } = [];
|
||||||
private Queue<Message> SpawnQueue { get; } = new();
|
internal IReadOnlyDictionary<Guid, Message> CurrentCloned {
|
||||||
|
get {
|
||||||
|
using var guard = this.CurrentMutex.With();
|
||||||
|
return this.Current.ToDictionary(e => e.Key, e => e.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private HashSet<uint> Trials { get; } = [];
|
private HashSet<uint> Trials { get; } = [];
|
||||||
private HashSet<uint> DeepDungeons { get; } = [];
|
private HashSet<uint> DeepDungeons { get; } = [];
|
||||||
@ -83,7 +88,6 @@ internal class Messages : IDisposable {
|
|||||||
|
|
||||||
this.Plugin.Framework.Update += this.DetermineIfSpawn;
|
this.Plugin.Framework.Update += this.DetermineIfSpawn;
|
||||||
this.Plugin.Framework.Update += this.RemoveConditionally;
|
this.Plugin.Framework.Update += this.RemoveConditionally;
|
||||||
this.Plugin.Framework.Update += this.HandleSpawnQueue;
|
|
||||||
this.Plugin.ClientState.TerritoryChanged += this.TerritoryChanged;
|
this.Plugin.ClientState.TerritoryChanged += this.TerritoryChanged;
|
||||||
this.Plugin.ClientState.Login += this.SpawnVfx;
|
this.Plugin.ClientState.Login += this.SpawnVfx;
|
||||||
this.Plugin.ClientState.Logout += this.RemoveVfx;
|
this.Plugin.ClientState.Logout += this.RemoveVfx;
|
||||||
@ -93,7 +97,6 @@ internal class Messages : IDisposable {
|
|||||||
this.Plugin.ClientState.Logout -= this.RemoveVfx;
|
this.Plugin.ClientState.Logout -= this.RemoveVfx;
|
||||||
this.Plugin.ClientState.Login -= this.SpawnVfx;
|
this.Plugin.ClientState.Login -= this.SpawnVfx;
|
||||||
this.Plugin.ClientState.TerritoryChanged -= this.TerritoryChanged;
|
this.Plugin.ClientState.TerritoryChanged -= this.TerritoryChanged;
|
||||||
this.Plugin.Framework.Update -= this.HandleSpawnQueue;
|
|
||||||
this.Plugin.Framework.Update -= this.RemoveConditionally;
|
this.Plugin.Framework.Update -= this.RemoveConditionally;
|
||||||
this.Plugin.Framework.Update -= this.DetermineIfSpawn;
|
this.Plugin.Framework.Update -= this.DetermineIfSpawn;
|
||||||
|
|
||||||
@ -154,20 +157,6 @@ internal class Messages : IDisposable {
|
|||||||
this._inGpose = nowGpose;
|
this._inGpose = nowGpose;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void HandleSpawnQueue(IFramework framework) {
|
|
||||||
if (!this.SpawnQueue.TryDequeue(out var message)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plugin.Log.Debug($"spawning vfx for {message.Id}");
|
|
||||||
var rotation = Quaternion.CreateFromYawPitchRoll(message.Yaw, 0, 0);
|
|
||||||
var path = GetPath(this.Plugin.DataManager, message);
|
|
||||||
if (this.Plugin.Vfx.SpawnStatic(message.Id, path, message.Position, rotation) == null) {
|
|
||||||
Plugin.Log.Debug("trying again");
|
|
||||||
this.SpawnQueue.Enqueue(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SpawnVfx() {
|
internal void SpawnVfx() {
|
||||||
var territory = this.Plugin.ClientState.TerritoryType;
|
var territory = this.Plugin.ClientState.TerritoryType;
|
||||||
if (territory == 0 || this.Plugin.Config.BannedTerritories.Contains(territory)) {
|
if (territory == 0 || this.Plugin.Config.BannedTerritories.Contains(territory)) {
|
||||||
@ -236,7 +225,9 @@ internal class Messages : IDisposable {
|
|||||||
|
|
||||||
foreach (var message in messages) {
|
foreach (var message in messages) {
|
||||||
this.Current[message.Id] = message;
|
this.Current[message.Id] = message;
|
||||||
this.SpawnQueue.Enqueue(message);
|
var path = GetPath(this.Plugin.DataManager, message);
|
||||||
|
var rotation = Quaternion.CreateFromYawPitchRoll(message.Yaw, 0, 0);
|
||||||
|
this.Plugin.Vfx.QueueSpawn(message.Id, path, message.Position, rotation);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.CurrentMutex.Release();
|
this.CurrentMutex.Release();
|
||||||
@ -244,7 +235,7 @@ internal class Messages : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveVfx() {
|
internal void RemoveVfx() {
|
||||||
this.Plugin.Vfx.RemoveAll();
|
this.Plugin.Vfx.QueueRemoveAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Clear() {
|
internal void Clear() {
|
||||||
@ -287,7 +278,9 @@ internal class Messages : IDisposable {
|
|||||||
this.CurrentMutex.Release();
|
this.CurrentMutex.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.SpawnQueue.Enqueue(message);
|
var path = GetPath(this.Plugin.DataManager, message);
|
||||||
|
var rotation = Quaternion.CreateFromYawPitchRoll(message.Yaw, 0, 0);
|
||||||
|
this.Plugin.Vfx.QueueSpawn(message.Id, path, message.Position, rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Remove(Guid id) {
|
internal void Remove(Guid id) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Dalamud.Interface.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OrangeGuidanceTomestone.Ui;
|
using OrangeGuidanceTomestone.Ui;
|
||||||
|
|
||||||
@ -9,6 +10,11 @@ public class PluginUi : IDisposable {
|
|||||||
internal MainWindow MainWindow { get; }
|
internal MainWindow MainWindow { get; }
|
||||||
internal Viewer Viewer { get; }
|
internal Viewer Viewer { get; }
|
||||||
internal ViewerButton ViewerButton { get; }
|
internal ViewerButton ViewerButton { get; }
|
||||||
|
#if DEBUG
|
||||||
|
internal bool Debug = true;
|
||||||
|
#else
|
||||||
|
internal bool Debug;
|
||||||
|
#endif
|
||||||
|
|
||||||
private List<(string, string)> Modals { get; } = [];
|
private List<(string, string)> Modals { get; } = [];
|
||||||
private Queue<string> ToShow { get; } = new();
|
private Queue<string> ToShow { get; } = new();
|
||||||
@ -33,6 +39,10 @@ public class PluginUi : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Draw() {
|
private void Draw() {
|
||||||
|
if (this.Debug) {
|
||||||
|
this.DrawDebug();
|
||||||
|
}
|
||||||
|
|
||||||
this.MainWindow.Draw();
|
this.MainWindow.Draw();
|
||||||
this.ViewerButton.Draw();
|
this.ViewerButton.Draw();
|
||||||
this.Viewer.Draw();
|
this.Viewer.Draw();
|
||||||
@ -80,4 +90,23 @@ public class PluginUi : IDisposable {
|
|||||||
this.Modals.Add((id, text));
|
this.Modals.Add((id, text));
|
||||||
this.ToShow.Enqueue(id);
|
this.ToShow.Enqueue(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawDebug() {
|
||||||
|
foreach (var msg in this.Plugin.Messages.CurrentCloned.Values) {
|
||||||
|
if (!this.Plugin.GameGui.WorldToScreen(msg.Position, out var screen)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.GetBackgroundDrawList().AddCircleFilled(screen, 6f * ImGuiHelpers.GlobalScale, 0xff0000ff);
|
||||||
|
|
||||||
|
var label = msg.Id.ToString("N");
|
||||||
|
var size = ImGui.CalcTextSize(label);
|
||||||
|
ImGui.GetBackgroundDrawList().AddRectFilled(
|
||||||
|
screen,
|
||||||
|
screen + size,
|
||||||
|
0xff000000
|
||||||
|
);
|
||||||
|
ImGui.GetForegroundDrawList().AddText(screen, 0xffffffff, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ internal class MessageList : ITab {
|
|||||||
|
|
||||||
if (resp.IsSuccessStatusCode) {
|
if (resp.IsSuccessStatusCode) {
|
||||||
this.Refresh();
|
this.Refresh();
|
||||||
this.Plugin.Vfx.RemoveStatic(id);
|
this.Plugin.Vfx.QueueRemove(id);
|
||||||
this.Plugin.Messages.Remove(id);
|
this.Plugin.Messages.Remove(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -38,6 +38,7 @@ internal class Settings : ITab {
|
|||||||
("Viewer", this.DrawViewer),
|
("Viewer", this.DrawViewer),
|
||||||
("Unlocks", this.DrawUnlocks),
|
("Unlocks", this.DrawUnlocks),
|
||||||
("Account", this.DrawAccount),
|
("Account", this.DrawAccount),
|
||||||
|
("Debug", this.DrawDebug),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,6 +228,10 @@ internal class Settings : ITab {
|
|||||||
this.DeleteAccountButton();
|
this.DeleteAccountButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawDebug(ref bool anyChanged, ref bool vfx) {
|
||||||
|
ImGui.Checkbox("Show debug information", ref this.Plugin.Ui.Debug);
|
||||||
|
}
|
||||||
|
|
||||||
private void ExtraCodeInput() {
|
private void ExtraCodeInput() {
|
||||||
ImGui.InputText("Extra code", ref this._extraCode, 128);
|
ImGui.InputText("Extra code", ref this._extraCode, 128);
|
||||||
if (!ImGui.Button("Claim")) {
|
if (!ImGui.Button("Claim")) {
|
||||||
|
13
client/Util/SemaphoreExt.cs
Normal file
13
client/Util/SemaphoreExt.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace OrangeGuidanceTomestone.Util;
|
||||||
|
|
||||||
|
internal static class SemaphoreExt {
|
||||||
|
internal static OnDispose With(this SemaphoreSlim semaphore) {
|
||||||
|
semaphore.Wait();
|
||||||
|
return new OnDispose(() => semaphore.Release());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task<OnDispose> WithAsync(this SemaphoreSlim semaphore, CancellationToken token = default) {
|
||||||
|
await semaphore.WaitAsync(token);
|
||||||
|
return new OnDispose(() => semaphore.Release());
|
||||||
|
}
|
||||||
|
}
|
123
client/Vfx.cs
123
client/Vfx.cs
@ -4,6 +4,7 @@ using System.Text;
|
|||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
|
using OrangeGuidanceTomestone.Util;
|
||||||
|
|
||||||
namespace OrangeGuidanceTomestone;
|
namespace OrangeGuidanceTomestone;
|
||||||
|
|
||||||
@ -11,24 +12,30 @@ internal unsafe class Vfx : IDisposable {
|
|||||||
private static readonly byte[] Pool = "Client.System.Scheduler.Instance.VfxObject\0"u8.ToArray();
|
private static readonly byte[] Pool = "Client.System.Scheduler.Instance.VfxObject\0"u8.ToArray();
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? F3 0F 10 35 ?? ?? ?? ?? 48 89 43 08")]
|
[Signature("E8 ?? ?? ?? ?? F3 0F 10 35 ?? ?? ?? ?? 48 89 43 08")]
|
||||||
private delegate* unmanaged<byte*, byte*, VfxStruct*> _staticVfxCreate;
|
private readonly delegate* unmanaged<byte*, byte*, VfxStruct*> _staticVfxCreate;
|
||||||
|
|
||||||
[Signature("E8 ?? ?? ?? ?? 8B 4B 7C 85 C9")]
|
[Signature("E8 ?? ?? ?? ?? 8B 4B 7C 85 C9")]
|
||||||
private delegate* unmanaged<VfxStruct*, float, int, ulong> _staticVfxRun;
|
private readonly delegate* unmanaged<VfxStruct*, float, int, ulong> _staticVfxRun;
|
||||||
|
|
||||||
[Signature("40 53 48 83 EC 20 48 8B D9 48 8B 89 ?? ?? ?? ?? 48 85 C9 74 28 33 D2 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9")]
|
[Signature("40 53 48 83 EC 20 48 8B D9 48 8B 89 ?? ?? ?? ?? 48 85 C9 74 28 33 D2 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9")]
|
||||||
private delegate* unmanaged<VfxStruct*, nint> _staticVfxRemove;
|
private readonly delegate* unmanaged<VfxStruct*, nint> _staticVfxRemove;
|
||||||
|
|
||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
|
private SemaphoreSlim Mutex { get; } = new(1, 1);
|
||||||
private Dictionary<Guid, nint> Spawned { get; } = [];
|
private Dictionary<Guid, nint> Spawned { get; } = [];
|
||||||
private Queue<nint> RemoveQueue { get; } = [];
|
private Queue<IQueueAction> Queue { get; } = [];
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
|
private enum Mode {
|
||||||
|
Add,
|
||||||
|
Remove,
|
||||||
|
}
|
||||||
|
|
||||||
internal Vfx(Plugin plugin) {
|
internal Vfx(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
this.Plugin = plugin;
|
||||||
|
|
||||||
this.Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
this.Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||||
this.Plugin.Framework.Update += this.OnFrameworkUpdate;
|
this.Plugin.Framework.Update += this.HandleQueues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
@ -37,30 +44,77 @@ internal unsafe class Vfx : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._disposed = true;
|
this._disposed = true;
|
||||||
this.RemoveAll();
|
this.Plugin.Framework.Update -= this.HandleQueues;
|
||||||
|
this.RemoveAllSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFrameworkUpdate(IFramework framework) {
|
private void HandleQueues(IFramework framework) {
|
||||||
if (this._disposed && this.RemoveQueue.Count == 0) {
|
if (!this.Queue.TryDequeue(out var action)) {
|
||||||
this.Plugin.Framework.Update -= this.OnFrameworkUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.RemoveQueue.TryDequeue(out var vfx)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.RemoveStatic((VfxStruct*) vfx)) {
|
switch (action) {
|
||||||
this.RemoveQueue.Enqueue(vfx);
|
case AddQueueAction add: {
|
||||||
|
using var guard = this.Mutex.With();
|
||||||
|
Plugin.Log.Debug($"adding vfx for {add.Id}");
|
||||||
|
if (this.Spawned.Remove(add.Id, out var existing)) {
|
||||||
|
Plugin.Log.Warning($"vfx for {add.Id} already exists, queuing remove");
|
||||||
|
this.Queue.Enqueue(new RemoveRawQueueAction(existing));
|
||||||
|
}
|
||||||
|
|
||||||
|
var vfx = this.SpawnStatic(add.Id, add.Path, add.Position, add.Rotation);
|
||||||
|
this.Spawned[add.Id] = (nint) vfx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RemoveQueueAction remove: {
|
||||||
|
using var guard = this.Mutex.With();
|
||||||
|
Plugin.Log.Debug($"removing vfx for {remove.Id}");
|
||||||
|
if (!this.Spawned.Remove(remove.Id, out var ptr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RemoveStatic((VfxStruct*) ptr);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
case RemoveRawQueueAction remove: {
|
||||||
|
Plugin.Log.Debug($"removing raw vfx at {remove.Pointer:X}");
|
||||||
|
this.RemoveStatic((VfxStruct*) remove.Pointer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveAll() {
|
internal void RemoveAllSync() {
|
||||||
foreach (var spawned in this.Spawned.Keys.ToArray()) {
|
using var guard = this.Mutex.With();
|
||||||
this.RemoveStatic(spawned);
|
|
||||||
|
foreach (var spawned in this.Spawned.Values.ToArray()) {
|
||||||
|
this.RemoveStatic((VfxStruct*) spawned);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Spawned.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void QueueSpawn(Guid id, string path, Vector3 pos, Quaternion rotation) {
|
||||||
|
using var guard = this.Mutex.With();
|
||||||
|
this.Queue.Enqueue(new AddQueueAction(id, path, pos, rotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void QueueRemove(Guid id) {
|
||||||
|
using var guard = this.Mutex.With();
|
||||||
|
this.Queue.Enqueue(new RemoveQueueAction(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void QueueRemoveAll() {
|
||||||
|
using var guard = this.Mutex.With();
|
||||||
|
|
||||||
|
foreach (var id in this.Spawned.Keys) {
|
||||||
|
this.Queue.Enqueue(new RemoveQueueAction(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal VfxStruct* SpawnStatic(Guid id, string path, Vector3 pos, Quaternion rotation) {
|
private VfxStruct* SpawnStatic(Guid id, string path, Vector3 pos, Quaternion rotation) {
|
||||||
VfxStruct* vfx;
|
VfxStruct* vfx;
|
||||||
fixed (byte* p = Encoding.UTF8.GetBytes(path).NullTerminate()) {
|
fixed (byte* p = Encoding.UTF8.GetBytes(path).NullTerminate()) {
|
||||||
fixed (byte* pool = Pool) {
|
fixed (byte* pool = Pool) {
|
||||||
@ -82,27 +136,11 @@ internal unsafe class Vfx : IDisposable {
|
|||||||
|
|
||||||
this._staticVfxRun(vfx, 0.0f, -1);
|
this._staticVfxRun(vfx, 0.0f, -1);
|
||||||
|
|
||||||
this.Spawned[id] = (nint) vfx;
|
|
||||||
|
|
||||||
return vfx;
|
return vfx;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool RemoveStatic(VfxStruct* vfx) {
|
private void RemoveStatic(VfxStruct* vfx) {
|
||||||
var result = this._staticVfxRemove(vfx);
|
this._staticVfxRemove(vfx);
|
||||||
var success = result != 0;
|
|
||||||
if (!success) {
|
|
||||||
this.RemoveQueue.Enqueue((nint) vfx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RemoveStatic(Guid id) {
|
|
||||||
if (!this.Spawned.Remove(id, out var vfx)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.RemoveStatic((VfxStruct*) vfx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
@ -132,3 +170,16 @@ internal unsafe class Vfx : IDisposable {
|
|||||||
public int StaticTarget;
|
public int StaticTarget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal interface IQueueAction;
|
||||||
|
|
||||||
|
internal sealed record AddQueueAction(
|
||||||
|
Guid Id,
|
||||||
|
string Path,
|
||||||
|
Vector3 Position,
|
||||||
|
Quaternion Rotation
|
||||||
|
) : IQueueAction;
|
||||||
|
|
||||||
|
internal sealed record RemoveQueueAction(Guid Id) : IQueueAction;
|
||||||
|
|
||||||
|
internal sealed record RemoveRawQueueAction(nint Pointer) : IQueueAction;
|
||||||
|
Loading…
Reference in New Issue
Block a user