merge 0.4.0 into main

This commit is contained in:
Stanley Dimant
2022-09-10 15:31:03 +02:00
20 changed files with 709 additions and 166 deletions

View File

@@ -12,6 +12,7 @@ using MareSynchronos.Interop;
using MareSynchronos.Models;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
using Newtonsoft.Json;
namespace MareSynchronos.Managers;
@@ -40,7 +41,7 @@ public class CachedPlayer
}
}
private bool _isDisposed = false;
private bool _isDisposed = true;
private CancellationTokenSource? _downloadCancellationTokenSource = new();
private string _lastGlamourerData = string.Empty;
@@ -142,6 +143,7 @@ public class CachedPlayer
{
Dictionary<string, string> moddedPaths;
int attempts = 0;
//Logger.Verbose(JsonConvert.SerializeObject(_cachedData, Formatting.Indented));
while ((toDownloadReplacements = TryCalculateModdedDictionary(out moddedPaths)).Count > 0 && attempts++ <= 10)
{
Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind);
@@ -162,19 +164,9 @@ public class CachedPlayer
ApplyBaseData(moddedPaths);
}
if (_dalamudUtil.IsInGpose)
{
Logger.Verbose("Player is in GPose, waiting");
while (_dalamudUtil.IsInGpose)
{
await Task.Delay(TimeSpan.FromSeconds(0.5));
downloadToken.ThrowIfCancellationRequested();
}
}
foreach (var kind in objectKind)
{
ApplyCustomizationData(kind);
ApplyCustomizationData(kind, downloadToken);
}
}, downloadToken).ContinueWith(task =>
{
@@ -194,7 +186,7 @@ public class CachedPlayer
try
{
using var db = new FileCacheContext();
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value).ToList())
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
{
foreach (var gamePath in item.GamePaths)
{
@@ -205,10 +197,20 @@ public class CachedPlayer
}
else
{
Logger.Verbose("Missing file: " + item.Hash);
missingFiles.Add(item);
}
}
}
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => !string.IsNullOrEmpty(v.FileSwapPath))).ToList())
{
foreach (var gamePath in item.GamePaths)
{
Logger.Verbose("Adding file swap for " + gamePath + ":" + item.FileSwapPath);
moddedDictionary[gamePath] = item.FileSwapPath;
}
}
}
catch (Exception ex)
{
@@ -224,14 +226,16 @@ public class CachedPlayer
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
}
private unsafe void ApplyCustomizationData(ObjectKind objectKind)
private unsafe void ApplyCustomizationData(ObjectKind objectKind, CancellationToken ct)
{
if (PlayerCharacter == IntPtr.Zero) return;
_cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData);
if (objectKind == ObjectKind.Player)
{
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter);
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, ct);
ct.ThrowIfCancellationRequested();
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
RequestedPenumbraRedraw = true;
Logger.Debug(
$"Request Redraw for {PlayerName}");
@@ -250,6 +254,8 @@ public class CachedPlayer
if (minionOrMount != null)
{
Logger.Debug($"Request Redraw for Minion/Mount");
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " minion or mount", (IntPtr)minionOrMount, ct);
ct.ThrowIfCancellationRequested();
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
_ipcManager.GlamourerApplyAll(glamourerData, obj: (IntPtr)minionOrMount);
@@ -262,17 +268,29 @@ public class CachedPlayer
}
else if (objectKind == ObjectKind.Pet)
{
int tick = 16;
var pet = _dalamudUtil.GetPet(PlayerCharacter);
if (pet != IntPtr.Zero)
{
Logger.Debug("Request Redraw for Pet");
var totalWait = 0;
var newPet = IntPtr.Zero;
const int maxWait = 3000;
Logger.Debug($"Request Redraw for Pet, waiting {maxWait}ms");
do
{
Thread.Sleep(tick);
totalWait += tick;
newPet = _dalamudUtil.GetPet(PlayerCharacter);
} while (newPet == pet && totalWait < maxWait);
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
_ipcManager.GlamourerApplyAll(glamourerData, pet);
_ipcManager.GlamourerApplyAll(glamourerData, newPet);
}
else
{
_ipcManager.PenumbraRedraw(pet);
_ipcManager.PenumbraRedraw(newPet);
}
}
}
@@ -282,6 +300,8 @@ public class CachedPlayer
if (companion != IntPtr.Zero)
{
Logger.Debug("Request Redraw for Companion");
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " companion", companion, ct);
ct.ThrowIfCancellationRequested();
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
_ipcManager.GlamourerApplyAll(glamourerData, companion);
@@ -348,6 +368,8 @@ public class CachedPlayer
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
_downloadCancellationTokenSource?.Cancel();
_downloadCancellationTokenSource?.Dispose();
if (PlayerCharacter != IntPtr.Zero)
{
foreach (var item in _cachedData.FileReplacements)
@@ -355,9 +377,6 @@ public class CachedPlayer
RevertCustomizationData(item.Key);
}
}
_downloadCancellationTokenSource?.Cancel();
_downloadCancellationTokenSource?.Dispose();
}
catch (Exception ex)
{
@@ -374,6 +393,7 @@ public class CachedPlayer
public void InitializePlayer(IntPtr character, string name, CharacterCacheDto? cache)
{
if (!_isDisposed) return;
Logger.Debug("Initializing Player " + this + " has cache: " + (cache != null));
IsVisible = true;
PlayerName = name;
@@ -426,14 +446,16 @@ public class CachedPlayer
_penumbraRedrawEventTask = Task.Run(() =>
{
PlayerCharacter = address;
using var cts = new CancellationTokenSource();
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, cts.Token);
cts.Dispose();
cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter, cts.Token);
if (RequestedPenumbraRedraw == false)
{
Logger.Debug("Unauthorized character change detected");
ApplyCustomizationData(ObjectKind.Player);
ApplyCustomizationData(ObjectKind.Player, cts.Token);
}
else
{
@@ -441,6 +463,7 @@ public class CachedPlayer
Logger.Debug(
$"Penumbra Redraw done for {PlayerName}");
}
cts.Dispose();
});
}

