fix palette application and add experimental less redraws option

This commit is contained in:
rootdarkarchon
2023-10-25 16:33:03 +02:00
parent 3383de7294
commit 2221f4d09e
11 changed files with 114 additions and 30 deletions

View File

@@ -51,7 +51,13 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
Mediator.Subscribe<PenumbraResourceLoadMessage>(this, Manager_PenumbraResourceLoadEvent); Mediator.Subscribe<PenumbraResourceLoadMessage>(this, Manager_PenumbraResourceLoadEvent);
Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (_) => Manager_PenumbraModSettingChanged()); Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (_) => Manager_PenumbraModSettingChanged());
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate());
Mediator.Subscribe<ClassJobChangedMessage>(this, (_) => DalamudUtil_ClassJobChanged()); Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
{
if (_playerRelatedPointers.Contains(msg.gameObjectHandler))
{
DalamudUtil_ClassJobChanged();
}
});
Mediator.Subscribe<AddWatchedGameObjectHandler>(this, (msg) => Mediator.Subscribe<AddWatchedGameObjectHandler>(this, (msg) =>
{ {
_playerRelatedPointers.Add(msg.Handler); _playerRelatedPointers.Add(msg.Handler);

View File

@@ -50,4 +50,5 @@ public class MareConfig : IMareConfiguration
public bool UseCompactor { get; set; } = false; public bool UseCompactor { get; set; } = false;
public int Version { get; set; } = 1; public int Version { get; set; } = 1;
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both; public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
public bool UseLessRedraws { get; set; } = false;
} }

View File

@@ -2,11 +2,12 @@
public enum PlayerChanges public enum PlayerChanges
{ {
Heels = 1, ModFiles = 1,
Customize = 2, ModManip = 2,
Palette = 3, Glamourer = 3,
Honorific = 4, Customize = 4,
ModFiles = 5, Heels = 5,
ModManip = 6, Palette = 6,
Glamourer = 7 Honorific = 7,
ForcedRedraw = 8,
} }

View File

@@ -1,6 +1,7 @@
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;
@@ -20,12 +21,13 @@ public class PairHandlerFactory
private readonly IpcManager _ipcManager; private readonly IpcManager _ipcManager;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly MareMediator _mareMediator; private readonly MareMediator _mareMediator;
private readonly MareConfigService _mareConfigService;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
FileCacheManager fileCacheManager, MareMediator mareMediator) FileCacheManager fileCacheManager, MareMediator mareMediator, MareConfigService mareConfigService)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_gameObjectHandlerFactory = gameObjectHandlerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -36,12 +38,13 @@ public class PairHandlerFactory
_hostApplicationLifetime = hostApplicationLifetime; _hostApplicationLifetime = hostApplicationLifetime;
_fileCacheManager = fileCacheManager; _fileCacheManager = fileCacheManager;
_mareMediator = mareMediator; _mareMediator = mareMediator;
_mareConfigService = mareConfigService;
} }
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto) public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
{ {
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory, return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
_fileCacheManager, _mareMediator); _fileCacheManager, _mareMediator, _mareConfigService);
} }
} }

View File

