dunno what's wrong with pets (fuck pets, not literally)

This commit is contained in:
Stanley Dimant
2025-02-10 21:43:34 +01:00
parent 79f5065128
commit 61b6365542
3 changed files with 85 additions and 75 deletions

View File

@@ -80,27 +80,28 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
public void CleanUpSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null) public void CleanUpSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null)
{ {
if (SemiTransientResources.TryGetValue(objectKind, out HashSet<string>? value)) if (!SemiTransientResources.TryGetValue(objectKind, out HashSet<string>? value))
return;
if (fileReplacement == null)
{ {
if (fileReplacement == null) value.Clear();
{ return;
value.Clear(); }
return;
}
bool removedPaths = false; int removedPaths = 0;
foreach (var replacement in fileReplacement.Where(p => !p.HasFileReplacement).SelectMany(p => p.GamePaths).ToList()) foreach (var replacement in fileReplacement.Where(p => !p.HasFileReplacement).SelectMany(p => p.GamePaths).ToList())
{ {
removedPaths = true; removedPaths++;
PlayerConfig.RemovePath(replacement); PlayerConfig.RemovePath(replacement);
} value.Remove(replacement);
}
if (removedPaths) if (removedPaths > 0)
{ {
// force reload semi transient resources Logger.LogTrace("Removed {amount} of SemiTransient paths during CleanUp, Saving from {name}", removedPaths, nameof(CleanUpSemiTransientResources));
_semiTransientResources = null; // force reload semi transient resources
_configurationService.Save(); _configurationService.Save();
}
} }
} }
@@ -131,27 +132,33 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
semiTransientResources.Add(gamePath); semiTransientResources.Add(gamePath);
} }
if (objectKind == ObjectKind.Player && newlyAddedGamePaths.Any()) bool saveConfig = false;
if (objectKind == ObjectKind.Player && newlyAddedGamePaths.Count != 0)
{ {
saveConfig = true;
foreach (var item in newlyAddedGamePaths.Where(f => !string.IsNullOrEmpty(f))) foreach (var item in newlyAddedGamePaths.Where(f => !string.IsNullOrEmpty(f)))
{ {
PlayerConfig.AddOrElevate(_dalamudUtil.ClassJobId, item); PlayerConfig.AddOrElevate(_dalamudUtil.ClassJobId, item);
} }
_configurationService.Save();
} }
else if (objectKind == ObjectKind.Pet && newlyAddedGamePaths.Any()) else if (objectKind == ObjectKind.Pet && newlyAddedGamePaths.Count != 0)
{ {
foreach (var item in newlyAddedGamePaths.Where(f => !string.IsNullOrEmpty(f))) saveConfig = true;
{
if (!PlayerConfig.JobSpecificPetCache.TryGetValue(_dalamudUtil.ClassJobId, out var petPerma))
{
PlayerConfig.JobSpecificPetCache[_dalamudUtil.ClassJobId] = petPerma = [];
}
petPerma.Add(item); if (!PlayerConfig.JobSpecificPetCache.TryGetValue(_dalamudUtil.ClassJobId, out var petPerma))
{
PlayerConfig.JobSpecificPetCache[_dalamudUtil.ClassJobId] = petPerma = [];
} }
foreach (var item in newlyAddedGamePaths.Where(f => !string.IsNullOrEmpty(f)))
{
petPerma.Add(item);
}
}
if (saveConfig)
{
Logger.LogTrace("Saving transient.json from {method}", nameof(PersistTransientResources));
_configurationService.Save(); _configurationService.Save();
} }
@@ -166,6 +173,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
if (objectKind == ObjectKind.Player) if (objectKind == ObjectKind.Player)
{ {
PlayerConfig.RemovePath(path); PlayerConfig.RemovePath(path);
Logger.LogTrace("Saving transient.json from {method}", nameof(RemoveTransientResource));
_configurationService.Save(); _configurationService.Save();
} }
} }
@@ -176,14 +184,13 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
if (SemiTransientResources.TryGetValue(objectKind, out var semiTransient) && semiTransient != null && semiTransient.Contains(item)) if (SemiTransientResources.TryGetValue(objectKind, out var semiTransient) && semiTransient != null && semiTransient.Contains(item))
return false; return false;
if (!TransientResources.TryGetValue(objectKind, out HashSet<string>? value)) if (!TransientResources.TryGetValue(objectKind, out HashSet<string>? transientResource))
{ {
value = new HashSet<string>(StringComparer.Ordinal); transientResource = new HashSet<string>(StringComparer.Ordinal);
TransientResources[objectKind] = value; TransientResources[objectKind] = transientResource;
} }
value.Add(item.ToLowerInvariant()); return transientResource.Add(item.ToLowerInvariant());
return true;
} }
internal void ClearTransientPaths(ObjectKind objectKind, List<string> list) internal void ClearTransientPaths(ObjectKind objectKind, List<string> list)
@@ -220,6 +227,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
if (removed > 0) if (removed > 0)
{ {
reloadSemiTransient = true; reloadSemiTransient = true;
Logger.LogTrace("Saving transient.json from {method}", nameof(ClearTransientPaths));
_configurationService.Save(); _configurationService.Save();
} }
} }
@@ -337,28 +345,29 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
// ^ all of the code above is just to sanitize the data // ^ all of the code above is just to sanitize the data
if (!TransientResources.TryGetValue(objectKind, out HashSet<string>? value)) if (!TransientResources.TryGetValue(objectKind, out HashSet<string>? transientResources))
{ {
value = new(StringComparer.OrdinalIgnoreCase); transientResources = new(StringComparer.OrdinalIgnoreCase);
TransientResources[objectKind] = value; TransientResources[objectKind] = transientResources;
} }
var owner = _playerRelatedPointers.FirstOrDefault(f => f.Address == gameObjectAddress); var owner = _playerRelatedPointers.FirstOrDefault(f => f.Address == gameObjectAddress);
bool alreadyTransient = false; bool alreadyTransient = false;
if (value.Contains(replacedGamePath) bool transientContains = transientResources.Contains(replacedGamePath);
|| SemiTransientResources.SelectMany(k => k.Value).Any(f => string.Equals(f, gamePath, StringComparison.OrdinalIgnoreCase))) bool semiTransientContains = SemiTransientResources.SelectMany(k => k.Value).Any(f => string.Equals(f, gamePath, StringComparison.OrdinalIgnoreCase));
if (transientContains || semiTransientContains)
{ {
if (!IsTransientRecording) if (!IsTransientRecording)
Logger.LogTrace("Not adding {replacedPath} : {filePath}, Reason: Transient: {contains}, SemiTransient: {contains2}", replacedGamePath, filePath, Logger.LogTrace("Not adding {replacedPath} => {filePath}, Reason: Transient: {contains}, SemiTransient: {contains2}", replacedGamePath, filePath,
value.Contains(replacedGamePath), SemiTransientResources.SelectMany(k => k.Value).Any(f => string.Equals(f, gamePath, StringComparison.OrdinalIgnoreCase))); transientContains, semiTransientContains);
alreadyTransient = true; alreadyTransient = true;
} }
else else
{ {
if (!IsTransientRecording) if (!IsTransientRecording)
{ {
bool isAdded = value.Add(replacedGamePath); bool isAdded = transientResources.Add(replacedGamePath);
if (isAdded) if (isAdded)
{ {
Logger.LogDebug("Adding {replacedGamePath} for {gameObject} ({filePath})", replacedGamePath, owner?.ToString() ?? gameObjectAddress.ToString("X"), filePath); Logger.LogDebug("Adding {replacedGamePath} for {gameObject} ({filePath})", replacedGamePath, owner?.ToString() ?? gameObjectAddress.ToString("X"), filePath);

View File

@@ -77,10 +77,10 @@ public sealed class IpcCallerHonorific : IIpcCaller
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
public string GetTitle() public async Task<string> GetTitle()
{ {
if (!APIAvailable) return string.Empty; if (!APIAvailable) return string.Empty;
string title = _honorificGetLocalCharacterTitle.InvokeFunc(); string title = await _dalamudUtil.RunOnFrameworkThread(() => _honorificGetLocalCharacterTitle.InvokeFunc()).ConfigureAwait(false);
return string.IsNullOrEmpty(title) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(title)); return string.IsNullOrEmpty(title) ? string.Empty : Convert.ToBase64String(Encoding.UTF8.GetBytes(title));
} }

View File

@@ -112,22 +112,22 @@ public class PlayerDataFactory
return ((Character*)playerPointer)->GameObject.DrawObject == null; return ((Character*)playerPointer)->GameObject.DrawObject == null;
} }
private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) private async Task<CharacterData> CreateCharacterData(CharacterData data, GameObjectHandler playerRelatedObject, CancellationToken token)
{ {
var objectKind = playerRelatedObject.ObjectKind; var objectKind = playerRelatedObject.ObjectKind;
_logger.LogDebug("Building character data for {obj}", playerRelatedObject); _logger.LogDebug("Building character data for {obj}", playerRelatedObject);
if (!previousData.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? value)) if (!data.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? value))
{ {
previousData.FileReplacements[objectKind] = new(FileReplacementComparer.Instance); data.FileReplacements[objectKind] = new(FileReplacementComparer.Instance);
} }
else else
{ {
value.Clear(); value.Clear();
} }
previousData.CustomizePlusScale.Remove(objectKind); data.CustomizePlusScale.Remove(objectKind);
// wait until chara is not drawing and present so nothing spontaneously explodes // wait until chara is not drawing and present so nothing spontaneously explodes
await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token).ConfigureAwait(false); await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token).ConfigureAwait(false);
@@ -151,13 +151,13 @@ public class PlayerDataFactory
resolvedPaths = (await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false)); resolvedPaths = (await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false));
if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data"); if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data");
previousData.FileReplacements[objectKind] = data.FileReplacements[objectKind] =
new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance) new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement([.. c.Value], c.Key)), FileReplacementComparer.Instance)
.Where(p => p.HasFileReplacement).ToHashSet(); .Where(p => p.HasFileReplacement).ToHashSet();
previousData.FileReplacements[objectKind].RemoveWhere(c => c.GamePaths.Any(g => !CacheMonitor.AllowedFileExtensions.Any(e => g.EndsWith(e, StringComparison.OrdinalIgnoreCase)))); data.FileReplacements[objectKind].RemoveWhere(c => c.GamePaths.Any(g => !CacheMonitor.AllowedFileExtensions.Any(e => g.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
_logger.LogDebug("== Static Replacements =="); _logger.LogDebug("== Static Replacements ==");
foreach (var replacement in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).OrderBy(i => i.GamePaths.First(), StringComparer.OrdinalIgnoreCase)) foreach (var replacement in data.FileReplacements[objectKind].Where(i => i.HasFileReplacement).OrderBy(i => i.GamePaths.First(), StringComparer.OrdinalIgnoreCase))
{ {
_logger.LogDebug("=> {repl}", replacement); _logger.LogDebug("=> {repl}", replacement);
} }
@@ -168,7 +168,7 @@ public class PlayerDataFactory
// or we get into redraw city for every change and nothing works properly // or we get into redraw city for every change and nothing works properly
if (objectKind == ObjectKind.Pet) if (objectKind == ObjectKind.Pet)
{ {
foreach (var item in previousData.FileReplacements[ObjectKind.Pet].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths)) foreach (var item in data.FileReplacements[ObjectKind.Pet].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths))
{ {
if (_transientResourceManager.AddTransientResource(objectKind, item)) if (_transientResourceManager.AddTransientResource(objectKind, item))
{ {
@@ -176,14 +176,14 @@ public class PlayerDataFactory
} }
} }
_logger.LogTrace("Clearing {count} Static Replacements for Pet", previousData.FileReplacements[ObjectKind.Pet].Count); _logger.LogTrace("Clearing {count} Static Replacements for Pet", data.FileReplacements[ObjectKind.Pet].Count);
previousData.FileReplacements[ObjectKind.Pet].Clear(); data.FileReplacements[ObjectKind.Pet].Clear();
} }
_logger.LogDebug("Handling transient update for {obj}", playerRelatedObject); _logger.LogDebug("Handling transient update for {obj}", playerRelatedObject);
// remove all potentially gathered paths from the transient resource manager that are resolved through static resolving // remove all potentially gathered paths from the transient resource manager that are resolved through static resolving
_transientResourceManager.ClearTransientPaths(objectKind, previousData.FileReplacements[objectKind].SelectMany(c => c.GamePaths).ToList()); _transientResourceManager.ClearTransientPaths(objectKind, data.FileReplacements[objectKind].SelectMany(c => c.GamePaths).ToList());
// get all remaining paths and resolve them // get all remaining paths and resolve them
var transientPaths = ManageSemiTransientData(objectKind); var transientPaths = ManageSemiTransientData(objectKind);
@@ -193,42 +193,43 @@ public class PlayerDataFactory
foreach (var replacement in resolvedTransientPaths.Select(c => new FileReplacement([.. c.Value], c.Key)).OrderBy(f => f.ResolvedPath, StringComparer.Ordinal)) foreach (var replacement in resolvedTransientPaths.Select(c => new FileReplacement([.. c.Value], c.Key)).OrderBy(f => f.ResolvedPath, StringComparer.Ordinal))
{ {
_logger.LogDebug("=> {repl}", replacement); _logger.LogDebug("=> {repl}", replacement);
previousData.FileReplacements[objectKind].Add(replacement); data.FileReplacements[objectKind].Add(replacement);
} }
// clean up all semi transient resources that don't have any file replacement (aka null resolve) // clean up all semi transient resources that don't have any file replacement (aka null resolve)
_transientResourceManager.CleanUpSemiTransientResources(objectKind, [.. previousData.FileReplacements[objectKind]]); _transientResourceManager.CleanUpSemiTransientResources(objectKind, [.. data.FileReplacements[objectKind]]);
// make sure we only return data that actually has file replacements // make sure we only return data that actually has file replacements
foreach (var item in previousData.FileReplacements) foreach (var item in data.FileReplacements)
{ {
previousData.FileReplacements[item.Key] = new HashSet<FileReplacement>(item.Value.Where(v => v.HasFileReplacement).OrderBy(v => v.ResolvedPath, StringComparer.Ordinal), FileReplacementComparer.Instance); data.FileReplacements[item.Key] = new HashSet<FileReplacement>(item.Value.Where(v => v.HasFileReplacement).OrderBy(v => v.ResolvedPath, StringComparer.Ordinal), FileReplacementComparer.Instance);
} }
// gather up data from ipc // gather up data from ipc
previousData.ManipulationString = _ipcManager.Penumbra.GetMetaManipulations(); data.ManipulationString = _ipcManager.Penumbra.GetMetaManipulations();
Task<string> getHeelsOffset = _ipcManager.Heels.GetOffsetAsync(); Task<string> getHeelsOffset = _ipcManager.Heels.GetOffsetAsync();
Task<string> getGlamourerData = _ipcManager.Glamourer.GetCharacterCustomizationAsync(playerRelatedObject.Address); Task<string> getGlamourerData = _ipcManager.Glamourer.GetCharacterCustomizationAsync(playerRelatedObject.Address);
Task<string?> getCustomizeData = _ipcManager.CustomizePlus.GetScaleAsync(playerRelatedObject.Address); Task<string?> getCustomizeData = _ipcManager.CustomizePlus.GetScaleAsync(playerRelatedObject.Address);
previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false); Task<string> getHonorificTitle = _ipcManager.Honorific.GetTitle();
_logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]); data.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false);
_logger.LogDebug("Glamourer is now: {data}", data.GlamourerString[playerRelatedObject.ObjectKind]);
var customizeScale = await getCustomizeData.ConfigureAwait(false); var customizeScale = await getCustomizeData.ConfigureAwait(false);
previousData.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale ?? string.Empty; data.CustomizePlusScale[playerRelatedObject.ObjectKind] = customizeScale ?? string.Empty;
_logger.LogDebug("Customize is now: {data}", previousData.CustomizePlusScale[playerRelatedObject.ObjectKind]); _logger.LogDebug("Customize is now: {data}", data.CustomizePlusScale[playerRelatedObject.ObjectKind]);
previousData.HonorificData = _ipcManager.Honorific.GetTitle(); data.HonorificData = await getHonorificTitle.ConfigureAwait(false);
_logger.LogDebug("Honorific is now: {data}", previousData.HonorificData); _logger.LogDebug("Honorific is now: {data}", data.HonorificData);
previousData.HeelsData = await getHeelsOffset.ConfigureAwait(false); data.HeelsData = await getHeelsOffset.ConfigureAwait(false);
_logger.LogDebug("Heels is now: {heels}", previousData.HeelsData); _logger.LogDebug("Heels is now: {heels}", data.HeelsData);
if (objectKind == ObjectKind.Player) if (objectKind == ObjectKind.Player)
{ {
previousData.MoodlesData = await _ipcManager.Moodles.GetStatusAsync(playerRelatedObject.Address).ConfigureAwait(false) ?? string.Empty; data.MoodlesData = await _ipcManager.Moodles.GetStatusAsync(playerRelatedObject.Address).ConfigureAwait(false) ?? string.Empty;
_logger.LogDebug("Moodles is now: {moodles}", previousData.MoodlesData); _logger.LogDebug("Moodles is now: {moodles}", data.MoodlesData);
previousData.PetNamesData = _ipcManager.PetNames.GetLocalNames(); data.PetNamesData = _ipcManager.PetNames.GetLocalNames();
_logger.LogDebug("Pet Nicknames is now: {petnames}", previousData.PetNamesData); _logger.LogDebug("Pet Nicknames is now: {petnames}", data.PetNamesData);
} }
if (previousData.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? fileReplacements)) if (data.FileReplacements.TryGetValue(objectKind, out HashSet<FileReplacement>? fileReplacements))
{ {
var toCompute = fileReplacements.Where(f => !f.IsFileSwap).ToArray(); var toCompute = fileReplacements.Where(f => !f.IsFileSwap).ToArray();
_logger.LogDebug("Getting Hashes for {amount} Files", toCompute.Length); _logger.LogDebug("Getting Hashes for {amount} Files", toCompute.Length);
@@ -248,7 +249,7 @@ public class PlayerDataFactory
{ {
try try
{ {
await VerifyPlayerAnimationBones(boneIndices, previousData, objectKind).ConfigureAwait(false); await VerifyPlayerAnimationBones(boneIndices, data, objectKind).ConfigureAwait(false);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -258,7 +259,7 @@ public class PlayerDataFactory
_logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(DateTime.UtcNow.Ticks - start.Ticks).TotalMilliseconds); _logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(DateTime.UtcNow.Ticks - start.Ticks).TotalMilliseconds);
return previousData; return data;
} }
private async Task VerifyPlayerAnimationBones(Dictionary<string, List<ushort>>? boneIndices, CharacterData previousData, ObjectKind objectKind) private async Task VerifyPlayerAnimationBones(Dictionary<string, List<ushort>>? boneIndices, CharacterData previousData, ObjectKind objectKind)