rebuild PlayerManager to CacheCreationService and optimize creation of the local file cache

This commit is contained in:
rootdarkarchon
2023-02-02 15:25:58 +01:00
parent 86549b2d3f
commit ede62fabae
23 changed files with 461 additions and 737 deletions

View File

@@ -1,6 +1,5 @@
using Newtonsoft.Json;
using System.Text;
using MareSynchronos.Utils;
using MareSynchronos.API.Data.Enum;
using MareSynchronos.API.Data;
@@ -10,7 +9,7 @@ namespace MareSynchronos.Models;
public class CharacterData
{
[JsonProperty]
public Dictionary<ObjectKind, List<FileReplacement>> FileReplacements { get; set; } = new();
public Dictionary<ObjectKind, HashSet<FileReplacement>> FileReplacements { get; set; } = new();
[JsonProperty]
public Dictionary<ObjectKind, string> GlamourerString { get; set; } = new();
@@ -33,7 +32,7 @@ public class CharacterData
{
if (!fileReplacement.HasFileReplacement) return;
if (!FileReplacements.ContainsKey(objectKind)) FileReplacements.Add(objectKind, new List<FileReplacement>());
if (!FileReplacements.ContainsKey(objectKind)) FileReplacements.Add(objectKind, new HashSet<FileReplacement>());
var existingReplacement = FileReplacements[objectKind].SingleOrDefault(f => string.Equals(f.ResolvedPath, fileReplacement.ResolvedPath, StringComparison.OrdinalIgnoreCase));
if (existingReplacement != null)
@@ -57,16 +56,9 @@ public class CharacterData
};
}).ToList());
Logger.Debug("Adding fileSwaps");
foreach (var item in FileReplacements)
{
Logger.Debug("Checking fileSwaps for " + item.Key);
var fileSwapsToAdd = item.Value.Where(f => f.IsFileSwap).Select(f => f.ToFileReplacementDto());
Logger.Debug("Adding " + fileSwapsToAdd.Count() + " file swaps");
foreach (var swap in fileSwapsToAdd)
{
Logger.Debug("Adding: " + swap.GamePaths.First() + ":" + swap.FileSwapPath);
}
fileReplacements[item.Key].AddRange(fileSwapsToAdd);
}

View File

@@ -1,79 +1,31 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using MareSynchronos.FileCache;
using MareSynchronos.Managers;
using MareSynchronos.Utils;
using MareSynchronos.API.Data;
namespace MareSynchronos.Models;
public class FileReplacement
{
private readonly FileCacheManager _fileDbManager;
private readonly IpcManager _ipcManager;
public FileReplacement(FileCacheManager fileDbManager, IpcManager ipcManager)
public FileReplacement(List<string> gamePaths, string filePath, FileCacheManager fileDbManager)
{
_fileDbManager = fileDbManager;
_ipcManager = ipcManager;
GamePaths = gamePaths.Select(g => g.Replace('\\', '/')).ToList();
ResolvedPath = filePath.Replace('\\', '/');
HashLazy = new(() => !IsFileSwap ? fileDbManager.GetFileCacheByPath(ResolvedPath)?.Hash ?? string.Empty : string.Empty);
}
public bool Computed => IsFileSwap || !HasFileReplacement || !string.IsNullOrEmpty(Hash);
public List<string> GamePaths { get; set; } = new();
public List<string> GamePaths { get; init; } = new();
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => !string.Equals(p, ResolvedPath, StringComparison.Ordinal));
public bool IsFileSwap => !Regex.IsMatch(ResolvedPath, @"^[a-zA-Z]:(/|\\)", RegexOptions.ECMAScript) && !string.Equals(GamePaths[0], ResolvedPath, StringComparison.Ordinal);
public string Hash { get; private set; } = string.Empty;
public string Hash => HashLazy.Value;
public string ResolvedPath { get; set; } = string.Empty;
private Lazy<string> HashLazy;
private void SetResolvedPath(string path)
{
ResolvedPath = path.ToLowerInvariant().Replace('\\', '/');
if (!HasFileReplacement || IsFileSwap) return;
_ = Task.Run(() =>
{
try
{
var cache = _fileDbManager.GetFileCacheByPath(ResolvedPath)!;
Hash = cache.Hash;
}
catch (Exception ex)
{
Logger.Warn("Could not set Hash for " + ResolvedPath + ", resetting to original", ex);
ResolvedPath = GamePaths[0];
}
});
}
public bool Verify()
{
if (!IsFileSwap)
{
var cache = _fileDbManager.GetFileCacheByPath(ResolvedPath);
if (cache == null)
{
Logger.Warn("Replacement Failed verification: " + GamePaths[0]);
return false;
}
Hash = cache.Hash;
return true;
}
ResolvePath(GamePaths[0]);
var success = IsFileSwap;
if (!success)
{
Logger.Warn("FileSwap Failed verification: " + GamePaths[0]);
}
return success;
}
public string ResolvedPath { get; init; } = string.Empty;
public FileReplacementData ToFileReplacementDto()
{
@@ -87,20 +39,6 @@ public class FileReplacement
public override string ToString()
{
StringBuilder builder = new();
builder.AppendLine($"Modded: {HasFileReplacement} - {string.Join(",", GamePaths)} => {ResolvedPath}");
return builder.ToString();
}
internal void ReverseResolvePath(string path)
{
GamePaths = _ipcManager.PenumbraReverseResolvePlayer(path).ToList();
SetResolvedPath(path);
}
internal void ResolvePath(string path)
{
GamePaths = new List<string> { path };
SetResolvedPath(_ipcManager.PenumbraResolvePath(path));
return $"Modded: {HasFileReplacement} - {string.Join(",", GamePaths)} => {ResolvedPath}";
}
}

