OrangeGuidanceTomestone/client/Messages.cs

307 lines
9.3 KiB
C#
Raw Normal View History

2023-02-20 03:34:32 +00:00
using System.Diagnostics;
2022-09-03 23:45:16 +00:00
using System.Numerics;
2022-09-09 09:42:46 +00:00
using Dalamud.Game.ClientState.Conditions;
2023-09-29 00:53:50 +00:00
using Dalamud.Plugin.Services;
2022-09-05 08:02:34 +00:00
using Lumina.Excel.GeneratedSheets;
2022-09-03 23:45:16 +00:00
using Newtonsoft.Json;
2022-09-04 05:03:59 +00:00
using OrangeGuidanceTomestone.Helpers;
2022-09-03 23:45:16 +00:00
namespace OrangeGuidanceTomestone;
internal class Messages : IDisposable {
2022-09-06 04:24:32 +00:00
internal static readonly string[] VfxPaths = {
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1a_o.avfx",
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1b_o.avfx",
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1c_o.avfx",
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1d_o.avfx",
"bg/ffxiv/fst_f1/common/vfx/eff/b0941trp1e_o.avfx",
2022-09-09 09:39:33 +00:00
"bg/ex2/02_est_e3/common/vfx/eff/b0941trp1f_o.avfx",
2022-09-06 04:24:32 +00:00
};
2022-09-03 23:45:16 +00:00
2023-09-29 00:53:50 +00:00
private static string GetPath(IDataManager data, Message message) {
2022-09-11 23:57:33 +00:00
var glyph = message.Glyph;
if (glyph < 0 || glyph >= VfxPaths.Length) {
// not checking if this exists, but the check is really only for the
// last file in the array anyway. we're guaranteed to have these
// files with an up-to-date install
return VfxPaths[0];
}
return data.FileExists(VfxPaths[glyph])
? VfxPaths[glyph]
: VfxPaths[message.Id.ToByteArray()[^1] % 5];
}
2022-09-09 09:42:46 +00:00
2022-09-03 23:45:16 +00:00
private Plugin Plugin { get; }
2022-09-04 02:23:35 +00:00
private SemaphoreSlim CurrentMutex { get; } = new(1, 1);
2022-09-04 02:52:18 +00:00
private Dictionary<Guid, Message> Current { get; } = new();
2022-09-03 23:45:16 +00:00
private Queue<Message> SpawnQueue { get; } = new();
2022-09-05 08:02:34 +00:00
private HashSet<uint> Trials { get; } = new();
private HashSet<uint> DeepDungeons { get; } = new();
2022-09-09 09:42:46 +00:00
private bool CutsceneActive {
get {
var condition = this.Plugin.Condition;
return condition[ConditionFlag.OccupiedInCutSceneEvent]
|| condition[ConditionFlag.WatchingCutscene78];
}
}
private bool GposeActive {
get {
var condition = this.Plugin.Condition;
return condition[ConditionFlag.WatchingCutscene];
}
}
2022-09-09 09:49:52 +00:00
private bool _inCutscene;
private bool _inGpose;
2022-09-03 23:45:16 +00:00
internal Messages(Plugin plugin) {
this.Plugin = plugin;
2022-09-05 08:02:34 +00:00
foreach (var cfc in this.Plugin.DataManager.GetExcelSheet<ContentFinderCondition>()!) {
2022-09-06 02:45:45 +00:00
// Trials, Raids, and Ultimate Raids
if (cfc.ContentType.Row is 4 or 5 or 28) {
// "Raids" - but we only want non-alliance raids
if (cfc.ContentType.Row == 5 && cfc.ContentMemberType.Row == 4) {
continue;
}
2022-09-05 08:02:34 +00:00
this.Trials.Add(cfc.TerritoryType.Row);
}
if (cfc.ContentType.Row == 21) {
this.DeepDungeons.Add(cfc.TerritoryType.Row);
}
}
2022-09-04 20:39:09 +00:00
if (this.Plugin.Config.ApiKey != string.Empty) {
this.SpawnVfx();
}
2022-09-03 23:45:16 +00:00
2023-02-20 03:34:32 +00:00
this.Plugin.Framework.Update += this.DetermineIfSpawn;
2022-09-09 09:49:52 +00:00
this.Plugin.Framework.Update += this.RemoveConditionally;
2022-09-03 23:45:16 +00:00
this.Plugin.Framework.Update += this.HandleSpawnQueue;
2023-02-20 03:44:59 +00:00
this.Plugin.ClientState.TerritoryChanged += this.TerritoryChanged;
2022-09-03 23:45:16 +00:00
this.Plugin.ClientState.Login += this.SpawnVfx;
this.Plugin.ClientState.Logout += this.RemoveVfx;
}
public void Dispose() {
this.Plugin.ClientState.Logout -= this.RemoveVfx;
this.Plugin.ClientState.Login -= this.SpawnVfx;
2023-02-20 03:44:59 +00:00
this.Plugin.ClientState.TerritoryChanged -= this.TerritoryChanged;
2022-09-03 23:45:16 +00:00
this.Plugin.Framework.Update -= this.HandleSpawnQueue;
2022-09-09 09:49:52 +00:00
this.Plugin.Framework.Update -= this.RemoveConditionally;
2023-02-20 03:34:32 +00:00
this.Plugin.Framework.Update -= this.DetermineIfSpawn;
2022-09-04 02:11:18 +00:00
2022-09-05 08:02:34 +00:00
this.RemoveVfx();
2022-09-03 23:45:16 +00:00
}
2023-02-20 03:34:32 +00:00
private readonly Stopwatch _timer = new();
2023-02-20 03:44:59 +00:00
2023-09-29 00:53:50 +00:00
private void TerritoryChanged(ushort territory) {
2023-02-20 03:44:59 +00:00
this._territoryChanged = true;
this.RemoveVfx();
}
2023-02-20 03:34:32 +00:00
private ushort _lastTerritory;
2023-02-20 03:44:59 +00:00
private bool _territoryChanged;
2023-02-20 03:34:32 +00:00
2023-09-29 00:53:50 +00:00
private void DetermineIfSpawn(IFramework framework) {
2023-02-20 03:34:32 +00:00
var current = this.Plugin.ClientState.TerritoryType;
2023-02-20 03:44:59 +00:00
var diffTerritory = current != this._lastTerritory;
var playerPresent = this.Plugin.ClientState.LocalPlayer != null;
if ((this._territoryChanged || diffTerritory) && playerPresent) {
this._territoryChanged = false;
2023-02-20 03:34:32 +00:00
this._timer.Start();
}
2023-02-20 03:44:59 +00:00
if (this._timer.Elapsed >= TimeSpan.FromSeconds(1.5)) {
2023-02-20 03:34:32 +00:00
this._timer.Reset();
this.SpawnVfx();
}
this._lastTerritory = current;
}
2023-09-29 00:53:50 +00:00
private void RemoveConditionally(IFramework framework) {
2022-09-09 09:49:52 +00:00
var nowCutscene = this.CutsceneActive;
var cutsceneChanged = this._inCutscene != nowCutscene;
if (this.Plugin.Config.DisableInCutscene && cutsceneChanged) {
if (nowCutscene) {
this.RemoveVfx();
} else {
this.SpawnVfx();
}
}
var nowGpose = this.GposeActive;
var gposeChanged = this._inGpose != nowGpose;
2022-09-09 09:52:10 +00:00
if (this.Plugin.Config.DisableInGpose && gposeChanged) {
2022-09-09 09:49:52 +00:00
if (nowGpose) {
this.RemoveVfx();
} else {
this.SpawnVfx();
}
}
this._inCutscene = nowCutscene;
this._inGpose = nowGpose;
}
2023-09-29 00:53:50 +00:00
private unsafe void HandleSpawnQueue(IFramework framework) {
2022-09-03 23:45:16 +00:00
if (!this.SpawnQueue.TryDequeue(out var message)) {
return;
}
2023-09-29 00:53:50 +00:00
Plugin.Log.Debug($"spawning vfx for {message.Id}");
2022-09-04 21:02:13 +00:00
var rotation = Quaternion.CreateFromYawPitchRoll(message.Yaw, 0, 0);
2022-09-11 23:57:33 +00:00
var path = GetPath(this.Plugin.DataManager, message);
2022-09-06 04:24:32 +00:00
if (this.Plugin.Vfx.SpawnStatic(message.Id, path, message.Position, rotation) == null) {
2023-09-29 00:53:50 +00:00
Plugin.Log.Debug("trying again");
2022-09-04 02:52:18 +00:00
this.SpawnQueue.Enqueue(message);
}
2022-09-03 23:45:16 +00:00
}
2022-09-04 05:10:48 +00:00
internal void SpawnVfx() {
2022-09-03 23:45:16 +00:00
var territory = this.Plugin.ClientState.TerritoryType;
2022-09-04 23:43:58 +00:00
if (territory == 0 || this.Plugin.Config.BannedTerritories.Contains(territory)) {
2022-09-03 23:45:16 +00:00
return;
}
2023-02-20 04:52:04 +00:00
var world = this.Plugin.ClientState.LocalPlayer?.CurrentWorld.Id ?? 0;
if (world == 0) {
return;
}
2023-02-20 02:55:17 +00:00
var housing = this.Plugin.Common.Functions.Housing.Location;
var ward = housing?.Ward;
2023-02-20 03:13:29 +00:00
var plot = housing?.CombinedPlot();
2023-01-29 09:26:58 +00:00
2022-09-05 08:02:34 +00:00
if (this.Plugin.Config.DisableTrials && this.Trials.Contains(territory)) {
return;
}
if (this.Plugin.Config.DisableDeepDungeon && this.DeepDungeons.Contains(territory)) {
return;
}
2022-09-09 09:42:46 +00:00
if (this.Plugin.Config.DisableInCutscene && this.CutsceneActive) {
return;
}
if (this.Plugin.Config.DisableInGpose && this.GposeActive) {
return;
}
2022-09-03 23:45:16 +00:00
this.RemoveVfx(null, null);
Task.Run(async () => {
2023-01-22 00:03:43 +00:00
try {
2023-02-20 04:52:04 +00:00
await this.DownloadMessages(world, territory, ward, plot);
2023-01-22 00:03:43 +00:00
} catch (Exception ex) {
2023-09-29 00:53:50 +00:00
Plugin.Log.Error(ex, $"Failed to get messages for territory {territory}");
2022-09-03 23:45:16 +00:00
}
});
}
2023-02-20 04:52:04 +00:00
private async Task DownloadMessages(uint world, ushort territory, ushort? ward, ushort? plot) {
2023-01-29 09:26:58 +00:00
var route = $"/messages/{territory}";
if (ward != null) {
route += $"?ward={ward}";
2023-02-20 02:55:17 +00:00
if (plot != null) {
route += $"&plot={plot}";
}
2023-02-20 04:52:04 +00:00
route += $"&world={world}";
2023-01-29 09:26:58 +00:00
}
2023-01-22 00:03:43 +00:00
var resp = await ServerHelper.SendRequest(
this.Plugin.Config.ApiKey,
HttpMethod.Get,
2023-01-29 09:26:58 +00:00
route
2023-01-22 00:03:43 +00:00
);
var json = await resp.Content.ReadAsStringAsync();
var messages = JsonConvert.DeserializeObject<Message[]>(json)!;
await this.CurrentMutex.WaitAsync();
2023-01-22 03:42:03 +00:00
try {
this.Current.Clear();
2023-01-22 00:03:43 +00:00
2023-01-22 03:42:03 +00:00
foreach (var message in messages) {
this.Current[message.Id] = message;
this.SpawnQueue.Enqueue(message);
}
} finally {
this.CurrentMutex.Release();
2023-01-22 00:03:43 +00:00
}
}
2022-09-03 23:45:16 +00:00
private void RemoveVfx(object? sender, EventArgs? e) {
2022-09-04 23:46:23 +00:00
this.RemoveVfx();
}
internal void RemoveVfx() {
2022-09-03 23:45:16 +00:00
this.Plugin.Vfx.RemoveAll();
}
2022-09-04 02:23:35 +00:00
2022-09-05 08:02:34 +00:00
internal void Clear() {
this.CurrentMutex.Wait();
2023-01-22 03:42:03 +00:00
try {
this.Current.Clear();
} finally {
this.CurrentMutex.Release();
}
2022-09-05 08:02:34 +00:00
}
2022-09-04 02:52:18 +00:00
internal IEnumerable<Message> Nearby() {
2022-09-04 02:23:35 +00:00
if (this.Plugin.ClientState.LocalPlayer is not { } player) {
return Array.Empty<Message>();
}
var position = player.Position;
2023-01-22 03:42:03 +00:00
List<Message> nearby;
2022-09-04 02:52:18 +00:00
this.CurrentMutex.Wait();
2023-01-22 03:42:03 +00:00
try {
nearby = this.Current
.Values
.Where(msg => Math.Abs(msg.Position.Y - position.Y) <= 1f)
.Where(msg => Vector3.Distance(msg.Position, position) <= 2f)
.ToList();
} finally {
this.CurrentMutex.Release();
}
2022-09-04 02:52:18 +00:00
return nearby;
}
internal void Add(Message message) {
2022-09-04 05:19:30 +00:00
this.CurrentMutex.Wait();
2023-01-22 03:42:03 +00:00
try {
this.Current[message.Id] = message;
} finally {
this.CurrentMutex.Release();
}
2022-09-04 02:52:18 +00:00
this.SpawnQueue.Enqueue(message);
2022-09-04 02:23:35 +00:00
}
2022-09-04 05:19:30 +00:00
internal void Remove(Guid id) {
this.CurrentMutex.Wait();
2023-01-22 03:42:03 +00:00
try {
this.Current.Remove(id);
} finally {
this.CurrentMutex.Release();
}
2022-09-04 05:19:30 +00:00
}
2022-09-03 23:45:16 +00:00
}