View File

@@ -23,6 +23,7 @@ namespace MareSynchronos.Managers
private readonly CancellationTokenSource _rescanTaskCancellationTokenSource = new();
private CancellationTokenSource _rescanTaskRunCancellationTokenSource = new();
private CancellationTokenSource? _scanCancellationTokenSource;
private object modifiedFilesLock = new object();
public FileCacheManager(IpcManager ipcManager, Configuration pluginConfiguration)
{
Logger.Verbose("Creating " + nameof(FileCacheManager));
@@ -48,7 +49,7 @@ namespace MareSynchronos.Managers
public string WatchedPenumbraDirectory => (_penumbraDirWatcher?.EnableRaisingEvents ?? false) ? _penumbraDirWatcher!.Path : "Not watched";
public FileCache? Create(string file, CancellationToken token)
public FileCache? Create(string file, CancellationToken? token)
{
FileInfo fileInfo = new(file);
int attempt = 0;
@@ -56,7 +57,7 @@ namespace MareSynchronos.Managers
{
Thread.Sleep(1000);
Logger.Debug("Waiting for file release " + fileInfo.FullName + " attempt " + attempt);
token.ThrowIfCancellationRequested();
token?.ThrowIfCancellationRequested();
}
if (attempt >= 10) return null;
@@ -64,7 +65,7 @@ namespace MareSynchronos.Managers
var sha1Hash = Crypto.GetFileHash(fileInfo.FullName);
return new FileCache()
{
Filepath = fileInfo.FullName.ToLower(),
Filepath = fileInfo.FullName.ToLowerInvariant(),
Hash = sha1Hash,
LastModifiedDate = fileInfo.LastWriteTimeUtc.Ticks.ToString(),
};
@@ -142,7 +143,10 @@ namespace MareSynchronos.Managers
private void OnModified(object sender, FileSystemEventArgs e)
{
_modifiedFiles.Add(e.FullPath);
lock (modifiedFilesLock)
{
_modifiedFiles.Add(e.FullPath);
}
_ = StartRescan();
}
@@ -189,21 +193,28 @@ namespace MareSynchronos.Managers
Logger.Debug("File changes detected");
if (!_modifiedFiles.Any()) return;
lock (modifiedFilesLock)
{
if (!_modifiedFiles.Any()) return;
}
_rescanTask = Task.Run(async () =>
{
var listCopy = _modifiedFiles.ToList();
_modifiedFiles.Clear();
List<string> modifiedFilesCopy = new List<string>();
lock (modifiedFilesLock)
{
modifiedFilesCopy = _modifiedFiles.ToList();
_modifiedFiles.Clear();
}
await using var db = new FileCacheContext();
foreach (var item in listCopy.Distinct())
foreach (var item in modifiedFilesCopy.Distinct())
{
var fi = new FileInfo(item);
if (!fi.Exists)
{
PluginLog.Verbose("Removed: " + item);
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == item.ToLower()));
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == item.ToLowerInvariant()));
}
else
{
@@ -211,7 +222,7 @@ namespace MareSynchronos.Managers
var fileCache = Create(item, _rescanTaskCancellationTokenSource.Token);
if (fileCache != null)
{
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == fileCache.Filepath.ToLower()));
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == fileCache.Filepath.ToLowerInvariant()));
await db.AddAsync(fileCache, _rescanTaskCancellationTokenSource.Token);
}
}

