some fixes for models sharing materials and code cleanup

This commit is contained in:
Stanley Dimant
2022-06-28 12:20:40 +02:00
parent 1021cca912
commit 3ee082d371
9 changed files with 95 additions and 170 deletions

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
@@ -25,9 +27,33 @@ namespace MareSynchronos.Factories
_ipcManager = ipcManager;
}
private FileReplacement CreateBaseFileReplacement()
private FileReplacement CreateFileReplacement(string path)
{
return new FileReplacement(_ipcManager.PenumbraModDirectory()!);
var fileReplacement = new FileReplacement(_ipcManager.PenumbraModDirectory()!);
if (!path.Contains(".tex", StringComparison.OrdinalIgnoreCase))
{
fileReplacement.GamePaths =
_ipcManager.PenumbraReverseResolvePath(path, _dalamudUtil.PlayerName).ToList();
fileReplacement.SetResolvedPath(path);
}
else
{
fileReplacement.GamePaths = new List<string> { path };
fileReplacement.SetResolvedPath(_ipcManager.PenumbraResolvePath(path, _dalamudUtil.PlayerName)!);
if (!fileReplacement.HasFileReplacement)
{
// try resolving tex with -- in name instead
path = path.Insert(path.LastIndexOf('/') + 1, "--");
var reResolvedPath = _ipcManager.PenumbraResolvePath(path, _dalamudUtil.PlayerName)!;
if (reResolvedPath != path)
{
fileReplacement.GamePaths = new List<string>() { path };
fileReplacement.SetResolvedPath(reResolvedPath);
}
}
}
return fileReplacement;
}
public CharacterData BuildCharacterData()
@@ -55,9 +81,9 @@ namespace MareSynchronos.Factories
ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName)
};
var model = (CharacterBase*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject();
for (var idx = 0; idx < model->SlotCount; ++idx)
for (var mdlIdx = 0; mdlIdx < model->SlotCount; ++mdlIdx)
{
var mdl = (RenderModel*)model->ModelArray[idx];
var mdl = (RenderModel*)model->ModelArray[mdlIdx];
if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara)
{
continue;
@@ -65,58 +91,40 @@ namespace MareSynchronos.Factories
var mdlPath = new Utf8String(mdl->ResourceHandle->FileName()).ToString();
FileReplacement cachedMdlResource = CreateBaseFileReplacement();
cachedMdlResource.GamePaths = _ipcManager.PenumbraReverseResolvePath(mdlPath, _dalamudUtil.PlayerName);
//Logger.Debug("Model " + string.Join(", ", cachedMdlResource.GamePaths));
cachedMdlResource.SetResolvedPath(mdlPath);
//Logger.Debug("\t\t=> " + cachedMdlResource.ResolvedPath);
FileReplacement mdlFileReplacement = CreateFileReplacement(mdlPath);
Logger.Verbose("Model " + string.Join(", ", mdlFileReplacement.GamePaths));
Logger.Verbose("\t\t=> " + mdlFileReplacement.ResolvedPath);
cache.AddAssociatedResource(cachedMdlResource, null!, null!);
cache.AddFileReplacement(mdlFileReplacement);
for (int mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++)
for (var mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++)
{
var mtrl = (Material*)mdl->Materials[mtrlIdx];
if (mtrl == null) continue;
//var mtrlFileResource = factory.CreateBaseFileReplacement();
var mtrlPath = new Utf8String(mtrl->ResourceHandle->FileName()).ToString().Split("|")[2];
var cachedMtrlResource = CreateBaseFileReplacement();
cachedMtrlResource.GamePaths = _ipcManager.PenumbraReverseResolvePath(mtrlPath, _dalamudUtil.PlayerName);
//Logger.Debug("\tMaterial " + string.Join(", ", cachedMtrlResource.GamePaths));
cachedMtrlResource.SetResolvedPath(mtrlPath);
cache.AddAssociatedResource(cachedMtrlResource, cachedMdlResource, null!);
//Logger.Debug("\t\t\t=> " + cachedMtrlResource.ResolvedPath);
var mtrlFileReplacement = CreateFileReplacement(mtrlPath);
Logger.Verbose("\tMaterial " + string.Join(", ", mtrlFileReplacement.GamePaths));
Logger.Verbose("\t\t\t=> " + mtrlFileReplacement.ResolvedPath);
cache.AddFileReplacement(mtrlFileReplacement);
var mtrlResource = (MtrlResource*)mtrl->ResourceHandle;
for (int resIdx = 0; resIdx < mtrlResource->NumTex; resIdx++)
var mtrlResourceHandle = (MtrlResource*)mtrl->ResourceHandle;
for (var resIdx = 0; resIdx < mtrlResourceHandle->NumTex; resIdx++)
{
var texPath = new Utf8String(mtrlResource->TexString(resIdx)).ToString();
var texPath = new Utf8String(mtrlResourceHandle->TexString(resIdx)).ToString();
if (string.IsNullOrEmpty(texPath.ToString())) continue;
if (string.IsNullOrEmpty(texPath)) continue;
var cachedTexResource = CreateBaseFileReplacement();
cachedTexResource.GamePaths = new[] { texPath };
cachedTexResource.SetResolvedPath(_ipcManager.PenumbraResolvePath(texPath, _dalamudUtil.PlayerName)!);
if (!cachedTexResource.HasFileReplacement)
{
// try resolving tex with -- in name instead
texPath = texPath.Insert(texPath.LastIndexOf('/') + 1, "--");
var reResolvedPath = _ipcManager.PenumbraResolvePath(texPath, _dalamudUtil.PlayerName)!;
if (reResolvedPath != texPath)
{
cachedTexResource.GamePaths = new[] { texPath };
cachedTexResource.SetResolvedPath(reResolvedPath);
}
}
//Logger.Debug("\t\tTexture " + string.Join(", ", cachedTexResource.GamePaths));
//Logger.Debug("\t\t\t\t=> " + cachedTexResource.ResolvedPath);
cache.AddAssociatedResource(cachedTexResource, cachedMdlResource, cachedMtrlResource);
var texFileReplacement = CreateFileReplacement(texPath);
Logger.Verbose("\t\tTexture " + string.Join(", ", texFileReplacement.GamePaths));
Logger.Verbose("\t\t\t\t=> " + texFileReplacement.ResolvedPath);
cache.AddFileReplacement(texFileReplacement);
}
}
}
st.Stop();
Logger.Debug("Building Character Data took " + st.Elapsed);
Logger.Verbose("Building Character Data took " + st.Elapsed);
return cache;
}