@@ -22,6 +22,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
private bool _haltProcessing = false; private bool _haltProcessing = false;
private bool _ignoreSendAfterRedraw = false; private bool _ignoreSendAfterRedraw = false;
private int _ptrNullCounter = 0; private int _ptrNullCounter = 0;
private byte _classJob = 0;
private CancellationTokenSource _zoningCts = new(); private CancellationTokenSource _zoningCts = new();
public GameObjectHandler(ILogger<GameObjectHandler> logger, PerformanceCollectorService performanceCollector, public GameObjectHandler(ILogger<GameObjectHandler> logger, PerformanceCollectorService performanceCollector,
@@ -215,6 +216,14 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase
&& ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human) && ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human)
{ {
var classJob = chara->CharacterData.ClassJob;
if (classJob != _classJob)
{
Logger.LogTrace("[{this}] classjob changed from {old} to {new}", this, _classJob, classJob);
_classJob = classJob;
Mediator.Publish(new ClassJobChangedMessage(this));
}
equipDiff = CompareAndUpdateEquipByteData((byte*)&((Human*)DrawObjectAddress)->Head); equipDiff = CompareAndUpdateEquipByteData((byte*)&((Human*)DrawObjectAddress)->Head);
ref var mh = ref chara->DrawData.Weapon(WeaponSlot.MainHand); ref var mh = ref chara->DrawData.Weapon(WeaponSlot.MainHand);

View File

@@ -2,6 +2,7 @@
using MareSynchronos.API.Dto.User; using MareSynchronos.API.Dto.User;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.Interop; using MareSynchronos.Interop;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Factories; using MareSynchronos.PlayerData.Factories;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;
@@ -21,6 +22,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private readonly DalamudUtilService _dalamudUtil; private readonly DalamudUtilService _dalamudUtil;
private readonly FileDownloadManager _downloadManager; private readonly FileDownloadManager _downloadManager;
private readonly FileCacheManager _fileDbManager; private readonly FileCacheManager _fileDbManager;
private readonly MareConfigService _mareConfigService;
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
private readonly IpcManager _ipcManager; private readonly IpcManager _ipcManager;
private readonly IHostApplicationLifetime _lifetime; private readonly IHostApplicationLifetime _lifetime;
@@ -34,13 +36,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private bool _forceApplyMods = false; private bool _forceApplyMods = false;
private bool _isVisible; private bool _isVisible;
private string _penumbraCollection; private string _penumbraCollection;
private bool _redrawOnNextApplication = false;
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser, public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
GameObjectHandlerFactory gameObjectHandlerFactory, GameObjectHandlerFactory gameObjectHandlerFactory,
IpcManager ipcManager, FileDownloadManager transferManager, IpcManager ipcManager, FileDownloadManager transferManager,
PluginWarningNotificationService pluginWarningNotificationManager, PluginWarningNotificationService pluginWarningNotificationManager,
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator) FileCacheManager fileDbManager, MareMediator mediator,
MareConfigService mareConfigService) : base(logger, mediator)
{ {
OnlineUser = onlineUser; OnlineUser = onlineUser;
_gameObjectHandlerFactory = gameObjectHandlerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -50,7 +54,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
_lifetime = lifetime; _lifetime = lifetime;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
_mareConfigService = mareConfigService;
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); _penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
@@ -70,6 +74,13 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_charaHandler = null; _charaHandler = null;
} }
}); });
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
{
if (_mareConfigService.Current.UseLessRedraws && msg.gameObjectHandler == _charaHandler)
{
_redrawOnNextApplication = true;
}
});
} }
public bool IsVisible public bool IsVisible
@@ -128,6 +139,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_forceApplyMods = false; _forceApplyMods = false;
} }
if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player))
{
player.Add(PlayerChanges.ForcedRedraw);
_redrawOnNextApplication = false;
}
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges)) if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
{ {
_pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges); _pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges);
@@ -229,6 +246,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
Logger.LogDebug("[{applicationId}] Applying Customization Data for {handler}", applicationId, handler); Logger.LogDebug("[{applicationId}] Applying Customization Data for {handler}", applicationId, handler);
await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, handler, applicationId, 30000, token).ConfigureAwait(false); await _dalamudUtil.WaitWhileCharacterIsDrawing(Logger, handler, applicationId, 30000, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
if (!_mareConfigService.Current.UseLessRedraws) changes.Value.Remove(PlayerChanges.ForcedRedraw);
foreach (var change in changes.Value.OrderBy(p => (int)p)) foreach (var change in changes.Value.OrderBy(p => (int)p))
{ {
Logger.LogDebug("[{applicationId}] Processing {change} for {handler}", applicationId, change, handler); Logger.LogDebug("[{applicationId}] Processing {change} for {handler}", applicationId, change, handler);
@@ -264,15 +282,20 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
} }
break; break;
case PlayerChanges.ModFiles: case PlayerChanges.ForcedRedraw:
case PlayerChanges.ModManip: await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
break;
default:
break; break;
} }
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
} }
if (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer)) if (!_mareConfigService.Current.UseLessRedraws && (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer)))
{
await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
}
} }
finally finally
{ {

View File

@@ -494,17 +494,6 @@ public class DalamudUtilService : IHostedService
_mediator.Publish(new DalamudLogoutMessage()); _mediator.Publish(new DalamudLogoutMessage());
} }
if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.IsValid())
{
var newclassJobId = _clientState.LocalPlayer.ClassJob.Id;
if (_classJobId != newclassJobId)
{
_classJobId = newclassJobId;
_mediator.Publish(new ClassJobChangedMessage(_classJobId));
}
}
_mediator.Publish(new DelayedFrameworkUpdateMessage()); _mediator.Publish(new DelayedFrameworkUpdateMessage());
_delayedFrameworkUpdateCheck = DateTime.Now; _delayedFrameworkUpdateCheck = DateTime.Now;

View File