View File

@@ -11,6 +11,8 @@ using System.Collections.Concurrent;
namespace MareSynchronos.Managers
{
public delegate void PenumbraRedrawEvent(IntPtr address, int objTblIdx);
public delegate void HeelsOffsetChange(float change);
public delegate void PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath);
public class IpcManager : IDisposable
{
private readonly ICallGateSubscriber<int> _glamourerApiVersion;
@@ -33,6 +35,14 @@ namespace MareSynchronos.Managers
private readonly ICallGateSubscriber<string, string[]>? _reverseResolvePlayer;
private readonly ICallGateSubscriber<string, string, Dictionary<string, string>, string, int, int>
_penumbraSetTemporaryMod;
private readonly ICallGateSubscriber<IntPtr, string, string, object?> _penumbraGameObjectResourcePathResolved;
private readonly ICallGateSubscriber<string> _heelsGetApiVersion;
private readonly ICallGateSubscriber<float> _heelsGetOffset;
private readonly ICallGateSubscriber<float, object?> _heelsOffsetUpdate;
private readonly ICallGateSubscriber<GameObject, float, object?> _heelsRegisterPlayer;
private readonly ICallGateSubscriber<GameObject, object?> _heelsUnregisterPlayer;
private readonly DalamudUtil _dalamudUtil;
private readonly ConcurrentQueue<Action> actionQueue = new();
@@ -58,7 +68,9 @@ namespace MareSynchronos.Managers
_glamourerApplyOnlyCustomization = pi.GetIpcSubscriber<string, GameObject?, object>("Glamourer.ApplyOnlyCustomizationToCharacter");
_glamourerApplyOnlyEquipment = pi.GetIpcSubscriber<string, GameObject?, object>("Glamourer.ApplyOnlyEquipmentToCharacter");
_glamourerRevertCustomization = pi.GetIpcSubscriber<GameObject?, object>("Glamourer.RevertCharacter");
_penumbraGameObjectResourcePathResolved = pi.GetIpcSubscriber<IntPtr, string, string, object?>("Penumbra.GameObjectResourcePathResolved");
_penumbraGameObjectResourcePathResolved.Subscribe(ResourceLoaded);
_penumbraObjectIsRedrawn.Subscribe(RedrawEvent);
_penumbraInit.Subscribe(PenumbraInit);
_penumbraDispose.Subscribe(PenumbraDispose);
@@ -82,6 +94,15 @@ namespace MareSynchronos.Managers
_dalamudUtil.FrameworkUpdate += HandleActionQueue;
}
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
{
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, true, System.Globalization.CultureInfo.InvariantCulture) != 0)
{
PenumbraResourceLoadEvent?.Invoke(ptr, arg1, arg2);
//Logger.Debug($"Resolved {ptr:X}: {arg1} => {arg2}");
}
}
private void HandleActionQueue()
{
if (actionQueue.TryDequeue(out var action))
@@ -95,6 +116,8 @@ namespace MareSynchronos.Managers
public event VoidDelegate? PenumbraInitialized;
public event VoidDelegate? PenumbraDisposed;
public event PenumbraRedrawEvent? PenumbraRedrawEvent;
public event HeelsOffsetChange? HeelsOffsetChangeEvent;
public event PenumbraResourceLoadEvent? PenumbraResourceLoadEvent;
public bool Initialized => CheckPenumbraApi();
public bool CheckGlamourerApi()
@@ -113,7 +136,7 @@ namespace MareSynchronos.Managers
{
try
{
return _penumbraApiVersion.InvokeFunc() is { Item1: 4, Item2: >= 11 };
return _penumbraApiVersion.InvokeFunc() is { Item1: 4, Item2: >= 13 };
}
catch
{
@@ -125,12 +148,43 @@ namespace MareSynchronos.Managers
{
Logger.Verbose("Disposing " + nameof(IpcManager));
int totalSleepTime = 0;
while (actionQueue.Count > 0 && totalSleepTime < 2000)
{
Logger.Verbose("Waiting for actionqueue to clear...");
System.Threading.Thread.Sleep(16);
totalSleepTime += 16;
}
Logger.Verbose("Action queue clear or not, disposing");
_dalamudUtil.FrameworkUpdate -= HandleActionQueue;
actionQueue.Clear();
_penumbraDispose.Unsubscribe(PenumbraDispose);
_penumbraInit.Unsubscribe(PenumbraInit);
_penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent);
_penumbraGameObjectResourcePathResolved.Unsubscribe(ResourceLoaded);
_heelsOffsetUpdate.Unsubscribe(HeelsOffsetChange);
}
public float GetHeelsOffset()
{
if (!CheckHeelsApi()) return 0.0f;
return _heelsGetOffset.InvokeFunc();
}
public void HeelsSetOffsetForPlayer(float offset, IntPtr character)
{
if(!CheckHeelsApi()) return;
actionQueue.Enqueue(() =>
{
var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj != null)
{
Logger.Verbose("Applying Heels data to " + character.ToString("X"));
_heelsRegisterPlayer.InvokeAction(gameObj, offset);
}
});
}
public void GlamourerApplyAll(string? customization, IntPtr obj)
@@ -246,11 +300,11 @@ namespace MareSynchronos.Managers
});
}
public string? PenumbraResolvePath(string path)
public string PenumbraResolvePath(string path)
{
if (!CheckPenumbraApi()) return null;
if (!CheckPenumbraApi()) return path;
var resolvedPath = _penumbraResolvePlayer!.InvokeFunc(path);
return resolvedPath;
return resolvedPath ?? path;
}
public string[] PenumbraReverseResolvePlayer(string path)