View File

@@ -3,12 +3,15 @@ using System.Runtime.InteropServices;
using MareSynchronos.Utils;
using Penumbra.String;
using MareSynchronos.API.Data.Enum;
using MareSynchronos.Mediator;
namespace MareSynchronos.Models;
public class PlayerRelatedObject
public class GameObjectHandler : MediatorSubscriberBase
{
private readonly MareMediator _mediator;
private readonly Func<IntPtr> getAddress;
private readonly bool _sendUpdates;
public unsafe Character* Character => (Character*)Address;
@@ -31,26 +34,31 @@ public class PlayerRelatedObject
}
}
public PlayerRelatedObject(ObjectKind objectKind, IntPtr address, IntPtr drawObjectAddress, Func<IntPtr> getAddress)
public GameObjectHandler(MareMediator mediator, ObjectKind objectKind, Func<IntPtr> getAddress, bool sendUpdates = true) : base(mediator)
{
_mediator = mediator;
ObjectKind = objectKind;
Address = address;
DrawObjectAddress = drawObjectAddress;
this.getAddress = getAddress;
_sendUpdates = sendUpdates;
_name = string.Empty;
Mediator.Subscribe<TransientResourceChangedMessage>(this, (msg) =>
{
var actualMsg = (TransientResourceChangedMessage)msg;
if (actualMsg.Address != Address || !sendUpdates) return;
Mediator.Publish(new CreateCacheForObjectMessage(this));
});
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => CheckAndUpdateObject());
}
public byte[] EquipSlotData { get; set; } = new byte[40];
public byte[] CustomizeData { get; set; } = new byte[26];
public byte? HatState { get; set; }
public byte? VisorWeaponState { get; set; }
private bool _doNotSendUpdate;
public bool HasTransientsUpdate { get; set; } = false;
public bool HasUnprocessedUpdate { get; set; } = false;
public bool DoNotSendUpdate { get; set; } = false;
public bool IsProcessing { get; set; } = false;
public unsafe void CheckAndUpdateObject()
public unsafe bool CheckAndUpdateObject()
{
var curPtr = CurrentAddress;
if (curPtr != IntPtr.Zero)
@@ -68,7 +76,10 @@ public class PlayerRelatedObject
Address = curPtr;
DrawObjectAddress = (IntPtr)chara->GameObject.DrawObject;
HasUnprocessedUpdate = true;
if (_sendUpdates && !_doNotSendUpdate)
Mediator.Publish(new CreateCacheForObjectMessage(this));
return true;
}
}
else if (Address != IntPtr.Zero || DrawObjectAddress != IntPtr.Zero)
@@ -77,12 +88,14 @@ public class PlayerRelatedObject
DrawObjectAddress = IntPtr.Zero;
Logger.Verbose(ObjectKind + " Changed: " + _name + ", now: " + Address + ", " + DrawObjectAddress);
}
return false;
}
private unsafe bool CompareAndUpdateByteData(byte* equipSlotData, byte* customizeData)
{
bool hasChanges = false;
DoNotSendUpdate = false;
_doNotSendUpdate = false;
for (int i = 0; i < EquipSlotData.Length; i++)
{
var data = Marshal.ReadByte((IntPtr)equipSlotData, i);
@@ -107,10 +120,10 @@ public class PlayerRelatedObject
var newWeaponOrVisorState = Marshal.ReadByte((IntPtr)customizeData + 31, 0);
if (newHatState != HatState)
{
if (HatState != null && !hasChanges && !HasUnprocessedUpdate)
if (HatState != null && !hasChanges)
{
Logger.Debug("Not Sending Update, only Hat changed");
DoNotSendUpdate = true;
_doNotSendUpdate = true;
}
HatState = newHatState;
hasChanges = true;
@@ -120,10 +133,10 @@ public class PlayerRelatedObject
if (newWeaponOrVisorState != VisorWeaponState)
{
if (VisorWeaponState != null && !hasChanges && !HasUnprocessedUpdate)
if (VisorWeaponState != null && !hasChanges)
{
Logger.Debug("Not Sending Update, only Visor/Weapon changed");
DoNotSendUpdate = true;
_doNotSendUpdate = true;
}
VisorWeaponState = newWeaponOrVisorState;
hasChanges = true;