add some preliminary vfx work

This commit is contained in:
Stanley Dimant
2022-08-15 17:36:43 +02:00
parent 4226f2e16d
commit 8459fe8f25
11 changed files with 217 additions and 26 deletions

View File

@@ -22,13 +22,15 @@ public class CharacterDataFactory
{
private readonly DalamudUtil _dalamudUtil;
private readonly IpcManager _ipcManager;
private readonly TransientResourceManager transientResourceManager;
public CharacterDataFactory(DalamudUtil dalamudUtil, IpcManager ipcManager)
public CharacterDataFactory(DalamudUtil dalamudUtil, IpcManager ipcManager, TransientResourceManager transientResourceManager)
{
Logger.Verbose("Creating " + nameof(CharacterDataFactory));
_dalamudUtil = dalamudUtil;
_ipcManager = ipcManager;
this.transientResourceManager = transientResourceManager;
}
public CharacterData BuildCharacterData(CharacterData previousData, ObjectKind objectKind, IntPtr playerPointer, CancellationToken token)
@@ -167,6 +169,26 @@ public class CharacterDataFactory
}
}
private void AddReplacement(string varPath, ObjectKind objectKind, CharacterData cache, int inheritanceLevel = 0)
{
if (varPath.IsNullOrEmpty()) return;
//Logger.Verbose("Adding File Replacement for Texture " + texPath);
if (cache.FileReplacements.ContainsKey(objectKind))
{
if (cache.FileReplacements[objectKind].Any(c => c.GamePaths.Contains(varPath)))
{
return;
}
}
var variousReplacement = CreateFileReplacement(varPath, false);
DebugPrint(variousReplacement, objectKind, "Various", inheritanceLevel);
cache.AddFileReplacement(objectKind, variousReplacement);
}
private void AddReplacementsFromTexture(string texPath, ObjectKind objectKind, CharacterData cache, int inheritanceLevel = 0, bool doNotReverseResolve = true)
{
if (texPath.IsNullOrEmpty()) return;
@@ -233,6 +255,11 @@ public class CharacterDataFactory
AddReplacementsFromRenderModel(mdl, objectKind, previousData, 0);
}
foreach (var item in previousData.FileReplacements[objectKind])
{
transientResourceManager.RemoveTransientResource((IntPtr)human, item);
}
if (objectKind == ObjectKind.Player)
{
var weaponObject = (Weapon*)((Object*)human)->ChildObject;
@@ -243,11 +270,33 @@ public class CharacterDataFactory
AddReplacementsFromRenderModel(mainHandWeapon, objectKind, previousData, 0);
foreach (var item in previousData.FileReplacements[objectKind])
{
transientResourceManager.RemoveTransientResource((IntPtr)weaponObject, item);
}
foreach (var item in transientResourceManager.GetTransientResources((IntPtr)weaponObject))
{
Logger.Verbose("Found transient weapon resource: " + item);
AddReplacementsFromTexture(item, objectKind, previousData, 0, false);
}
if (weaponObject->NextSibling != (IntPtr)weaponObject)
{
var offHandWeapon = ((Weapon*)weaponObject->NextSibling)->WeaponRenderModel->RenderModel;
AddReplacementsFromRenderModel(offHandWeapon, objectKind, previousData, 1);
foreach (var item in previousData.FileReplacements[objectKind])
{
transientResourceManager.RemoveTransientResource((IntPtr)offHandWeapon, item);
}
foreach (var item in transientResourceManager.GetTransientResources((IntPtr)weaponObject))
{
Logger.Verbose("Found transient offhand weapon resource: " + item);
AddReplacement(item, objectKind, previousData, 1);
}
}
}
@@ -268,11 +317,21 @@ public class CharacterDataFactory
{
Logger.Warn("Could not get Legacy Body Decal Data");
}
foreach (var item in previousData.FileReplacements[objectKind])
{
transientResourceManager.RemoveTransientResource((IntPtr)human, item);
}
}
foreach (var item in transientResourceManager.GetTransientResources((IntPtr)human))
{
Logger.Verbose("Found transient resource: " + item);
AddReplacement(item, objectKind, previousData, 1);
}
st.Stop();
Logger.Verbose("Building " + objectKind + " Data took " + st.Elapsed);
return previousData;
}
@@ -282,8 +341,6 @@ public class CharacterDataFactory
string skeletonPath = $"chara/human/c{raceSexIdString}/skeleton/base/b0001/skl_c{raceSexIdString}b0001.sklb";
//Logger.Verbose("Adding File Replacement for Skeleton " + skeletonPath);
var replacement = CreateFileReplacement(skeletonPath, true);
cache.AddFileReplacement(objectKind, replacement);

View File