View File

@@ -9,7 +9,6 @@ using MareSynchronos.API;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
using MareSynchronos.WebAPI.Utils;
using Newtonsoft.Json;
namespace MareSynchronos.Managers;
@@ -199,30 +198,6 @@ public class OnlinePlayerManager : IDisposable
return;
}
if (_dalamudUtil.IsInGpose)
{
_playerTokenDisposal.TryGetValue(cachedPlayer, out var cancellationTokenSource);
cancellationTokenSource?.Cancel();
cachedPlayer.IsVisible = false;
_playerTokenDisposal[cachedPlayer] = new CancellationTokenSource();
cancellationTokenSource = _playerTokenDisposal[cachedPlayer];
var token = cancellationTokenSource.Token;
Task.Run(async () =>
{
Logger.Verbose("Cannot dispose Player, in GPose");
while (_dalamudUtil.IsInGpose)
{
await Task.Delay(TimeSpan.FromSeconds(0.5));
if (token.IsCancellationRequested) return;
}
cachedPlayer.DisposePlayer();
_onlineCachedPlayers.TryRemove(characterHash, out _);
}, token);
return;
}
cachedPlayer.DisposePlayer();
_onlineCachedPlayers.TryRemove(characterHash, out _);
}

View File

@@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
using System.Collections.Generic;
using System.Linq;
using MareSynchronos.Models;
using Newtonsoft.Json;
namespace MareSynchronos.Managers
{
@@ -19,6 +20,7 @@ namespace MareSynchronos.Managers
private readonly ApiController _apiController;
private readonly CharacterDataFactory _characterDataFactory;
private readonly DalamudUtil _dalamudUtil;
private readonly TransientResourceManager _transientResourceManager;
private readonly IpcManager _ipcManager;
public event PlayerHasChanged? PlayerHasChanged;
public CharacterCacheDto? LastCreatedCharacterData { get; private set; }
@@ -30,7 +32,7 @@ namespace MareSynchronos.Managers
private List<PlayerRelatedObject> playerRelatedObjects = new List<PlayerRelatedObject>();
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil)
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager)
{
Logger.Verbose("Creating " + nameof(PlayerManager));
@@ -38,9 +40,10 @@ namespace MareSynchronos.Managers
_ipcManager = ipcManager;
_characterDataFactory = characterDataFactory;
_dalamudUtil = dalamudUtil;
_transientResourceManager = transientResourceManager;
_apiController.Connected += ApiControllerOnConnected;
_apiController.Disconnected += ApiController_Disconnected;
_transientResourceManager.TransientResourceLoaded += HandleTransientResourceLoad;
_dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate;
Logger.Debug("Watching Player, ApiController is Connected: " + _apiController.IsConnected);
@@ -58,6 +61,29 @@ namespace MareSynchronos.Managers
};
}
public void HandleTransientResourceLoad(IntPtr gameObj)
{
foreach (var obj in playerRelatedObjects)
{
if (obj.Address == gameObj && !obj.HasUnprocessedUpdate)
{
obj.HasUnprocessedUpdate = true;
OnPlayerOrAttachedObjectsChanged();
return;
}
}
}
private void HeelsOffsetChanged(float change)
{
var player = playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
if (LastCreatedCharacterData != null && LastCreatedCharacterData.HeelsOffset != change && !player.IsProcessing)
{
Logger.Debug("Heels offset changed to " + change);
playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player).HasUnprocessedUpdate = true;
}
}
public void Dispose()
{
Logger.Verbose("Disposing " + nameof(PlayerManager));
@@ -67,6 +93,11 @@ namespace MareSynchronos.Managers
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
_transientResourceManager.TransientResourceLoaded -= HandleTransientResourceLoad;
_playerChangedCts?.Cancel();
_ipcManager.HeelsOffsetChangeEvent -= HeelsOffsetChanged;
}
private unsafe void DalamudUtilOnDelayedFrameworkUpdate()
@@ -110,6 +141,7 @@ namespace MareSynchronos.Managers
while (!PermanentDataCache.IsReady && !token.IsCancellationRequested)
{
Logger.Verbose("Waiting until cache is ready");
await Task.Delay(50, token);
}
@@ -117,7 +149,9 @@ namespace MareSynchronos.Managers
Logger.Verbose("Cache creation complete");
return PermanentDataCache.ToCharacterCacheDto();
var cache = PermanentDataCache.ToCharacterCacheDto();
//Logger.Verbose(JsonConvert.SerializeObject(cache, Formatting.Indented));
return cache;
}
private void IpcManager_PenumbraRedrawEvent(IntPtr address, int idx)
@@ -129,6 +163,7 @@ namespace MareSynchronos.Managers
if (address == item.Address)
{
Logger.Debug("Penumbra redraw Event for " + item.ObjectKind);
//_transientResourceManager.CleanSemiTransientResources(item.ObjectKind);
item.HasUnprocessedUpdate = true;
}
}
@@ -141,8 +176,6 @@ namespace MareSynchronos.Managers
private void OnPlayerOrAttachedObjectsChanged()
{
if (_dalamudUtil.IsInGpose) return;
var unprocessedObjects = playerRelatedObjects.Where(c => c.HasUnprocessedUpdate).ToList();
foreach (var unprocessedObject in unprocessedObjects)
{
@@ -176,7 +209,10 @@ namespace MareSynchronos.Managers
Task.Run(async () =>
{
_dalamudUtil.WaitWhileSelfIsDrawing(token);
foreach(var item in unprocessedObjects)
{
_dalamudUtil.WaitWhileCharacterIsDrawing("self " + item.ObjectKind.ToString(), item.Address, token);
}
CharacterCacheDto? cacheDto = (await CreateFullCharacterCacheDto(token));
if (cacheDto == null || token.IsCancellationRequested) return;

View File

@@ -0,0 +1,175 @@
using MareSynchronos.API;
using MareSynchronos.Models;
using MareSynchronos.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MareSynchronos.Managers
{
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
public class TransientResourceManager : IDisposable
{
private readonly IpcManager manager;
private readonly DalamudUtil dalamudUtil;
public event TransientResourceLoadedEvent? TransientResourceLoaded;
private Dictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
private Dictionary<ObjectKind, HashSet<FileReplacement>> SemiTransientResources { get; } = new();
public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil)
{
manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent;
this.manager = manager;
this.dalamudUtil = dalamudUtil;
dalamudUtil.FrameworkUpdate += DalamudUtil_FrameworkUpdate;
dalamudUtil.ClassJobChanged += DalamudUtil_ClassJobChanged;
}
private void DalamudUtil_ClassJobChanged()
{
if (SemiTransientResources.ContainsKey(ObjectKind.Pet))
{
SemiTransientResources[ObjectKind.Pet].Clear();
}
}
private void DalamudUtil_FrameworkUpdate()
{
foreach (var item in TransientResources.ToList())
{
if (!dalamudUtil.IsGameObjectPresent(item.Key))
{
Logger.Debug("Object not present anymore: " + item.Key.ToString("X"));
TransientResources.Remove(item.Key);
}
}
}
public void CleanSemiTransientResources(ObjectKind objectKind)
{
if (SemiTransientResources.ContainsKey(objectKind))
{
SemiTransientResources[objectKind].Clear();
}
}
public List<string> GetTransientResources(IntPtr gameObject)
{
if (TransientResources.TryGetValue(gameObject, out var result))
{
return result.ToList();
}
return new List<string>();
}
public List<FileReplacement> GetSemiTransientResources(ObjectKind objectKind)
{
if (SemiTransientResources.TryGetValue(objectKind, out var result))
{
return result.ToList();
}
return new List<FileReplacement>();
}
private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath)
{
if (!TransientResources.ContainsKey(gameObject))
{
TransientResources[gameObject] = new();
}
if (filePath.StartsWith("|"))
{
filePath = filePath.Split("|")[2];
}
filePath = filePath.ToLowerInvariant();
var replacedGamePath = gamePath.ToLowerInvariant().Replace("\\", "/");
if (TransientResources[gameObject].Contains(replacedGamePath) ||
SemiTransientResources.Any(r => r.Value.Any(f => f.GamePaths.First().ToLowerInvariant() == replacedGamePath
&& f.ResolvedPath.ToLowerInvariant() == filePath)))
{
Logger.Debug("Not adding " + replacedGamePath + ":" + filePath);
Logger.Verbose("SemiTransientAny: " + SemiTransientResources.Any(r => r.Value.Any(f => f.GamePaths.First().ToLowerInvariant() == replacedGamePath
&& f.ResolvedPath.ToLowerInvariant() == filePath)).ToString() + ", TransientAny: " + TransientResources[gameObject].Contains(replacedGamePath));
}
else
{
TransientResources[gameObject].Add(replacedGamePath);
Logger.Debug($"Adding {replacedGamePath} for {gameObject} ({filePath})");
TransientResourceLoaded?.Invoke(gameObject);
}
}
public void RemoveTransientResource(IntPtr gameObject, FileReplacement fileReplacement)
{
if (TransientResources.ContainsKey(gameObject))
{
TransientResources[gameObject].RemoveWhere(f => fileReplacement.ResolvedPath.ToLowerInvariant() == f.ToLowerInvariant());
}
}
public void PersistTransientResources(IntPtr gameObject, ObjectKind objectKind, Func<string, bool, FileReplacement> createFileReplacement)
{
if (!SemiTransientResources.ContainsKey(objectKind))
{
SemiTransientResources[objectKind] = new HashSet<FileReplacement>();
}
if (!TransientResources.TryGetValue(gameObject, out var resources))
{
return;
}
var transientResources = resources.ToList();
Logger.Debug("Persisting " + transientResources.Count + " transient resources");
foreach (var item in transientResources)
{
var existingResource = SemiTransientResources[objectKind].Any(f => f.GamePaths.First().ToLowerInvariant() == item.ToLowerInvariant());
if (existingResource)
{
Logger.Debug("Semi Transient resource replaced: " + item);
SemiTransientResources[objectKind].RemoveWhere(f => f.GamePaths.First().ToLowerInvariant() == item.ToLowerInvariant());
}
if (!SemiTransientResources[objectKind].Any(f => f.GamePaths.First().ToLowerInvariant() == item.ToLowerInvariant()))
{
Logger.Debug("Persisting " + item.ToLowerInvariant());
var fileReplacement = createFileReplacement(item.ToLowerInvariant(), true);
if (!fileReplacement.HasFileReplacement)
fileReplacement = createFileReplacement(item.ToLowerInvariant(), false);
SemiTransientResources[objectKind].Add(fileReplacement);
}
}
TransientResources[gameObject].Clear();
}
public void Dispose()
{
dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
manager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged;
TransientResources.Clear();
}
internal void AddSemiTransientResource(ObjectKind objectKind, FileReplacement item)
{
if (!SemiTransientResources.ContainsKey(objectKind))
{
SemiTransientResources[objectKind] = new HashSet<FileReplacement>();
}
if (!SemiTransientResources[objectKind].Any(f => f.ResolvedPath.ToLowerInvariant() == item.ResolvedPath.ToLowerInvariant()))
{
SemiTransientResources[objectKind].Add(item);
}
}
}
}