diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 27e6ae8..5826c62 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -433,15 +433,12 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task PenumbraRemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, string characterName) + public async Task PenumbraRemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, string collName) { if (!CheckPenumbraApi()) return; await _dalamudUtil.RunOnFrameworkThread(() => { - var collName = "Mare_" + characterName; logger.LogTrace("[{applicationId}] Removing temp collection for {collName}", applicationId, collName); - var ret = _penumbraRemoveTemporaryMod.Invoke("MareChara", collName, 0); - logger.LogTrace("[{applicationId}] RemoveTemporaryMod: {ret}", applicationId, ret); var ret2 = _penumbraRemoveTemporaryCollection.Invoke(collName); logger.LogTrace("[{applicationId}] RemoveTemporaryCollection: {ret2}", applicationId, ret2); }).ConfigureAwait(false); @@ -452,25 +449,61 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase return await _dalamudUtil.RunOnFrameworkThread(() => _penumbraResolvePaths.Invoke(forward, reverse)).ConfigureAwait(false); } - public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, string characterName, int? idx, Dictionary modPaths, string manipulationData) + public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, string collName, Dictionary modPaths) { - if (!CheckPenumbraApi() || idx == null) return; + if (!CheckPenumbraApi()) return; await _dalamudUtil.RunOnFrameworkThread(() => { - var collName = "Mare_" + characterName; - var ret = _penumbraCreateNamedTemporaryCollection.Invoke(collName); - logger.LogTrace("[{applicationId}] Creating Temp Collection {collName}, Success: {ret}", applicationId, collName, ret); - var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx.Value, c: true); - logger.LogTrace("[{applicationId}] Assigning Temp Collection {collName} to index {idx}, Success: {ret}", applicationId, collName, idx, retAssign); foreach (var mod in modPaths) { logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value); } + var retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Files", collName, 0); + logger.LogTrace("[{applicationId}] Removing prev mod files mod for {collName}, Success: {ret}", applicationId, collName, retRemove); + var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Files", collName, modPaths, string.Empty, 0); + logger.LogTrace("[{applicationId}] Setting temp files mod for {collName}, Success: {ret}", applicationId, collName, retAdd); + }).ConfigureAwait(false); + } + public async Task PenumbraSetManipulationDataAsync(ILogger logger, Guid applicationId, string collName, string manipulationData) + { + if (!CheckPenumbraApi()) return; + + await _dalamudUtil.RunOnFrameworkThread(() => + { logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData); - var ret2 = _penumbraAddTemporaryMod.Invoke("MareChara", collName, modPaths, manipulationData, 0); - logger.LogTrace("[{applicationId}] Setting temp mods for {collName}, Success: {ret2}", applicationId, collName, ret2); + var retRemove = _penumbraRemoveTemporaryMod.Invoke("MareChara_Meta", collName, 0); + logger.LogTrace("[{applicationId}] Removing prev meta mod for {collName}, Success: {ret}", applicationId, collName, retRemove); + var retAdd = _penumbraAddTemporaryMod.Invoke("MareChara_Meta", collName, new Dictionary(), manipulationData, 0); + logger.LogTrace("[{applicationId}] Setting temp meta mod for {collName}, Success: {ret}", applicationId, collName, retAdd); + }).ConfigureAwait(false); + } + + public async Task PenumbraCreateTemporaryCollection(ILogger logger, string uid) + { + if (!CheckPenumbraApi()) return string.Empty; + + return await _dalamudUtil.RunOnFrameworkThread(() => + { + var collName = "Mare_" + uid; + var retRemove = _penumbraRemoveTemporaryCollection.Invoke(collName); + logger.LogTrace("Removing Temp Collection {collName}, Success: {ret}", collName, retRemove); + var retCreate = _penumbraCreateNamedTemporaryCollection.Invoke(collName); + logger.LogTrace("Creating Temp Collection {collName}, Success: {ret}", collName, retCreate); + return collName; + }).ConfigureAwait(false); + } + + public async Task PenumbraAssignTemporaryCollection(ILogger logger, string collName, int idx) + { + if (!CheckPenumbraApi()) return; + + await _dalamudUtil.RunOnFrameworkThread(() => + { + var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx, c: true); + logger.LogTrace("Assigning Temp Collection {collName} to index {idx}, Success: {ret}", collName, idx, retAssign); + return collName; }).ConfigureAwait(false); } @@ -687,6 +720,8 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private void PenumbraInit() { + _penumbraAvailable = true; + PenumbraModDirectory = _penumbraResolveModDir.Invoke(); Mediator.Publish(new PenumbraInitializedMessage()); _penumbraRedraw!.Invoke("self", RedrawType.Redraw); } diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 6acb24b..27d3534 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.8.35 + 0.8.36 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs index 8709464..c209e7e 100644 --- a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs +++ b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs @@ -10,6 +10,7 @@ using MareSynchronos.Interop; using MareSynchronos.Services; using MareSynchronos.Utils; using MareSynchronos.PlayerData.Factories; +using Lumina.Excel.GeneratedSheets; namespace MareSynchronos.PlayerData.Export; @@ -66,9 +67,10 @@ public class MareCharaFileManager var applicationId = Guid.NewGuid(); _ipcManager.ToggleGposeQueueMode(on: true); await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false); - await _ipcManager.PenumbraSetTemporaryModsAsync(_logger, applicationId, charaTarget.Name.TextValue, charaTarget.ObjectTableIndex(), - extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal), - LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false); + var coll = await _ipcManager.PenumbraCreateTemporaryCollection(_logger, charaTarget.Name.TextValue).ConfigureAwait(false); + await _ipcManager.PenumbraAssignTemporaryCollection(_logger, coll, charaTarget.ObjectTableIndex()!.Value).ConfigureAwait(false); + await _ipcManager.PenumbraSetTemporaryModsAsync(_logger, applicationId, coll, extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal)).ConfigureAwait(false); + await _ipcManager.PenumbraSetManipulationDataAsync(_logger, applicationId, coll, LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => charaTarget.Address, false).ConfigureAwait(false); await _ipcManager.GlamourerApplyAllAsync(_logger, tempHandler, LoadedCharaFile.CharaFileData.GlamourerData, applicationId, disposeCts.Token).ConfigureAwait(false); _dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address, 30000); diff --git a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs index ed6332f..5ad6085 100644 --- a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs +++ b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs @@ -39,6 +39,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase private string _lastGlamourerData = string.Empty; private string _originalGlamourerData = string.Empty; private CancellationTokenSource _redrawCts = new(); + private string _penumbraCollection; public CachedPlayer(ILogger logger, OnlineUserIdentDto onlineUser, GameObjectHandlerFactory gameObjectHandlerFactory, @@ -57,8 +58,17 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase { _charaHandler?.Invalidate(); IsVisible = false; - } - ); + }); + Mediator.Subscribe(this, (_) => + { + _penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollection(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); + if (!IsVisible && _charaHandler != null) + { + PlayerName = string.Empty; + _charaHandler.Dispose(); + _charaHandler = null; + } + }); _pluginWarnings ??= new() { ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings, @@ -66,6 +76,10 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase ShownPalettePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings, ShownHonorificWarning = mareConfigService.Current.DisableOptionalPluginWarnings, }; + Task.Run(async () => + { + _penumbraCollection = await _ipcManager.PenumbraCreateTemporaryCollection(logger, OnlineUser.User.UID).ConfigureAwait(false); + }); } private enum PlayerChanges @@ -74,7 +88,9 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase Customize = 2, Palette = 3, Honorific = 4, - Mods = 5, + ModFiles = 5, + ModManip = 6, + Glamourer = 7 } public bool IsVisible { get; private set; } @@ -167,7 +183,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (_dalamudUtil is { IsZoning: false, IsInCutscene: false }) { Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser); - _ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, name).GetAwaiter().GetResult(); + _ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult(); foreach (KeyValuePair> item in _cachedData?.FileReplacements ?? new()) { @@ -252,12 +268,15 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase await _ipcManager.HonorificSetTitleAsync(handler.Address, charaData.HonorificData).ConfigureAwait(false); break; - case PlayerChanges.Mods: + case PlayerChanges.Glamourer: if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData)) { await _ipcManager.GlamourerApplyAllAsync(Logger, handler, glamourerData, applicationId, token).ConfigureAwait(false); } - else + break; + case PlayerChanges.ModFiles: + case PlayerChanges.ModManip: + if (!changes.Value.Contains(PlayerChanges.Glamourer)) { await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); } @@ -295,19 +314,20 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (hasNewButNotOldFileReplacements || hasOldButNotNewFileReplacements || hasNewButNotOldGlamourerData || hasOldButNotNewGlamourerData) { Logger.LogDebug("Updating {object}/{kind} (Some new data arrived: NewButNotOldFiles:{hasNewButNotOldFileReplacements}," + - " OldButNotNewFiles:{hasOldButNotNewFileReplacements}, NewButNotOldGlam:{hasNewButNotOldGlamourerData}, OldButNotNewGlam:{hasOldButNotNewGlamourerData}) => {change}", - this, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.Mods); - charaDataToUpdate[objectKind].Add(PlayerChanges.Mods); + " OldButNotNewFiles:{hasOldButNotNewFileReplacements}, NewButNotOldGlam:{hasNewButNotOldGlamourerData}, OldButNotNewGlam:{hasOldButNotNewGlamourerData}) => {change}, {change2}", + this, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer); + charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); + charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer); } else { if (hasNewAndOldFileReplacements) { bool listsAreEqual = oldData.FileReplacements[objectKind].SequenceEqual(newData.FileReplacements[objectKind], Data.FileReplacementDataComparer.Instance); - if (!listsAreEqual || forced) + if (!listsAreEqual) { - Logger.LogDebug("Updating {object}/{kind} (FileReplacements not equal) => {change}", this, objectKind, PlayerChanges.Mods); - charaDataToUpdate[objectKind].Add(PlayerChanges.Mods); + Logger.LogDebug("Updating {object}/{kind} (FileReplacements not equal) => {change}", this, objectKind, PlayerChanges.ModFiles); + charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); } } @@ -316,8 +336,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase bool glamourerDataDifferent = !string.Equals(oldData.GlamourerData[objectKind], newData.GlamourerData[objectKind], StringComparison.Ordinal); if (glamourerDataDifferent || forced) { - Logger.LogDebug("Updating {object}/{kind} (Glamourer different) => {change}", this, objectKind, PlayerChanges.Mods); - charaDataToUpdate[objectKind].Add(PlayerChanges.Mods); + Logger.LogDebug("Updating {object}/{kind} (Glamourer different) => {change}", this, objectKind, PlayerChanges.Glamourer); + charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer); } } } @@ -325,10 +345,10 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (objectKind != ObjectKind.Player) continue; bool manipDataDifferent = !string.Equals(oldData.ManipulationData, newData.ManipulationData, StringComparison.Ordinal); - if (manipDataDifferent || forced) + if (manipDataDifferent) { - Logger.LogDebug("Updating {object}/{kind} (Diff manip data) => {change}", this, objectKind, PlayerChanges.Mods); - charaDataToUpdate[objectKind].Add(PlayerChanges.Mods); + Logger.LogDebug("Updating {object}/{kind} (Diff manip data) => {change}", this, objectKind, PlayerChanges.ModManip); + charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip); } bool heelsOffsetDifferent = oldData.HeelsOffset != newData.HeelsOffset; @@ -377,7 +397,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase return; } - var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.Mods)); + var updateModdedPaths = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModFiles)); + var updateManip = updatedData.Values.Any(v => v.Any(p => p == PlayerChanges.ModManip)); _downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource(); var downloadToken = _downloadCancellationTokenSource.Token; @@ -447,20 +468,14 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase token.ThrowIfCancellationRequested(); - if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData))) + if (updateModdedPaths) { - await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((_) => _ipcManager - .PenumbraRemoveTemporaryCollectionAsync(Logger, _applicationId, PlayerName!) - .ConfigureAwait(true).GetAwaiter().GetResult(), token).ConfigureAwait(false); - token.ThrowIfCancellationRequested(); + await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false); + } - await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((chara) => - { - var objTableIndex = chara.ObjectTableIndex(); - _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, PlayerName!, objTableIndex, moddedPaths, charaData.ManipulationData) - .ConfigureAwait(true).GetAwaiter().GetResult(); - }, token).ConfigureAwait(false); - token.ThrowIfCancellationRequested(); + if (updateManip) + { + await _ipcManager.PenumbraSetManipulationDataAsync(Logger, _applicationId, _penumbraCollection, charaData.ManipulationData).ConfigureAwait(false); } token.ThrowIfCancellationRequested(); @@ -542,6 +557,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase await _ipcManager.HonorificSetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false); }); + _ipcManager.PenumbraAssignTemporaryCollection(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult(); + _downloadManager.Initialize(); } @@ -561,7 +578,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (_cachedData != null) { await ApplyCustomizationDataAsync(applicationId, new(ObjectKind.Player, - new HashSet(new[] { PlayerChanges.Palette, PlayerChanges.Customize, PlayerChanges.Heels, PlayerChanges.Mods })), + new HashSet(new[] { PlayerChanges.Palette, PlayerChanges.Customize, PlayerChanges.Heels, PlayerChanges.Glamourer })), _cachedData, token).ConfigureAwait(false); } }, token);