@@ -203,7 +203,7 @@ namespace MareSynchronos.Managers
{
PluginLog.Verbose("Removed: " + item);
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLowerInvariant() == item.ToLowerInvariant()));
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == item.ToLowerInvariant()));
}
else
{
@@ -211,7 +211,7 @@ namespace MareSynchronos.Managers
var fileCache = Create(item, _rescanTaskCancellationTokenSource.Token);
if (fileCache != null)
{
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLowerInvariant() == fileCache.Filepath.ToLowerInvariant()));
db.RemoveRange(db.FileCaches.Where(f => f.Filepath.ToLower() == fileCache.Filepath.ToLowerInvariant()));
await db.AddAsync(fileCache, _rescanTaskCancellationTokenSource.Token);
}
}

View File

@@ -9,6 +9,7 @@ using MareSynchronos.WebAPI;
namespace MareSynchronos.Managers
{
public delegate void PenumbraRedrawEvent(IntPtr address, int objTblIdx);
public delegate void PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath);
public class IpcManager : IDisposable
{
private readonly ICallGateSubscriber<int> _glamourerApiVersion;
@@ -31,7 +32,7 @@ namespace MareSynchronos.Managers
private readonly ICallGateSubscriber<string, string[]>? _reverseResolvePlayer;
private readonly ICallGateSubscriber<string, string, Dictionary<string, string>, string, int, int>
_penumbraSetTemporaryMod;
private readonly ICallGateSubscriber<string, string, string> _penumbraPlayerPathResolved;
private readonly ICallGateSubscriber<IntPtr, string, string, string> _penumbraResourceLoaded;
private readonly DalamudUtil _dalamudUtil;
public IpcManager(DalamudPluginInterface pi, DalamudUtil dalamudUtil)
@@ -56,9 +57,9 @@ namespace MareSynchronos.Managers
_glamourerApplyOnlyCustomization = pi.GetIpcSubscriber<string, GameObject?, object>("Glamourer.ApplyOnlyCustomizationToCharacter");
_glamourerApplyOnlyEquipment = pi.GetIpcSubscriber<string, GameObject?, object>("Glamourer.ApplyOnlyEquipmentToCharacter");
_glamourerRevertCustomization = pi.GetIpcSubscriber<GameObject?, object>("Glamourer.RevertCharacter");
_penumbraPlayerPathResolved = pi.GetIpcSubscriber<string, string, string>("Penumbra.PlayerFileResourceResolved");
_penumbraResourceLoaded = pi.GetIpcSubscriber<IntPtr, string, string, string>("Penumbra.ResourceLoaded");
_penumbraPlayerPathResolved.Subscribe(PlayerPathResolved);
_penumbraResourceLoaded.Subscribe(ResourceLoaded);
_penumbraObjectIsRedrawn.Subscribe(RedrawEvent);
_penumbraInit.Subscribe(PenumbraInit);
_penumbraDispose.Subscribe(PenumbraDispose);
@@ -81,14 +82,19 @@ namespace MareSynchronos.Managers
this._dalamudUtil = dalamudUtil;
}
private void PlayerPathResolved(string arg1, string arg2)
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
{
Logger.Debug($"Resolved {arg1} => {arg2}");
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, true, System.Globalization.CultureInfo.InvariantCulture) != 0)
{
PenumbraResourceLoadEvent?.Invoke(ptr, arg1, arg2);
//Logger.Debug($"Resolved {ptr:X}: {arg1} => {arg2}");
}
}
public event VoidDelegate? PenumbraInitialized;
public event VoidDelegate? PenumbraDisposed;
public event PenumbraRedrawEvent? PenumbraRedrawEvent;
public event PenumbraResourceLoadEvent? PenumbraResourceLoadEvent;
public bool Initialized => CheckPenumbraApi();
public bool CheckGlamourerApi()
@@ -122,6 +128,7 @@ namespace MareSynchronos.Managers
_penumbraDispose.Unsubscribe(PenumbraDispose);
_penumbraInit.Unsubscribe(PenumbraInit);
_penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent);
_penumbraResourceLoaded.Unsubscribe(ResourceLoaded);
}
public void GlamourerApplyAll(string? customization, IntPtr obj)

View File

