test some refactoring for cachedplayer

This commit is contained in:
rootdarkarchon
2023-02-17 19:58:55 +01:00
parent 7f36e80e2a
commit 55ae736783
3 changed files with 94 additions and 153 deletions

View File

@@ -21,6 +21,14 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
{ {
_characterDataFactory = characterDataFactory; _characterDataFactory = characterDataFactory;
_playerRelatedObjects.AddRange(new List<GameObjectHandler>()
{
new(Mediator, ObjectKind.Player, () => dalamudUtil.PlayerPointer),
new(Mediator, ObjectKind.MinionOrMount, () => (IntPtr)((Character*)dalamudUtil.PlayerPointer)->CompanionObject),
new(Mediator, ObjectKind.Pet, () => dalamudUtil.GetPet()),
new(Mediator, ObjectKind.Companion, () => dalamudUtil.GetCompanion()),
});
Mediator.Subscribe<CreateCacheForObjectMessage>(this, (msg) => Mediator.Subscribe<CreateCacheForObjectMessage>(this, (msg) =>
{ {
var actualMsg = (CreateCacheForObjectMessage)msg; var actualMsg = (CreateCacheForObjectMessage)msg;
@@ -42,14 +50,6 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
Mediator.Subscribe<HeelsOffsetMessage>(this, (msg) => HeelsOffsetChanged((HeelsOffsetMessage)msg)); Mediator.Subscribe<HeelsOffsetMessage>(this, (msg) => HeelsOffsetChanged((HeelsOffsetMessage)msg));
Mediator.Subscribe<PalettePlusMessage>(this, (msg) => PalettePlusChanged((PalettePlusMessage)msg)); Mediator.Subscribe<PalettePlusMessage>(this, (msg) => PalettePlusChanged((PalettePlusMessage)msg));
Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (msg) => _cachesToCreate[ObjectKind.Player] = _playerRelatedObjects.First(p => p.ObjectKind == ObjectKind.Player)); Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (msg) => _cachesToCreate[ObjectKind.Player] = _playerRelatedObjects.First(p => p.ObjectKind == ObjectKind.Player));
_playerRelatedObjects.AddRange(new List<GameObjectHandler>()
{
new(Mediator, ObjectKind.Player, () => dalamudUtil.PlayerPointer),
new(Mediator, ObjectKind.MinionOrMount, () => (IntPtr)((Character*)dalamudUtil.PlayerPointer)->CompanionObject),
new(Mediator, ObjectKind.Pet, () => dalamudUtil.GetPet()),
new(Mediator, ObjectKind.Companion, () => dalamudUtil.GetCompanion()),
});
} }
private void PalettePlusChanged(PalettePlusMessage msg) private void PalettePlusChanged(PalettePlusMessage msg)

View File

@@ -1,6 +1,5 @@
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Logging; using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
@@ -45,33 +44,26 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
Logger.Debug("Checking for files to download for player " + PlayerName); Logger.Debug("Checking for files to download for player " + PlayerName);
Logger.Debug("Hash for data is " + characterData.DataHash.Value + ", current cache hash is " + _cachedData.DataHash.Value); Logger.Debug("Hash for data is " + characterData.DataHash.Value + ", current cache hash is " + _cachedData.DataHash.Value);
if (!_ipcManager.CheckPenumbraApi()) if (!_ipcManager.CheckPenumbraApi()) return;
{ if (!_ipcManager.CheckGlamourerApi()) return;
return;
}
if (!_ipcManager.CheckGlamourerApi())
{
return;
}
if (string.Equals(characterData.DataHash.Value, _cachedData.DataHash.Value, StringComparison.Ordinal) && !forced) return; if (string.Equals(characterData.DataHash.Value, _cachedData.DataHash.Value, StringComparison.Ordinal) && !forced) return;
CheckUpdatedData(characterData, out bool updateModdedPaths, out List<ObjectKind> charaDataToUpdate); CheckUpdatedData(characterData, out var charaDataToUpdate);
NotifyForMissingPlugins(characterData, warning); NotifyForMissingPlugins(characterData, warning);
_cachedData = characterData; _cachedData = characterData;
DownloadAndApplyCharacter(characterData, charaDataToUpdate, updateModdedPaths); DownloadAndApplyCharacter(characterData, charaDataToUpdate);
} }
private void CheckUpdatedData(API.Data.CharacterData newData, out bool updateModdedPaths, out List<ObjectKind> charaDataToUpdate) private void CheckUpdatedData(API.Data.CharacterData newData, out Dictionary<ObjectKind, HashSet<PlayerChanges>> charaDataToUpdate)
{ {
updateModdedPaths = false;
charaDataToUpdate = new(); charaDataToUpdate = new();
foreach (var objectKind in Enum.GetValues<ObjectKind>()) foreach (var objectKind in Enum.GetValues<ObjectKind>())
{ {
charaDataToUpdate[objectKind] = new();
_cachedData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements); _cachedData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
newData.FileReplacements.TryGetValue(objectKind, out var newFileReplacements); newData.FileReplacements.TryGetValue(objectKind, out var newFileReplacements);
_cachedData.GlamourerData.TryGetValue(objectKind, out var existingGlamourerData); _cachedData.GlamourerData.TryGetValue(objectKind, out var existingGlamourerData);
@@ -89,9 +81,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData) if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData)
{ {
Logger.Debug($"Updating {objectKind} (Some new data arrived: {hasNewButNotOldFileReplacements} {hasOldButNotNewFileReplacements} {hasNewButNotOldGlamourerData} {hasOldButNotNewGlamourerData})"); Logger.Debug($"Updating {objectKind} (Some new data arrived: {hasNewButNotOldFileReplacements} {hasOldButNotNewFileReplacements} {hasNewButNotOldGlamourerData} {hasOldButNotNewGlamourerData})");
updateModdedPaths = true; charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
charaDataToUpdate.Add(objectKind);
continue;
} }
if (hasNewAndOldFileReplacements) if (hasNewAndOldFileReplacements)
@@ -100,9 +90,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (!listsAreEqual) if (!listsAreEqual)
{ {
Logger.Debug($"Updating {objectKind} (FileReplacements not equal)"); Logger.Debug($"Updating {objectKind} (FileReplacements not equal)");
updateModdedPaths = true; charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
charaDataToUpdate.Add(objectKind);
continue;
} }
} }
@@ -112,8 +100,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (glamourerDataDifferent) if (glamourerDataDifferent)
{ {
Logger.Debug($"Updating {objectKind} (Diff glamourer data)"); Logger.Debug($"Updating {objectKind} (Diff glamourer data)");
charaDataToUpdate.Add(objectKind); charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
continue;
} }
} }
@@ -123,35 +110,45 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (manipDataDifferent) if (manipDataDifferent)
{ {
Logger.Debug($"Updating {objectKind} (Diff manip data)"); Logger.Debug($"Updating {objectKind} (Diff manip data)");
charaDataToUpdate.Add(objectKind); charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
continue;
} }
bool heelsOffsetDifferent = _cachedData.HeelsOffset != newData.HeelsOffset; bool heelsOffsetDifferent = _cachedData.HeelsOffset != newData.HeelsOffset;
if (heelsOffsetDifferent) if (heelsOffsetDifferent)
{ {
Logger.Debug($"Updating {objectKind} (Diff heels data)"); Logger.Debug($"Updating {objectKind} (Diff heels data)");
charaDataToUpdate.Add(objectKind); charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
continue;
} }
bool customizeDataDifferent = !string.Equals(_cachedData.CustomizePlusData, newData.CustomizePlusData, StringComparison.Ordinal); bool customizeDataDifferent = !string.Equals(_cachedData.CustomizePlusData, newData.CustomizePlusData, StringComparison.Ordinal);
if (customizeDataDifferent) if (customizeDataDifferent)
{ {
Logger.Debug($"Updating {objectKind} (Diff customize data)"); Logger.Debug($"Updating {objectKind} (Diff customize data)");
charaDataToUpdate.Add(objectKind); charaDataToUpdate[objectKind].Add(PlayerChanges.Customize);
continue;
} }
bool palettePlusDataDifferent = !string.Equals(_cachedData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal); bool palettePlusDataDifferent = !string.Equals(_cachedData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal);
if (palettePlusDataDifferent) if (palettePlusDataDifferent)
{ {
Logger.Debug($"Updating {objectKind} (Diff palette data)"); Logger.Debug($"Updating {objectKind} (Diff palette data)");
charaDataToUpdate.Add(objectKind); charaDataToUpdate[objectKind].Add(PlayerChanges.Palette);
continue;
} }
} }
} }
foreach (var data in charaDataToUpdate.ToList())
{
if (!data.Value.Any()) charaDataToUpdate.Remove(data.Key);
else charaDataToUpdate[data.Key] = data.Value.OrderBy(p => (int)p).ToHashSet();
}
}
private enum PlayerChanges
{
Heels = 1,
Customize = 2,
Palette = 3,
Mods = 4
} }
private void NotifyForMissingPlugins(API.Data.CharacterData characterData, OptionalPluginWarning warning) private void NotifyForMissingPlugins(API.Data.CharacterData characterData, OptionalPluginWarning warning)
@@ -270,120 +267,63 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData); _ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
} }
private async Task ApplyCustomizationData(ObjectKind objectKind) private async Task ApplyCustomizationData(KeyValuePair<ObjectKind, HashSet<PlayerChanges>> changes, API.Data.CharacterData charaData)
{ {
if (PlayerCharacter == IntPtr.Zero) return; if (PlayerCharacter == IntPtr.Zero) return;
_cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData);
var handler = changes.Key switch
{
ObjectKind.Player => _currentOtherChara!,
ObjectKind.Companion => new GameObjectHandler(Mediator, changes.Key, () => _dalamudUtil.GetCompanion(PlayerCharacter), false),
ObjectKind.MinionOrMount => new GameObjectHandler(Mediator, changes.Key, () => _dalamudUtil.GetMinionOrMount(PlayerCharacter) ?? IntPtr.Zero, false),
ObjectKind.Pet => new GameObjectHandler(Mediator, changes.Key, () => _dalamudUtil.GetPet(PlayerCharacter), false),
_ => throw new NotSupportedException("ObjectKind not supported: " + changes.Key)
};
CancellationTokenSource applicationTokenSource = new(); CancellationTokenSource applicationTokenSource = new();
applicationTokenSource.CancelAfter(TimeSpan.FromSeconds(30)); applicationTokenSource.CancelAfter(TimeSpan.FromSeconds(30));
switch (objectKind) if (handler.Address == IntPtr.Zero) return;
_dalamudUtil.WaitWhileCharacterIsDrawing(handler, 30000);
foreach (var change in changes.Value)
{ {
case ObjectKind.Player: Logger.Debug("Processing " + change + " for " + handler);
_dalamudUtil.WaitWhileCharacterIsDrawing(_currentOtherChara!, 30000); switch (change)
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter); {
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData); case PlayerChanges.Palette:
_ipcManager.PalettePlusSetPalette(PlayerCharacter, _cachedData.PalettePlusData); _ipcManager.PalettePlusSetPalette(handler.Address, charaData.PalettePlusData);
Logger.Debug($"Request Redraw for {PlayerName}");
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
await _ipcManager.GlamourerApplyAll(glamourerData, _currentOtherChara!, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(_currentOtherChara!, applicationTokenSource.Token).ConfigureAwait(false);
}
break;
case ObjectKind.MinionOrMount:
{
var minionOrMount = _dalamudUtil.GetMinionOrMount(PlayerCharacter);
if (minionOrMount != null)
{
using GameObjectHandler tempHandler = new(Mediator, ObjectKind.MinionOrMount,
() => minionOrMount.Value, false);
Logger.Debug($"Request Redraw for {PlayerName} Minion/Mount");
_dalamudUtil.WaitWhileCharacterIsDrawing(tempHandler, 30000);
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
await _ipcManager.GlamourerApplyAll(glamourerData, tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
}
break; break;
} case PlayerChanges.Customize:
_ipcManager.CustomizePlusSetBodyScale(handler.Address, charaData.CustomizePlusData);
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 {PlayerName} Pet");
do
{
Thread.Sleep(tick);
totalWait += tick;
newPet = _dalamudUtil.GetPet(PlayerCharacter);
} while (newPet == pet && totalWait < maxWait);
using var tempHandler = new GameObjectHandler(Mediator, ObjectKind.Pet, () => newPet, false);
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
await _ipcManager.GlamourerApplyAll(glamourerData, tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
}
break; break;
} case PlayerChanges.Heels:
_ipcManager.HeelsSetOffsetForPlayer(charaData.HeelsOffset, handler.Address);
case ObjectKind.Companion:
{
var companion = _dalamudUtil.GetCompanion(PlayerCharacter);
if (companion != IntPtr.Zero)
{
Logger.Debug($"Request Redraw for {PlayerName} Companion");
using GameObjectHandler tempHandler = new(Mediator, ObjectKind.Companion, () => companion, false);
_dalamudUtil.WaitWhileCharacterIsDrawing(tempHandler, 30000);
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
{
await _ipcManager.GlamourerApplyAll(glamourerData, tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
}
}
break; break;
} case PlayerChanges.Mods:
if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData))
{
await _ipcManager.GlamourerApplyAll(glamourerData, handler, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(handler, applicationTokenSource.Token).ConfigureAwait(false);
}
break;
}
} }
} }
private void DownloadAndApplyCharacter(API.Data.CharacterData charaData, List<ObjectKind> objectKind, bool updateModdedPaths) private void DownloadAndApplyCharacter(API.Data.CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData)
{ {
if (!objectKind.Any()) if (!updatedData.Any())
{ {
Logger.Debug("Nothing to update for " + this); Logger.Debug("Nothing to update for " + this);
} }
var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.Mods));
_downloadCancellationTokenSource?.Cancel(); _downloadCancellationTokenSource?.Cancel();
_downloadCancellationTokenSource?.Dispose(); _downloadCancellationTokenSource?.Dispose();
_downloadCancellationTokenSource = new CancellationTokenSource(); _downloadCancellationTokenSource = new CancellationTokenSource();
@@ -403,7 +343,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
{ {
downloadId = _apiController.GetDownloadId(); downloadId = _apiController.GetDownloadId();
Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind); Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + updatedData);
if (toDownloadReplacements.Any()) if (toDownloadReplacements.Any())
{ {
await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken).ConfigureAwait(false); await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken).ConfigureAwait(false);
@@ -435,14 +375,14 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
_applicationTask = Task.Run(async () => _applicationTask = Task.Run(async () =>
{ {
if (moddedPaths.Any()) if (updateModdedPaths)
{ {
ApplyBaseData(moddedPaths); ApplyBaseData(moddedPaths);
} }
foreach (var kind in objectKind) foreach (var kind in updatedData)
{ {
await ApplyCustomizationData(kind).ConfigureAwait(false); await ApplyCustomizationData(kind, charaData).ConfigureAwait(false);
} }
}); });
@@ -475,7 +415,9 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
{ {
_dalamudUtil.WaitWhileCharacterIsDrawing(_currentOtherChara!, ct: token); _dalamudUtil.WaitWhileCharacterIsDrawing(_currentOtherChara!, ct: token);
Logger.Debug("Unauthorized character change detected"); Logger.Debug("Unauthorized character change detected");
await ApplyCustomizationData(ObjectKind.Player).ConfigureAwait(false); await ApplyCustomizationData(new(ObjectKind.Player,
new HashSet<PlayerChanges>(new[] { PlayerChanges.Palette, PlayerChanges.Customize, PlayerChanges.Heels, PlayerChanges.Mods })),
_cachedData).ConfigureAwait(false);
}, token); }, token);
} }