View File

@@ -113,21 +113,21 @@ namespace MareSynchronos.Managers
public void GlamourerApplyAll(string customization, string characterName)
{
if (!CheckGlamourerApi()) return;
Logger.Debug("GlamourerString: " + customization);
Logger.Debug("Glamourer apply all to " + characterName);
_glamourerApplyAll!.InvokeAction(customization, characterName);
}
public void GlamourerApplyOnlyEquipment(string customization, string characterName)
{
if (!CheckGlamourerApi()) return;
Logger.Debug("GlamourerString: " + customization);
Logger.Debug("Glamourer apply only equipment to " + characterName);
_glamourerApplyOnlyEquipment!.InvokeAction(customization, characterName);
}
public void GlamourerApplyOnlyCustomization(string customization, string characterName)
{
if (!CheckGlamourerApi()) return;
Logger.Debug("GlamourerString: " + customization);
Logger.Debug("Glamourer apply only customization to " + characterName);
_glamourerApplyOnlyCustomization!.InvokeAction(customization, characterName);
}
@@ -148,7 +148,6 @@ namespace MareSynchronos.Managers
if (!CheckPenumbraApi()) return string.Empty;
Logger.Debug("Creating temp collection for " + characterName);
var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true);
Logger.Debug("Penumbra ret: " + ret.Item1);
return ret.Item2;
}
@@ -198,13 +197,6 @@ namespace MareSynchronos.Managers
if (!CheckPenumbraApi()) return;
Logger.Debug("Assigning temp mods for " + collectionName);
Logger.Debug("ManipulationString: " + manipulationData);
var orderedModPaths = modPaths.OrderBy(p => p.Key.EndsWith(".mdl") ? 0 : p.Key.EndsWith(".mtrl") ? 1 : 2)
.ToDictionary(k => k.Key, k => k.Value);
foreach (var item in orderedModPaths)
{
//Logger.Debug(item.Key + " => " + item.Value);
}
var ret = _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", collectionName, modPaths, manipulationData, 0);
Logger.Debug("Penumbra Ret: " + ret.ToString());
}