@@ -21,6 +21,7 @@ namespace MareSynchronos.Managers
private readonly ApiController _apiController;
private readonly CharacterDataFactory _characterDataFactory;
private readonly DalamudUtil _dalamudUtil;
private readonly TransientResourceManager _transientResourceManager;
private readonly IpcManager _ipcManager;
public event PlayerHasChanged? PlayerHasChanged;
public CharacterCacheDto? LastCreatedCharacterData { get; private set; }
@@ -34,7 +35,7 @@ namespace MareSynchronos.Managers
private List<PlayerRelatedObject> playerRelatedObjects = new List<PlayerRelatedObject>();
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil)
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager)
{
Logger.Verbose("Creating " + nameof(PlayerManager));
@@ -42,10 +43,11 @@ namespace MareSynchronos.Managers
_ipcManager = ipcManager;
_characterDataFactory = characterDataFactory;
_dalamudUtil = dalamudUtil;
_transientResourceManager = transientResourceManager;
_apiController.Connected += ApiControllerOnConnected;
_apiController.Disconnected += ApiController_Disconnected;
_dalamudUtil.FrameworkUpdate += DalamudUtilOnFrameworkUpdate;
_transientResourceManager.TransientResourceLoaded += HandleTransientResourceLoad;
Logger.Debug("Watching Player, ApiController is Connected: " + _apiController.IsConnected);
if (_apiController.IsConnected)
@@ -63,6 +65,19 @@ namespace MareSynchronos.Managers
};
}
public void HandleTransientResourceLoad(IntPtr drawObj)
{
foreach (var obj in playerRelatedObjects)
{
if (obj.DrawObjectAddress == drawObj && !obj.HasUnprocessedUpdate)
{
obj.HasUnprocessedUpdate = true;
OnPlayerOrAttachedObjectsChanged();
return;
}
}
}
public void Dispose()
{
Logger.Verbose("Disposing " + nameof(PlayerManager));
@@ -72,13 +87,17 @@ namespace MareSynchronos.Managers
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
_dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate;
_transientResourceManager.TransientResourceLoaded -= HandleTransientResourceLoad;
_playerChangedCts?.Cancel();
}
private unsafe void DalamudUtilOnFrameworkUpdate()
{
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return;
//if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return;
if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return;
//if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return;
playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject());
if (playerRelatedObjects.Any(c => c.HasUnprocessedUpdate && !c.IsProcessing))
@@ -86,7 +105,7 @@ namespace MareSynchronos.Managers
OnPlayerOrAttachedObjectsChanged();
}
_lastPlayerObjectCheck = DateTime.Now;
//_lastPlayerObjectCheck = DateTime.Now;
}
private void ApiControllerOnConnected()
@@ -119,6 +138,7 @@ namespace MareSynchronos.Managers
while (!PermanentDataCache.IsReady && !token.IsCancellationRequested)
{
Logger.Verbose("Waiting until cache is ready");
await Task.Delay(50, token);
}

View File

@@ -0,0 +1,87 @@
using MareSynchronos.Models;
using MareSynchronos.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MareSynchronos.Managers
{
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
public class TransientResourceManager : IDisposable
{
private readonly IpcManager manager;
private readonly DalamudUtil dalamudUtil;
public event TransientResourceLoadedEvent? TransientResourceLoaded;
private Dictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil)
{
manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent;
this.manager = manager;
this.dalamudUtil = dalamudUtil;
dalamudUtil.FrameworkUpdate += DalamudUtil_FrameworkUpdate;
}
private void DalamudUtil_FrameworkUpdate()
{
foreach (var item in TransientResources.ToList())
{
if (!dalamudUtil.IsDrawObjectPresent(item.Key))
{
Logger.Debug("Object not present anymore: " + item.Key);
TransientResources.Remove(item.Key);
}
}
}
public List<string> GetTransientResources(IntPtr drawObject)
{
if (TransientResources.TryGetValue(drawObject, out var result))
{
return result.ToList();
}
return new List<string>();
}
private void Manager_PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath)
{
if (!TransientResources.ContainsKey(drawObject))
{
TransientResources[drawObject] = new();
}
if (filePath.StartsWith("|"))
{
filePath = filePath.Split("|")[2];
}
var newPath = filePath.ToLowerInvariant().Replace("\\", "/");
if (filePath != gamePath && !TransientResources[drawObject].Contains(newPath))
{
TransientResources[drawObject].Add(newPath);
Logger.Debug($"Adding {filePath.ToLowerInvariant().Replace("\\", "/")} for {drawObject}");
TransientResourceLoaded?.Invoke(drawObject);
}
}
public void RemoveTransientResource(IntPtr drawObject, FileReplacement fileReplacement)
{
if (TransientResources.ContainsKey(drawObject))
{
TransientResources[drawObject].RemoveWhere(f => fileReplacement.ResolvedPath == f);
}
}
public void Dispose()
{
dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
manager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
TransientResources.Clear();
}
}
}

View File

