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;
_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) =>
{
var actualMsg = (CreateCacheForObjectMessage)msg;
@@ -42,14 +50,6 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable
Mediator.Subscribe<HeelsOffsetMessage>(this, (msg) => HeelsOffsetChanged((HeelsOffsetMessage)msg));
Mediator.Subscribe<PalettePlusMessage>(this, (msg) => PalettePlusChanged((PalettePlusMessage)msg));
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)

View File

@@ -1,6 +1,5 @@
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum;
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("Hash for data is " + characterData.DataHash.Value + ", current cache hash is " + _cachedData.DataHash.Value);
if (!_ipcManager.CheckPenumbraApi())
{
return;
}
if (!_ipcManager.CheckGlamourerApi())
{
return;
}
if (!_ipcManager.CheckPenumbraApi()) return;
if (!_ipcManager.CheckGlamourerApi()) 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);
_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();
foreach (var objectKind in Enum.GetValues<ObjectKind>())
{
charaDataToUpdate[objectKind] = new();
_cachedData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
newData.FileReplacements.TryGetValue(objectKind, out var newFileReplacements);
_cachedData.GlamourerData.TryGetValue(objectKind, out var existingGlamourerData);
@@ -89,9 +81,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData)
{
Logger.Debug($"Updating {objectKind} (Some new data arrived: {hasNewButNotOldFileReplacements} {hasOldButNotNewFileReplacements} {hasNewButNotOldGlamourerData} {hasOldButNotNewGlamourerData})");
updateModdedPaths = true;
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
}
if (hasNewAndOldFileReplacements)
@@ -100,9 +90,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (!listsAreEqual)
{
Logger.Debug($"Updating {objectKind} (FileReplacements not equal)");
updateModdedPaths = true;
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
}
}
@@ -112,8 +100,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (glamourerDataDifferent)
{
Logger.Debug($"Updating {objectKind} (Diff glamourer data)");
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
}
}
@@ -123,35 +110,45 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
if (manipDataDifferent)
{
Logger.Debug($"Updating {objectKind} (Diff manip data)");
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Mods);
}
bool heelsOffsetDifferent = _cachedData.HeelsOffset != newData.HeelsOffset;
if (heelsOffsetDifferent)
{
Logger.Debug($"Updating {objectKind} (Diff heels data)");
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
}
bool customizeDataDifferent = !string.Equals(_cachedData.CustomizePlusData, newData.CustomizePlusData, StringComparison.Ordinal);
if (customizeDataDifferent)
{
Logger.Debug($"Updating {objectKind} (Diff customize data)");
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Customize);
}
bool palettePlusDataDifferent = !string.Equals(_cachedData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal);
if (palettePlusDataDifferent)
{
Logger.Debug($"Updating {objectKind} (Diff palette data)");
charaDataToUpdate.Add(objectKind);
continue;
charaDataToUpdate[objectKind].Add(PlayerChanges.Palette);
}
}
}
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)
@@ -270,120 +267,63 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
_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;
_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();
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:
_dalamudUtil.WaitWhileCharacterIsDrawing(_currentOtherChara!, 30000);
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData);
_ipcManager.PalettePlusSetPalette(PlayerCharacter, _cachedData.PalettePlusData);
Logger.Debug($"Request Redraw for {PlayerName}");
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
Logger.Debug("Processing " + change + " for " + handler);
switch (change)
{
await _ipcManager.GlamourerApplyAll(glamourerData, _currentOtherChara!, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(_currentOtherChara!, applicationTokenSource.Token).ConfigureAwait(false);
}
case PlayerChanges.Palette:
_ipcManager.PalettePlusSetPalette(handler.Address, charaData.PalettePlusData);
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);
}
}
case PlayerChanges.Customize:
_ipcManager.CustomizePlusSetBodyScale(handler.Address, charaData.CustomizePlusData);
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 {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);
}
}
case PlayerChanges.Heels:
_ipcManager.HeelsSetOffsetForPlayer(charaData.HeelsOffset, handler.Address);
break;
}
case ObjectKind.Companion:
case PlayerChanges.Mods:
if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData))
{
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);
await _ipcManager.GlamourerApplyAll(glamourerData, handler, applicationTokenSource.Token).ConfigureAwait(false);
}
else
{
await _ipcManager.PenumbraRedraw(tempHandler, applicationTokenSource.Token).ConfigureAwait(false);
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);
}
var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.Mods));
_downloadCancellationTokenSource?.Cancel();
_downloadCancellationTokenSource?.Dispose();
_downloadCancellationTokenSource = new CancellationTokenSource();
@@ -403,7 +343,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
{
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())
{
await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken).ConfigureAwait(false);
@@ -435,14 +375,14 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable
_applicationTask = Task.Run(async () =>
{
if (moddedPaths.Any())
if (updateModdedPaths)
{
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);
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);
}

View File

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