clean up character data factory
This commit is contained in:
		| @@ -1,5 +1,4 @@ | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using Dalamud.Utility; |  | ||||||
| using FFXIVClientStructs.FFXIV.Client.Game.Character; | using FFXIVClientStructs.FFXIV.Client.Game.Character; | ||||||
| using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; | using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; | ||||||
| using FFXIVClientStructs.FFXIV.Client.System.Resource; | using FFXIVClientStructs.FFXIV.Client.System.Resource; | ||||||
| @@ -36,7 +35,7 @@ public class CharacterDataFactory | |||||||
|         return ((Character*)playerPointer)->GameObject.DrawObject == null; |         return ((Character*)playerPointer)->GameObject.DrawObject == null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public CharacterData BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) |     public async Task<CharacterData> BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) | ||||||
|     { |     { | ||||||
|         if (!_ipcManager.Initialized) |         if (!_ipcManager.Initialized) | ||||||
|         { |         { | ||||||
| @@ -75,9 +74,7 @@ public class CharacterDataFactory | |||||||
|  |  | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             _pathsToForwardResolve.Clear(); |             return await CreateCharacterData(previousData, playerRelatedObject, token).ConfigureAwait(false); | ||||||
|             _pathsToReverseResolve.Clear(); |  | ||||||
|             return CreateCharacterData(previousData, playerRelatedObject, token); |  | ||||||
|         } |         } | ||||||
|         catch (OperationCanceledException) |         catch (OperationCanceledException) | ||||||
|         { |         { | ||||||
| @@ -94,7 +91,171 @@ public class CharacterDataFactory | |||||||
|         return previousData; |         return previousData; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private unsafe void AddReplacementsFromRenderModel(RenderModel* mdl) |     private async Task<CharacterData> CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) | ||||||
|  |     { | ||||||
|  |         var objectKind = playerRelatedObject.ObjectKind; | ||||||
|  |         var charaPointer = playerRelatedObject.Address; | ||||||
|  |  | ||||||
|  |         Logger.Debug("Building character data for " + objectKind); | ||||||
|  |  | ||||||
|  |         if (!previousData.FileReplacements.ContainsKey(objectKind)) | ||||||
|  |         { | ||||||
|  |             previousData.FileReplacements[objectKind] = new(FileReplacementComparer.Instance); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             previousData.FileReplacements[objectKind].Clear(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // wait until chara is not drawing and present so nothing spontaneously explodes | ||||||
|  |         _dalamudUtil.WaitWhileCharacterIsDrawing(playerRelatedObject.ObjectKind.ToString(), playerRelatedObject.Address, 30000, ct: token); | ||||||
|  |         var chara = _dalamudUtil.CreateGameObject(charaPointer)!; | ||||||
|  |         while (!DalamudUtil.IsObjectPresent(chara)) | ||||||
|  |         { | ||||||
|  |             Logger.Verbose("Character is null but it shouldn't be, waiting"); | ||||||
|  |             await Task.Delay(50).ConfigureAwait(false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Stopwatch st = Stopwatch.StartNew(); | ||||||
|  |  | ||||||
|  |         // gather up data from ipc | ||||||
|  |         previousData.ManipulationString = await _dalamudUtil.RunOnFrameworkThread(_ipcManager.PenumbraGetMetaManipulations).ConfigureAwait(false); | ||||||
|  |         previousData.GlamourerString[playerRelatedObject.ObjectKind] = await _dalamudUtil.RunOnFrameworkThread(() => _ipcManager.GlamourerGetCharacterCustomization(playerRelatedObject.Address)) | ||||||
|  |             .ConfigureAwait(false); | ||||||
|  |         previousData.HeelsOffset = await _dalamudUtil.RunOnFrameworkThread(_ipcManager.GetHeelsOffset).ConfigureAwait(false); | ||||||
|  |         previousData.CustomizePlusScale = await _dalamudUtil.RunOnFrameworkThread(_ipcManager.GetCustomizePlusScale).ConfigureAwait(false); | ||||||
|  |         previousData.PalettePlusPalette = await _dalamudUtil.RunOnFrameworkThread(_ipcManager.PalettePlusBuildPalette).ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |         // gather static replacements from render model | ||||||
|  |         var (forwardResolve, reverseResolve) = BuildDataFromModel(objectKind, charaPointer, token); | ||||||
|  |         Dictionary<string, List<string>> resolvedPaths = GetFileReplacementsFromPaths(forwardResolve, reverseResolve); | ||||||
|  |         previousData.FileReplacements[objectKind] =  | ||||||
|  |             new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)), FileReplacementComparer.Instance) | ||||||
|  |             .Where(p => p.HasFileReplacement).ToHashSet(); | ||||||
|  |  | ||||||
|  |         Logger.Debug("== Static Replacements =="); | ||||||
|  |         foreach (var replacement in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).OrderBy(i => i.GamePaths.First(), StringComparer.OrdinalIgnoreCase)) | ||||||
|  |         { | ||||||
|  |             Logger.Debug(replacement.ToString()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // if it's pet then it's summoner, if it's summoner we actually want to keep all filereplacements alive at all times  | ||||||
|  |         // or we get into redraw city for every change and nothing works properly | ||||||
|  |         if (objectKind == ObjectKind.Pet) | ||||||
|  |         { | ||||||
|  |             foreach (var item in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths)) | ||||||
|  |             { | ||||||
|  |                 _transientResourceManager.AddSemiTransientResource(objectKind, item); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Logger.Debug("Handling transient update for " + objectKind); | ||||||
|  |  | ||||||
|  |         // remove all potentially gathered paths from the transient resource manager that are resolved through static resolving | ||||||
|  |         _transientResourceManager.ClearTransientPaths(charaPointer, previousData.FileReplacements[objectKind].SelectMany(c => c.GamePaths).ToList()); | ||||||
|  |  | ||||||
|  |         // get all remaining paths and resolve them | ||||||
|  |         var transientPaths = ManageSemiTransientData(objectKind, charaPointer); | ||||||
|  |         var resolvedTransientPaths = GetFileReplacementsFromPaths(transientPaths, new HashSet<string>(StringComparer.Ordinal)); | ||||||
|  |  | ||||||
|  |         Logger.Debug("== Transient Replacements =="); | ||||||
|  |         foreach (var replacement in resolvedTransientPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)).OrderBy(f => f.ResolvedPath, StringComparer.Ordinal)) | ||||||
|  |         { | ||||||
|  |             Logger.Debug(replacement.ToString()); | ||||||
|  |             previousData.FileReplacements[objectKind].Add(replacement); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // clean up all semi transient resources that don't have any file replacement (aka null resolve) | ||||||
|  |         _transientResourceManager.CleanUpSemiTransientResources(objectKind, previousData.FileReplacements[objectKind].ToList()); | ||||||
|  |  | ||||||
|  |         // make sure we only return data that actually has file replacements | ||||||
|  |         foreach (var item in previousData.FileReplacements) | ||||||
|  |         { | ||||||
|  |             previousData.FileReplacements[item.Key] = new HashSet<FileReplacement>(item.Value.Where(v => v.HasFileReplacement).OrderBy(v => v.ResolvedPath, StringComparer.Ordinal), FileReplacementComparer.Instance); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         st.Stop(); | ||||||
|  |         Logger.Info("Building character data for " + objectKind + " took " + st.ElapsedMilliseconds + "ms"); | ||||||
|  |         return previousData; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private unsafe (HashSet<string> forwardResolve, HashSet<string> reverseResolve) BuildDataFromModel(ObjectKind objectKind, nint charaPointer, CancellationToken token) | ||||||
|  |     { | ||||||
|  |         HashSet<string> forwardResolve = new(StringComparer.Ordinal); | ||||||
|  |         HashSet<string> reverseResolve = new(StringComparer.Ordinal); | ||||||
|  |         var human = (Human*)((Character*)charaPointer)->GameObject.GetDrawObject(); | ||||||
|  |         for (var mdlIdx = 0; mdlIdx < human->CharacterBase.SlotCount; ++mdlIdx) | ||||||
|  |         { | ||||||
|  |             var mdl = (RenderModel*)human->CharacterBase.ModelArray[mdlIdx]; | ||||||
|  |             if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) | ||||||
|  |             { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             token.ThrowIfCancellationRequested(); | ||||||
|  |  | ||||||
|  |             AddReplacementsFromRenderModel(mdl, forwardResolve, reverseResolve); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (objectKind == ObjectKind.Player) | ||||||
|  |         { | ||||||
|  |             AddPlayerSpecificReplacements(objectKind, charaPointer, human, forwardResolve, reverseResolve); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return (forwardResolve, reverseResolve); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private unsafe void AddPlayerSpecificReplacements(ObjectKind objectKind, IntPtr charaPointer, Human* human, HashSet<string> forwardResolve, HashSet<string> reverseResolve) | ||||||
|  |     { | ||||||
|  |         var weaponObject = (Weapon*)((Object*)human)->ChildObject; | ||||||
|  |  | ||||||
|  |         if ((IntPtr)weaponObject != IntPtr.Zero) | ||||||
|  |         { | ||||||
|  |             var mainHandWeapon = weaponObject->WeaponRenderModel->RenderModel; | ||||||
|  |  | ||||||
|  |             AddReplacementsFromRenderModel(mainHandWeapon, forwardResolve, reverseResolve); | ||||||
|  |  | ||||||
|  |             foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)weaponObject)) | ||||||
|  |             { | ||||||
|  |                 Logger.Verbose("Found transient weapon resource: " + item); | ||||||
|  |                 forwardResolve.Add(item); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (weaponObject->NextSibling != (IntPtr)weaponObject) | ||||||
|  |             { | ||||||
|  |                 var offHandWeapon = ((Weapon*)weaponObject->NextSibling)->WeaponRenderModel->RenderModel; | ||||||
|  |  | ||||||
|  |                 AddReplacementsFromRenderModel(offHandWeapon, forwardResolve, reverseResolve); | ||||||
|  |  | ||||||
|  |                 foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)offHandWeapon)) | ||||||
|  |                 { | ||||||
|  |                     Logger.Verbose("Found transient offhand weapon resource: " + item); | ||||||
|  |                     forwardResolve.Add(item); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         AddReplacementSkeleton(((HumanExt*)human)->Human.RaceSexId, forwardResolve); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->Decal->FileName()).ToString(), forwardResolve, reverseResolve, doNotReverseResolve: false); | ||||||
|  |         } | ||||||
|  |         catch | ||||||
|  |         { | ||||||
|  |             Logger.Warn("Could not get Decal data"); | ||||||
|  |         } | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->LegacyBodyDecal->FileName()).ToString(), forwardResolve, reverseResolve, doNotReverseResolve: false); | ||||||
|  |         } | ||||||
|  |         catch | ||||||
|  |         { | ||||||
|  |             Logger.Warn("Could not get Legacy Body Decal Data"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private unsafe void AddReplacementsFromRenderModel(RenderModel* mdl, HashSet<string> forwardResolve, HashSet<string> reverseResolve) | ||||||
|     { |     { | ||||||
|         if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) |         if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) | ||||||
|         { |         { | ||||||
| @@ -113,18 +274,18 @@ public class CharacterDataFactory | |||||||
|         } |         } | ||||||
|         Logger.Verbose("Checking File Replacement for Model " + mdlPath); |         Logger.Verbose("Checking File Replacement for Model " + mdlPath); | ||||||
|  |  | ||||||
|         AddResolvePath(mdlPath); |         reverseResolve.Add(mdlPath); | ||||||
|  |  | ||||||
|         for (var mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++) |         for (var mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++) | ||||||
|         { |         { | ||||||
|             var mtrl = (Material*)mdl->Materials[mtrlIdx]; |             var mtrl = (Material*)mdl->Materials[mtrlIdx]; | ||||||
|             if (mtrl == null) continue; |             if (mtrl == null) continue; | ||||||
|  |  | ||||||
|             AddReplacementsFromMaterial(mtrl); |             AddReplacementsFromMaterial(mtrl, forwardResolve, reverseResolve); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private unsafe void AddReplacementsFromMaterial(Material* mtrl) |     private unsafe void AddReplacementsFromMaterial(Material* mtrl, HashSet<string> forwardResolve, HashSet<string> reverseResolve) | ||||||
|     { |     { | ||||||
|         string fileName; |         string fileName; | ||||||
|         try |         try | ||||||
| @@ -141,8 +302,7 @@ public class CharacterDataFactory | |||||||
|         Logger.Verbose("Checking File Replacement for Material " + fileName); |         Logger.Verbose("Checking File Replacement for Material " + fileName); | ||||||
|         var mtrlPath = fileName.Split("|")[2]; |         var mtrlPath = fileName.Split("|")[2]; | ||||||
|  |  | ||||||
|  |         reverseResolve.Add(mtrlPath); | ||||||
|         AddResolvePath(mtrlPath); |  | ||||||
|  |  | ||||||
|         var mtrlResourceHandle = (MtrlResource*)mtrl->ResourceHandle; |         var mtrlResourceHandle = (MtrlResource*)mtrl->ResourceHandle; | ||||||
|         for (var resIdx = 0; resIdx < mtrlResourceHandle->NumTex; resIdx++) |         for (var resIdx = 0; resIdx < mtrlResourceHandle->NumTex; resIdx++) | ||||||
| @@ -161,14 +321,14 @@ public class CharacterDataFactory | |||||||
|  |  | ||||||
|             Logger.Verbose("Checking File Replacement for Texture " + texPath); |             Logger.Verbose("Checking File Replacement for Texture " + texPath); | ||||||
|  |  | ||||||
|             AddReplacementsFromTexture(texPath); |             AddReplacementsFromTexture(texPath, forwardResolve, reverseResolve); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             var shpkPath = "shader/sm5/shpk/" + new ByteString(mtrlResourceHandle->ShpkString).ToString(); |             var shpkPath = "shader/sm5/shpk/" + new ByteString(mtrlResourceHandle->ShpkString).ToString(); | ||||||
|             Logger.Verbose("Checking File Replacement for Shader " + shpkPath); |             Logger.Verbose("Checking File Replacement for Shader " + shpkPath); | ||||||
|             AddReplacementsFromShader(shpkPath); |             forwardResolve.Add(shpkPath); | ||||||
|         } |         } | ||||||
|         catch |         catch | ||||||
|         { |         { | ||||||
| @@ -176,137 +336,51 @@ public class CharacterDataFactory | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void AddReplacement(string varPath, bool doNotReverseResolve = false) |     private void AddReplacementsFromTexture(string texPath, HashSet<string> forwardResolve, HashSet<string> reverseResolve, bool doNotReverseResolve = true) | ||||||
|     { |  | ||||||
|         if (varPath.IsNullOrEmpty()) return; |  | ||||||
|  |  | ||||||
|         AddResolvePath(varPath, doNotReverseResolve); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void AddReplacementsFromShader(string shpkPath) |  | ||||||
|     { |  | ||||||
|         if (string.IsNullOrEmpty(shpkPath)) return; |  | ||||||
|  |  | ||||||
|         AddResolvePath(shpkPath, doNotReverseResolve: true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void AddReplacementsFromTexture(string texPath, bool doNotReverseResolve = true) |  | ||||||
|     { |     { | ||||||
|         if (string.IsNullOrEmpty(texPath)) return; |         if (string.IsNullOrEmpty(texPath)) return; | ||||||
|  |  | ||||||
|         AddResolvePath(texPath, doNotReverseResolve); |         if (doNotReverseResolve) | ||||||
|  |             forwardResolve.Add(texPath); | ||||||
|  |         else | ||||||
|  |             reverseResolve.Add(texPath); | ||||||
|  |  | ||||||
|         if (texPath.Contains("/--", StringComparison.Ordinal)) return; |         if (texPath.Contains("/--", StringComparison.Ordinal)) return; | ||||||
|  |  | ||||||
|         AddResolvePath(texPath.Insert(texPath.LastIndexOf('/') + 1, "--"), doNotReverseResolve); |         var dx11Path = texPath.Insert(texPath.LastIndexOf('/') + 1, "--"); | ||||||
|  |         if (doNotReverseResolve) | ||||||
|  |             forwardResolve.Add(dx11Path); | ||||||
|  |         else | ||||||
|  |             reverseResolve.Add(dx11Path); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private unsafe CharacterData CreateCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) |     private void AddReplacementSkeleton(ushort raceSexId, HashSet<string> forwardResolve) | ||||||
|     { |     { | ||||||
|         var objectKind = playerRelatedObject.ObjectKind; |         string raceSexIdString = raceSexId.ToString("0000"); | ||||||
|         var charaPointer = playerRelatedObject.Address; |  | ||||||
|  |  | ||||||
|         if (!previousData.FileReplacements.ContainsKey(objectKind)) |         string skeletonPath = $"chara/human/c{raceSexIdString}/skeleton/base/b0001/skl_c{raceSexIdString}b0001.sklb"; | ||||||
|         { |         forwardResolve.Add(skeletonPath); | ||||||
|             previousData.FileReplacements[objectKind] = new(FileReplacementComparer.Instance); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         _dalamudUtil.WaitWhileCharacterIsDrawing(playerRelatedObject.ObjectKind.ToString(), playerRelatedObject.Address, 30000, ct: token); |     private HashSet<string> ManageSemiTransientData(ObjectKind objectKind, IntPtr charaPointer) | ||||||
|  |  | ||||||
|         Stopwatch st = Stopwatch.StartNew(); |  | ||||||
|  |  | ||||||
|         Logger.Debug("Handling unprocessed update for " + objectKind); |  | ||||||
|  |  | ||||||
|         if (previousData.FileReplacements.ContainsKey(objectKind)) |  | ||||||
|     { |     { | ||||||
|             previousData.FileReplacements[objectKind].Clear(); |         _transientResourceManager.PersistTransientResources(charaPointer, objectKind); | ||||||
|  |  | ||||||
|  |         HashSet<string> pathsToResolve = new(StringComparer.Ordinal); | ||||||
|  |         foreach (var path in _transientResourceManager.GetSemiTransientResources(objectKind).Where(path => !string.IsNullOrEmpty(path))) | ||||||
|  |         { | ||||||
|  |             pathsToResolve.Add(path); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var chara = _dalamudUtil.CreateGameObject(charaPointer)!; |         return pathsToResolve; | ||||||
|         while (!DalamudUtil.IsObjectPresent(chara)) |  | ||||||
|         { |  | ||||||
|             Logger.Verbose("Character is null but it shouldn't be, waiting"); |  | ||||||
|             Thread.Sleep(50); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|         var human = (Human*)((Character*)charaPointer)->GameObject.GetDrawObject(); |     private Dictionary<string, List<string>> GetFileReplacementsFromPaths(HashSet<string> forwardResolve, HashSet<string> reverseResolve) | ||||||
|  |  | ||||||
|         for (var mdlIdx = 0; mdlIdx < human->CharacterBase.SlotCount; ++mdlIdx) |  | ||||||
|     { |     { | ||||||
|             var mdl = (RenderModel*)human->CharacterBase.ModelArray[mdlIdx]; |         var forwardPaths = forwardResolve.ToArray(); | ||||||
|             if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) |         var reversePaths = reverseResolve.ToArray(); | ||||||
|             { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             token.ThrowIfCancellationRequested(); |  | ||||||
|  |  | ||||||
|             AddReplacementsFromRenderModel(mdl); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (objectKind == ObjectKind.Player) |  | ||||||
|         { |  | ||||||
|             AddPlayerSpecificReplacements(objectKind, charaPointer, human); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Dictionary<string, List<string>> resolvedPaths = GetFileReplacementsFromPaths(); |  | ||||||
|         previousData.FileReplacements[objectKind] = new HashSet<FileReplacement>(resolvedPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)), FileReplacementComparer.Instance) |  | ||||||
|             .Where(p => p.HasFileReplacement).ToHashSet(); |  | ||||||
|  |  | ||||||
|         previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); |  | ||||||
|         previousData.GlamourerString[objectKind] = _ipcManager.GlamourerGetCharacterCustomization(charaPointer); |  | ||||||
|         previousData.HeelsOffset = _ipcManager.GetHeelsOffset(); |  | ||||||
|         previousData.CustomizePlusScale = _ipcManager.GetCustomizePlusScale(); |  | ||||||
|         previousData.PalettePlusPalette = _ipcManager.PalettePlusBuildPalette(); |  | ||||||
|  |  | ||||||
|         Logger.Debug("== Static Replacements =="); |  | ||||||
|         foreach (var item in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).OrderBy(i => i.GamePaths.First(), StringComparer.OrdinalIgnoreCase)) |  | ||||||
|         { |  | ||||||
|             Logger.Debug(item.ToString()); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (objectKind == ObjectKind.Pet) |  | ||||||
|         { |  | ||||||
|             foreach (var item in previousData.FileReplacements[objectKind].Where(i => i.HasFileReplacement).SelectMany(p => p.GamePaths)) |  | ||||||
|             { |  | ||||||
|                 _transientResourceManager.AddSemiTransientResource(objectKind, item); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Logger.Debug("Handling transient update for " + objectKind); |  | ||||||
|         _transientResourceManager.ClearTransientPaths(charaPointer, previousData.FileReplacements[objectKind].SelectMany(c => c.GamePaths).ToList()); |  | ||||||
|  |  | ||||||
|         _pathsToForwardResolve.Clear(); |  | ||||||
|         _pathsToReverseResolve.Clear(); |  | ||||||
|  |  | ||||||
|         ManageSemiTransientData(objectKind, charaPointer); |  | ||||||
|  |  | ||||||
|         var resolvedTransientPaths = GetFileReplacementsFromPaths(); |  | ||||||
|         Logger.Debug("== Transient Replacements =="); |  | ||||||
|         foreach (var replacement in resolvedTransientPaths.Select(c => new FileReplacement(c.Value, c.Key, _fileCacheManager)).OrderBy(f => f.ResolvedPath, StringComparer.Ordinal)) |  | ||||||
|         { |  | ||||||
|             Logger.Debug(replacement.ToString()); |  | ||||||
|             previousData.FileReplacements[objectKind].Add(replacement); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         _transientResourceManager.CleanSemiTransientResources(objectKind, previousData.FileReplacements[objectKind].ToList()); |  | ||||||
|  |  | ||||||
|         foreach (var item in previousData.FileReplacements) |  | ||||||
|         { |  | ||||||
|             previousData.FileReplacements[item.Key] = new HashSet<FileReplacement>(item.Value.Where(v => v.HasFileReplacement).OrderBy(v => v.ResolvedPath, StringComparer.Ordinal), FileReplacementComparer.Instance); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         st.Stop(); |  | ||||||
|         Logger.Info("Building character data for " + objectKind + " took " + st.ElapsedMilliseconds + "ms"); |  | ||||||
|         return previousData; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private Dictionary<string, List<string>> GetFileReplacementsFromPaths() |  | ||||||
|     { |  | ||||||
|         var forwardPaths = _pathsToForwardResolve.ToArray(); |  | ||||||
|         var reversePaths = _pathsToReverseResolve.ToArray(); |  | ||||||
|         Dictionary<string, List<string>> resolvedPaths = new(StringComparer.Ordinal); |         Dictionary<string, List<string>> resolvedPaths = new(StringComparer.Ordinal); | ||||||
|         var result = _ipcManager.PenumbraResolvePaths(_pathsToForwardResolve.ToArray(), _pathsToReverseResolve.ToArray()); |         var result = _ipcManager.PenumbraResolvePaths(forwardPaths, reversePaths); | ||||||
|         for (int i = 0; i < forwardPaths.Length; i++) |         for (int i = 0; i < forwardPaths.Length; i++) | ||||||
|         { |         { | ||||||
|             var filePath = result.forward[i].ToLowerInvariant(); |             var filePath = result.forward[i].ToLowerInvariant(); | ||||||
| @@ -335,82 +409,4 @@ public class CharacterDataFactory | |||||||
|  |  | ||||||
|         return resolvedPaths; |         return resolvedPaths; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private unsafe void ManageSemiTransientData(ObjectKind objectKind, IntPtr charaPointer) |  | ||||||
|     { |  | ||||||
|         _transientResourceManager.PersistTransientResources(charaPointer, objectKind); |  | ||||||
|  |  | ||||||
|         foreach (var item in _transientResourceManager.GetSemiTransientResources(objectKind)) |  | ||||||
|         { |  | ||||||
|             AddResolvePath(item, true); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private unsafe void AddPlayerSpecificReplacements(ObjectKind objectKind, IntPtr charaPointer, Human* human) |  | ||||||
|     { |  | ||||||
|         var weaponObject = (Weapon*)((Object*)human)->ChildObject; |  | ||||||
|  |  | ||||||
|         if ((IntPtr)weaponObject != IntPtr.Zero) |  | ||||||
|         { |  | ||||||
|             var mainHandWeapon = weaponObject->WeaponRenderModel->RenderModel; |  | ||||||
|  |  | ||||||
|             AddReplacementsFromRenderModel(mainHandWeapon); |  | ||||||
|  |  | ||||||
|             foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)weaponObject)) |  | ||||||
|             { |  | ||||||
|                 Logger.Verbose("Found transient weapon resource: " + item); |  | ||||||
|                 AddReplacement(item, doNotReverseResolve: true); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (weaponObject->NextSibling != (IntPtr)weaponObject) |  | ||||||
|             { |  | ||||||
|                 var offHandWeapon = ((Weapon*)weaponObject->NextSibling)->WeaponRenderModel->RenderModel; |  | ||||||
|  |  | ||||||
|                 AddReplacementsFromRenderModel(offHandWeapon); |  | ||||||
|  |  | ||||||
|                 foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)offHandWeapon)) |  | ||||||
|                 { |  | ||||||
|                     Logger.Verbose("Found transient offhand weapon resource: " + item); |  | ||||||
|                     AddReplacement(item, doNotReverseResolve: true); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         AddReplacementSkeleton(((HumanExt*)human)->Human.RaceSexId); |  | ||||||
|         try |  | ||||||
|         { |  | ||||||
|             AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->Decal->FileName()).ToString(), doNotReverseResolve: false); |  | ||||||
|         } |  | ||||||
|         catch |  | ||||||
|         { |  | ||||||
|             Logger.Warn("Could not get Decal data"); |  | ||||||
|         } |  | ||||||
|         try |  | ||||||
|         { |  | ||||||
|             AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->LegacyBodyDecal->FileName()).ToString(), doNotReverseResolve: false); |  | ||||||
|         } |  | ||||||
|         catch |  | ||||||
|         { |  | ||||||
|             Logger.Warn("Could not get Legacy Body Decal Data"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void AddReplacementSkeleton(ushort raceSexId) |  | ||||||
|     { |  | ||||||
|         string raceSexIdString = raceSexId.ToString("0000"); |  | ||||||
|  |  | ||||||
|         string skeletonPath = $"chara/human/c{raceSexIdString}/skeleton/base/b0001/skl_c{raceSexIdString}b0001.sklb"; |  | ||||||
|  |  | ||||||
|         AddResolvePath(skeletonPath, doNotReverseResolve: true); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void AddResolvePath(string path, bool doNotReverseResolve = false) |  | ||||||
|     { |  | ||||||
|         if (string.IsNullOrEmpty(path)) return; |  | ||||||
|         if (doNotReverseResolve) _pathsToForwardResolve.Add(path.ToLowerInvariant()); |  | ||||||
|         else _pathsToReverseResolve.Add(path.ToLowerInvariant()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private readonly HashSet<string> _pathsToForwardResolve = new(StringComparer.Ordinal); |  | ||||||
|     private readonly HashSet<string> _pathsToReverseResolve = new(StringComparer.Ordinal); |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -97,13 +97,13 @@ public class CacheCreationService : MediatorSubscriberBase, IDisposable | |||||||
|         { |         { | ||||||
|             var toCreate = _cachesToCreate.ToList(); |             var toCreate = _cachesToCreate.ToList(); | ||||||
|             _cachesToCreate.Clear(); |             _cachesToCreate.Clear(); | ||||||
|             _cacheCreationTask = Task.Run(() => |             _cacheCreationTask = Task.Run(async () => | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     foreach (var obj in toCreate) |                     foreach (var obj in toCreate) | ||||||
|                     { |                     { | ||||||
|                         var data = _characterDataFactory.BuildCharacterData(_lastCreatedData, obj.Value, _cts.Token); |                         var data = await _characterDataFactory.BuildCharacterData(_lastCreatedData, obj.Value, _cts.Token).ConfigureAwait(false); | ||||||
|                     } |                     } | ||||||
|                     Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); |                     Mediator.Publish(new CharacterDataCreatedMessage(_lastCreatedData)); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ public class TransientResourceManager : MediatorSubscriberBase, IDisposable | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void CleanSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null) |     public void CleanUpSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null) | ||||||
|     { |     { | ||||||
|         if (SemiTransientResources.ContainsKey(objectKind)) |         if (SemiTransientResources.ContainsKey(objectKind)) | ||||||
|         { |         { | ||||||
| @@ -98,7 +98,6 @@ public class TransientResourceManager : MediatorSubscriberBase, IDisposable | |||||||
|  |  | ||||||
|             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()) | ||||||
|             { |             { | ||||||
|  |  | ||||||
|                 SemiTransientResources[objectKind].RemoveWhere(p => string.Equals(p, replacement, StringComparison.OrdinalIgnoreCase)); |                 SemiTransientResources[objectKind].RemoveWhere(p => string.Equals(p, replacement, StringComparison.OrdinalIgnoreCase)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 rootdarkarchon
					rootdarkarchon