@@ -27,9 +27,9 @@ namespace MareSynchronos.Models
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => p != ResolvedPath);
public string Hash { get; set; } = string.Empty;
public string ResolvedPath { get; set; } = string.Empty;
public void SetResolvedPath(string path)
{
ResolvedPath = path.ToLowerInvariant().Replace('/', '\\').Replace(_penumbraDirectory, "").Replace('\\', '/');
@@ -93,6 +93,7 @@ namespace MareSynchronos.Models
try
{
Logger.Debug("Adding new file to DB: " + fi.FullName + ", " + hash);
db.Add(new FileCache()
{
Hash = hash,

View File

@@ -53,7 +53,7 @@ namespace MareSynchronos.Models
if (addr || equip || drawObj || nameChange)
{
_name = name;
Logger.Verbose(ObjectKind + " Changed: " + _name + ", now: " + curPtr + ", " + (IntPtr)chara->GameObject.DrawObject);
Logger.Verbose($"{ObjectKind} changed: {_name}, now: {curPtr:X}, {(IntPtr)chara->GameObject.DrawObject:X}");
Address = curPtr;
DrawObjectAddress = (IntPtr)chara->GameObject.DrawObject;

View File

@@ -30,6 +30,7 @@ namespace MareSynchronos
private readonly SettingsUi _settingsUi;
private readonly WindowSystem _windowSystem;
private PlayerManager? _playerManager;
private TransientResourceManager? _transientResourceManager;
private readonly DalamudUtil _dalamudUtil;
private OnlinePlayerManager? _characterCacheManager;
private readonly DownloadUi _downloadUi;
@@ -129,6 +130,7 @@ namespace MareSynchronos
_ipcManager?.Dispose();
_playerManager?.Dispose();
_characterCacheManager?.Dispose();
_transientResourceManager?.Dispose();
Logger.Debug("Shut down");
}
@@ -160,6 +162,7 @@ namespace MareSynchronos
Logger.Debug("Client logout");
_characterCacheManager?.Dispose();
_playerManager?.Dispose();
_transientResourceManager?.Dispose();
PluginInterface.UiBuilder.Draw -= Draw;
PluginInterface.UiBuilder.OpenConfigUi -= OpenUi;
_commandManager.RemoveHandler(CommandName);
@@ -169,6 +172,7 @@ namespace MareSynchronos
{
_characterCacheManager?.Dispose();
_playerManager?.Dispose();
_transientResourceManager?.Dispose();
Task.Run(WaitForPlayerAndLaunchCharacterManager);
}
@@ -182,10 +186,11 @@ namespace MareSynchronos
try
{
_transientResourceManager = new TransientResourceManager(_ipcManager, _dalamudUtil);
var characterCacheFactory =
new CharacterDataFactory(_dalamudUtil, _ipcManager);
new CharacterDataFactory(_dalamudUtil, _ipcManager, _transientResourceManager);
_playerManager = new PlayerManager(_apiController, _ipcManager,
characterCacheFactory, _dalamudUtil);
characterCacheFactory, _dalamudUtil, _transientResourceManager);
_characterCacheManager = new OnlinePlayerManager(_framework,
_apiController, _dalamudUtil, _ipcManager, _playerManager);
}

View File

@@ -28,6 +28,19 @@ namespace MareSynchronos.Utils
public event LogOut? LogOut;
public event FrameworkUpdate? FrameworkUpdate;
public unsafe bool IsDrawObjectPresent(IntPtr key)
{
foreach (var obj in _objectTable)
{
if ((IntPtr)((GameObject*)obj.Address)->GetDrawObject() == key)
{
return true;
}
}
return false;
}
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework)
{
_clientState = clientState;

View File

@@ -172,7 +172,7 @@ namespace MareSynchronos.WebAPI
{
CurrentUploads.Add(new UploadFileTransfer(file)
{
Total = new FileInfo(db.FileCaches.FirstOrDefault(f => f.Hash.ToLowerInvariant() == file.Hash.ToLowerInvariant())
Total = new FileInfo(db.FileCaches.FirstOrDefault(f => f.Hash.ToLower() == file.Hash.ToLower())
?.Filepath ?? string.Empty).Length
});
}

View File

@@ -128,6 +128,10 @@ namespace MareSynchronos.WebAPI
public async Task CreateConnections()
{
Logger.Info("Recreating Connection");
await StopConnection(_connectionCancellationTokenSource.Token);
if (_pluginConfiguration.FullPause)
{
ServerState = ServerState.Disconnected;
@@ -135,10 +139,6 @@ namespace MareSynchronos.WebAPI
return;
}
Logger.Info("Recreating Connection");
await StopConnection(_connectionCancellationTokenSource.Token);
_connectionCancellationTokenSource.Cancel();
_connectionCancellationTokenSource = new CancellationTokenSource();
var token = _connectionCancellationTokenSource.Token;
@@ -329,6 +329,7 @@ namespace MareSynchronos.WebAPI
{
if (_mareHub is not null)
{
_uploadCancellationTokenSource?.Cancel();
Logger.Info("Stopping existing connection");
await _mareHub.StopAsync(token);
_mareHub.Closed -= MareHubOnClosed;