Client rework for API change and paradigm shift (#39)
* most of the groups refactoring on client * register OnMethods for group stuff * start implementing client (still pretty broken) * finish implementing new api first iteration * idk rework everything for pair shit (still WIP); goal is to remove PairedClients and GroupPairClients from ApiController * move everything to PairManager, remove dictionaries from APiController * remove admin stuff from client, cleanup * adjust reconnection handling, add new settings, todo still to remove access from old stuff that's marked obsolete from config * add back adding servers, fix intro ui * fix obsolete calls * adjust config namespace * add UI for setting animation/sound permissions to syncshells * add ConfigurationService to hot reload config on change from external * move transient data cache to configuration * add deleting service to ui * fix saving of transient resources * fix group pair user assignments * halt scanner when penumbra inactive, add visible/online/offline split to individual pairs and tags * add presence to syncshell ui * move fullpause from config to server config * fixes in code style * more codestyle * show info icon on player in shells, don't show icon when no changes from default state are made, add online notifs * fixes to intro UI --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Logging;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
@@ -13,21 +10,30 @@ using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class CachedPlayer
|
||||
public class CachedPlayer : IDisposable
|
||||
{
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager fileDbManager;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private API.Data.CharacterData _cachedData = new();
|
||||
private PlayerRelatedObject? _currentCharacterEquipment;
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||
private bool _isVisible;
|
||||
|
||||
public CachedPlayer(string nameHash, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
private string _lastGlamourerData = string.Empty;
|
||||
|
||||
private string _originalGlamourerData = string.Empty;
|
||||
|
||||
private Task? _penumbraRedrawEventTask;
|
||||
|
||||
public CachedPlayer(OnlineUserIdentDto onlineUser, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
{
|
||||
PlayerNameHash = nameHash;
|
||||
OnlineUser = onlineUser;
|
||||
_ipcManager = ipcManager;
|
||||
_apiController = apiController;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
this.fileDbManager = fileDbManager;
|
||||
_fileDbManager = fileDbManager;
|
||||
}
|
||||
|
||||
public bool IsVisible
|
||||
@@ -40,35 +46,24 @@ public class CachedPlayer
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDisposed = true;
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||
|
||||
private string _lastGlamourerData = string.Empty;
|
||||
|
||||
private string _originalGlamourerData = string.Empty;
|
||||
|
||||
public OnlineUserIdentDto OnlineUser { get; set; }
|
||||
public IntPtr PlayerCharacter { get; set; } = IntPtr.Zero;
|
||||
|
||||
public string? PlayerName { get; private set; }
|
||||
|
||||
public string PlayerNameHash { get; }
|
||||
public string PlayerNameHash => OnlineUser.Ident;
|
||||
|
||||
public bool RequestedPenumbraRedraw { get; set; }
|
||||
|
||||
public bool WasVisible { get; private set; }
|
||||
|
||||
private CharacterCacheDto _cachedData = new();
|
||||
|
||||
private PlayerRelatedObject? _currentCharacterEquipment;
|
||||
|
||||
public void ApplyCharacterData(CharacterCacheDto characterData, OptionalPluginWarning warning)
|
||||
public void ApplyCharacterData(API.Data.CharacterData characterData, OptionalPluginWarning warning)
|
||||
{
|
||||
Logger.Debug("Received data for " + this);
|
||||
|
||||
Logger.Debug("Checking for files to download for player " + PlayerName);
|
||||
Logger.Debug("Hash for data is " + characterData.GetHashCode() + ", current cache hash is " + _cachedData.GetHashCode());
|
||||
Logger.Debug("Hash for data is " + characterData.DataHash.Value + ", current cache hash is " + _cachedData.DataHash.Value);
|
||||
|
||||
if (characterData.GetHashCode() == _cachedData.GetHashCode()) return;
|
||||
if (string.Equals(characterData.DataHash.Value, _cachedData.DataHash.Value, StringComparison.Ordinal)) return;
|
||||
|
||||
bool updateModdedPaths = false;
|
||||
List<ObjectKind> charaDataToUpdate = new();
|
||||
@@ -122,13 +117,12 @@ public class CachedPlayer
|
||||
bool heelsOffsetDifferent = _cachedData.HeelsOffset != characterData.HeelsOffset;
|
||||
if (heelsOffsetDifferent)
|
||||
{
|
||||
|
||||
Logger.Debug("Updating " + objectKind);
|
||||
charaDataToUpdate.Add(objectKind);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool customizeDataDifferent = _cachedData.CustomizePlusData != characterData.CustomizePlusData;
|
||||
bool customizeDataDifferent = !string.Equals(_cachedData.CustomizePlusData, characterData.CustomizePlusData, StringComparison.Ordinal);
|
||||
if (customizeDataDifferent)
|
||||
{
|
||||
Logger.Debug("Updating " + objectKind);
|
||||
@@ -160,6 +154,184 @@ public class CachedPlayer
|
||||
DownloadAndApplyCharacter(charaDataToUpdate, updateModdedPaths);
|
||||
}
|
||||
|
||||
public bool CheckExistence()
|
||||
{
|
||||
var curPlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!)?.Address ?? IntPtr.Zero;
|
||||
if (PlayerCharacter == IntPtr.Zero || PlayerCharacter != curPlayerCharacter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentCharacterEquipment?.CheckAndUpdateObject();
|
||||
if (_currentCharacterEquipment?.HasUnprocessedUpdate ?? false)
|
||||
{
|
||||
OnPlayerChanged();
|
||||
}
|
||||
|
||||
IsVisible = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (string.IsNullOrEmpty(PlayerName)) return;
|
||||
Logger.Debug("Disposing " + PlayerName + " (" + OnlineUser + ")");
|
||||
try
|
||||
{
|
||||
Logger.Verbose("Restoring state for " + PlayerName);
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
|
||||
_downloadCancellationTokenSource?.Cancel();
|
||||
_downloadCancellationTokenSource?.Dispose();
|
||||
_downloadCancellationTokenSource = null;
|
||||
if (PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements)
|
||||
{
|
||||
RevertCustomizationData(item.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.Message + Environment.NewLine + ex.StackTrace);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cachedData = new();
|
||||
var tempPlayerName = PlayerName;
|
||||
PlayerName = string.Empty;
|
||||
PlayerCharacter = IntPtr.Zero;
|
||||
IsVisible = false;
|
||||
Logger.Debug("Disposing " + tempPlayerName + " complete");
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(IntPtr character, string name)
|
||||
{
|
||||
IsVisible = true;
|
||||
PlayerName = name;
|
||||
PlayerCharacter = character;
|
||||
Logger.Debug("Initializing Player " + this);
|
||||
|
||||
_ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent;
|
||||
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
_currentCharacterEquipment = new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero,
|
||||
() => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName)?.Address ?? IntPtr.Zero);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return OnlineUser.User.AliasOrUID + ":" + PlayerName + ":HasChar " + (PlayerCharacter != IntPtr.Zero);
|
||||
}
|
||||
|
||||
private void ApplyBaseData(Dictionary<string, string> moddedPaths)
|
||||
{
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName!);
|
||||
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
|
||||
}
|
||||
|
||||
private unsafe void ApplyCustomizationData(ObjectKind objectKind, CancellationToken ct)
|
||||
{
|
||||
if (PlayerCharacter == IntPtr.Zero) return;
|
||||
_cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData);
|
||||
|
||||
switch (objectKind)
|
||||
{
|
||||
case ObjectKind.Player:
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
|
||||
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData);
|
||||
RequestedPenumbraRedraw = true;
|
||||
Logger.Debug(
|
||||
$"Request Redraw for {PlayerName}");
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(PlayerCharacter);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectKind.MinionOrMount:
|
||||
{
|
||||
var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject;
|
||||
if (minionOrMount != null)
|
||||
{
|
||||
Logger.Debug($"Request Redraw for Minion/Mount");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " minion or mount", (IntPtr)minionOrMount, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, (IntPtr)minionOrMount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw((IntPtr)minionOrMount);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectKind.Pet:
|
||||
{
|
||||
int tick = 16;
|
||||
var pet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
if (pet != IntPtr.Zero)
|
||||
{
|
||||
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, newPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(newPet);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectKind.Companion:
|
||||
{
|
||||
var companion = _dalamudUtil.GetCompanion(PlayerCharacter);
|
||||
if (companion != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug("Request Redraw for Companion");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " companion", companion, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, companion);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(companion);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadAndApplyCharacter(List<ObjectKind> objectKind, bool updateModdedPaths)
|
||||
{
|
||||
if (!objectKind.Any())
|
||||
@@ -175,7 +347,7 @@ public class CachedPlayer
|
||||
var downloadId = _apiController.GetDownloadId();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
List<FileReplacementDto> toDownloadReplacements;
|
||||
List<FileReplacementData> toDownloadReplacements;
|
||||
|
||||
if (updateModdedPaths)
|
||||
{
|
||||
@@ -213,7 +385,6 @@ public class CachedPlayer
|
||||
{
|
||||
ApplyCustomizationData(kind, downloadToken);
|
||||
}
|
||||
|
||||
}, downloadToken).ContinueWith(task =>
|
||||
{
|
||||
_downloadCancellationTokenSource = null;
|
||||
@@ -225,138 +396,44 @@ public class CachedPlayer
|
||||
});
|
||||
}
|
||||
|
||||
private List<FileReplacementDto> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
||||
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
||||
{
|
||||
List<FileReplacementDto> missingFiles = new();
|
||||
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
try
|
||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
||||
if (player == null || !string.Equals(player.Name.ToString(), PlayerName, StringComparison.OrdinalIgnoreCase)) return;
|
||||
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
||||
|
||||
_penumbraRedrawEventTask = Task.Run(() =>
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
PlayerCharacter = address;
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, cts.Token);
|
||||
cts.Dispose();
|
||||
cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
if (RequestedPenumbraRedraw == false)
|
||||
{
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
var fileCache = fileDbManager.GetFileCacheByHash(item.Hash);
|
||||
if (fileCache != null)
|
||||
{
|
||||
moddedDictionary[gamePath] = fileCache.ResolvedFilepath;
|
||||
}
|
||||
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)
|
||||
{
|
||||
PluginLog.Error(ex, "Something went wrong during calculation replacements");
|
||||
}
|
||||
Logger.Debug("ModdedPaths calculated, missing files: " + missingFiles.Count);
|
||||
return missingFiles;
|
||||
}
|
||||
|
||||
private void ApplyBaseData(Dictionary<string, string> moddedPaths)
|
||||
{
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName!);
|
||||
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
|
||||
}
|
||||
|
||||
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(PlayerName!, PlayerCharacter, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
|
||||
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData);
|
||||
RequestedPenumbraRedraw = true;
|
||||
Logger.Debug(
|
||||
$"Request Redraw for {PlayerName}");
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter);
|
||||
Logger.Debug("Unauthorized character change detected");
|
||||
ApplyCustomizationData(ObjectKind.Player, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(PlayerCharacter);
|
||||
RequestedPenumbraRedraw = false;
|
||||
Logger.Debug(
|
||||
$"Penumbra Redraw done for {PlayerName}");
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.MinionOrMount)
|
||||
{
|
||||
var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject;
|
||||
if (minionOrMount != null)
|
||||
{
|
||||
Logger.Debug($"Request Redraw for Minion/Mount");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " minion or mount", (IntPtr)minionOrMount, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, (IntPtr)minionOrMount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw((IntPtr)minionOrMount);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Pet)
|
||||
{
|
||||
int tick = 16;
|
||||
var pet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
if (pet != IntPtr.Zero)
|
||||
{
|
||||
var totalWait = 0;
|
||||
var newPet = IntPtr.Zero;
|
||||
const int maxWait = 3000;
|
||||
Logger.Debug($"Request Redraw for Pet, waiting {maxWait}ms");
|
||||
cts.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Thread.Sleep(tick);
|
||||
totalWait += tick;
|
||||
newPet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
} while (newPet == pet && totalWait < maxWait);
|
||||
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, newPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(newPet);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Companion)
|
||||
private void OnPlayerChanged()
|
||||
{
|
||||
Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}");
|
||||
_currentCharacterEquipment!.HasUnprocessedUpdate = false;
|
||||
if (!RequestedPenumbraRedraw && PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
var companion = _dalamudUtil.GetCompanion(PlayerCharacter);
|
||||
if (companion != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug("Request Redraw for Companion");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " companion", companion, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, companion);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(companion);
|
||||
}
|
||||
}
|
||||
Logger.Debug($"Saving new Glamourer data");
|
||||
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,129 +481,43 @@ public class CachedPlayer
|
||||
}
|
||||
}
|
||||
|
||||
public void DisposePlayer()
|
||||
private List<FileReplacementData> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
if (string.IsNullOrEmpty(PlayerName)) return;
|
||||
Logger.Debug("Disposing " + PlayerName + " (" + PlayerNameHash + ")");
|
||||
_isDisposed = true;
|
||||
List<FileReplacementData> missingFiles = new();
|
||||
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
try
|
||||
{
|
||||
Logger.Verbose("Restoring state for " + PlayerName);
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
|
||||
_downloadCancellationTokenSource?.Cancel();
|
||||
_downloadCancellationTokenSource?.Dispose();
|
||||
_downloadCancellationTokenSource = null;
|
||||
if (PlayerCharacter != IntPtr.Zero)
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements)
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
RevertCustomizationData(item.Key);
|
||||
var fileCache = _fileDbManager.GetFileCacheByHash(item.Hash);
|
||||
if (fileCache != null)
|
||||
{
|
||||
moddedDictionary[gamePath] = fileCache.ResolvedFilepath;
|
||||
}
|
||||
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)
|
||||
{
|
||||
Logger.Warn(ex.Message + Environment.NewLine + ex.StackTrace);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cachedData = new();
|
||||
var tempPlayerName = PlayerName;
|
||||
PlayerName = string.Empty;
|
||||
PlayerCharacter = IntPtr.Zero;
|
||||
IsVisible = false;
|
||||
Logger.Debug("Disposing " + tempPlayerName + " complete");
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializePlayer(IntPtr character, string name, CharacterCacheDto? cache, OptionalPluginWarning displayedChatWarning)
|
||||
{
|
||||
if (!_isDisposed) return;
|
||||
IsVisible = true;
|
||||
PlayerName = name;
|
||||
PlayerCharacter = character;
|
||||
Logger.Debug("Initializing Player " + this + " has cache: " + (cache != null));
|
||||
|
||||
_dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent;
|
||||
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
_currentCharacterEquipment = new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero,
|
||||
() => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName)?.Address ?? IntPtr.Zero);
|
||||
_isDisposed = false;
|
||||
if (cache != null)
|
||||
{
|
||||
ApplyCharacterData(cache, displayedChatWarning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnDelayedFrameworkUpdate()
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
|
||||
|
||||
var curPlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!)?.Address ?? IntPtr.Zero;
|
||||
if (PlayerCharacter == IntPtr.Zero || PlayerCharacter != curPlayerCharacter)
|
||||
{
|
||||
DisposePlayer();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentCharacterEquipment?.CheckAndUpdateObject();
|
||||
if (_currentCharacterEquipment?.HasUnprocessedUpdate ?? false)
|
||||
{
|
||||
OnPlayerChanged();
|
||||
}
|
||||
|
||||
IsVisible = true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return PlayerNameHash + ":" + PlayerName + ":HasChar " + (PlayerCharacter != IntPtr.Zero);
|
||||
}
|
||||
|
||||
private Task? _penumbraRedrawEventTask;
|
||||
|
||||
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
||||
{
|
||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
||||
if (player == null || !string.Equals(player.Name.ToString(), PlayerName, StringComparison.OrdinalIgnoreCase)) return;
|
||||
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
||||
|
||||
_penumbraRedrawEventTask = Task.Run(() =>
|
||||
{
|
||||
PlayerCharacter = address;
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, cts.Token);
|
||||
cts.Dispose();
|
||||
cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
if (RequestedPenumbraRedraw == false)
|
||||
{
|
||||
Logger.Debug("Unauthorized character change detected");
|
||||
ApplyCustomizationData(ObjectKind.Player, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
RequestedPenumbraRedraw = false;
|
||||
Logger.Debug(
|
||||
$"Penumbra Redraw done for {PlayerName}");
|
||||
}
|
||||
cts.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPlayerChanged()
|
||||
{
|
||||
Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}");
|
||||
_currentCharacterEquipment!.HasUnprocessedUpdate = false;
|
||||
if (!RequestedPenumbraRedraw && PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug($"Saving new Glamourer data");
|
||||
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
PluginLog.Error(ex, "Something went wrong during calculation replacements");
|
||||
}
|
||||
Logger.Debug("ModdedPaths calculated, missing files: " + missingFiles.Count);
|
||||
return missingFiles;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using MareSynchronos.Utils;
|
||||
using Action = System.Action;
|
||||
@@ -9,14 +7,11 @@ using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.Delegates;
|
||||
|
||||
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 delegate void CustomizePlusScaleChange(string? scale);
|
||||
|
||||
public class IpcManager : IDisposable
|
||||
{
|
||||
private readonly ICallGateSubscriber<int> _glamourerApiVersion;
|
||||
@@ -57,10 +52,10 @@ public class IpcManager : IDisposable
|
||||
private readonly ICallGateSubscriber<string?, object> _customizePlusOnScaleUpdate;
|
||||
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private bool inGposeQueueMode = false;
|
||||
private ConcurrentQueue<Action> actionQueue => inGposeQueueMode ? gposeActionQueue : normalQueue;
|
||||
private readonly ConcurrentQueue<Action> normalQueue = new();
|
||||
private readonly ConcurrentQueue<Action> gposeActionQueue = new();
|
||||
private bool _inGposeQueueMode = false;
|
||||
private ConcurrentQueue<Action> ActionQueue => _inGposeQueueMode ? _gposeActionQueue : _normalQueue;
|
||||
private readonly ConcurrentQueue<Action> _normalQueue = new();
|
||||
private readonly ConcurrentQueue<Action> _gposeActionQueue = new();
|
||||
|
||||
public IpcManager(DalamudPluginInterface pi, DalamudUtil dalamudUtil)
|
||||
{
|
||||
@@ -121,7 +116,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void HandleGposeActionQueue()
|
||||
{
|
||||
if (gposeActionQueue.TryDequeue(out var action))
|
||||
if (_gposeActionQueue.TryDequeue(out var action))
|
||||
{
|
||||
if (action == null) return;
|
||||
Logger.Debug("Execution action in gpose queue: " + action.Method);
|
||||
@@ -131,7 +126,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
public void ToggleGposeQueueMode(bool on)
|
||||
{
|
||||
inGposeQueueMode = on;
|
||||
_inGposeQueueMode = on;
|
||||
}
|
||||
|
||||
private void PenumbraModSettingChangedHandler()
|
||||
@@ -141,15 +136,15 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void ClearActionQueue()
|
||||
{
|
||||
actionQueue.Clear();
|
||||
gposeActionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
_gposeActionQueue.Clear();
|
||||
}
|
||||
|
||||
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, true, System.Globalization.CultureInfo.InvariantCulture) != 0)
|
||||
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, ignoreCase: true, System.Globalization.CultureInfo.InvariantCulture) != 0)
|
||||
{
|
||||
PenumbraResourceLoadEvent?.Invoke(ptr, arg1, arg2);
|
||||
}
|
||||
@@ -158,7 +153,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void HandleActionQueue()
|
||||
{
|
||||
if (actionQueue.TryDequeue(out var action))
|
||||
if (ActionQueue.TryDequeue(out var action))
|
||||
{
|
||||
if (action == null) return;
|
||||
Logger.Debug("Execution action in queue: " + action.Method);
|
||||
@@ -169,10 +164,10 @@ public class IpcManager : IDisposable
|
||||
public event VoidDelegate? PenumbraModSettingChanged;
|
||||
public event VoidDelegate? PenumbraInitialized;
|
||||
public event VoidDelegate? PenumbraDisposed;
|
||||
public event PenumbraRedrawEvent? PenumbraRedrawEvent;
|
||||
public event HeelsOffsetChange? HeelsOffsetChangeEvent;
|
||||
public event PenumbraResourceLoadEvent? PenumbraResourceLoadEvent;
|
||||
public event CustomizePlusScaleChange? CustomizePlusScaleChange;
|
||||
public event DrawObjectDelegate? PenumbraRedrawEvent;
|
||||
public event FloatDelegate? HeelsOffsetChangeEvent;
|
||||
public event PenumbraFileResourceDelegate? PenumbraResourceLoadEvent;
|
||||
public event StringDelegate? CustomizePlusScaleChange;
|
||||
|
||||
public bool Initialized => CheckPenumbraApi();
|
||||
public bool CheckGlamourerApi()
|
||||
@@ -228,11 +223,11 @@ public class IpcManager : IDisposable
|
||||
Logger.Verbose("Disposing " + nameof(IpcManager));
|
||||
|
||||
int totalSleepTime = 0;
|
||||
while (actionQueue.Count > 0 && totalSleepTime < 2000)
|
||||
while (!ActionQueue.IsEmpty && totalSleepTime < 2000)
|
||||
{
|
||||
Logger.Verbose("Waiting for actionqueue to clear...");
|
||||
HandleActionQueue();
|
||||
System.Threading.Thread.Sleep(16);
|
||||
Thread.Sleep(16);
|
||||
totalSleepTime += 16;
|
||||
}
|
||||
|
||||
@@ -244,7 +239,7 @@ public class IpcManager : IDisposable
|
||||
_dalamudUtil.FrameworkUpdate -= HandleActionQueue;
|
||||
_dalamudUtil.ZoneSwitchEnd -= ClearActionQueue;
|
||||
_dalamudUtil.GposeFrameworkUpdate -= HandleGposeActionQueue;
|
||||
actionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
|
||||
_penumbraGameObjectResourcePathResolved.Dispose();
|
||||
_penumbraDispose.Dispose();
|
||||
@@ -263,7 +258,7 @@ public class IpcManager : IDisposable
|
||||
public void HeelsSetOffsetForPlayer(float offset, IntPtr character)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
@@ -277,7 +272,7 @@ public class IpcManager : IDisposable
|
||||
public void HeelsRestoreOffsetForPlayer(IntPtr character)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
@@ -299,7 +294,7 @@ public class IpcManager : IDisposable
|
||||
public void CustomizePlusSetBodyScale(IntPtr character, string scale)
|
||||
{
|
||||
if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -314,7 +309,7 @@ public class IpcManager : IDisposable
|
||||
public void CustomizePlusRevert(IntPtr character)
|
||||
{
|
||||
if (!CheckCustomizePlusApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -328,7 +323,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyAll(string? customization, IntPtr obj)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(obj);
|
||||
if (gameObj is Character c)
|
||||
@@ -342,7 +337,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyOnlyEquipment(string customization, IntPtr character)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -356,7 +351,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyOnlyCustomization(string customization, IntPtr character)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -393,7 +388,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerRevertCharacterCustomization(GameObject character)
|
||||
{
|
||||
if (!CheckGlamourerApi()) return;
|
||||
actionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character));
|
||||
ActionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character));
|
||||
}
|
||||
|
||||
public string PenumbraGetMetaManipulations()
|
||||
@@ -411,7 +406,7 @@ public class IpcManager : IDisposable
|
||||
public void PenumbraRedraw(IntPtr obj)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(obj);
|
||||
if (gameObj != null)
|
||||
@@ -425,13 +420,13 @@ public class IpcManager : IDisposable
|
||||
public void PenumbraRedraw(string actorName)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() => _penumbraRedraw!.Invoke(actorName, RedrawType.Redraw));
|
||||
ActionQueue.Enqueue(() => _penumbraRedraw!.Invoke(actorName, RedrawType.Redraw));
|
||||
}
|
||||
|
||||
public void PenumbraRemoveTemporaryCollection(string characterName)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var collName = "Mare_" + characterName;
|
||||
Logger.Verbose("Removing temp collection for " + collName);
|
||||
@@ -464,7 +459,7 @@ public class IpcManager : IDisposable
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var idx = _dalamudUtil.GetIndexFromObjectTableByName(characterName);
|
||||
if (idx == null)
|
||||
@@ -474,7 +469,7 @@ public class IpcManager : IDisposable
|
||||
var collName = "Mare_" + characterName;
|
||||
var ret = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
||||
Logger.Verbose("Creating Temp Collection " + collName + ", Success: " + ret);
|
||||
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx.Value, true);
|
||||
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx.Value, c: true);
|
||||
Logger.Verbose("Assigning Temp Collection " + collName + " to index " + idx.Value);
|
||||
foreach (var mod in modPaths)
|
||||
{
|
||||
@@ -511,6 +506,6 @@ public class IpcManager : IDisposable
|
||||
private void PenumbraDispose()
|
||||
{
|
||||
PenumbraDisposed?.Invoke();
|
||||
actionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
@@ -17,39 +10,24 @@ public class OnlinePlayerManager : IDisposable
|
||||
{
|
||||
private readonly ApiController _apiController;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly PlayerManager _playerManager;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ConcurrentDictionary<string, CachedPlayer> _onlineCachedPlayers = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, CharacterCacheDto> _temporaryStoredCharacterCache = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<CachedPlayer, CancellationTokenSource> _playerTokenDisposal = new();
|
||||
private readonly ConcurrentDictionary<string, OptionalPluginWarning> _shownWarnings = new(StringComparer.Ordinal);
|
||||
private readonly PairManager _pairManager;
|
||||
|
||||
private List<string> OnlineVisiblePlayerHashes => _onlineCachedPlayers.Select(p => p.Value).Where(p => p.PlayerCharacter != IntPtr.Zero)
|
||||
.Select(p => p.PlayerNameHash).ToList();
|
||||
|
||||
public OnlinePlayerManager(ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager, PlayerManager playerManager, FileCacheManager fileDbManager, Configuration configuration)
|
||||
public OnlinePlayerManager(ApiController apiController, DalamudUtil dalamudUtil, PlayerManager playerManager, FileCacheManager fileDbManager, PairManager pairManager)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(OnlinePlayerManager));
|
||||
|
||||
_apiController = apiController;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_ipcManager = ipcManager;
|
||||
_playerManager = playerManager;
|
||||
_fileDbManager = fileDbManager;
|
||||
_configuration = configuration;
|
||||
_apiController.PairedClientOnline += ApiControllerOnPairedClientOnline;
|
||||
_apiController.PairedClientOffline += ApiControllerOnPairedClientOffline;
|
||||
_apiController.Connected += ApiControllerOnConnected;
|
||||
_apiController.Disconnected += ApiControllerOnDisconnected;
|
||||
_apiController.CharacterReceived += ApiControllerOnCharacterReceived;
|
||||
_pairManager = pairManager;
|
||||
|
||||
_ipcManager.PenumbraDisposed += IpcManagerOnPenumbraDisposed;
|
||||
_playerManager.PlayerHasChanged += PlayerManagerOnPlayerHasChanged;
|
||||
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
_dalamudUtil.ZoneSwitchStart += DalamudUtilOnZoneSwitched;
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
@@ -57,49 +35,9 @@ public class OnlinePlayerManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnZoneSwitched()
|
||||
private void PlayerManagerOnPlayerHasChanged(CharacterData characterCache)
|
||||
{
|
||||
DisposePlayers();
|
||||
}
|
||||
|
||||
private void ApiControllerOnCharacterReceived(object? sender, CharacterReceivedEventArgs e)
|
||||
{
|
||||
if (!_shownWarnings.ContainsKey(e.CharacterNameHash)) _shownWarnings[e.CharacterNameHash] = new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
};
|
||||
if (_onlineCachedPlayers.TryGetValue(e.CharacterNameHash, out var visiblePlayer) && visiblePlayer.IsVisible)
|
||||
{
|
||||
Logger.Debug("Received data and applying to " + e.CharacterNameHash);
|
||||
visiblePlayer.ApplyCharacterData(e.CharacterData, _shownWarnings[e.CharacterNameHash]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("Received data but no fitting character visible for " + e.CharacterNameHash);
|
||||
_temporaryStoredCharacterCache[e.CharacterNameHash] = e.CharacterData;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayerManagerOnPlayerHasChanged(CharacterCacheDto characterCache)
|
||||
{
|
||||
PushCharacterData(OnlineVisiblePlayerHashes);
|
||||
}
|
||||
|
||||
private void ApiControllerOnConnected()
|
||||
{
|
||||
var apiTask = _apiController.UserGetOnlineCharacters();
|
||||
|
||||
Task.WaitAll(apiTask);
|
||||
|
||||
AddInitialPairs(apiTask.Result);
|
||||
|
||||
_playerManager.PlayerHasChanged += PlayerManagerOnPlayerHasChanged;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
PushCharacterData(_pairManager.VisibleUsers);
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
@@ -107,146 +45,53 @@ public class OnlinePlayerManager : IDisposable
|
||||
_dalamudUtil.DelayedFrameworkUpdate += FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
private void IpcManagerOnPenumbraDisposed()
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
DisposePlayers();
|
||||
}
|
||||
|
||||
private void DisposePlayers()
|
||||
{
|
||||
foreach (var kvp in _onlineCachedPlayers)
|
||||
{
|
||||
kvp.Value.DisposePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnDisconnected()
|
||||
{
|
||||
RestoreAllCharacters();
|
||||
_playerManager.PlayerHasChanged -= PlayerManagerOnPlayerHasChanged;
|
||||
}
|
||||
|
||||
public void AddInitialPairs(List<string> apiTaskResult)
|
||||
{
|
||||
_onlineCachedPlayers.Clear();
|
||||
foreach (var hash in apiTaskResult)
|
||||
{
|
||||
_onlineCachedPlayers.TryAdd(hash, CreateCachedPlayer(hash));
|
||||
}
|
||||
Logger.Verbose("Online and paired users: " + string.Join(Environment.NewLine, _onlineCachedPlayers.Select(k => k.Key)));
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(OnlinePlayerManager));
|
||||
|
||||
RestoreAllCharacters();
|
||||
|
||||
_apiController.PairedClientOnline -= ApiControllerOnPairedClientOnline;
|
||||
_apiController.PairedClientOffline -= ApiControllerOnPairedClientOffline;
|
||||
_apiController.Disconnected -= ApiControllerOnDisconnected;
|
||||
_apiController.Connected -= ApiControllerOnConnected;
|
||||
|
||||
_ipcManager.PenumbraDisposed -= ApiControllerOnDisconnected;
|
||||
|
||||
_playerManager.PlayerHasChanged -= PlayerManagerOnPlayerHasChanged;
|
||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
_dalamudUtil.ZoneSwitchStart -= DalamudUtilOnZoneSwitched;
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
private void RestoreAllCharacters()
|
||||
{
|
||||
DisposePlayers();
|
||||
_onlineCachedPlayers.Clear();
|
||||
}
|
||||
|
||||
private void ApiControllerOnPairedClientOffline(string charHash)
|
||||
{
|
||||
Logger.Debug("Player offline: " + charHash);
|
||||
RemovePlayer(charHash);
|
||||
}
|
||||
|
||||
private void ApiControllerOnPairedClientOnline(string charHash)
|
||||
{
|
||||
Logger.Debug("Player online: " + charHash);
|
||||
AddPlayer(charHash);
|
||||
return;
|
||||
}
|
||||
|
||||
private void AddPlayer(string characterNameHash)
|
||||
{
|
||||
if (_onlineCachedPlayers.TryGetValue(characterNameHash, out var cachedPlayer))
|
||||
{
|
||||
PushCharacterData(new List<string>() { characterNameHash });
|
||||
_playerTokenDisposal.TryGetValue(cachedPlayer, out var cancellationTokenSource);
|
||||
cancellationTokenSource?.Cancel();
|
||||
return;
|
||||
}
|
||||
_onlineCachedPlayers.TryAdd(characterNameHash, CreateCachedPlayer(characterNameHash));
|
||||
}
|
||||
|
||||
private void RemovePlayer(string characterHash)
|
||||
{
|
||||
if (!_onlineCachedPlayers.TryGetValue(characterHash, out var cachedPlayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cachedPlayer.DisposePlayer();
|
||||
_onlineCachedPlayers.TryRemove(characterHash, out _);
|
||||
}
|
||||
|
||||
private void FrameworkOnUpdate()
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_apiController.IsConnected) return;
|
||||
|
||||
var playerCharacters = _dalamudUtil.GetPlayerCharacters();
|
||||
var onlinePairs = _pairManager.OnlineUserPairs;
|
||||
foreach (var pChar in playerCharacters)
|
||||
{
|
||||
var hashedName = Crypto.GetHash256(pChar);
|
||||
if (_onlineCachedPlayers.TryGetValue(hashedName, out var existingPlayer) && !string.IsNullOrEmpty(existingPlayer.PlayerName))
|
||||
{
|
||||
existingPlayer.IsVisible = true;
|
||||
continue;
|
||||
}
|
||||
var pair = _pairManager.FindPair(pChar);
|
||||
if (pair == null) continue;
|
||||
|
||||
if (existingPlayer != null)
|
||||
{
|
||||
_temporaryStoredCharacterCache.TryRemove(hashedName, out var cache);
|
||||
if (!_shownWarnings.ContainsKey(hashedName)) _shownWarnings[hashedName] = new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
};
|
||||
existingPlayer.InitializePlayer(pChar.Address, pChar.Name.ToString(), cache, _shownWarnings[hashedName]);
|
||||
}
|
||||
pair.InitializePair(pChar.Address, pChar.Name.ToString());
|
||||
}
|
||||
|
||||
var newlyVisiblePlayers = _onlineCachedPlayers.Select(v => v.Value)
|
||||
.Where(p => p.PlayerCharacter != IntPtr.Zero && p.IsVisible && !p.WasVisible).Select(p => p.PlayerNameHash)
|
||||
var newlyVisiblePlayers = onlinePairs.Select(v => v.CachedPlayer)
|
||||
.Where(p => p != null && p.PlayerCharacter != IntPtr.Zero && p.IsVisible && !p.WasVisible).Select(p => (UserDto)p!.OnlineUser)
|
||||
.ToList();
|
||||
if (newlyVisiblePlayers.Any())
|
||||
{
|
||||
Logger.Verbose("Has new visible players, pushing character data");
|
||||
PushCharacterData(newlyVisiblePlayers);
|
||||
PushCharacterData(newlyVisiblePlayers.Select(c => c.User).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
private void PushCharacterData(List<string> visiblePlayers)
|
||||
private void PushCharacterData(List<UserData> visiblePlayers)
|
||||
{
|
||||
if (visiblePlayers.Any() && _playerManager.LastCreatedCharacterData != null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData,
|
||||
visiblePlayers).ConfigureAwait(false);
|
||||
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData, visiblePlayers).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private CachedPlayer CreateCachedPlayer(string hashedName)
|
||||
{
|
||||
return new CachedPlayer(hashedName, _ipcManager, _apiController, _dalamudUtil, _fileDbManager);
|
||||
}
|
||||
}
|
||||
329
MareSynchronos/Managers/PairManager.cs
Normal file
329
MareSynchronos/Managers/PairManager.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Comparer;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class PairManager : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<UserData, Pair> _allClientPairs = new(UserDataComparer.Instance);
|
||||
private readonly ConcurrentDictionary<GroupData, GroupFullInfoDto> _allGroups = new(GroupDataComparer.Instance);
|
||||
private readonly CachedPlayerFactory _cachedPlayerFactory;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly PairFactory _pairFactory;
|
||||
private readonly UiBuilder _uiBuilder;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
|
||||
public PairManager(CachedPlayerFactory cachedPlayerFactory, DalamudUtil dalamudUtil, PairFactory pairFactory, UiBuilder uiBuilder, ConfigurationService configurationService)
|
||||
{
|
||||
_cachedPlayerFactory = cachedPlayerFactory;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_pairFactory = pairFactory;
|
||||
_uiBuilder = uiBuilder;
|
||||
_configurationService = configurationService;
|
||||
_dalamudUtil.ZoneSwitchStart += DalamudUtilOnZoneSwitched;
|
||||
_dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_directPairsInternal = DirectPairsLazy();
|
||||
_groupPairsInternal = GroupPairsLazy();
|
||||
}
|
||||
|
||||
private void RecreateLazy()
|
||||
{
|
||||
_directPairsInternal = DirectPairsLazy();
|
||||
_groupPairsInternal = GroupPairsLazy();
|
||||
}
|
||||
|
||||
private Lazy<List<Pair>> _directPairsInternal;
|
||||
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> _groupPairsInternal;
|
||||
public Dictionary<GroupFullInfoDto, List<Pair>> GroupPairs => _groupPairsInternal.Value;
|
||||
public List<Pair> DirectPairs => _directPairsInternal.Value;
|
||||
|
||||
private Lazy<List<Pair>> DirectPairsLazy() => new(() => _allClientPairs.Select(k => k.Value).Where(k => k.UserPair != null).ToList());
|
||||
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> GroupPairsLazy()
|
||||
{
|
||||
return new Lazy<Dictionary<GroupFullInfoDto, List<Pair>>>(() =>
|
||||
{
|
||||
Dictionary<GroupFullInfoDto, List<Pair>> outDict = new();
|
||||
foreach (var group in _allGroups)
|
||||
{
|
||||
outDict[group.Value] = _allClientPairs.Select(p => p.Value).Where(p => p.GroupPair.Any(g => GroupDataComparer.Instance.Equals(group.Key, g.Key.Group))).ToList();
|
||||
}
|
||||
return outDict;
|
||||
});
|
||||
}
|
||||
|
||||
public List<Pair> OnlineUserPairs => _allClientPairs.Where(p => !string.IsNullOrEmpty(p.Value.PlayerNameHash)).Select(p => p.Value).ToList();
|
||||
public List<UserData> VisibleUsers => _allClientPairs.Where(p => p.Value.CachedPlayer != null && p.Value.CachedPlayer.IsVisible).Select(p => p.Key).ToList();
|
||||
|
||||
public void AddGroup(GroupFullInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group] = dto;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void RemoveGroup(GroupData data)
|
||||
{
|
||||
_allGroups.TryRemove(data, out _);
|
||||
foreach (var item in _allClientPairs.ToList())
|
||||
{
|
||||
foreach (var grpPair in item.Value.GroupPair.Select(k => k.Key).ToList())
|
||||
{
|
||||
if (GroupDataComparer.Instance.Equals(grpPair.Group, data))
|
||||
{
|
||||
_allClientPairs[item.Key].GroupPair.Remove(grpPair);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_allClientPairs[item.Key].HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(item.Key, out _);
|
||||
}
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void AddGroupPair(GroupPairFullInfoDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) _allClientPairs[dto.User] = _pairFactory.Create();
|
||||
|
||||
var group = _allGroups[dto.Group];
|
||||
_allClientPairs[dto.User].GroupPair[group] = dto;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void AddUserPair(UserPairDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) _allClientPairs[dto.User] = _pairFactory.Create();
|
||||
|
||||
_allClientPairs[dto.User].UserPair = dto;
|
||||
_allClientPairs[dto.User].ApplyLastReceivedData();
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void ClearPairs()
|
||||
{
|
||||
Logger.Debug("Clearing all Pairs");
|
||||
DisposePairs();
|
||||
_allClientPairs.Clear();
|
||||
_allGroups.Clear();
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_dalamudUtil.ZoneSwitchStart -= DalamudUtilOnZoneSwitched;
|
||||
DisposePairs();
|
||||
}
|
||||
|
||||
public void DisposePairs()
|
||||
{
|
||||
Logger.Debug("Disposing all Pairs");
|
||||
foreach (var item in _allClientPairs)
|
||||
{
|
||||
item.Value.CachedPlayer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Pair? FindPair(PlayerCharacter? pChar)
|
||||
{
|
||||
if (pChar == null) return null;
|
||||
var hash = pChar.GetHash256();
|
||||
return OnlineUserPairs.Find(p => string.Equals(p.PlayerNameHash, hash, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public void MarkPairOffline(UserData user)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(user, out var pair))
|
||||
{
|
||||
pair.CachedPlayer?.Dispose();
|
||||
pair.CachedPlayer = null;
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkPairOnline(OnlineUserIdentDto dto, ApiController controller)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto);
|
||||
|
||||
if (_allClientPairs[dto.User].CachedPlayer != null) return;
|
||||
|
||||
if (_configurationService.Current.ShowOnlineNotifications)
|
||||
{
|
||||
var pair = _allClientPairs[dto.User];
|
||||
if (_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs && pair.UserPair != null || !_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs)
|
||||
{
|
||||
_uiBuilder.AddNotification(string.Empty, "[Mare Synchronos] " + (pair.GetNote() ?? pair.UserData.AliasOrUID) + " is now online", Dalamud.Interface.Internal.Notifications.NotificationType.Info, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
_allClientPairs[dto.User].CachedPlayer?.Dispose();
|
||||
_allClientPairs[dto.User].CachedPlayer = _cachedPlayerFactory.Create(dto, controller);
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void ReceiveCharaData(OnlineUserCharaDataDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto.User);
|
||||
|
||||
var pair = _allClientPairs[dto.User];
|
||||
if (!pair.PlayerName.IsNullOrEmpty())
|
||||
{
|
||||
pair.ApplyData(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
_allClientPairs[dto.User].LastReceivedCharacterData = dto.CharaData;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveGroupPair(GroupPairDto dto)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
pair.GroupPair.Remove(group);
|
||||
|
||||
if (!pair.HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(dto.User, out _);
|
||||
}
|
||||
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUserPair(UserDto dto)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
pair.UserPair = null;
|
||||
if (!pair.HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(dto.User, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
pair.ApplyLastReceivedData();
|
||||
}
|
||||
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
if (!_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
throw new InvalidOperationException("No such pair for " + dto);
|
||||
}
|
||||
|
||||
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
|
||||
|
||||
pair.UserPair.OtherPermissions = dto.Permissions;
|
||||
if (!pair.UserPair.OtherPermissions.IsPaired())
|
||||
{
|
||||
pair.ApplyLastReceivedData();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSelfPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
if (!_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
throw new InvalidOperationException("No such pair for " + dto);
|
||||
}
|
||||
|
||||
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
|
||||
|
||||
pair.UserPair.OwnPermissions = dto.Permissions;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnDelayedFrameworkUpdate()
|
||||
{
|
||||
foreach (var player in _allClientPairs.Select(p => p.Value).Where(p => p.CachedPlayer != null && p.CachedPlayer.IsVisible).ToList())
|
||||
{
|
||||
if (!player.CachedPlayer!.CheckExistence())
|
||||
{
|
||||
player.CachedPlayer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnZoneSwitched()
|
||||
{
|
||||
DisposePairs();
|
||||
}
|
||||
|
||||
public void SetGroupInfo(GroupInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group].Group = dto.Group;
|
||||
_allGroups[dto.Group].Owner = dto.Owner;
|
||||
_allGroups[dto.Group].GroupPermissions = dto.GroupPermissions;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupPermissions(GroupPermissionDto dto)
|
||||
{
|
||||
var prevPermissions = _allGroups[dto.Group].GroupPermissions;
|
||||
_allGroups[dto.Group].GroupPermissions = dto.Permissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.Permissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.Permissions.IsDisableSounds())
|
||||
{
|
||||
RecreateLazy();
|
||||
var group = _allGroups[dto.Group];
|
||||
GroupPairs[group].ForEach(p => p.ApplyLastReceivedData());
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupPairUserPermissions(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
var prevPermissions = _allClientPairs[dto.User].GroupPair[group].GroupUserPermissions;
|
||||
_allClientPairs[dto.User].GroupPair[group].GroupUserPermissions = dto.GroupPairPermissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.GroupPairPermissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.GroupPairPermissions.IsDisableSounds())
|
||||
{
|
||||
_allClientPairs[dto.User].ApplyLastReceivedData();
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupUserPermissions(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
var prevPermissions = _allGroups[dto.Group].GroupUserPermissions;
|
||||
_allGroups[dto.Group].GroupUserPermissions = dto.GroupPairPermissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.GroupPairPermissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.GroupPairPermissions.IsDisableSounds())
|
||||
{
|
||||
RecreateLazy();
|
||||
var group = _allGroups[dto.Group];
|
||||
GroupPairs[group].ForEach(p => p.ApplyLastReceivedData());
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupStatusInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group].GroupUserInfo = dto.GroupUserInfo;
|
||||
}
|
||||
|
||||
internal void SetGroupPairStatusInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
_allClientPairs[dto.User].GroupPair[group].GroupPairStatusInfo = dto.GroupUserInfo;
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,17 @@
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.UI;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.Delegates;
|
||||
#if DEBUG
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public delegate void PlayerHasChanged(CharacterCacheDto characterCache);
|
||||
|
||||
public class PlayerManager : IDisposable
|
||||
{
|
||||
@@ -28,15 +22,15 @@ public class PlayerManager : IDisposable
|
||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
||||
private readonly SettingsUi _settingsUi;
|
||||
private readonly IpcManager _ipcManager;
|
||||
public event PlayerHasChanged? PlayerHasChanged;
|
||||
public CharacterCacheDto? LastCreatedCharacterData { get; private set; }
|
||||
public CharacterData PermanentDataCache { get; private set; } = new();
|
||||
private readonly Dictionary<ObjectKind, Func<bool>> objectKindsToUpdate = new();
|
||||
public event CharacterDataDelegate? PlayerHasChanged;
|
||||
public API.Data.CharacterData? LastCreatedCharacterData { get; private set; }
|
||||
public Models.CharacterData PermanentDataCache { get; private set; } = new();
|
||||
private readonly Dictionary<ObjectKind, Func<bool>> _objectKindsToUpdate = new();
|
||||
|
||||
private CancellationTokenSource? _playerChangedCts = new();
|
||||
private CancellationTokenSource _transientUpdateCts = new();
|
||||
|
||||
private List<PlayerRelatedObject> playerRelatedObjects = new();
|
||||
private readonly List<PlayerRelatedObject> _playerRelatedObjects = new();
|
||||
|
||||
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
|
||||
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager,
|
||||
@@ -66,7 +60,7 @@ public class PlayerManager : IDisposable
|
||||
ApiControllerOnConnected();
|
||||
}
|
||||
|
||||
playerRelatedObjects = new List<PlayerRelatedObject>()
|
||||
_playerRelatedObjects = new List<PlayerRelatedObject>()
|
||||
{
|
||||
new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero, () => _dalamudUtil.PlayerPointer),
|
||||
new PlayerRelatedObject(ObjectKind.MinionOrMount, IntPtr.Zero, IntPtr.Zero, () => (IntPtr)((Character*)_dalamudUtil.PlayerPointer)->CompanionObject),
|
||||
@@ -77,12 +71,12 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void DalamudUtilOnFrameworkUpdate()
|
||||
{
|
||||
_transientResourceManager.PlayerRelatedPointers = playerRelatedObjects.Select(f => f.CurrentAddress).ToArray();
|
||||
_transientResourceManager.PlayerRelatedPointers = _playerRelatedObjects.Select(f => f.CurrentAddress).ToArray();
|
||||
}
|
||||
|
||||
public void HandleTransientResourceLoad(IntPtr gameObj)
|
||||
public void HandleTransientResourceLoad(IntPtr gameObj, int idx)
|
||||
{
|
||||
foreach (var obj in playerRelatedObjects)
|
||||
foreach (var obj in _playerRelatedObjects)
|
||||
{
|
||||
if (obj.Address == gameObj && !obj.HasUnprocessedUpdate)
|
||||
{
|
||||
@@ -105,7 +99,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void HeelsOffsetChanged(float change)
|
||||
{
|
||||
var player = playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
var player = _playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && LastCreatedCharacterData.HeelsOffset != change && !player.IsProcessing)
|
||||
{
|
||||
Logger.Debug("Heels offset changed to " + change);
|
||||
@@ -116,8 +110,8 @@ public class PlayerManager : IDisposable
|
||||
private void CustomizePlusChanged(string? change)
|
||||
{
|
||||
change ??= string.Empty;
|
||||
var player = playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && LastCreatedCharacterData.CustomizePlusData != change && !player.IsProcessing)
|
||||
var player = _playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && !string.Equals(LastCreatedCharacterData.CustomizePlusData, change, StringComparison.Ordinal) && !player.IsProcessing)
|
||||
{
|
||||
Logger.Debug("CustomizePlus data changed to " + change);
|
||||
player.HasTransientsUpdate = true;
|
||||
@@ -146,8 +140,8 @@ public class PlayerManager : IDisposable
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return;
|
||||
|
||||
playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject());
|
||||
if (playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && !c.IsProcessing))
|
||||
_playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject());
|
||||
if (_playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && !c.IsProcessing))
|
||||
{
|
||||
OnPlayerOrAttachedObjectsChanged();
|
||||
}
|
||||
@@ -167,9 +161,9 @@ public class PlayerManager : IDisposable
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
|
||||
}
|
||||
|
||||
private async Task<CharacterCacheDto?> CreateFullCharacterCacheDto(CancellationToken token)
|
||||
private async Task<API.Data.CharacterData?> CreateFullCharacterCacheDto(CancellationToken token)
|
||||
{
|
||||
foreach (var unprocessedObject in playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList())
|
||||
foreach (var unprocessedObject in _playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList())
|
||||
{
|
||||
Logger.Verbose("Building Cache for " + unprocessedObject.ObjectKind);
|
||||
PermanentDataCache = _characterDataFactory.BuildCharacterData(PermanentDataCache, unprocessedObject, token);
|
||||
@@ -194,7 +188,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
Logger.Verbose("Cache creation complete");
|
||||
|
||||
var cache = PermanentDataCache.ToCharacterCacheDto();
|
||||
var cache = PermanentDataCache.ToAPI();
|
||||
//Logger.Verbose(JsonConvert.SerializeObject(cache, Formatting.Indented));
|
||||
return cache;
|
||||
}
|
||||
@@ -203,7 +197,7 @@ public class PlayerManager : IDisposable
|
||||
{
|
||||
Logger.Verbose("RedrawEvent for addr " + address);
|
||||
|
||||
foreach (var item in playerRelatedObjects)
|
||||
foreach (var item in _playerRelatedObjects)
|
||||
{
|
||||
if (address == item.Address)
|
||||
{
|
||||
@@ -212,7 +206,7 @@ public class PlayerManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
if (playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && (!c.IsProcessing || (c.IsProcessing && c.DoNotSendUpdate))))
|
||||
if (_playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && (!c.IsProcessing || (c.IsProcessing && c.DoNotSendUpdate))))
|
||||
{
|
||||
OnPlayerOrAttachedObjectsChanged();
|
||||
}
|
||||
@@ -220,7 +214,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void OnPlayerOrAttachedObjectsChanged()
|
||||
{
|
||||
var unprocessedObjects = playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList();
|
||||
var unprocessedObjects = _playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList();
|
||||
foreach (var unprocessedObject in unprocessedObjects)
|
||||
{
|
||||
unprocessedObject.IsProcessing = true;
|
||||
@@ -253,7 +247,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
CharacterCacheDto? cacheDto = null;
|
||||
API.Data.CharacterData? cacheDto = null;
|
||||
try
|
||||
{
|
||||
_periodicFileScanner.HaltScan("Character creation");
|
||||
@@ -278,15 +272,13 @@ public class PlayerManager : IDisposable
|
||||
//Logger.Verbose(json);
|
||||
#endif
|
||||
|
||||
if ((LastCreatedCharacterData?.GetHashCode() ?? 0) == cacheDto.GetHashCode())
|
||||
if (string.Equals(LastCreatedCharacterData?.DataHash.Value ?? string.Empty, cacheDto.DataHash.Value, StringComparison.Ordinal))
|
||||
{
|
||||
Logger.Debug("Not sending data, already sent");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastCreatedCharacterData = cacheDto;
|
||||
}
|
||||
|
||||
LastCreatedCharacterData = cacheDto;
|
||||
|
||||
if (_apiController.IsConnected && !token.IsCancellationRequested && !doNotSendUpdate)
|
||||
{
|
||||
|
||||
168
MareSynchronos/Managers/ServerConfigurationManager.cs
Normal file
168
MareSynchronos/Managers/ServerConfigurationManager.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class ServerConfigurationManager
|
||||
{
|
||||
private readonly Dictionary<JwtCache, string> _tokenDictionary = new();
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
|
||||
public string CurrentApiUrl => string.IsNullOrEmpty(_configService.Current.CurrentServer) ? ApiController.MainServiceUri : _configService.Current.CurrentServer;
|
||||
public ServerStorage? CurrentServer => (_configService.Current.ServerStorage.ContainsKey(CurrentApiUrl) ? _configService.Current.ServerStorage[CurrentApiUrl] : null);
|
||||
|
||||
public ServerConfigurationManager(ConfigurationService configService, DalamudUtil dalamudUtil)
|
||||
{
|
||||
_configService = configService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
}
|
||||
|
||||
public bool HasValidConfig()
|
||||
{
|
||||
return CurrentServer != null && (CurrentServer?.Authentications.Any() ?? false);
|
||||
}
|
||||
|
||||
public string[] GetServerApiUrls()
|
||||
{
|
||||
return _configService.Current.ServerStorage.Keys.ToArray();
|
||||
}
|
||||
|
||||
public string[] GetServerNames()
|
||||
{
|
||||
return _configService.Current.ServerStorage.Values.Select(v => v.ServerName).ToArray();
|
||||
}
|
||||
|
||||
public ServerStorage GetServerByIndex(int idx)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _configService.Current.ServerStorage.ElementAt(idx).Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_configService.Current.CurrentServer = ApiController.MainServiceUri;
|
||||
if (!_configService.Current.ServerStorage.ContainsKey(ApiController.MainServer))
|
||||
{
|
||||
_configService.Current.ServerStorage.Add(_configService.Current.CurrentServer, new ServerStorage() { ServerUri = ApiController.MainServiceUri, ServerName = ApiController.MainServer });
|
||||
}
|
||||
_configService.Save();
|
||||
return CurrentServer!;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCurrentServerIndex()
|
||||
{
|
||||
return Array.IndexOf(_configService.Current.ServerStorage.Keys.ToArray(), CurrentApiUrl);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
public void SelectServer(int idx)
|
||||
{
|
||||
_configService.Current.CurrentServer = GetServerByIndex(idx).ServerUri;
|
||||
CurrentServer!.FullPause = false;
|
||||
Save();
|
||||
}
|
||||
|
||||
public string? GetSecretKey(int serverIdx = -1)
|
||||
{
|
||||
ServerStorage? currentServer;
|
||||
currentServer = serverIdx == -1 ? CurrentServer : GetServerByIndex(serverIdx);
|
||||
Save();
|
||||
if (currentServer == null)
|
||||
{
|
||||
currentServer = new();
|
||||
Save();
|
||||
}
|
||||
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
if (!currentServer.Authentications.Any() && currentServer.SecretKeys.Any())
|
||||
{
|
||||
currentServer.Authentications.Add(new Authentication()
|
||||
{
|
||||
CharacterName = charaName,
|
||||
WorldId = worldId,
|
||||
SecretKeyIdx = currentServer.SecretKeys.Last().Key,
|
||||
});
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
var auth = currentServer.Authentications.Find(f => string.Equals(f.CharacterName, charaName, StringComparison.Ordinal) && f.WorldId == worldId);
|
||||
if (auth == null) return null;
|
||||
|
||||
if (currentServer.SecretKeys.TryGetValue(auth.SecretKeyIdx, out var secretKey))
|
||||
{
|
||||
return secretKey.Key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string? GetToken()
|
||||
{
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
var secretKey = GetSecretKey();
|
||||
if (secretKey == null) return null;
|
||||
if (_tokenDictionary.TryGetValue(new JwtCache(CurrentApiUrl, charaName, worldId, secretKey), out var token))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SaveToken(string token)
|
||||
{
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
var secretKey = GetSecretKey();
|
||||
if (string.IsNullOrEmpty(secretKey)) throw new InvalidOperationException("No secret key set");
|
||||
_tokenDictionary[new JwtCache(CurrentApiUrl, charaName, worldId, secretKey)] = token;
|
||||
}
|
||||
|
||||
internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1, bool addLastSecretKey = false)
|
||||
{
|
||||
if (serverSelectionIndex == -1) serverSelectionIndex = GetCurrentServerIndex();
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Add(new Authentication()
|
||||
{
|
||||
CharacterName = _dalamudUtil.PlayerName,
|
||||
WorldId = _dalamudUtil.WorldId,
|
||||
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1,
|
||||
});
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void AddEmptyCharacterToServer(int serverSelectionIndex)
|
||||
{
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Add(new Authentication());
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void RemoveCharacterFromServer(int serverSelectionIndex, Authentication item)
|
||||
{
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Remove(item);
|
||||
}
|
||||
|
||||
internal void AddServer(ServerStorage serverStorage)
|
||||
{
|
||||
_configService.Current.ServerStorage[serverStorage.ServerUri] = serverStorage;
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void DeleteServer(ServerStorage selectedServer)
|
||||
{
|
||||
_configService.Current.ServerStorage.Remove(selectedServer.ServerUri);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,70 @@
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.Delegates;
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
|
||||
|
||||
public class TransientResourceManager : IDisposable
|
||||
{
|
||||
private readonly IpcManager manager;
|
||||
private readonly DalamudUtil dalamudUtil;
|
||||
private readonly string configurationDirectory;
|
||||
|
||||
public event TransientResourceLoadedEvent? TransientResourceLoaded;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
public event DrawObjectDelegate? TransientResourceLoaded;
|
||||
public IntPtr[] PlayerRelatedPointers = Array.Empty<IntPtr>();
|
||||
private readonly string[] FileTypesToHandle = new[] { "tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk" };
|
||||
private string PersistentDataCache => Path.Combine(configurationDirectory, "PersistentTransientData.lst");
|
||||
private readonly string[] _fileTypesToHandle = new[] { "tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk" };
|
||||
[Obsolete]
|
||||
private string PersistentDataCache => Path.Combine(_configurationService.ConfigurationDirectory, "PersistentTransientData.lst");
|
||||
private string PlayerPersistentDataKey => _dalamudUtil.PlayerName + "_" + _dalamudUtil.WorldId;
|
||||
|
||||
private ConcurrentDictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
|
||||
private ConcurrentDictionary<ObjectKind, HashSet<FileReplacement>> SemiTransientResources { get; } = new();
|
||||
public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil, FileReplacementFactory fileReplacementFactory, string configurationDirectory)
|
||||
public TransientResourceManager(IpcManager manager, ConfigurationService configurationService, DalamudUtil dalamudUtil, FileReplacementFactory fileReplacementFactory)
|
||||
{
|
||||
manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent;
|
||||
manager.PenumbraModSettingChanged += Manager_PenumbraModSettingChanged;
|
||||
this.manager = manager;
|
||||
this.dalamudUtil = dalamudUtil;
|
||||
this.configurationDirectory = configurationDirectory;
|
||||
_ipcManager = manager;
|
||||
_configurationService = configurationService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
dalamudUtil.FrameworkUpdate += DalamudUtil_FrameworkUpdate;
|
||||
dalamudUtil.ClassJobChanged += DalamudUtil_ClassJobChanged;
|
||||
// migrate obsolete data to new format
|
||||
if (File.Exists(PersistentDataCache))
|
||||
{
|
||||
var persistentEntities = File.ReadAllLines(PersistentDataCache);
|
||||
SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet<FileReplacement>());
|
||||
var persistentEntities = File.ReadAllLines(PersistentDataCache).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey] = persistentEntities;
|
||||
_configurationService.Save();
|
||||
File.Delete(PersistentDataCache);
|
||||
}
|
||||
|
||||
SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet<FileReplacement>());
|
||||
if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var linesInConfig))
|
||||
{
|
||||
int restored = 0;
|
||||
foreach (var line in persistentEntities)
|
||||
foreach (var file in linesInConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileReplacement = fileReplacementFactory.Create();
|
||||
fileReplacement.ResolvePath(line);
|
||||
fileReplacement.ResolvePath(file);
|
||||
if (fileReplacement.HasFileReplacement)
|
||||
{
|
||||
Logger.Debug("Loaded persistent transient resource " + line);
|
||||
Logger.Debug("Loaded persistent transient resource " + file);
|
||||
SemiTransientResources[ObjectKind.Player].Add(fileReplacement);
|
||||
restored++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Error during loading persistent transient resource " + line, ex);
|
||||
Logger.Warn("Error during loading persistent transient resource " + file, ex);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Debug($"Restored {restored}/{persistentEntities.Count()} semi persistent resources");
|
||||
}
|
||||
Logger.Debug($"Restored {restored}/{linesInConfig.Count()} semi persistent resources");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +83,7 @@ public class TransientResourceManager : IDisposable
|
||||
return !verified;
|
||||
});
|
||||
if (!successfulValidation)
|
||||
TransientResourceLoaded?.Invoke(dalamudUtil.PlayerPointer);
|
||||
TransientResourceLoaded?.Invoke(_dalamudUtil.PlayerPointer, -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -95,7 +100,7 @@ public class TransientResourceManager : IDisposable
|
||||
{
|
||||
foreach (var item in TransientResources.ToList())
|
||||
{
|
||||
if (!dalamudUtil.IsGameObjectPresent(item.Key))
|
||||
if (!_dalamudUtil.IsGameObjectPresent(item.Key))
|
||||
{
|
||||
Logger.Debug("Object not present anymore: " + item.Key.ToString("X"));
|
||||
TransientResources.TryRemove(item.Key, out _);
|
||||
@@ -133,7 +138,7 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath)
|
||||
{
|
||||
if (!FileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!_fileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -171,7 +176,7 @@ public class TransientResourceManager : IDisposable
|
||||
{
|
||||
TransientResources[gameObject].Add(replacedGamePath);
|
||||
Logger.Debug($"Adding {replacedGamePath} for {gameObject} ({filePath})");
|
||||
TransientResourceLoaded?.Invoke(gameObject);
|
||||
TransientResourceLoaded?.Invoke(gameObject, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,9 +215,9 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
var fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), true);
|
||||
var fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), arg2: true);
|
||||
if (!fileReplacement.HasFileReplacement)
|
||||
fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), false);
|
||||
fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), arg2: false);
|
||||
if (fileReplacement.HasFileReplacement)
|
||||
{
|
||||
Logger.Debug("Persisting " + gamePath.ToLowerInvariant());
|
||||
@@ -234,22 +239,26 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
if (objectKind == ObjectKind.Player && SemiTransientResources.TryGetValue(ObjectKind.Player, out var fileReplacements))
|
||||
{
|
||||
File.WriteAllLines(PersistentDataCache, fileReplacements.SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey]
|
||||
= fileReplacements.SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Save();
|
||||
}
|
||||
TransientResources[gameObject].Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
|
||||
manager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
|
||||
dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged;
|
||||
manager.PenumbraModSettingChanged -= Manager_PenumbraModSettingChanged;
|
||||
_dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
|
||||
_ipcManager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
|
||||
_dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged;
|
||||
_ipcManager.PenumbraModSettingChanged -= Manager_PenumbraModSettingChanged;
|
||||
TransientResources.Clear();
|
||||
SemiTransientResources.Clear();
|
||||
if (SemiTransientResources.ContainsKey(ObjectKind.Player))
|
||||
{
|
||||
File.WriteAllLines(PersistentDataCache, SemiTransientResources[ObjectKind.Player].SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey]
|
||||
= SemiTransientResources[ObjectKind.Player].SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user