View File

@@ -97,10 +97,9 @@ public class GameObjectHandler : MediatorSubscriberBase
private void FrameworkUpdate() private void FrameworkUpdate()
{ {
if (_delayedZoningTask?.IsCompleted ?? true) if (!_delayedZoningTask?.IsCompleted ?? false) return;
{
CheckAndUpdateObject(); CheckAndUpdateObject();
}
} }
private void ZoneSwitchEnd() private void ZoneSwitchEnd()
@@ -146,11 +145,11 @@ public class GameObjectHandler : MediatorSubscriberBase
private unsafe void CheckAndUpdateObject() private unsafe void CheckAndUpdateObject()
{ {
var curPtr = CurrentAddress; var curPtr = CurrentAddress;
bool drawObj = false; bool drawObjDiff = false;
try try
{ {
var drawObjAddr = (IntPtr)((GameObject*)curPtr)->GetDrawObject(); var drawObjAddr = (IntPtr)((GameObject*)curPtr)->GetDrawObject();
drawObj = drawObjAddr != DrawObjectAddress; drawObjDiff = drawObjAddr != DrawObjectAddress;
DrawObjectAddress = drawObjAddr; DrawObjectAddress = drawObjAddr;
IsBeingDrawn = (((CharacterBase*)drawObjAddr)->HasModelInSlotLoaded != 0) IsBeingDrawn = (((CharacterBase*)drawObjAddr)->HasModelInSlotLoaded != 0)
@@ -172,26 +171,25 @@ public class GameObjectHandler : MediatorSubscriberBase
_clearCts?.Cancel(); _clearCts?.Cancel();
_clearCts = null; _clearCts = null;
} }
bool addrDiff = Address != curPtr;
Address = curPtr;
var chara = (Character*)curPtr; var chara = (Character*)curPtr;
bool addr = Address == IntPtr.Zero || Address != curPtr; bool equipDiff = CompareAndUpdateEquipByteData(chara->EquipSlotData);
bool equip = CompareAndUpdateEquipByteData(chara->EquipSlotData); var customizeDiff = CompareAndUpdateCustomizeData(chara->CustomizeData);
var customize = CompareAndUpdateCustomizeData(chara->CustomizeData);
var name = new ByteString(chara->GameObject.Name).ToString(); var name = new ByteString(chara->GameObject.Name).ToString();
bool nameChange = (!string.Equals(name, Name, StringComparison.Ordinal)); bool nameChange = (!string.Equals(name, Name, StringComparison.Ordinal));
if (addr || equip || customize || drawObj || nameChange) if (addrDiff || equipDiff || customizeDiff || drawObjDiff || nameChange)
{ {
Name = name; Name = name;
Logger.Verbose($"{ObjectKind} changed: {Name}, now: {curPtr:X}, {(IntPtr)chara->GameObject.DrawObject:X}"); Logger.Verbose($"{ObjectKind} changed: {Name}, now: {curPtr:X}, {(IntPtr)chara->GameObject.DrawObject:X}");
Address = curPtr; if (_sendUpdates && !_doNotSendUpdate)
DrawObjectAddress = (IntPtr)chara->GameObject.DrawObject;
if (_sendUpdates && !_doNotSendUpdate && DrawObjectAddress != IntPtr.Zero)
{ {
Logger.Debug("Sending CreateCacheObjectMessage for " + ObjectKind); Logger.Debug("Sending CreateCacheObjectMessage for " + ObjectKind);
Mediator.Publish(new CreateCacheForObjectMessage(this)); Mediator.Publish(new CreateCacheForObjectMessage(this));
} }
if (equip) if (equipDiff && !_sendUpdates)
{ {
Mediator.Publish(new CharacterChangedMessage(this)); Mediator.Publish(new CharacterChangedMessage(this));
} }
@@ -237,6 +235,7 @@ public class GameObjectHandler : MediatorSubscriberBase
return hasChanges; return hasChanges;
} }
private unsafe bool CompareAndUpdateCustomizeData(byte* customizeData) private unsafe bool CompareAndUpdateCustomizeData(byte* customizeData)
{ {
bool hasChanges = false; bool hasChanges = false;