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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user