View File

@@ -20,7 +20,6 @@ namespace MareSynchronos.Managers
private readonly DalamudUtil _dalamudUtil;
private readonly IpcManager _ipcManager;
private string _lastSentHash = string.Empty;
private Task? _playerChangedTask;
private CancellationTokenSource? _playerChangedCts;
public PlayerManager(ApiController apiController, IpcManager ipcManager,
@@ -68,8 +67,8 @@ namespace MareSynchronos.Managers
_cachedPlayersManager.AddInitialPairs(apiTask.Result);
_ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
_ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName);
_dalamudUtil.PlayerChanged += Watcher_PlayerChanged;
PlayerChanged(_dalamudUtil.PlayerName);
}
private void ApiController_Disconnected(object? sender, EventArgs args)
@@ -111,24 +110,18 @@ namespace MareSynchronos.Managers
private void PlayerChanged(string name)
{
//if (sender == null) return;
Logger.Debug("Player changed: " + name);
_playerChangedCts?.Cancel();
_playerChangedCts = new CancellationTokenSource();
var token = _playerChangedCts.Token;/*
if (_playerChangedTask is { IsCompleted: false })
{
PluginLog.Warning("PlayerChanged Task still running");
return;
}*/
var token = _playerChangedCts.Token;
if (!_ipcManager.Initialized)
{
PluginLog.Warning("Penumbra not active, doing nothing.");
Logger.Warn("Penumbra not active, doing nothing.");
return;
}
_playerChangedTask = Task.Run(async () =>
Task.Run(async () =>
{
int attempts = 0;
while (!_apiController.IsConnected && attempts < 10 && !token.IsCancellationRequested)

View File

@@ -146,7 +146,6 @@ public class CachedPlayer
Logger.Debug(
$"Request Redraw for {PlayerName}");
_ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, cache.ManipulationData);
_ipcManager.GlamourerRevertCharacterCustomization(PlayerName!);
_ipcManager.GlamourerApplyAll(cache.GlamourerData, PlayerName!);
}
@@ -164,7 +163,6 @@ public class CachedPlayer
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
if (IsVisible)
{
_ipcManager.GlamourerRevertCharacterCustomization(PlayerName);
_ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName);
_ipcManager.GlamourerApplyOnlyEquipment(_lastGlamourerData, PlayerName);
}

View File

@@ -1,10 +1,8 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MareSynchronos.API;
using MareSynchronos.Utils;
namespace MareSynchronos.Models
{
@@ -12,12 +10,7 @@ namespace MareSynchronos.Models
public class CharacterData
{
[JsonProperty]
public List<FileReplacement> AllReplacements =>
FileReplacements.Where(f => f.HasFileReplacement)
.Concat(FileReplacements.SelectMany(f => f.Associated)).Where(f => f.HasFileReplacement)
.Concat(FileReplacements.SelectMany(f => f.Associated).SelectMany(f => f.Associated)).Where(f => f.HasFileReplacement)
.Distinct().OrderBy(f => f.GamePaths[0])
.ToList();
public List<FileReplacement> AllReplacements => FileReplacements.Where(f => f.HasFileReplacement).GroupBy(f => f.Hash).Select(g => g.First()).ToList();
[JsonProperty]
public string CacheHash { get; set; } = string.Empty;
@@ -34,53 +27,18 @@ namespace MareSynchronos.Models
public string ManipulationString { get; set; } = string.Empty;
public void AddAssociatedResource(FileReplacement resource, FileReplacement? mdlParent, FileReplacement? mtrlParent)
public void AddFileReplacement(FileReplacement fileReplacement)
{
try
{
if (mdlParent == null)
{
resource.IsInUse = true;
FileReplacements.Add(resource);
return;
}
if (!fileReplacement.HasFileReplacement) return;
var mdlReplacements = FileReplacements.Where(f => f == mdlParent && mtrlParent == null);
foreach (var mdlReplacement in mdlReplacements)
{
mdlReplacement.AddAssociated(resource);
}
var mtrlReplacements = FileReplacements.Where(f => f == mdlParent).SelectMany(a => a.Associated).Where(f => f == mtrlParent);
foreach (var mtrlReplacement in mtrlReplacements)
{
mtrlReplacement.AddAssociated(resource);
}
}
catch (Exception ex)
var existingReplacement = FileReplacements.SingleOrDefault(f => f.ResolvedPath == fileReplacement.ResolvedPath);
if (existingReplacement != null)
{
Logger.Debug(ex.Message);
existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e)));
}
}
public void Invalidate(List<FileReplacement>? fileReplacements = null)
{
try
else
{
var fileReplacement = fileReplacements ?? FileReplacements.ToList();
foreach (var item in fileReplacement)
{
item.IsInUse = false;
Invalidate(item.Associated);
if (FileReplacements.Contains(item))
{
FileReplacements.Remove(item);
}
}
}
catch (Exception ex)
{
Logger.Debug(ex.Message);
FileReplacements.Add(fileReplacement);
}
}
@@ -95,6 +53,7 @@ namespace MareSynchronos.Models
ManipulationData = ManipulationString
};
}
public override string ToString()
{
StringBuilder stringBuilder = new();

View File

@@ -24,14 +24,12 @@ namespace MareSynchronos.Models
this._penumbraDirectory = penumbraDirectory;
}
public List<FileReplacement> Associated { get; set; } = new();
public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)) && Associated.All(f => f.Computed);
public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true));
[JsonProperty]
public string[] GamePaths { get; set; } = Array.Empty<string>();
public List<string> GamePaths { get; set; } = new();
public bool HasFileReplacement => GamePaths.Length >= 1 && GamePaths[0] != ResolvedPath;
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => p != ResolvedPath);
[JsonProperty]
public string Hash { get; set; } = string.Empty;
@@ -43,35 +41,7 @@ namespace MareSynchronos.Models
[JsonProperty]
public string ResolvedPath { get; set; } = string.Empty;
public void AddAssociated(FileReplacement fileReplacement)
{
fileReplacement.IsInUse = true;
Associated.Add(fileReplacement);
}
public override bool Equals(object? obj)
{
if (obj == null) return true;
if (obj.GetType() == typeof(FileReplacement))
{
return Hash == ((FileReplacement)obj).Hash;
}
return base.Equals(obj);
}
public override int GetHashCode()
{
int result = 13;
result *= 397;
result += Hash.GetHashCode();
result += ResolvedPath.GetHashCode();
return result;
}
public void SetResolvedPath(string path)
{
ResolvedPath = path.ToLower().Replace('/', '\\').Replace(_penumbraDirectory, "").Replace('\\', '/');
@@ -113,7 +83,7 @@ namespace MareSynchronos.Models
{
return new FileReplacementDto
{
GamePaths = GamePaths,
GamePaths = GamePaths.ToArray(),
Hash = Hash,
};
}
@@ -121,14 +91,6 @@ namespace MareSynchronos.Models
{
StringBuilder builder = new();
builder.AppendLine($"Modded: {HasFileReplacement} - {string.Join(",", GamePaths)} => {ResolvedPath}");
foreach (var l1 in Associated)
{
builder.AppendLine($" + Modded: {l1.HasFileReplacement} - {string.Join(",", l1.GamePaths)} => {l1.ResolvedPath}");
foreach (var l2 in l1.Associated)
{
builder.AppendLine($" + Modded: {l2.HasFileReplacement} - {string.Join(",", l2.GamePaths)} => {l2.ResolvedPath}");
}
}
return builder.ToString();
}

View File

@@ -184,7 +184,7 @@ namespace MareSynchronos.UI
{
_pluginConfiguration.FullPause = false;
_pluginConfiguration.Save();
Task.WaitAll(_apiController.Register());
Task.Run(_apiController.Register);
}
}
else