@@ -18,7 +18,7 @@ public record OpenSettingsUiMessage : MessageBase;
public record DalamudLoginMessage : MessageBase; public record DalamudLoginMessage : MessageBase;
public record DalamudLogoutMessage : MessageBase; public record DalamudLogoutMessage : MessageBase;
public record FrameworkUpdateMessage : SameThreadMessage; public record FrameworkUpdateMessage : SameThreadMessage;
public record ClassJobChangedMessage(uint? ClassJob) : MessageBase; public record ClassJobChangedMessage(GameObjectHandler gameObjectHandler) : MessageBase;
public record DelayedFrameworkUpdateMessage : SameThreadMessage; public record DelayedFrameworkUpdateMessage : SameThreadMessage;
public record ZoneSwitchStartMessage : MessageBase; public record ZoneSwitchStartMessage : MessageBase;
public record ZoneSwitchEndMessage : MessageBase; public record ZoneSwitchEndMessage : MessageBase;

View File

@@ -183,8 +183,8 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
var ySize = TransferPartHeight == 0 var ySize = TransferPartHeight == 0
? 1 ? 1
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y : (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y
+ ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY(); + ImGui.GetTextLineHeight() - ImGui.GetStyle().WindowPadding.Y - ImGui.GetStyle().WindowBorderSize) - TransferPartHeight - ImGui.GetCursorPosY();
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false); ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);

View File

@@ -515,6 +515,18 @@ public class SettingsUi : WindowMediatorSubscriberBase
} }
_lastTab = "General"; _lastTab = "General";
UiSharedService.FontText("General Settings", _uiShared.UidFont);
bool lessRedraws = _configService.Current.UseLessRedraws;
if (ImGui.Checkbox("[Experimental] Use less redraws", ref lessRedraws))
{
_configService.Current.UseLessRedraws = lessRedraws;
_configService.Save();
}
UiSharedService.DrawHelpText("This will attempt to use less redraws. Changes that solely affect the players body appearance (i.e. clothes) should not cause a redraw anymore." + Environment.NewLine +
"Some changes, especially to hair, face, tail or any vfx, animation or skeleton changes, or class changes will still force a redraw." + Environment.NewLine + Environment.NewLine +
"WARNING: this is an experimental, little tested feature and can potentially lead to issues with animation state or crashes. Use at your own risk.");
ImGui.Separator();
UiSharedService.FontText("Notes", _uiShared.UidFont); UiSharedService.FontText("Notes", _uiShared.UidFont);
if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
{ {

View File

@@ -59,6 +59,7 @@ public static class VariousExtensions
cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer); cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer);
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer); charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
} }
else else
{ {
@@ -69,6 +70,43 @@ public static class VariousExtensions
{ {
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (FileReplacements not equal) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModFiles); logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (FileReplacements not equal) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModFiles);
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles); charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
if (forceApplyMods || objectKind != ObjectKind.Player)
{
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
}
else
{
var existingFace = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var existingHair = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var existingTail = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newFace = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newHair = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newTail = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var existingTransients = existingFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl")))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newTransients = newFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl")))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase,
existingFace.Count, newFace.Count, existingHair.Count, newHair.Count, existingTail.Count, newTail.Count, existingTransients.Count, newTransients.Count);
var differentFace = !existingFace.SequenceEqual(newFace, PlayerData.Data.FileReplacementDataComparer.Instance);
var differentHair = !existingHair.SequenceEqual(newHair, PlayerData.Data.FileReplacementDataComparer.Instance);
var differentTail = !existingTail.SequenceEqual(newTail, PlayerData.Data.FileReplacementDataComparer.Instance);
var differenTransients = !existingTransients.SequenceEqual(newTransients, PlayerData.Data.FileReplacementDataComparer.Instance);
if (differentFace || differentHair || differentTail || differenTransients)
{
logger.LogDebug("[BASE-{appbase}] Different Subparts: Face: {face}, Hair: {hair}, Tail: {tail}, Transients: {transients} => {change}", applicationBase,
differentFace, differentHair, differentTail, differenTransients, PlayerChanges.ForcedRedraw);
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
}
}
} }
} }
@@ -100,6 +138,7 @@ public static class VariousExtensions
{ {
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff manip data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModManip); logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff manip data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModManip);
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip); charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
} }
bool heelsOffsetDifferent = !string.Equals(oldData.HeelsData, newData.HeelsData, StringComparison.Ordinal); bool heelsOffsetDifferent = !string.Equals(oldData.HeelsData, newData.HeelsData, StringComparison.Ordinal);
@@ -109,7 +148,8 @@ public static class VariousExtensions
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels); charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
} }
bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal); bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal)
|| (charaDataToUpdate.TryGetValue(objectKind, out var playerChanges) && playerChanges.Contains(PlayerChanges.Glamourer));
if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData))) if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData)))
{ {
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette); logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette);