rework a lot of stuff: downloads, how to watch a player

This commit is contained in:
Stanley Dimant
2022-06-30 13:24:35 +02:00
parent eb39429777
commit 3618540402
18 changed files with 319 additions and 233 deletions

View File

@@ -58,9 +58,9 @@ namespace MareSynchronos.Factories
public CharacterData BuildCharacterData()
{
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized)
if (!_ipcManager.Initialized)
{
throw new ArgumentException("Player is not present or Penumbra is not connected");
throw new ArgumentException("Penumbra is not connected");
}
return CreateCharacterData();
@@ -74,6 +74,7 @@ namespace MareSynchronos.Factories
Logger.Debug("Character is null but it shouldn't be, waiting");
Thread.Sleep(50);
}
_dalamudUtil.WaitWhileCharacterIsDrawing(_dalamudUtil.PlayerPointer);
var cache = new CharacterData
{
JobId = _dalamudUtil.PlayerJobId,

View File

@@ -4,16 +4,15 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using MareSynchronos.API;
using MareSynchronos.FileCacheDB;
using MareSynchronos.Managers;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
using MareSynchronos.WebAPI.Utils;
using Penumbra.GameData.Structs;
namespace MareSynchronos.Models;
namespace MareSynchronos.Managers;
public class CachedPlayer
{
@@ -37,7 +36,6 @@ public class CachedPlayer
{
WasVisible = _isVisible;
_isVisible = value;
}
}
@@ -60,6 +58,8 @@ public class CachedPlayer
private readonly Dictionary<string, CharacterCacheDto> _cache = new();
private CharacterEquipment? _currentCharacterEquipment;
private void ApiControllerOnCharacterReceived(object? sender, CharacterReceivedEventArgs e)
{
if (string.IsNullOrEmpty(PlayerName) || e.CharacterNameHash != PlayerNameHash) return;
@@ -157,12 +157,14 @@ public class CachedPlayer
try
{
Logger.Debug("Restoring state for " + PlayerName);
_dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate;
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
_apiController.CharacterReceived -= ApiControllerOnCharacterReceived;
_downloadCancellationTokenSource?.Cancel();
_downloadCancellationTokenSource?.Dispose();
_downloadCancellationTokenSource = null;
_dalamudUtil.RemovePlayerFromWatch(PlayerName);
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
if (IsVisible)
if (PlayerCharacter != null)
{
_ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName);
_ipcManager.GlamourerApplyOnlyEquipment(_lastGlamourerData, PlayerName);
@@ -174,9 +176,6 @@ public class CachedPlayer
}
finally
{
_dalamudUtil.PlayerChanged -= WatcherOnPlayerChanged;
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
_apiController.CharacterReceived -= ApiControllerOnCharacterReceived;
PlayerName = string.Empty;
PlayerCharacter = null;
IsVisible = false;
@@ -189,11 +188,31 @@ public class CachedPlayer
PlayerName = character.Name.ToString();
PlayerCharacter = character;
Logger.Debug("Initializing Player " + this);
_dalamudUtil.AddPlayerToWatch(PlayerName!);
_dalamudUtil.PlayerChanged += WatcherOnPlayerChanged;
_dalamudUtil.FrameworkUpdate += DalamudUtilOnFrameworkUpdate;
_ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent;
_apiController.CharacterReceived += ApiControllerOnCharacterReceived;
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerName);
_currentCharacterEquipment = new CharacterEquipment(PlayerCharacter);
_lastPlayerObjectCheck = DateTime.Now;
}
private void DalamudUtilOnFrameworkUpdate()
{
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!);
if (PlayerCharacter == null)
{
DisposePlayer();
return;
}
if (!_currentCharacterEquipment!.CompareAndUpdate(PlayerCharacter))
{
OnPlayerChanged();
}
IsVisible = true;
}
public override string ToString()
@@ -205,7 +224,7 @@ public class CachedPlayer
private void IpcManagerOnPenumbraRedrawEvent(object? sender, EventArgs e)
{
var player = _dalamudUtil.GetPlayerCharacterFromObjectTableIndex((int)sender!);
var player = _dalamudUtil.GetPlayerCharacterFromObjectTableByIndex((int)sender!);
if (player == null || player.Name.ToString() != PlayerName) return;
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
@@ -228,16 +247,11 @@ public class CachedPlayer
});
}
private void WatcherOnPlayerChanged(Character actor)
private void OnPlayerChanged()
{
if (actor.Name.ToString() != PlayerName) return;
Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}");
PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!);
if (PlayerCharacter is null)
{
Logger.Debug($"Invalid PlayerCharacter for {PlayerName}");
}
else if (!RequestedPenumbraRedraw && PlayerCharacter is not null)
if (!RequestedPenumbraRedraw && PlayerCharacter is not null)
{
Logger.Debug($"Saving new Glamourer data");
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerName!);

View File

@@ -19,8 +19,8 @@ namespace MareSynchronos.Managers
private FileSystemWatcher? _cacheDirWatcher;
private FileSystemWatcher? _penumbraDirWatcher;
private Task? _rescanTask;
private CancellationTokenSource? _rescanTaskCancellationTokenSource;
private CancellationTokenSource? _rescanTaskRunCancellationTokenSource;
private CancellationTokenSource _rescanTaskCancellationTokenSource = new CancellationTokenSource();
private CancellationTokenSource _rescanTaskRunCancellationTokenSource = new CancellationTokenSource();
private CancellationTokenSource? _scanCancellationTokenSource;
private Task? _scanTask;
public FileCacheManager(IpcManager ipcManager, Configuration pluginConfiguration)
@@ -94,7 +94,6 @@ namespace MareSynchronos.Managers
_penumbraDirWatcher = new FileSystemWatcher(_ipcManager.PenumbraModDirectory()!)
{
IncludeSubdirectories = true,
InternalBufferSize = 1048576
};
_penumbraDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
_penumbraDirWatcher.Deleted += OnModified;
@@ -108,8 +107,7 @@ namespace MareSynchronos.Managers
_cacheDirWatcher = new FileSystemWatcher(_pluginConfiguration.CacheFolder)
{
IncludeSubdirectories = true,
InternalBufferSize = 1048576
IncludeSubdirectories = false,
};
_cacheDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
_cacheDirWatcher.Deleted += OnModified;
@@ -150,7 +148,7 @@ namespace MareSynchronos.Managers
private void OnModified(object sender, FileSystemEventArgs e)
{
_modifiedFiles.Add(e.FullPath);
Task.Run(() => _ = RescanTask());
_ = StartRescan();
}
private void RecalculateFileCacheSize()
@@ -169,12 +167,12 @@ namespace MareSynchronos.Managers
}
}
public async Task RescanTask(bool force = false)
public async Task StartRescan(bool force = false)
{
_rescanTaskRunCancellationTokenSource?.Cancel();
_rescanTaskRunCancellationTokenSource.Cancel();
_rescanTaskRunCancellationTokenSource = new CancellationTokenSource();
var token = _rescanTaskRunCancellationTokenSource.Token;
if(!force)
if (!force)
await Task.Delay(TimeSpan.FromSeconds(1), token);
while ((!_rescanTask?.IsCompleted ?? false) && !token.IsCancellationRequested)
{
@@ -183,11 +181,10 @@ namespace MareSynchronos.Managers
if (token.IsCancellationRequested) return;
PluginLog.Debug("File changes detected, scanning the changes");
PluginLog.Debug("File changes detected, scanning the changes ");
if (!_modifiedFiles.Any()) return;
_rescanTaskCancellationTokenSource = new CancellationTokenSource();
_rescanTask = Task.Run(async () =>
{
var listCopy = _modifiedFiles.ToList();

View File

@@ -119,14 +119,14 @@ namespace MareSynchronos.Managers
public void GlamourerApplyOnlyEquipment(string customization, string characterName)
{
if (!CheckGlamourerApi()) return;
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
Logger.Debug("Glamourer apply only equipment to " + characterName);
_glamourerApplyOnlyEquipment!.InvokeAction(customization, characterName);
}
public void GlamourerApplyOnlyCustomization(string customization, string characterName)
{
if (!CheckGlamourerApi()) return;
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
Logger.Debug("Glamourer apply only customization to " + characterName);
_glamourerApplyOnlyCustomization!.InvokeAction(customization, characterName);
}

View File

@@ -3,25 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Game;
using MareSynchronos.Models;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
namespace MareSynchronos.Managers;
public class CachedPlayersManager : IDisposable
public class OnlinePlayerManager : IDisposable
{
private readonly ApiController _apiController;
private readonly DalamudUtil _dalamudUtil;
private readonly Framework _framework;
private readonly IpcManager _ipcManager;
private readonly List<CachedPlayer> _onlineCachedPlayers = new();
private readonly List<string> _localVisiblePlayers = new();
private DateTime _lastPlayerObjectCheck = DateTime.Now;
public CachedPlayersManager(Framework framework, ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager)
public OnlinePlayerManager(Framework framework, ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager)
{
Logger.Debug("Creating " + nameof(CachedPlayersManager));
Logger.Debug("Creating " + nameof(OnlinePlayerManager));
_framework = framework;
_apiController = apiController;
@@ -74,7 +72,7 @@ public class CachedPlayersManager : IDisposable
public void Dispose()
{
Logger.Debug("Disposing " + nameof(CachedPlayersManager));
Logger.Debug("Disposing " + nameof(OnlinePlayerManager));
RestoreAllCharacters();
@@ -142,9 +140,9 @@ public class CachedPlayersManager : IDisposable
private void RemovePlayer(string characterHash)
{
var cachedPlayer = _onlineCachedPlayers.Single(p => p.PlayerNameHash == characterHash);
var cachedPlayer = _onlineCachedPlayers.First(p => p.PlayerNameHash == characterHash);
cachedPlayer.DisposePlayer();
_onlineCachedPlayers.Remove(cachedPlayer);
_onlineCachedPlayers.RemoveAll(c => c.PlayerNameHash == cachedPlayer.PlayerNameHash);
}
private void FrameworkOnUpdate(Framework framework)
@@ -153,26 +151,21 @@ public class CachedPlayersManager : IDisposable
if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return;
_localVisiblePlayers.Clear();
var playerCharacters = _dalamudUtil.GetPlayerCharacters();
foreach (var pChar in playerCharacters)
{
var pObjName = pChar.Name.ToString();
_localVisiblePlayers.Add(pObjName);
var existingCachedPlayer = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerName == pObjName);
var hashedName = Crypto.GetHash256(pChar);
var existingCachedPlayer = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName && !string.IsNullOrEmpty(p.PlayerName));
if (existingCachedPlayer != null)
{
existingCachedPlayer.IsVisible = true;
continue;
}
var hashedName = Crypto.GetHash256(pChar);
_onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName)?.InitializePlayer(pChar);
}
_onlineCachedPlayers.Where(p => !string.IsNullOrEmpty(p.PlayerName) && !_localVisiblePlayers.Contains(p.PlayerName))
.ToList().ForEach(p => p.DisposePlayer());
Task.Run(async () => await UpdatePlayersFromService(_onlineCachedPlayers
.Where(p => p.PlayerCharacter != null && p.IsVisible && !p.WasVisible)
.ToDictionary(k => k.PlayerNameHash, k => (int)k.PlayerCharacter!.ClassJob.Id)));

View File

@@ -9,31 +9,34 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.ClientState.Objects;
using Penumbra.GameData.Structs;
namespace MareSynchronos.Managers
{
public class PlayerManager : IDisposable
{
private readonly ApiController _apiController;
private readonly CachedPlayersManager _cachedPlayersManager;
private readonly OnlinePlayerManager _onlinePlayerManager;
private readonly CharacterDataFactory _characterDataFactory;
private readonly DalamudUtil _dalamudUtil;
private readonly IpcManager _ipcManager;
private string _lastSentHash = string.Empty;
private CancellationTokenSource? _playerChangedCts;
private DateTime _lastPlayerObjectCheck;
private CharacterEquipment _currentCharacterEquipment;
public PlayerManager(ApiController apiController, IpcManager ipcManager,
CharacterDataFactory characterDataFactory, CachedPlayersManager cachedPlayersManager, DalamudUtil dalamudUtil)
CharacterDataFactory characterDataFactory, OnlinePlayerManager onlinePlayerManager, DalamudUtil dalamudUtil)
{
Logger.Debug("Creating " + nameof(PlayerManager));
_apiController = apiController;
_ipcManager = ipcManager;
_characterDataFactory = characterDataFactory;
_cachedPlayersManager = cachedPlayersManager;
_onlinePlayerManager = onlinePlayerManager;
_dalamudUtil = dalamudUtil;
_dalamudUtil.AddPlayerToWatch(_dalamudUtil.PlayerName);
_apiController.Connected += ApiController_Connected;
_apiController.Disconnected += ApiController_Disconnected;
@@ -48,12 +51,25 @@ namespace MareSynchronos.Managers
{
Logger.Debug("Disposing " + nameof(PlayerManager));
_dalamudUtil.RemovePlayerFromWatch(_dalamudUtil.PlayerName);
_apiController.Connected -= ApiController_Connected;
_apiController.Disconnected -= ApiController_Disconnected;
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
_dalamudUtil.PlayerChanged -= Watcher_PlayerChanged;
_dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate;
}
private void DalamudUtilOnFrameworkUpdate()
{
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return;
if (_dalamudUtil.IsPlayerPresent && !_currentCharacterEquipment!.CompareAndUpdate(_dalamudUtil.PlayerCharacter))
{
OnPlayerChanged();
}
_lastPlayerObjectCheck = DateTime.Now;
}
private void ApiController_Connected(object? sender, EventArgs args)
@@ -64,11 +80,13 @@ namespace MareSynchronos.Managers
Task.WaitAll(apiTask);
_cachedPlayersManager.AddInitialPairs(apiTask.Result);
_onlinePlayerManager.AddInitialPairs(apiTask.Result);
_ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
_dalamudUtil.PlayerChanged += Watcher_PlayerChanged;
PlayerChanged(_dalamudUtil.PlayerName);
_dalamudUtil.FrameworkUpdate += DalamudUtilOnFrameworkUpdate;
_currentCharacterEquipment = new CharacterEquipment(_dalamudUtil.PlayerCharacter);
PlayerChanged();
}
private void ApiController_Disconnected(object? sender, EventArgs args)
@@ -76,7 +94,7 @@ namespace MareSynchronos.Managers
Logger.Debug(nameof(ApiController_Disconnected));
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
_dalamudUtil.PlayerChanged -= Watcher_PlayerChanged;
_dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate;
}
private async Task<CharacterData> CreateFullCharacterCache(CancellationToken token)
@@ -102,19 +120,32 @@ namespace MareSynchronos.Managers
private void IpcManager_PenumbraRedrawEvent(object? objectTableIndex, EventArgs e)
{
var player = _dalamudUtil.GetPlayerCharacterFromObjectTableIndex((int)objectTableIndex!);
var player = _dalamudUtil.GetPlayerCharacterFromObjectTableByIndex((int)objectTableIndex!);
if (player != null && player.Name.ToString() != _dalamudUtil.PlayerName) return;
Logger.Debug("Penumbra Redraw Event for " + _dalamudUtil.PlayerName);
PlayerChanged(_dalamudUtil.PlayerName);
PlayerChanged();
}
private void PlayerChanged(string name)
private void PlayerChanged()
{
Logger.Debug("Player changed: " + name);
Logger.Debug("Player changed: " + _dalamudUtil.PlayerName);
_playerChangedCts?.Cancel();
_playerChangedCts = new CancellationTokenSource();
var token = _playerChangedCts.Token;
// fix for redraw from anamnesis
while ((!_dalamudUtil.IsPlayerPresent || _dalamudUtil.PlayerName == "--") && !token.IsCancellationRequested)
{
Logger.Debug("Waiting Until Player is Present");
Thread.Sleep(100);
}
if (token.IsCancellationRequested)
{
Logger.Debug("Cancelled");
return;
}
if (!_ipcManager.Initialized)
{
Logger.Warn("Penumbra not active, doing nothing.");
@@ -159,22 +190,12 @@ namespace MareSynchronos.Managers
}, token);
}
private void Watcher_PlayerChanged(Dalamud.Game.ClientState.Objects.Types.Character actor)
private void OnPlayerChanged()
{
Task.Run(() =>
{
// fix for redraw from anamnesis
while (!_dalamudUtil.IsPlayerPresent)
{
Logger.Debug("Waiting Until Player is Present");
Thread.Sleep(100);
}
if (actor.Name.ToString() == _dalamudUtil.PlayerName)
{
Logger.Debug("Watcher: PlayerChanged");
PlayerChanged(actor.Name.ToString());
}
Logger.Debug("Watcher: PlayerChanged");
PlayerChanged();
});
}

View File

@@ -33,7 +33,6 @@
<ItemGroup>
<ProjectReference Include="..\..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
<ProjectReference Include="..\..\..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj" />
<ProjectReference Include="..\..\server\MareSynchronosServer\MareSynchronos.API\MareSynchronos.API.csproj" />
</ItemGroup>

View File

@@ -13,7 +13,6 @@ using MareSynchronos.WebAPI;
using Dalamud.Interface.Windowing;
using MareSynchronos.UI;
using MareSynchronos.Utils;
using Penumbra.PlayerWatch;
namespace MareSynchronos
{
@@ -32,7 +31,7 @@ namespace MareSynchronos
private readonly WindowSystem _windowSystem;
private PlayerManager? _playerManager;
private readonly DalamudUtil _dalamudUtil;
private CachedPlayersManager? _characterCacheManager;
private OnlinePlayerManager? _characterCacheManager;
private readonly DownloadUi _downloadUi;
private readonly FileDialogManager _fileDialogManager;
@@ -52,7 +51,7 @@ namespace MareSynchronos
new FileCacheContext().Dispose(); // make sure db is initialized I guess
// those can be initialized outside of game login
_dalamudUtil = new DalamudUtil(clientState, objectTable, PlayerWatchFactory.Create(framework, clientState, objectTable));
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework);
_apiController = new ApiController(_configuration, _dalamudUtil);
_ipcManager = new IpcManager(PluginInterface);
@@ -115,7 +114,6 @@ namespace MareSynchronos
_ipcManager?.Dispose();
_playerManager?.Dispose();
_characterCacheManager?.Dispose();
_dalamudUtil.Dispose();
}
@@ -170,7 +168,7 @@ namespace MareSynchronos
{
var characterCacheFactory =
new CharacterDataFactory(_dalamudUtil, _ipcManager);
_characterCacheManager = new CachedPlayersManager(_framework,
_characterCacheManager = new OnlinePlayerManager(_framework,
_apiController, _dalamudUtil, _ipcManager);
_playerManager = new PlayerManager(_apiController, _ipcManager,
characterCacheFactory, _characterCacheManager, _dalamudUtil);

View File

@@ -52,10 +52,11 @@ public class DownloadUi : Window, IDisposable
if (_apiController.CurrentUploads.Any())
{
var doneUploads = _apiController.CurrentUploads.Count(c => c.Total == c.Transferred);
var totalUploads = _apiController.CurrentUploads.Count;
var totalUploaded = _apiController.CurrentUploads.Sum(c => c.Transferred);
var totalToUpload = _apiController.CurrentUploads.Sum(c => c.Total);
var currentUploads = _apiController.CurrentUploads.ToList();
var doneUploads = currentUploads.Count(c => c.IsTransferred);
var totalUploads = currentUploads.Count;
var totalUploaded = currentUploads.Sum(c => c.Transferred);
var totalToUpload = currentUploads.Sum(c => c.Total);
UiShared.DrawOutlinedFont(drawList, "▲",
new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * 0.5)),
UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2);
@@ -69,11 +70,12 @@ public class DownloadUi : Window, IDisposable
if (_apiController.CurrentDownloads.Any())
{
var multBase = _apiController.CurrentUploads.Any() ? 0 : 2;
var doneDownloads = _apiController.CurrentDownloads.Count(c => c.Total == c.Transferred);
var totalDownloads = _apiController.CurrentDownloads.Count;
var totalDownloaded = _apiController.CurrentDownloads.Sum(c => c.Transferred);
var totalToDownload = _apiController.CurrentDownloads.Sum(c => c.Total);
var currentDownloads = _apiController.CurrentDownloads.ToList();
var multBase = currentDownloads.Any() ? 0 : 2;
var doneDownloads = currentDownloads.Count(c => c.IsTransferred);
var totalDownloads = currentDownloads.Count;
var totalDownloaded = currentDownloads.Sum(c => c.Transferred);
var totalToDownload = currentDownloads.Sum(c => c.Total);
UiShared.DrawOutlinedFont(drawList, "▼",
new Vector2(basePosition.X + 0, basePosition.Y + (int)(yDistance * multBase + (yDistance * 0.5))),
UiShared.Color(255, 255, 255, 255), UiShared.Color(0, 0, 0, 255), 2);

View File

@@ -132,7 +132,7 @@ namespace MareSynchronos.UI
ImGui.TableHeadersRow();
foreach (var forbiddenFile in _apiController.ForbiddenFiles)
foreach (var forbiddenFile in _apiController.AdminForbiddenFiles)
{
ImGui.TableNextColumn();
@@ -203,7 +203,7 @@ namespace MareSynchronos.UI
ImGui.TableHeadersRow();
foreach (var bannedUser in _apiController.BannedUsers)
foreach (var bannedUser in _apiController.AdminBannedUsers)
{
ImGui.TableNextColumn();
ImGui.Text(bannedUser.CharacterHash);
@@ -581,7 +581,7 @@ namespace MareSynchronos.UI
File.Delete(file);
}
_uiShared.ForceRescan();
//_uiShared.ForceRescan();
});
}
ImGui.TreePop();

View File

@@ -32,6 +32,7 @@ namespace MareSynchronos.UI
_fileDialogManager = fileDialogManager;
_pluginConfiguration = pluginConfiguration;
_dalamudUtil = dalamudUtil;
isDirectoryWritable = IsDirectoryWritable(_pluginConfiguration.CacheFolder);
}
public bool DrawOtherPluginState()
@@ -58,11 +59,6 @@ namespace MareSynchronos.UI
return true;
}
public void ForceRescan()
{
Task.Run(() => _ = _fileCacheManager.RescanTask(true));
}
public void DrawFileScanState()
{
ImGui.Text("File Scanner Status");
@@ -300,7 +296,9 @@ namespace MareSynchronos.UI
if (ImGui.InputText("Cache Folder##cache", ref cacheDirectory, 255))
{
_pluginConfiguration.CacheFolder = cacheDirectory;
if (!string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) && Directory.Exists(_pluginConfiguration.CacheFolder))
if (!string.IsNullOrEmpty(_pluginConfiguration.CacheFolder)
&& Directory.Exists(_pluginConfiguration.CacheFolder)
&& (isDirectoryWritable = IsDirectoryWritable(_pluginConfiguration.CacheFolder)))
{
_pluginConfiguration.Save();
_fileCacheManager.StartWatchers();
@@ -317,20 +315,26 @@ namespace MareSynchronos.UI
if (!success) return;
_pluginConfiguration.CacheFolder = path;
_pluginConfiguration.Save();
_fileCacheManager.StartWatchers();
isDirectoryWritable = IsDirectoryWritable(_pluginConfiguration.CacheFolder);
if (isDirectoryWritable)
{
_pluginConfiguration.Save();
_fileCacheManager.StartWatchers();
}
});
}
ImGui.PopFont();
if (!Directory.Exists(cacheDirectory) || !IsDirectoryWritable(cacheDirectory))
if (!Directory.Exists(cacheDirectory) || !isDirectoryWritable)
{
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
TextWrapped("The folder you selected does not exist. Please provide a valid path.");
TextWrapped("The folder you selected does not exist or cannot be written to. Please provide a valid path.");
ImGui.PopStyleColor();
}
}
private bool isDirectoryWritable = false;
public bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
{
try

View File

@@ -2,11 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.PlayerWatch;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
namespace MareSynchronos.Utils
@@ -16,30 +16,37 @@ namespace MareSynchronos.Utils
public delegate void LogIn();
public delegate void LogOut();
public delegate void FrameworkUpdate();
public class DalamudUtil : IDisposable
{
private readonly ClientState _clientState;
private readonly ObjectTable _objectTable;
private readonly IPlayerWatcher _watcher;
private readonly Framework _framework;
public event PlayerChange? PlayerChanged;
public event LogIn? LogIn;
public event LogOut? LogOut;
public event FrameworkUpdate? FrameworkUpdate;
public DalamudUtil(ClientState clientState, ObjectTable objectTable, IPlayerWatcher watcher)
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework)
{
_clientState = clientState;
_objectTable = objectTable;
_watcher = watcher;
_watcher.Enable();
_watcher.PlayerChanged += WatcherOnPlayerChanged;
_framework = framework;
_clientState.Login += ClientStateOnLogin;
_clientState.Logout += ClientStateOnLogout;
_framework.Update += FrameworkOnUpdate;
if (IsLoggedIn)
{
ClientStateOnLogin(null, EventArgs.Empty);
}
}
private void FrameworkOnUpdate(Framework framework)
{
FrameworkUpdate?.Invoke();
}
private void ClientStateOnLogout(object? sender, EventArgs e)
{
LogOut?.Invoke();
@@ -57,17 +64,6 @@ namespace MareSynchronos.Utils
PlayerChanged?.Invoke(actor);
}
public void AddPlayerToWatch(string playerName)
{
_watcher.AddPlayerToWatch(playerName);
}
public void RemovePlayerFromWatch(string playerName)
{
_watcher.RemovePlayerFromWatch(playerName);
}
public bool IsPlayerPresent => _clientState.LocalPlayer != null;
public string PlayerName => _clientState.LocalPlayer?.Name.ToString() ?? "--";
@@ -76,6 +72,8 @@ namespace MareSynchronos.Utils
public IntPtr PlayerPointer => _clientState.LocalPlayer!.Address;
public PlayerCharacter PlayerCharacter => _clientState.LocalPlayer!;
public string PlayerNameHashed => Crypto.GetHash256(PlayerName + _clientState.LocalPlayer!.HomeWorld.Id);
public Dictionary<string, PlayerCharacter> GetLocalPlayers()
@@ -105,7 +103,7 @@ namespace MareSynchronos.Utils
obj.Name.ToString() != PlayerName).Select(p => (PlayerCharacter)p).ToList();
}
public PlayerCharacter? GetPlayerCharacterFromObjectTableIndex(int index)
public PlayerCharacter? GetPlayerCharacterFromObjectTableByIndex(int index)
{
var objTableObj = _objectTable[index];
if (objTableObj!.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) return null;
@@ -132,8 +130,8 @@ namespace MareSynchronos.Utils
// ReSharper disable once LoopVariableIsNeverChangedInsideLoop
while ((obj->RenderFlags & 0b100000000000) == 0b100000000000 && (!ct?.IsCancellationRequested ?? true)) // 0b100000000000 is "still rendering" or something
{
Logger.Debug("Waiting for character to finish drawing");
Thread.Sleep(1000);
Logger.Verbose("Waiting for character to finish drawing");
Thread.Sleep(250);
}
if (ct?.IsCancellationRequested ?? false) return;
@@ -145,8 +143,9 @@ namespace MareSynchronos.Utils
public void Dispose()
{
_watcher.Disable();
_watcher.Dispose();
_clientState.Login -= ClientStateOnLogin;
_clientState.Logout -= ClientStateOnLogout;
_framework.Update -= FrameworkOnUpdate;
}
}
}

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Logging;
using LZ4;
using MareSynchronos.API;
using MareSynchronos.FileCacheDB;
@@ -49,31 +50,60 @@ namespace MareSynchronos.WebAPI
public async Task DownloadFiles(List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
{
IsDownloading = true;
Logger.Debug("Downloading files");
List<FileTransfer> fileTransferList = new List<FileTransfer>();
List<DownloadFileDto> downloadFiles = new List<DownloadFileDto>();
foreach (var file in fileReplacementDto)
{
var downloadFileDto = await _fileHub!.InvokeAsync<DownloadFileDto>("GetFileSize", file.Hash, ct);
CurrentDownloads.Add(new FileTransfer
downloadFiles.Add(await _fileHub!.InvokeAsync<DownloadFileDto>("GetFileSize", file.Hash, ct));
/*var downloadFileDto = ;
var downloadFileTransfer = new DownloadFileTransfer(downloadFileDto);
if (CurrentDownloads.Any(f => f.Hash == downloadFileTransfer.Hash))
{
Total = downloadFileDto.Size,
Hash = downloadFileDto.Hash
});
}
if (fileTransferList.All(f => f.Hash != downloadFileTransfer.Hash))
{
fileTransferList.Add(downloadFileTransfer);
}
List<string> downloadedHashes = new();
foreach (var file in fileReplacementDto.Where(f => CurrentDownloads.Single(t => f.Hash == t.Hash).Transferred > 0))
{
if (downloadedHashes.Contains(file.Hash))
{
continue;
}
if (fileTransferList.All(f => f.Hash != downloadFileTransfer.Hash))
{
fileTransferList.Add(downloadFileTransfer);
}
CurrentDownloads.Add(new DownloadFileTransfer(downloadFileDto));*/
}
downloadFiles = downloadFiles.Distinct().ToList();
foreach (var dto in downloadFiles)
{
var downloadFileTransfer = new DownloadFileTransfer(dto);
if (CurrentDownloads.All(f => f.Hash != downloadFileTransfer.Hash))
{
CurrentDownloads.Add(downloadFileTransfer);
}
fileTransferList.Add(downloadFileTransfer);
}
foreach (var file in CurrentDownloads.Where(c => c.IsForbidden))
{
if (ForbiddenTransfers.All(f => f.Hash != file.Hash))
{
ForbiddenTransfers.Add(file);
}
}
foreach (var file in fileTransferList.Where(f => f.CanBeTransferred))
{
var hash = file.Hash;
var tempFile = await DownloadFile(hash, ct);
if (ct.IsCancellationRequested)
{
File.Delete(tempFile);
CurrentDownloads.RemoveAll(d => fileReplacementDto.Any(f => f.Hash == d.Hash));
break;
}
@@ -94,7 +124,6 @@ namespace MareSynchronos.WebAPI
fi.CreationTime = RandomDayFunc().Invoke();
fi.LastAccessTime = RandomDayFunc().Invoke();
fi.LastWriteTime = RandomDayFunc().Invoke();
downloadedHashes.Add(hash);
}
var allFilesInDb = false;
@@ -102,14 +131,81 @@ namespace MareSynchronos.WebAPI
{
await using (var db = new FileCacheContext())
{
allFilesInDb = downloadedHashes.All(h => db.FileCaches.Any(f => f.Hash == h));
allFilesInDb = fileTransferList.Where(c => c.CanBeTransferred).All(h => db.FileCaches.Any(f => f.Hash == h.Hash));
}
await Task.Delay(250, ct);
}
CurrentDownloads.Clear();
IsDownloading = false;
CurrentDownloads.RemoveAll(d => d.Transferred == d.Total);
}
public async Task SendCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
{
if (!IsConnected || SecretKey == "-") return;
Logger.Debug("Sending Character data to service " + ApiUri);
CancelUpload();
_uploadCancellationTokenSource = new CancellationTokenSource();
var uploadToken = _uploadCancellationTokenSource.Token;
Logger.Verbose("New Token Created");
var filesToUpload = await _fileHub!.InvokeAsync<List<UploadFileDto>>("SendFiles", character.FileReplacements.Select(c => c.Hash).Distinct(), uploadToken);
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
{
await using var db = new FileCacheContext();
CurrentUploads.Add(new UploadFileTransfer(file)
{
Total = new FileInfo(db.FileCaches.First(f => f.Hash == file.Hash).Filepath).Length
});
}
foreach (var file in CurrentUploads.Where(c => c.IsForbidden))
{
if (ForbiddenTransfers.All(f => f.Hash != file.Hash))
{
ForbiddenTransfers.Add(file);
}
}
Logger.Verbose("Compressing and uploading files");
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred))
{
Logger.Verbose("Compressing and uploading " + file);
var data = await GetCompressedFileData(file.Hash, uploadToken);
CurrentUploads.Single(e => e.Hash == data.Item1).Total = data.Item2.Length;
_ = UploadFile(data.Item2, file.Hash, uploadToken);
if (!uploadToken.IsCancellationRequested) continue;
Logger.Warn("Cancel in filesToUpload loop detected");
CurrentUploads.Clear();
break;
}
Logger.Verbose("Upload tasks complete, waiting for server to confirm");
var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
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.Verbose("Waiting for uploads to finish");
}
CurrentUploads.Clear();
if (!uploadToken.IsCancellationRequested)
{
Logger.Verbose("=== Pushing character data ===");
await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken);
}
else
{
Logger.Warn("=== Upload operation was cancelled ===");
}
Logger.Verbose("Upload complete for " + character.Hash);
_uploadCancellationTokenSource = null;
}
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)