View File

@@ -16,5 +16,11 @@ namespace MareSynchronos.Utils
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
PluginLog.Warning($"[{caller}] {warn}");
}
public static void Verbose(string verbose)
{
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
PluginLog.Verbose($"[{caller}] {verbose}");
}
}
}

View File

@@ -13,6 +13,7 @@ using MareSynchronos.API;
using MareSynchronos.FileCacheDB;
using MareSynchronos.Utils;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
namespace MareSynchronos.WebAPI
{
@@ -226,7 +227,7 @@ namespace MareSynchronos.WebAPI
{
if (_uploadCancellationTokenSource != null)
{
PluginLog.Warning("Cancelling upload");
Logger.Warn("Cancelling upload");
_uploadCancellationTokenSource?.Cancel();
_fileHub!.InvokeAsync("AbortUpload");
}
@@ -319,7 +320,7 @@ namespace MareSynchronos.WebAPI
public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash)
{
Logger.Debug("Received DTO for " + characterHash);
Logger.Verbose("Received DTO for " + characterHash);
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character));
return Task.CompletedTask;
}
@@ -342,33 +343,39 @@ namespace MareSynchronos.WebAPI
CancelUpload();
_uploadCancellationTokenSource = new CancellationTokenSource();
var uploadToken = _uploadCancellationTokenSource.Token;
Logger.Debug("New Token Created");
Logger.Verbose("New Token Created");
var filesToUpload = await _fileHub!.InvokeAsync<List<string>>("SendFiles", character.FileReplacements.Select(c => c.Hash).Distinct(), uploadToken);
IsUploading = true;
Logger.Debug("Compressing files");
foreach (var file in filesToUpload)
{
Logger.Debug(file);
await using var db = new FileCacheContext();
CurrentUploads[file] = (0, new FileInfo(db.FileCaches.First(f => f.Hash == file).Filepath).Length);
}
Logger.Verbose("Compressing and uploading files");
foreach (var file in filesToUpload)
{
Logger.Verbose("Compressing and uploading " + file);
var data = await GetCompressedFileData(file, uploadToken);
CurrentUploads[data.Item1] = (0, data.Item2.Length);
_ = UploadFile(data.Item2, file, uploadToken);
if (!uploadToken.IsCancellationRequested) continue;
PluginLog.Warning("Cancel in filesToUpload loop detected");
Logger.Warn("Cancel in filesToUpload loop detected");
CurrentUploads.Clear();
break;
}
Logger.Debug("Upload tasks complete, waiting for server to confirm");
Logger.Verbose("Upload tasks complete, waiting for server to confirm");
var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
Logger.Debug("Uploads open: " + anyUploadsOpen);
Logger.Verbose("Uploads open: " + anyUploadsOpen);
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
{
anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken);
Logger.Debug("Waiting for uploads to finish");
Logger.Verbose("Waiting for uploads to finish");
}
CurrentUploads.Clear();
@@ -376,15 +383,15 @@ namespace MareSynchronos.WebAPI
if (!uploadToken.IsCancellationRequested)
{
Logger.Debug("=== Pushing character data ===");
Logger.Verbose("=== Pushing character data ===");
await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken);
}
else
{
PluginLog.Warning("=== Upload operation was cancelled ===");
Logger.Warn("=== Upload operation was cancelled ===");
}
Logger.Debug("== Upload complete for " + character.JobId);
Logger.Verbose("Upload complete for " + character.Hash);
_uploadCancellationTokenSource = null;
}