View File

@@ -40,70 +40,6 @@ namespace MareSynchronos.WebAPI
await CreateConnections();
}
public async Task SendCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
{
if (!IsConnected || SecretKey == "-") return;
Logger.Debug("Sending Character data to service " + ApiUri);
CancelUpload();
_uploadCancellationTokenSource = new CancellationTokenSource();
var uploadToken = _uploadCancellationTokenSource.Token;
Logger.Verbose("New Token Created");
var filesToUpload = await _fileHub!.InvokeAsync<List<UploadFileDto>>("SendFiles", character.FileReplacements.Select(c => c.Hash).Distinct(), uploadToken);
IsUploading = true;
foreach (var file in filesToUpload.Where(f => f.IsForbidden == false))
{
await using var db = new FileCacheContext();
CurrentUploads.Add(new FileTransfer()
{
Hash = file.Hash,
Total = new FileInfo(db.FileCaches.First(f => f.Hash == file.Hash).Filepath).Length
});
}
Logger.Verbose("Compressing and uploading files");
foreach (var file in filesToUpload)
{
Logger.Verbose("Compressing and uploading " + file);
var data = await GetCompressedFileData(file.Hash, uploadToken);
CurrentUploads.Single(e => e.Hash == data.Item1).Total = data.Item2.Length;
_ = UploadFile(data.Item2, file.Hash, uploadToken);
if (!uploadToken.IsCancellationRequested) continue;
Logger.Warn("Cancel in filesToUpload loop detected");
CurrentUploads.Clear();
break;
}
Logger.Verbose("Upload tasks complete, waiting for server to confirm");
var anyUploadsOpen = await _fileHub!.InvokeAsync<bool>("IsUploadFinished", uploadToken);
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.Verbose("Waiting for uploads to finish");
}
CurrentUploads.Clear();
IsUploading = false;
if (!uploadToken.IsCancellationRequested)
{
Logger.Verbose("=== Pushing character data ===");
await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken);
}
else
{
Logger.Warn("=== Upload operation was cancelled ===");
}
Logger.Verbose("Upload complete for " + character.Hash);
_uploadCancellationTokenSource = null;
}
public async Task<List<string>> GetOnlineCharacters()
{
return await _userHub!.InvokeAsync<List<string>>("GetOnlineCharacters");

View File

@@ -89,15 +89,17 @@ namespace MareSynchronos.WebAPI
public List<FileTransfer> CurrentUploads { get; } = new();
public List<BannedUserDto> BannedUsers { get; private set; } = new();
public List<FileTransfer> ForbiddenTransfers { get; } = new();
public List<ForbiddenFileDto> ForbiddenFiles { get; private set; } = new();
public List<BannedUserDto> AdminBannedUsers { get; private set; } = new();
public List<ForbiddenFileDto> AdminForbiddenFiles { get; private set; } = new();
public bool IsConnected => !string.IsNullOrEmpty(UID);
public bool IsDownloading { get; private set; }
public bool IsDownloading => CurrentDownloads.Count > 0;
public bool IsUploading { get; private set; }
public bool IsUploading => CurrentUploads.Count > 0;
public List<ClientPairDto> PairedClients { get; set; } = new();
@@ -168,8 +170,8 @@ namespace MareSynchronos.WebAPI
if (IsModerator)
{
ForbiddenFiles = await _adminHub.InvokeAsync<List<ForbiddenFileDto>>("GetForbiddenFiles", token);
BannedUsers = await _adminHub.InvokeAsync<List<BannedUserDto>>("GetBannedUsers", token);
AdminForbiddenFiles = await _adminHub.InvokeAsync<List<ForbiddenFileDto>>("GetForbiddenFiles", token);
AdminBannedUsers = await _adminHub.InvokeAsync<List<BannedUserDto>>("GetBannedUsers", token);
_adminHub.On<BannedUserDto>("UpdateOrAddBannedUser", UpdateOrAddBannedUserCallback);
_adminHub.On<BannedUserDto>("DeleteBannedUser", DeleteBannedUserCallback);
_adminHub.On<ForbiddenFileDto>("UpdateOrAddForbiddenFile", UpdateOrAddForbiddenFileCallback);

View File

@@ -54,10 +54,10 @@ namespace MareSynchronos.WebAPI
private void UpdateOrAddBannedUserCallback(BannedUserDto obj)
{
var user = BannedUsers.SingleOrDefault(b => b.CharacterHash == obj.CharacterHash);
var user = AdminBannedUsers.SingleOrDefault(b => b.CharacterHash == obj.CharacterHash);
if (user == null)
{
BannedUsers.Add(obj);
AdminBannedUsers.Add(obj);
}
else
{
@@ -67,15 +67,15 @@ namespace MareSynchronos.WebAPI
private void DeleteBannedUserCallback(BannedUserDto obj)
{
BannedUsers.RemoveAll(a => a.CharacterHash == obj.CharacterHash);
AdminBannedUsers.RemoveAll(a => a.CharacterHash == obj.CharacterHash);
}
private void UpdateOrAddForbiddenFileCallback(ForbiddenFileDto obj)
{
var user = ForbiddenFiles.SingleOrDefault(b => b.Hash == obj.Hash);
var user = AdminForbiddenFiles.SingleOrDefault(b => b.Hash == obj.Hash);
if (user == null)
{
ForbiddenFiles.Add(obj);
AdminForbiddenFiles.Add(obj);
}
else
{
@@ -85,7 +85,7 @@ namespace MareSynchronos.WebAPI
private void DeleteForbiddenFileCallback(ForbiddenFileDto obj)
{
ForbiddenFiles.RemoveAll(f => f.Hash == obj.Hash);
AdminForbiddenFiles.RemoveAll(f => f.Hash == obj.Hash);
}
}
}

View File

@@ -1,8 +1,42 @@
namespace MareSynchronos.WebAPI.Utils;
using MareSynchronos.API;
public class FileTransfer
namespace MareSynchronos.WebAPI.Utils;
public abstract class FileTransfer
{
protected readonly ITransferFileDto TransferDto;
protected FileTransfer(ITransferFileDto transferDto)
{
TransferDto = transferDto;
}
public string ForbiddenBy => TransferDto.ForbiddenBy;
public long Transferred { get; set; } = 0;
public long Total { get; set; } = 0;
public string Hash { get; set; } = string.Empty;
public abstract long Total { get; set; }
public string Hash => TransferDto.Hash;
public bool IsInTransfer => Transferred != Total && Transferred > 0;
public bool IsTransferred => Transferred == Total;
public virtual bool CanBeTransferred => !TransferDto.IsForbidden;
public bool IsForbidden => TransferDto.IsForbidden;
}
public class UploadFileTransfer : FileTransfer
{
public UploadFileTransfer(UploadFileDto dto) : base(dto) { }
public override long Total { get; set; }
}
public class DownloadFileTransfer : FileTransfer
{
private DownloadFileDto Dto => (DownloadFileDto)TransferDto;
public DownloadFileTransfer(DownloadFileDto dto) : base(dto) { }
public override long Total
{
set { }
get => Dto.Size;
}
public override bool CanBeTransferred => Dto.FileExists && !Dto.IsForbidden && Dto.Size > 0;
}