2.0.0 #92
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,3 +13,6 @@
|
|||||||
[submodule "OtterGui"]
|
[submodule "OtterGui"]
|
||||||
path = OtterGui
|
path = OtterGui
|
||||||
url = https://github.com/Ottermandias/OtterGui
|
url = https://github.com/Ottermandias/OtterGui
|
||||||
|
[submodule "ffxiv_pictomancy"]
|
||||||
|
path = ffxiv_pictomancy
|
||||||
|
url = https://github.com/sourpuh/ffxiv_pictomancy
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Penumbra.GameData", "Penumb
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtterGui", "OtterGui\OtterGui.csproj", "{C77A2833-3FE4-405B-811D-439B1FF859D9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtterGui", "OtterGui\OtterGui.csproj", "{C77A2833-3FE4-405B-811D-439B1FF859D9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pictomancy", "ffxiv_pictomancy\Pictomancy\Pictomancy.csproj", "{825F17D8-2704-24F6-DF8B-2542AC92C765}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -102,6 +104,18 @@ Global
|
|||||||
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x64.Build.0 = Release|x64
|
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x64.Build.0 = Release|x64
|
||||||
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x86.ActiveCfg = Release|x64
|
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x86.ActiveCfg = Release|x64
|
||||||
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x86.Build.0 = Release|x64
|
{C77A2833-3FE4-405B-811D-439B1FF859D9}.Release|x86.Build.0 = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|x86.ActiveCfg = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Debug|x86.Build.0 = Debug|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|x64.Build.0 = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|x86.ActiveCfg = Release|x64
|
||||||
|
{825F17D8-2704-24F6-DF8B-2542AC92C765}.Release|x86.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(descriptor.HashedContentId) &&
|
if (!string.IsNullOrEmpty(descriptor.HashedContentId) &&
|
||||||
_actorObjectService.TryGetActorByHash(descriptor.HashedContentId, out var current) &&
|
_actorObjectService.TryGetValidatedActorByHash(descriptor.HashedContentId, out var current) &&
|
||||||
current.OwnedKind == kind)
|
current.OwnedKind == kind)
|
||||||
{
|
{
|
||||||
return current.Address;
|
return current.Address;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
||||||
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||||
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
|
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
|
||||||
|
<ProjectReference Include="..\ffxiv_pictomancy\Pictomancy\Pictomancy.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using LightlessSync.FileCache;
|
using LightlessSync.FileCache;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.PairProcessing;
|
|
||||||
using LightlessSync.Services.TextureCompression;
|
using LightlessSync.Services.TextureCompression;
|
||||||
using LightlessSync.WebAPI.Files;
|
using LightlessSync.WebAPI.Files;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -15,7 +14,6 @@ public class FileDownloadManagerFactory
|
|||||||
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
||||||
private readonly FileCacheManager _fileCacheManager;
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
private readonly FileCompactor _fileCompactor;
|
private readonly FileCompactor _fileCompactor;
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
|
||||||
private readonly LightlessConfigService _configService;
|
private readonly LightlessConfigService _configService;
|
||||||
private readonly TextureDownscaleService _textureDownscaleService;
|
private readonly TextureDownscaleService _textureDownscaleService;
|
||||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||||
@@ -26,7 +24,6 @@ public class FileDownloadManagerFactory
|
|||||||
FileTransferOrchestrator fileTransferOrchestrator,
|
FileTransferOrchestrator fileTransferOrchestrator,
|
||||||
FileCacheManager fileCacheManager,
|
FileCacheManager fileCacheManager,
|
||||||
FileCompactor fileCompactor,
|
FileCompactor fileCompactor,
|
||||||
PairProcessingLimiter pairProcessingLimiter,
|
|
||||||
LightlessConfigService configService,
|
LightlessConfigService configService,
|
||||||
TextureDownscaleService textureDownscaleService,
|
TextureDownscaleService textureDownscaleService,
|
||||||
TextureMetadataHelper textureMetadataHelper)
|
TextureMetadataHelper textureMetadataHelper)
|
||||||
@@ -36,7 +33,6 @@ public class FileDownloadManagerFactory
|
|||||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_textureDownscaleService = textureDownscaleService;
|
_textureDownscaleService = textureDownscaleService;
|
||||||
_textureMetadataHelper = textureMetadataHelper;
|
_textureMetadataHelper = textureMetadataHelper;
|
||||||
@@ -50,7 +46,6 @@ public class FileDownloadManagerFactory
|
|||||||
_fileTransferOrchestrator,
|
_fileTransferOrchestrator,
|
||||||
_fileCacheManager,
|
_fileCacheManager,
|
||||||
_fileCompactor,
|
_fileCompactor,
|
||||||
_pairProcessingLimiter,
|
|
||||||
_configService,
|
_configService,
|
||||||
_textureDownscaleService,
|
_textureDownscaleService,
|
||||||
_textureMetadataHelper);
|
_textureMetadataHelper);
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
public DrawCondition CurrentDrawCondition { get; set; } = DrawCondition.None;
|
public DrawCondition CurrentDrawCondition { get; set; } = DrawCondition.None;
|
||||||
public byte Gender { get; private set; }
|
public byte Gender { get; private set; }
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
public uint EntityId { get; private set; } = uint.MaxValue;
|
||||||
public ObjectKind ObjectKind { get; }
|
public ObjectKind ObjectKind { get; }
|
||||||
public byte RaceId { get; private set; }
|
public byte RaceId { get; private set; }
|
||||||
public byte TribeId { get; private set; }
|
public byte TribeId { get; private set; }
|
||||||
@@ -142,6 +143,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
{
|
{
|
||||||
Address = IntPtr.Zero;
|
Address = IntPtr.Zero;
|
||||||
DrawObjectAddress = IntPtr.Zero;
|
DrawObjectAddress = IntPtr.Zero;
|
||||||
|
EntityId = uint.MaxValue;
|
||||||
_haltProcessing = false;
|
_haltProcessing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,13 +173,16 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
|||||||
Address = _getAddress();
|
Address = _getAddress();
|
||||||
if (Address != IntPtr.Zero)
|
if (Address != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
var drawObjAddr = (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address)->DrawObject;
|
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address;
|
||||||
|
var drawObjAddr = (IntPtr)gameObject->DrawObject;
|
||||||
DrawObjectAddress = drawObjAddr;
|
DrawObjectAddress = drawObjAddr;
|
||||||
|
EntityId = gameObject->EntityId;
|
||||||
CurrentDrawCondition = DrawCondition.None;
|
CurrentDrawCondition = DrawCondition.None;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DrawObjectAddress = IntPtr.Zero;
|
DrawObjectAddress = IntPtr.Zero;
|
||||||
|
EntityId = uint.MaxValue;
|
||||||
CurrentDrawCondition = DrawCondition.DrawObjectZero;
|
CurrentDrawCondition = DrawCondition.DrawObjectZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
LightlessSync/PlayerData/Pairs/IPairHandlerAdapter.cs
Normal file
27
LightlessSync/PlayerData/Pairs/IPairHandlerAdapter.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using LightlessSync.API.Data;
|
||||||
|
|
||||||
|
namespace LightlessSync.PlayerData.Pairs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// orchestrates the lifecycle of a paired character
|
||||||
|
/// </summary>
|
||||||
|
public interface IPairHandlerAdapter : IDisposable, IPairPerformanceSubject
|
||||||
|
{
|
||||||
|
new string Ident { get; }
|
||||||
|
bool Initialized { get; }
|
||||||
|
bool IsVisible { get; }
|
||||||
|
bool ScheduledForDeletion { get; set; }
|
||||||
|
CharacterData? LastReceivedCharacterData { get; }
|
||||||
|
long LastAppliedDataBytes { get; }
|
||||||
|
new string? PlayerName { get; }
|
||||||
|
string PlayerNameHash { get; }
|
||||||
|
uint PlayerCharacterId { get; }
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void ApplyData(CharacterData data);
|
||||||
|
void ApplyLastReceivedData(bool forced = false);
|
||||||
|
bool FetchPerformanceMetricsFromCache();
|
||||||
|
void LoadCachedCharacterData(CharacterData data);
|
||||||
|
void SetUploading(bool uploading);
|
||||||
|
void SetPaused(bool paused);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LightlessSync.PlayerData.Pairs;
|
||||||
|
|
||||||
|
public interface IPairHandlerAdapterFactory
|
||||||
|
{
|
||||||
|
IPairHandlerAdapter Create(string ident);
|
||||||
|
}
|
||||||
@@ -16,43 +16,17 @@ using LightlessSync.Services.TextureCompression;
|
|||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using LightlessSync.WebAPI.Files;
|
using LightlessSync.WebAPI.Files;
|
||||||
using LightlessSync.WebAPI.Files.Models;
|
using LightlessSync.WebAPI.Files.Models;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||||
|
using FileReplacementDataComparer = LightlessSync.PlayerData.Data.FileReplacementDataComparer;
|
||||||
|
|
||||||
namespace LightlessSync.PlayerData.Pairs;
|
namespace LightlessSync.PlayerData.Pairs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// orchestrates the lifecycle of a paired character
|
/// handles lifecycle, visibility, queued data, character data for a paired user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPairHandlerAdapter : IDisposable, IPairPerformanceSubject
|
internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPairHandlerAdapter
|
||||||
{
|
|
||||||
string Ident { get; }
|
|
||||||
bool Initialized { get; }
|
|
||||||
bool IsVisible { get; }
|
|
||||||
bool ScheduledForDeletion { get; set; }
|
|
||||||
CharacterData? LastReceivedCharacterData { get; }
|
|
||||||
long LastAppliedDataBytes { get; }
|
|
||||||
string? PlayerName { get; }
|
|
||||||
string PlayerNameHash { get; }
|
|
||||||
uint PlayerCharacterId { get; }
|
|
||||||
|
|
||||||
void Initialize();
|
|
||||||
void ApplyData(CharacterData data);
|
|
||||||
void ApplyLastReceivedData(bool forced = false);
|
|
||||||
bool FetchPerformanceMetricsFromCache();
|
|
||||||
void LoadCachedCharacterData(CharacterData data);
|
|
||||||
void SetUploading(bool uploading);
|
|
||||||
void SetPaused(bool paused);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IPairHandlerAdapterFactory
|
|
||||||
{
|
|
||||||
IPairHandlerAdapter Create(string ident);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPairHandlerAdapter, IPairPerformanceSubject
|
|
||||||
{
|
{
|
||||||
private sealed record CombatData(Guid ApplicationId, CharacterData CharacterData, bool Forced);
|
private sealed record CombatData(Guid ApplicationId, CharacterData CharacterData, bool Forced);
|
||||||
|
|
||||||
@@ -70,14 +44,14 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private readonly PairStateCache _pairStateCache;
|
private readonly PairStateCache _pairStateCache;
|
||||||
private readonly PairPerformanceMetricsCache _performanceMetricsCache;
|
private readonly PairPerformanceMetricsCache _performanceMetricsCache;
|
||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
private CancellationTokenSource? _applicationCancellationTokenSource;
|
||||||
private Guid _applicationId;
|
private Guid _applicationId;
|
||||||
private Task? _applicationTask;
|
private Task? _applicationTask;
|
||||||
private CharacterData? _cachedData = null;
|
private CharacterData? _cachedData = null;
|
||||||
private GameObjectHandler? _charaHandler;
|
private GameObjectHandler? _charaHandler;
|
||||||
private readonly Dictionary<ObjectKind, Guid?> _customizeIds = [];
|
private readonly Dictionary<ObjectKind, Guid?> _customizeIds = [];
|
||||||
private CombatData? _dataReceivedInDowntime;
|
private CombatData? _dataReceivedInDowntime;
|
||||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
private CancellationTokenSource? _downloadCancellationTokenSource;
|
||||||
private bool _forceApplyMods = false;
|
private bool _forceApplyMods = false;
|
||||||
private bool _forceFullReapply;
|
private bool _forceFullReapply;
|
||||||
private Dictionary<(string GamePath, string? Hash), string>? _lastAppliedModdedPaths;
|
private Dictionary<(string GamePath, string? Hash), string>? _lastAppliedModdedPaths;
|
||||||
@@ -86,6 +60,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private Guid _penumbraCollection;
|
private Guid _penumbraCollection;
|
||||||
private readonly object _collectionGate = new();
|
private readonly object _collectionGate = new();
|
||||||
private bool _redrawOnNextApplication = false;
|
private bool _redrawOnNextApplication = false;
|
||||||
|
private bool _explicitRedrawQueued;
|
||||||
private readonly object _initializationGate = new();
|
private readonly object _initializationGate = new();
|
||||||
private readonly object _pauseLock = new();
|
private readonly object _pauseLock = new();
|
||||||
private Task _pauseTransitionTask = Task.CompletedTask;
|
private Task _pauseTransitionTask = Task.CompletedTask;
|
||||||
@@ -183,7 +158,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = GetPrimaryUserData();
|
|
||||||
if (LastAppliedDataBytes < 0 || LastAppliedDataTris < 0
|
if (LastAppliedDataBytes < 0 || LastAppliedDataTris < 0
|
||||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||||
{
|
{
|
||||||
@@ -441,9 +415,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
||||||
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
|
public uint PlayerCharacterId => _charaHandler?.EntityId ?? uint.MaxValue;
|
||||||
? uint.MaxValue
|
|
||||||
: ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_charaHandler!.Address)->EntityId;
|
|
||||||
public string? PlayerName { get; private set; }
|
public string? PlayerName { get; private set; }
|
||||||
public string PlayerNameHash => Ident;
|
public string PlayerNameHash => Ident;
|
||||||
|
|
||||||
@@ -490,14 +462,14 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
if (shouldForce)
|
if (shouldForce)
|
||||||
{
|
{
|
||||||
_forceApplyMods = true;
|
_forceApplyMods = true;
|
||||||
_cachedData = null;
|
_forceFullReapply = true;
|
||||||
LastAppliedDataBytes = -1;
|
LastAppliedDataBytes = -1;
|
||||||
LastAppliedDataTris = -1;
|
LastAppliedDataTris = -1;
|
||||||
LastAppliedApproximateVRAMBytes = -1;
|
LastAppliedApproximateVRAMBytes = -1;
|
||||||
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sanitized = CloneAndSanitizeLastReceived(out var dataHash);
|
var sanitized = CloneAndSanitizeLastReceived(out _);
|
||||||
if (sanitized is null)
|
if (sanitized is null)
|
||||||
{
|
{
|
||||||
Logger.LogTrace("Sanitized data null for {Ident}", Ident);
|
Logger.LogTrace("Sanitized data null for {Ident}", Ident);
|
||||||
@@ -746,7 +718,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
if (characterData is null)
|
if (characterData is null)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("[BASE-{appBase}] Received null character data, skipping application for {handler}", applicationBase, GetLogIdentifier());
|
Logger.LogWarning("[BASE-{appBase}] Received null character data, skipping application for {handler}", applicationBase, GetLogIdentifier());
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +729,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: you are in combat, deferring application")));
|
"Cannot apply character data: you are in combat, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,7 +739,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: you are performing music, deferring application")));
|
"Cannot apply character data: you are performing music, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is performing", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is performing", applicationBase);
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +749,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: you are in an instance, deferring application")));
|
"Cannot apply character data: you are in an instance, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in instance", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in instance", applicationBase);
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,7 +759,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: you are in a cutscene, deferring application")));
|
"Cannot apply character data: you are in a cutscene, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in a cutscene", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in a cutscene", applicationBase);
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +769,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: you are in GPose, deferring application")));
|
"Cannot apply character data: you are in GPose, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in GPose", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in GPose", applicationBase);
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,7 +779,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
"Cannot apply character data: Penumbra or Glamourer is not available, deferring application")));
|
"Cannot apply character data: Penumbra or Glamourer is not available, deferring application")));
|
||||||
Logger.LogInformation("[BASE-{appbase}] Application of data for {player} while Penumbra/Glamourer unavailable, returning", applicationBase, GetLogIdentifier());
|
Logger.LogInformation("[BASE-{appbase}] Application of data for {player} while Penumbra/Glamourer unavailable, returning", applicationBase, GetLogIdentifier());
|
||||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -828,7 +800,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
|
|
||||||
Logger.LogDebug("[BASE-{appbase}] Applying data for {player}, forceApplyCustomization: {forced}, forceApplyMods: {forceMods}", applicationBase, GetLogIdentifier(), forceApplyCustomization, _forceApplyMods);
|
Logger.LogDebug("[BASE-{appbase}] Applying data for {player}, forceApplyCustomization: {forced}, forceApplyMods: {forceMods}", applicationBase, GetLogIdentifier(), forceApplyCustomization, _forceApplyMods);
|
||||||
Logger.LogDebug("[BASE-{appbase}] Hash for data is {newHash}, current cache hash is {oldHash}", applicationBase, characterData.DataHash.Value, _cachedData?.DataHash.Value ?? "NODATA");
|
Logger.LogDebug("[BASE-{appbase}] Hash for data is {newHash}, current cache hash is {oldHash}", applicationBase, characterData.DataHash.Value, _cachedData?.DataHash.Value ?? "NODATA");
|
||||||
@@ -850,10 +822,13 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
_forceApplyMods = false;
|
_forceApplyMods = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_explicitRedrawQueued = false;
|
||||||
|
|
||||||
if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player))
|
if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player))
|
||||||
{
|
{
|
||||||
player.Add(PlayerChanges.ForcedRedraw);
|
player.Add(PlayerChanges.ForcedRedraw);
|
||||||
_redrawOnNextApplication = false;
|
_redrawOnNextApplication = false;
|
||||||
|
_explicitRedrawQueued = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
||||||
@@ -863,7 +838,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
|
|
||||||
Logger.LogDebug("[BASE-{appbase}] Downloading and applying character for {name}", applicationBase, GetPrimaryAliasOrUidSafe());
|
Logger.LogDebug("[BASE-{appbase}] Downloading and applying character for {name}", applicationBase, GetPrimaryAliasOrUidSafe());
|
||||||
|
|
||||||
var forceFullReapply = _forceFullReapply || forceApplyCustomization
|
var forceFullReapply = _forceFullReapply
|
||||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedDataTris < 0;
|
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedDataTris < 0;
|
||||||
|
|
||||||
DownloadAndApplyCharacter(applicationBase, characterData.DeepClone(), charaDataToUpdate, forceFullReapply);
|
DownloadAndApplyCharacter(applicationBase, characterData.DeepClone(), charaDataToUpdate, forceFullReapply);
|
||||||
@@ -875,12 +850,12 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return $"{alias}:{PlayerName ?? string.Empty}:{(PlayerCharacter != nint.Zero ? "HasChar" : "NoChar")}";
|
return $"{alias}:{PlayerName ?? string.Empty}:{(PlayerCharacter != nint.Zero ? "HasChar" : "NoChar")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUploading(bool isUploading = true)
|
public void SetUploading(bool uploading)
|
||||||
{
|
{
|
||||||
Logger.LogTrace("Setting {name} uploading {uploading}", GetPrimaryAliasOrUidSafe(), isUploading);
|
Logger.LogTrace("Setting {name} uploading {uploading}", GetPrimaryAliasOrUidSafe(), uploading);
|
||||||
if (_charaHandler != null)
|
if (_charaHandler != null)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new PlayerUploadingMessage(_charaHandler, isUploading));
|
Mediator.Publish(new PlayerUploadingMessage(_charaHandler, uploading));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,7 +879,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|
||||||
SetUploading(isUploading: false);
|
SetUploading(false);
|
||||||
var name = PlayerName;
|
var name = PlayerName;
|
||||||
var user = GetPrimaryUserDataSafe();
|
var user = GetPrimaryUserDataSafe();
|
||||||
var alias = GetPrimaryAliasOrUidSafe();
|
var alias = GetPrimaryAliasOrUidSafe();
|
||||||
@@ -1046,6 +1021,11 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PlayerChanges.ForcedRedraw:
|
case PlayerChanges.ForcedRedraw:
|
||||||
|
if (!ShouldPerformForcedRedraw(changes.Key, changes.Value, charaData))
|
||||||
|
{
|
||||||
|
Logger.LogTrace("[{applicationId}] Skipping forced redraw for {handler}", applicationId, handler);
|
||||||
|
break;
|
||||||
|
}
|
||||||
await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
|
await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1061,6 +1041,45 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldPerformForcedRedraw(ObjectKind objectKind, ICollection<PlayerChanges> changeSet, CharacterData newData)
|
||||||
|
{
|
||||||
|
if (objectKind != ObjectKind.Player)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasModFiles = changeSet.Contains(PlayerChanges.ModFiles);
|
||||||
|
var hasManip = changeSet.Contains(PlayerChanges.ModManip);
|
||||||
|
var modsChanged = hasModFiles && PlayerModFilesChanged(newData, _cachedData);
|
||||||
|
var manipChanged = hasManip && !string.Equals(_cachedData?.ManipulationData, newData.ManipulationData, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
if (modsChanged)
|
||||||
|
{
|
||||||
|
_explicitRedrawQueued = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manipChanged)
|
||||||
|
{
|
||||||
|
_explicitRedrawQueued = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_explicitRedrawQueued)
|
||||||
|
{
|
||||||
|
_explicitRedrawQueued = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((hasModFiles || hasManip) && (_forceFullReapply || _needsCollectionRebuild))
|
||||||
|
{
|
||||||
|
_explicitRedrawQueued = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static Dictionary<ObjectKind, HashSet<PlayerChanges>> BuildFullChangeSet(CharacterData characterData)
|
private static Dictionary<ObjectKind, HashSet<PlayerChanges>> BuildFullChangeSet(CharacterData characterData)
|
||||||
{
|
{
|
||||||
var result = new Dictionary<ObjectKind, HashSet<PlayerChanges>>();
|
var result = new Dictionary<ObjectKind, HashSet<PlayerChanges>>();
|
||||||
@@ -1126,6 +1145,39 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool PlayerModFilesChanged(CharacterData newData, CharacterData? previousData)
|
||||||
|
{
|
||||||
|
return !FileReplacementListsEqual(
|
||||||
|
TryGetFileReplacementList(newData, ObjectKind.Player),
|
||||||
|
TryGetFileReplacementList(previousData, ObjectKind.Player));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyCollection<FileReplacementData>? TryGetFileReplacementList(CharacterData? data, ObjectKind objectKind)
|
||||||
|
{
|
||||||
|
if (data is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.FileReplacements.TryGetValue(objectKind, out var list) ? list : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool FileReplacementListsEqual(IReadOnlyCollection<FileReplacementData>? left, IReadOnlyCollection<FileReplacementData>? right)
|
||||||
|
{
|
||||||
|
if (left is null || left.Count == 0)
|
||||||
|
{
|
||||||
|
return right is null || right.Count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right is null || right.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var comparer = FileReplacementDataComparer.Instance;
|
||||||
|
return !left.Except(right, comparer).Any() && !right.Except(left, comparer).Any();
|
||||||
|
}
|
||||||
|
|
||||||
private void DownloadAndApplyCharacter(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData, bool forceFullReapply)
|
private void DownloadAndApplyCharacter(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData, bool forceFullReapply)
|
||||||
{
|
{
|
||||||
if (!updatedData.Any())
|
if (!updatedData.Any())
|
||||||
@@ -1165,7 +1217,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
|
|
||||||
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
|
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
|
||||||
var downloadToken = _downloadCancellationTokenSource.Token;
|
var downloadToken = _downloadCancellationTokenSource.Token;
|
||||||
_ = DownloadAndApplyCharacterAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, cachedModdedPaths, downloadToken).ConfigureAwait(false);
|
_ = DownloadAndApplyCharacterAsync(applicationBase, charaData, updatedData, updateModdedPaths, updateManip, cachedModdedPaths, downloadToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task? _pairDownloadTask;
|
private Task? _pairDownloadTask;
|
||||||
@@ -1173,107 +1226,114 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
private async Task DownloadAndApplyCharacterAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData,
|
private async Task DownloadAndApplyCharacterAsync(Guid applicationBase, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData,
|
||||||
bool updateModdedPaths, bool updateManip, Dictionary<(string GamePath, string? Hash), string>? cachedModdedPaths, CancellationToken downloadToken)
|
bool updateModdedPaths, bool updateManip, Dictionary<(string GamePath, string? Hash), string>? cachedModdedPaths, CancellationToken downloadToken)
|
||||||
{
|
{
|
||||||
await using var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
||||||
bool skipDownscaleForPair = ShouldSkipDownscale();
|
try
|
||||||
var user = GetPrimaryUserData();
|
|
||||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
|
||||||
|
|
||||||
if (updateModdedPaths)
|
|
||||||
{
|
{
|
||||||
if (cachedModdedPaths is not null)
|
bool skipDownscaleForPair = ShouldSkipDownscale();
|
||||||
|
var user = GetPrimaryUserData();
|
||||||
|
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
||||||
|
|
||||||
|
if (updateModdedPaths)
|
||||||
{
|
{
|
||||||
moddedPaths = new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer);
|
if (cachedModdedPaths is not null)
|
||||||
|
{
|
||||||
|
moddedPaths = new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int attempts = 0;
|
||||||
|
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
||||||
|
|
||||||
|
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (_pairDownloadTask != null && !_pairDownloadTask.IsCompleted)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Finishing prior running download task for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
||||||
|
await _pairDownloadTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
||||||
|
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, user, nameof(PairHandlerAdapter), EventSeverity.Informational,
|
||||||
|
$"Starting download for {toDownloadReplacements.Count} files")));
|
||||||
|
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, toDownloadFiles))
|
||||||
|
{
|
||||||
|
_downloadManager.ClearDownload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlerForDownload = _charaHandler;
|
||||||
|
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair).ConfigureAwait(false));
|
||||||
|
|
||||||
|
await _pairDownloadTask.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (downloadToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
||||||
|
|
||||||
|
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2), downloadToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _playerPerformanceService.CheckBothThresholds(this, charaData).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int attempts = 0;
|
moddedPaths = cachedModdedPaths is not null
|
||||||
List<FileReplacementData> toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
? new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer)
|
||||||
|
: [];
|
||||||
while (toDownloadReplacements.Count > 0 && attempts++ <= 10 && !downloadToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (_pairDownloadTask != null && !_pairDownloadTask.IsCompleted)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("[BASE-{appBase}] Finishing prior running download task for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
|
||||||
await _pairDownloadTask.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
|
||||||
|
|
||||||
Mediator.Publish(new EventMessage(new Event(PlayerName, user, nameof(PairHandlerAdapter), EventSeverity.Informational,
|
|
||||||
$"Starting download for {toDownloadReplacements.Count} files")));
|
|
||||||
var toDownloadFiles = await _downloadManager.InitiateDownloadList(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, toDownloadFiles))
|
|
||||||
{
|
|
||||||
_downloadManager.ClearDownload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var handlerForDownload = _charaHandler;
|
|
||||||
_pairDownloadTask = Task.Run(async () => await _downloadManager.DownloadFiles(handlerForDownload, toDownloadReplacements, downloadToken, skipDownscaleForPair).ConfigureAwait(false));
|
|
||||||
|
|
||||||
await _pairDownloadTask.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (downloadToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Logger.LogTrace("[BASE-{appBase}] Detected cancellation", applicationBase);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
|
||||||
|
|
||||||
if (toDownloadReplacements.TrueForAll(c => _downloadManager.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2), downloadToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await _playerPerformanceService.CheckBothThresholds(this, charaData).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var handlerForApply = _charaHandler;
|
||||||
|
if (handlerForApply is null || handlerForApply.Address == nint.Zero)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Handler not available for {player}, cached data for later application", applicationBase, GetLogIdentifier());
|
||||||
|
_cachedData = charaData;
|
||||||
|
_pairStateCache.Store(Ident, charaData);
|
||||||
|
_forceFullReapply = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appToken = _applicationCancellationTokenSource?.Token;
|
||||||
|
while ((!_applicationTask?.IsCompleted ?? false)
|
||||||
|
&& !downloadToken.IsCancellationRequested
|
||||||
|
&& (!appToken?.IsCancellationRequested ?? false))
|
||||||
|
{
|
||||||
|
Logger.LogDebug("[BASE-{appBase}] Waiting for current data application (Id: {id}) for player ({handler}) to finish", applicationBase, _applicationId, PlayerName);
|
||||||
|
await Task.Delay(250).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloadToken.IsCancellationRequested || (appToken?.IsCancellationRequested ?? false))
|
||||||
|
{
|
||||||
|
_forceFullReapply = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_applicationCancellationTokenSource = _applicationCancellationTokenSource.CancelRecreate() ?? new CancellationTokenSource();
|
||||||
|
var token = _applicationCancellationTokenSource.Token;
|
||||||
|
|
||||||
|
_applicationTask = ApplyCharacterDataAsync(applicationBase, handlerForApply, charaData, updatedData, updateModdedPaths, updateManip, moddedPaths, token);
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
moddedPaths = cachedModdedPaths is not null
|
await concurrencyLease.DisposeAsync().ConfigureAwait(false);
|
||||||
? new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer)
|
|
||||||
: [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var handlerForApply = _charaHandler;
|
|
||||||
if (handlerForApply is null || handlerForApply.Address == nint.Zero)
|
|
||||||
{
|
|
||||||
Logger.LogDebug("[BASE-{appBase}] Handler not available for {player}, cached data for later application", applicationBase, GetLogIdentifier());
|
|
||||||
_cachedData = charaData;
|
|
||||||
_pairStateCache.Store(Ident, charaData);
|
|
||||||
_forceFullReapply = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var appToken = _applicationCancellationTokenSource?.Token;
|
|
||||||
while ((!_applicationTask?.IsCompleted ?? false)
|
|
||||||
&& !downloadToken.IsCancellationRequested
|
|
||||||
&& (!appToken?.IsCancellationRequested ?? false))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("[BASE-{appBase}] Waiting for current data application (Id: {id}) for player ({handler}) to finish", applicationBase, _applicationId, PlayerName);
|
|
||||||
await Task.Delay(250).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadToken.IsCancellationRequested || (appToken?.IsCancellationRequested ?? false))
|
|
||||||
{
|
|
||||||
_forceFullReapply = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_applicationCancellationTokenSource = _applicationCancellationTokenSource.CancelRecreate() ?? new CancellationTokenSource();
|
|
||||||
var token = _applicationCancellationTokenSource.Token;
|
|
||||||
|
|
||||||
_applicationTask = ApplyCharacterDataAsync(applicationBase, handlerForApply, charaData, updatedData, updateModdedPaths, updateManip, moddedPaths, token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ApplyCharacterDataAsync(Guid applicationBase, GameObjectHandler handlerForApply, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData, bool updateModdedPaths, bool updateManip,
|
private async Task ApplyCharacterDataAsync(Guid applicationBase, GameObjectHandler handlerForApply, CharacterData charaData, Dictionary<ObjectKind, HashSet<PlayerChanges>> updatedData, bool updateModdedPaths, bool updateManip,
|
||||||
@@ -1416,6 +1476,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_forceFullReapply = true;
|
||||||
ApplyCharacterData(appData, cachedData!, forceApplyCustomization: true);
|
ApplyCharacterData(appData, cachedData!, forceApplyCustomization: true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1432,6 +1493,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_forceFullReapply = true;
|
||||||
ApplyLastReceivedData(forced: true);
|
ApplyLastReceivedData(forced: true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1468,21 +1530,37 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
_serverConfigManager.AutoPopulateNoteForUid(user.UID, name);
|
_serverConfigManager.AutoPopulateNoteForUid(user.UID, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mediator.Subscribe<HonorificReadyMessage>(this, async (_) =>
|
Mediator.Subscribe<HonorificReadyMessage>(this, _message =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_cachedData?.HonorificData)) return;
|
var honorificData = _cachedData?.HonorificData;
|
||||||
Logger.LogTrace("Reapplying Honorific data for {handler}", GetLogIdentifier());
|
if (string.IsNullOrEmpty(honorificData))
|
||||||
await _ipcManager.Honorific.SetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false);
|
return;
|
||||||
|
|
||||||
|
_ = ReapplyHonorificAsync(honorificData!);
|
||||||
});
|
});
|
||||||
|
|
||||||
Mediator.Subscribe<PetNamesReadyMessage>(this, async (_) =>
|
Mediator.Subscribe<PetNamesReadyMessage>(this, _message =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_cachedData?.PetNamesData)) return;
|
var petNamesData = _cachedData?.PetNamesData;
|
||||||
Logger.LogTrace("Reapplying Pet Names data for {handler}", GetLogIdentifier());
|
if (string.IsNullOrEmpty(petNamesData))
|
||||||
await _ipcManager.PetNames.SetPlayerData(PlayerCharacter, _cachedData.PetNamesData).ConfigureAwait(false);
|
return;
|
||||||
|
|
||||||
|
_ = ReapplyPetNamesAsync(petNamesData!);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ReapplyHonorificAsync(string honorificData)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Reapplying Honorific data for {handler}", GetLogIdentifier());
|
||||||
|
await _ipcManager.Honorific.SetTitleAsync(PlayerCharacter, honorificData).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReapplyPetNamesAsync(string petNamesData)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Reapplying Pet Names data for {handler}", GetLogIdentifier());
|
||||||
|
await _ipcManager.PetNames.SetPlayerData(PlayerCharacter, petNamesData).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken)
|
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(Ident);
|
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(Ident);
|
||||||
@@ -1572,14 +1650,11 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
{
|
{
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
var fileCache = _fileDbManager.GetFileCacheByHash(item.Hash);
|
var fileCache = _fileDbManager.GetFileCacheByHash(item.Hash);
|
||||||
if (fileCache != null)
|
if (fileCache is not null && !File.Exists(fileCache.ResolvedFilepath))
|
||||||
{
|
{
|
||||||
if (!File.Exists(fileCache.ResolvedFilepath))
|
Logger.LogTrace("[BASE-{appBase}] Cached path {Path} missing on disk for hash {Hash}, removing cache entry", applicationBase, fileCache.ResolvedFilepath, item.Hash);
|
||||||
{
|
_fileDbManager.RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
||||||
Logger.LogTrace("[BASE-{appBase}] Cached path {Path} missing on disk for hash {Hash}, removing cache entry", applicationBase, fileCache.ResolvedFilepath, item.Hash);
|
fileCache = null;
|
||||||
_fileDbManager.RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
|
||||||
fileCache = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileCache != null)
|
if (fileCache != null)
|
||||||
@@ -1701,7 +1776,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
if (penumbraCollection != Guid.Empty)
|
if (penumbraCollection != Guid.Empty)
|
||||||
{
|
{
|
||||||
await _ipcManager.Penumbra.AssignTemporaryCollectionAsync(Logger, penumbraCollection, character.ObjectIndex).ConfigureAwait(false);
|
await _ipcManager.Penumbra.AssignTemporaryCollectionAsync(Logger, penumbraCollection, character.ObjectIndex).ConfigureAwait(false);
|
||||||
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, applicationId, penumbraCollection, new Dictionary<string, string>()).ConfigureAwait(false);
|
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, applicationId, penumbraCollection, new Dictionary<string, string>(StringComparer.Ordinal)).ConfigureAwait(false);
|
||||||
await _ipcManager.Penumbra.SetManipulationDataAsync(Logger, applicationId, penumbraCollection, string.Empty).ConfigureAwait(false);
|
await _ipcManager.Penumbra.SetManipulationDataAsync(Logger, applicationId, penumbraCollection, string.Empty).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1775,83 +1850,3 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
|
||||||
{
|
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
|
||||||
private readonly LightlessMediator _mediator;
|
|
||||||
private readonly PairManager _pairManager;
|
|
||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
|
||||||
private readonly IpcManager _ipcManager;
|
|
||||||
private readonly FileDownloadManagerFactory _fileDownloadManagerFactory;
|
|
||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
|
||||||
private readonly FileCacheManager _fileCacheManager;
|
|
||||||
private readonly PlayerPerformanceService _playerPerformanceService;
|
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
|
||||||
private readonly ServerConfigurationManager _serverConfigManager;
|
|
||||||
private readonly TextureDownscaleService _textureDownscaleService;
|
|
||||||
private readonly PairStateCache _pairStateCache;
|
|
||||||
private readonly PairPerformanceMetricsCache _pairPerformanceMetricsCache;
|
|
||||||
|
|
||||||
public PairHandlerAdapterFactory(
|
|
||||||
ILoggerFactory loggerFactory,
|
|
||||||
LightlessMediator mediator,
|
|
||||||
PairManager pairManager,
|
|
||||||
GameObjectHandlerFactory gameObjectHandlerFactory,
|
|
||||||
IpcManager ipcManager,
|
|
||||||
FileDownloadManagerFactory fileDownloadManagerFactory,
|
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager,
|
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
IHostApplicationLifetime lifetime,
|
|
||||||
FileCacheManager fileCacheManager,
|
|
||||||
PlayerPerformanceService playerPerformanceService,
|
|
||||||
PairProcessingLimiter pairProcessingLimiter,
|
|
||||||
ServerConfigurationManager serverConfigManager,
|
|
||||||
TextureDownscaleService textureDownscaleService,
|
|
||||||
PairStateCache pairStateCache,
|
|
||||||
PairPerformanceMetricsCache pairPerformanceMetricsCache)
|
|
||||||
{
|
|
||||||
_loggerFactory = loggerFactory;
|
|
||||||
_mediator = mediator;
|
|
||||||
_pairManager = pairManager;
|
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
|
||||||
_ipcManager = ipcManager;
|
|
||||||
_fileDownloadManagerFactory = fileDownloadManagerFactory;
|
|
||||||
_pluginWarningNotificationManager = pluginWarningNotificationManager;
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
_lifetime = lifetime;
|
|
||||||
_fileCacheManager = fileCacheManager;
|
|
||||||
_playerPerformanceService = playerPerformanceService;
|
|
||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
|
||||||
_serverConfigManager = serverConfigManager;
|
|
||||||
_textureDownscaleService = textureDownscaleService;
|
|
||||||
_pairStateCache = pairStateCache;
|
|
||||||
_pairPerformanceMetricsCache = pairPerformanceMetricsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPairHandlerAdapter Create(string ident)
|
|
||||||
{
|
|
||||||
var downloadManager = _fileDownloadManagerFactory.Create();
|
|
||||||
var dalamudUtilService = _serviceProvider.GetRequiredService<DalamudUtilService>();
|
|
||||||
return new PairHandlerAdapter(
|
|
||||||
_loggerFactory.CreateLogger<PairHandlerAdapter>(),
|
|
||||||
_mediator,
|
|
||||||
_pairManager,
|
|
||||||
ident,
|
|
||||||
_gameObjectHandlerFactory,
|
|
||||||
_ipcManager,
|
|
||||||
downloadManager,
|
|
||||||
_pluginWarningNotificationManager,
|
|
||||||
dalamudUtilService,
|
|
||||||
_lifetime,
|
|
||||||
_fileCacheManager,
|
|
||||||
_playerPerformanceService,
|
|
||||||
_pairProcessingLimiter,
|
|
||||||
_serverConfigManager,
|
|
||||||
_textureDownscaleService,
|
|
||||||
_pairStateCache,
|
|
||||||
_pairPerformanceMetricsCache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
93
LightlessSync/PlayerData/Pairs/PairHandlerAdapterFactory.cs
Normal file
93
LightlessSync/PlayerData/Pairs/PairHandlerAdapterFactory.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using LightlessSync.FileCache;
|
||||||
|
using LightlessSync.Interop.Ipc;
|
||||||
|
using LightlessSync.PlayerData.Factories;
|
||||||
|
using LightlessSync.Services;
|
||||||
|
using LightlessSync.Services.Mediator;
|
||||||
|
using LightlessSync.Services.PairProcessing;
|
||||||
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
|
using LightlessSync.Services.TextureCompression;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LightlessSync.PlayerData.Pairs;
|
||||||
|
|
||||||
|
internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
||||||
|
{
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
private readonly LightlessMediator _mediator;
|
||||||
|
private readonly PairManager _pairManager;
|
||||||
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
|
private readonly IpcManager _ipcManager;
|
||||||
|
private readonly FileDownloadManagerFactory _fileDownloadManagerFactory;
|
||||||
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
|
private readonly PlayerPerformanceService _playerPerformanceService;
|
||||||
|
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||||
|
private readonly ServerConfigurationManager _serverConfigManager;
|
||||||
|
private readonly TextureDownscaleService _textureDownscaleService;
|
||||||
|
private readonly PairStateCache _pairStateCache;
|
||||||
|
private readonly PairPerformanceMetricsCache _pairPerformanceMetricsCache;
|
||||||
|
|
||||||
|
public PairHandlerAdapterFactory(
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
LightlessMediator mediator,
|
||||||
|
PairManager pairManager,
|
||||||
|
GameObjectHandlerFactory gameObjectHandlerFactory,
|
||||||
|
IpcManager ipcManager,
|
||||||
|
FileDownloadManagerFactory fileDownloadManagerFactory,
|
||||||
|
PluginWarningNotificationService pluginWarningNotificationManager,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
IHostApplicationLifetime lifetime,
|
||||||
|
FileCacheManager fileCacheManager,
|
||||||
|
PlayerPerformanceService playerPerformanceService,
|
||||||
|
PairProcessingLimiter pairProcessingLimiter,
|
||||||
|
ServerConfigurationManager serverConfigManager,
|
||||||
|
TextureDownscaleService textureDownscaleService,
|
||||||
|
PairStateCache pairStateCache,
|
||||||
|
PairPerformanceMetricsCache pairPerformanceMetricsCache)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_mediator = mediator;
|
||||||
|
_pairManager = pairManager;
|
||||||
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
|
_ipcManager = ipcManager;
|
||||||
|
_fileDownloadManagerFactory = fileDownloadManagerFactory;
|
||||||
|
_pluginWarningNotificationManager = pluginWarningNotificationManager;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
_lifetime = lifetime;
|
||||||
|
_fileCacheManager = fileCacheManager;
|
||||||
|
_playerPerformanceService = playerPerformanceService;
|
||||||
|
_pairProcessingLimiter = pairProcessingLimiter;
|
||||||
|
_serverConfigManager = serverConfigManager;
|
||||||
|
_textureDownscaleService = textureDownscaleService;
|
||||||
|
_pairStateCache = pairStateCache;
|
||||||
|
_pairPerformanceMetricsCache = pairPerformanceMetricsCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPairHandlerAdapter Create(string ident)
|
||||||
|
{
|
||||||
|
var downloadManager = _fileDownloadManagerFactory.Create();
|
||||||
|
var dalamudUtilService = _serviceProvider.GetRequiredService<DalamudUtilService>();
|
||||||
|
return new PairHandlerAdapter(
|
||||||
|
_loggerFactory.CreateLogger<PairHandlerAdapter>(),
|
||||||
|
_mediator,
|
||||||
|
_pairManager,
|
||||||
|
ident,
|
||||||
|
_gameObjectHandlerFactory,
|
||||||
|
_ipcManager,
|
||||||
|
downloadManager,
|
||||||
|
_pluginWarningNotificationManager,
|
||||||
|
dalamudUtilService,
|
||||||
|
_lifetime,
|
||||||
|
_fileCacheManager,
|
||||||
|
_playerPerformanceService,
|
||||||
|
_pairProcessingLimiter,
|
||||||
|
_serverConfigManager,
|
||||||
|
_textureDownscaleService,
|
||||||
|
_pairStateCache,
|
||||||
|
_pairPerformanceMetricsCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ using LightlessSync.Services.ActorTracking;
|
|||||||
using LightlessSync.Services.CharaData;
|
using LightlessSync.Services.CharaData;
|
||||||
using LightlessSync.Services.Events;
|
using LightlessSync.Services.Events;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
|
using LightlessSync.Services.Rendering;
|
||||||
using LightlessSync.Services.ServerConfiguration;
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
using LightlessSync.Services.TextureCompression;
|
using LightlessSync.Services.TextureCompression;
|
||||||
using LightlessSync.UI;
|
using LightlessSync.UI;
|
||||||
@@ -33,8 +34,6 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NReco.Logging.File;
|
using NReco.Logging.File;
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using OtterTex;
|
using OtterTex;
|
||||||
@@ -178,6 +177,10 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
sp.GetRequiredService<CharaDataManager>(),
|
sp.GetRequiredService<CharaDataManager>(),
|
||||||
sp.GetRequiredService<LightlessMediator>()));
|
sp.GetRequiredService<LightlessMediator>()));
|
||||||
|
|
||||||
|
services.AddSingleton(sp => new PictomancyService(
|
||||||
|
sp.GetRequiredService<ILogger<PictomancyService>>(),
|
||||||
|
pluginInterface));
|
||||||
|
|
||||||
// Tag (Groups) UIs
|
// Tag (Groups) UIs
|
||||||
services.AddSingleton<SelectPairForTagUi>();
|
services.AddSingleton<SelectPairForTagUi>();
|
||||||
services.AddSingleton<RenamePairTagUi>();
|
services.AddSingleton<RenamePairTagUi>();
|
||||||
@@ -260,11 +263,14 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
|
|
||||||
services.AddSingleton(sp => new LightFinderPlateHandler(
|
services.AddSingleton(sp => new LightFinderPlateHandler(
|
||||||
sp.GetRequiredService<ILogger<LightFinderPlateHandler>>(),
|
sp.GetRequiredService<ILogger<LightFinderPlateHandler>>(),
|
||||||
sp.GetRequiredService<LightlessMediator>(),
|
addonLifecycle,
|
||||||
pluginInterface,
|
gameGui,
|
||||||
sp.GetRequiredService<LightlessConfigService>(),
|
sp.GetRequiredService<LightlessConfigService>(),
|
||||||
|
sp.GetRequiredService<LightlessMediator>(),
|
||||||
objectTable,
|
objectTable,
|
||||||
gameGui));
|
sp.GetRequiredService<PairUiService>(),
|
||||||
|
pluginInterface,
|
||||||
|
sp.GetRequiredService<PictomancyService>()));
|
||||||
|
|
||||||
services.AddSingleton(sp => new LightFinderScannerService(
|
services.AddSingleton(sp => new LightFinderScannerService(
|
||||||
sp.GetRequiredService<ILogger<LightFinderScannerService>>(),
|
sp.GetRequiredService<ILogger<LightFinderScannerService>>(),
|
||||||
|
|||||||
@@ -1,27 +1,20 @@
|
|||||||
using LightlessSync;
|
using System.Collections.Concurrent;
|
||||||
using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Game;
|
|
||||||
using Dalamud.Game.ClientState;
|
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.Interop;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||||
using FFXIVClientStructs.Interop;
|
using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace LightlessSync.Services.ActorTracking;
|
namespace LightlessSync.Services.ActorTracking;
|
||||||
|
|
||||||
public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
public sealed class ActorObjectService : IHostedService, IDisposable
|
||||||
{
|
{
|
||||||
public readonly record struct ActorDescriptor(
|
public readonly record struct ActorDescriptor(
|
||||||
string Name,
|
string Name,
|
||||||
@@ -38,25 +31,13 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
private readonly IFramework _framework;
|
private readonly IFramework _framework;
|
||||||
private readonly IGameInteropProvider _interop;
|
private readonly IGameInteropProvider _interop;
|
||||||
private readonly IObjectTable _objectTable;
|
private readonly IObjectTable _objectTable;
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly LightlessMediator _mediator;
|
private readonly LightlessMediator _mediator;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<nint, ActorDescriptor> _activePlayers = new();
|
private readonly ConcurrentDictionary<nint, ActorDescriptor> _activePlayers = new();
|
||||||
private readonly ConcurrentDictionary<string, ActorDescriptor> _actorsByHash = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, ActorDescriptor> _actorsByHash = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<nint, ActorDescriptor>> _actorsByName = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, ConcurrentDictionary<nint, ActorDescriptor>> _actorsByName = new(StringComparer.Ordinal);
|
||||||
private ActorDescriptor[] _playerCharacterSnapshot = Array.Empty<ActorDescriptor>();
|
private readonly OwnedObjectTracker _ownedTracker = new();
|
||||||
private nint[] _playerAddressSnapshot = Array.Empty<nint>();
|
private ActorSnapshot _snapshot = ActorSnapshot.Empty;
|
||||||
private readonly HashSet<nint> _renderedPlayers = new();
|
|
||||||
private readonly HashSet<nint> _renderedCompanions = new();
|
|
||||||
private readonly Dictionary<nint, LightlessObjectKind> _ownedObjects = new();
|
|
||||||
private nint[] _renderedPlayerSnapshot = Array.Empty<nint>();
|
|
||||||
private nint[] _renderedCompanionSnapshot = Array.Empty<nint>();
|
|
||||||
private nint[] _ownedObjectSnapshot = Array.Empty<nint>();
|
|
||||||
private IReadOnlyDictionary<nint, LightlessObjectKind> _ownedObjectMapSnapshot = new Dictionary<nint, LightlessObjectKind>();
|
|
||||||
private nint _localPlayerAddress = nint.Zero;
|
|
||||||
private nint _localPetAddress = nint.Zero;
|
|
||||||
private nint _localMinionMountAddress = nint.Zero;
|
|
||||||
private nint _localCompanionAddress = nint.Zero;
|
|
||||||
|
|
||||||
private Hook<Character.Delegates.OnInitialize>? _onInitializeHook;
|
private Hook<Character.Delegates.OnInitialize>? _onInitializeHook;
|
||||||
private Hook<Character.Delegates.Terminate>? _onTerminateHook;
|
private Hook<Character.Delegates.Terminate>? _onTerminateHook;
|
||||||
@@ -80,16 +61,30 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
_framework = framework;
|
_framework = framework;
|
||||||
_interop = interop;
|
_interop = interop;
|
||||||
_objectTable = objectTable;
|
_objectTable = objectTable;
|
||||||
_clientState = clientState;
|
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<nint> PlayerAddresses => Volatile.Read(ref _playerAddressSnapshot);
|
private ActorSnapshot Snapshot => Volatile.Read(ref _snapshot);
|
||||||
|
|
||||||
|
public IReadOnlyList<nint> PlayerAddresses => Snapshot.PlayerAddresses;
|
||||||
|
|
||||||
public IEnumerable<ActorDescriptor> PlayerDescriptors => _activePlayers.Values;
|
public IEnumerable<ActorDescriptor> PlayerDescriptors => _activePlayers.Values;
|
||||||
public IReadOnlyList<ActorDescriptor> PlayerCharacterDescriptors => Volatile.Read(ref _playerCharacterSnapshot);
|
public IReadOnlyList<ActorDescriptor> PlayerCharacterDescriptors => Snapshot.PlayerDescriptors;
|
||||||
|
|
||||||
public bool TryGetActorByHash(string hash, out ActorDescriptor descriptor) => _actorsByHash.TryGetValue(hash, out descriptor);
|
public bool TryGetActorByHash(string hash, out ActorDescriptor descriptor) => _actorsByHash.TryGetValue(hash, out descriptor);
|
||||||
|
public bool TryGetValidatedActorByHash(string hash, out ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
descriptor = default;
|
||||||
|
if (!_actorsByHash.TryGetValue(hash, out var candidate))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ValidateDescriptorThreadSafe(candidate))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
descriptor = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetPlayerByName(string name, out ActorDescriptor descriptor)
|
public bool TryGetPlayerByName(string name, out ActorDescriptor descriptor)
|
||||||
{
|
{
|
||||||
descriptor = default;
|
descriptor = default;
|
||||||
@@ -100,6 +95,9 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
ActorDescriptor? best = null;
|
ActorDescriptor? best = null;
|
||||||
foreach (var candidate in entries.Values)
|
foreach (var candidate in entries.Values)
|
||||||
{
|
{
|
||||||
|
if (!ValidateDescriptorThreadSafe(candidate))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (best is null || IsBetterNameMatch(candidate, best.Value))
|
if (best is null || IsBetterNameMatch(candidate, best.Value))
|
||||||
{
|
{
|
||||||
best = candidate;
|
best = candidate;
|
||||||
@@ -115,23 +113,54 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public bool HooksActive => _hooksActive;
|
public bool HooksActive => _hooksActive;
|
||||||
public IReadOnlyList<nint> RenderedPlayerAddresses => Volatile.Read(ref _renderedPlayerSnapshot);
|
public IReadOnlyList<nint> RenderedPlayerAddresses => Snapshot.OwnedObjects.RenderedPlayers;
|
||||||
public IReadOnlyList<nint> RenderedCompanionAddresses => Volatile.Read(ref _renderedCompanionSnapshot);
|
public IReadOnlyList<nint> RenderedCompanionAddresses => Snapshot.OwnedObjects.RenderedCompanions;
|
||||||
public IReadOnlyList<nint> OwnedObjectAddresses => Volatile.Read(ref _ownedObjectSnapshot);
|
public IReadOnlyList<nint> OwnedObjectAddresses => Snapshot.OwnedObjects.OwnedAddresses;
|
||||||
public IReadOnlyDictionary<nint, LightlessObjectKind> OwnedObjects => Volatile.Read(ref _ownedObjectMapSnapshot);
|
public IReadOnlyDictionary<nint, LightlessObjectKind> OwnedObjects => Snapshot.OwnedObjects.Map;
|
||||||
public nint LocalPlayerAddress => Volatile.Read(ref _localPlayerAddress);
|
public nint LocalPlayerAddress => Snapshot.OwnedObjects.LocalPlayer;
|
||||||
public nint LocalPetAddress => Volatile.Read(ref _localPetAddress);
|
public nint LocalPetAddress => Snapshot.OwnedObjects.LocalPet;
|
||||||
public nint LocalMinionOrMountAddress => Volatile.Read(ref _localMinionMountAddress);
|
public nint LocalMinionOrMountAddress => Snapshot.OwnedObjects.LocalMinionOrMount;
|
||||||
public nint LocalCompanionAddress => Volatile.Read(ref _localCompanionAddress);
|
public nint LocalCompanionAddress => Snapshot.OwnedObjects.LocalCompanion;
|
||||||
|
|
||||||
|
public bool TryGetOwnedKind(nint address, out LightlessObjectKind kind)
|
||||||
|
=> OwnedObjects.TryGetValue(address, out kind);
|
||||||
|
|
||||||
|
public bool TryGetOwnedActor(LightlessObjectKind kind, out ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
descriptor = default;
|
||||||
|
if (!TryGetOwnedObject(kind, out var address))
|
||||||
|
return false;
|
||||||
|
return TryGetDescriptor(address, out descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetOwnedObjectByIndex(ushort objectIndex, out LightlessObjectKind ownedKind)
|
||||||
|
{
|
||||||
|
ownedKind = default;
|
||||||
|
var ownedSnapshot = OwnedObjects;
|
||||||
|
foreach (var (address, kind) in ownedSnapshot)
|
||||||
|
{
|
||||||
|
if (!TryGetDescriptor(address, out var descriptor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (descriptor.ObjectIndex == objectIndex)
|
||||||
|
{
|
||||||
|
ownedKind = kind;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetOwnedObject(LightlessObjectKind kind, out nint address)
|
public bool TryGetOwnedObject(LightlessObjectKind kind, out nint address)
|
||||||
{
|
{
|
||||||
|
var ownedSnapshot = Snapshot.OwnedObjects;
|
||||||
address = kind switch
|
address = kind switch
|
||||||
{
|
{
|
||||||
LightlessObjectKind.Player => Volatile.Read(ref _localPlayerAddress),
|
LightlessObjectKind.Player => ownedSnapshot.LocalPlayer,
|
||||||
LightlessObjectKind.Pet => Volatile.Read(ref _localPetAddress),
|
LightlessObjectKind.Pet => ownedSnapshot.LocalPet,
|
||||||
LightlessObjectKind.MinionOrMount => Volatile.Read(ref _localMinionMountAddress),
|
LightlessObjectKind.MinionOrMount => ownedSnapshot.LocalMinionOrMount,
|
||||||
LightlessObjectKind.Companion => Volatile.Read(ref _localCompanionAddress),
|
LightlessObjectKind.Companion => ownedSnapshot.LocalCompanion,
|
||||||
_ => nint.Zero
|
_ => nint.Zero
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,7 +187,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
|
|
||||||
public bool TryGetPlayerAddressByHash(string hash, out nint address)
|
public bool TryGetPlayerAddressByHash(string hash, out nint address)
|
||||||
{
|
{
|
||||||
if (TryGetActorByHash(hash, out var descriptor) && descriptor.Address != nint.Zero)
|
if (TryGetValidatedActorByHash(hash, out var descriptor) && descriptor.Address != nint.Zero)
|
||||||
{
|
{
|
||||||
address = descriptor.Address;
|
address = descriptor.Address;
|
||||||
return true;
|
return true;
|
||||||
@@ -168,6 +197,50 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task WaitForFullyLoadedAsync(nint address, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (address == nint.Zero)
|
||||||
|
throw new ArgumentException("Address cannot be zero.", nameof(address));
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var isLoaded = await _framework.RunOnFrameworkThread(() => IsObjectFullyLoaded(address)).ConfigureAwait(false);
|
||||||
|
if (isLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateDescriptorThreadSafe(ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (_framework.IsInFrameworkUpdateThread)
|
||||||
|
return ValidateDescriptorInternal(descriptor);
|
||||||
|
|
||||||
|
return _framework.RunOnFrameworkThread(() => ValidateDescriptorInternal(descriptor)).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateDescriptorInternal(ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor.Address == nint.Zero)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (descriptor.ObjectKind == DalamudObjectKind.Player &&
|
||||||
|
!string.IsNullOrEmpty(descriptor.HashedContentId))
|
||||||
|
{
|
||||||
|
var liveHash = DalamudUtilService.GetHashedCIDFromPlayerPointer(descriptor.Address);
|
||||||
|
if (!string.Equals(liveHash, descriptor.HashedContentId, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
UntrackGameObject(descriptor.Address);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void RefreshTrackedActors(bool force = false)
|
public void RefreshTrackedActors(bool force = false)
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
@@ -185,7 +258,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_framework.RunOnFrameworkThread(RefreshTrackedActorsInternal);
|
_ = _framework.RunOnFrameworkThread(RefreshTrackedActorsInternal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,23 +284,12 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
_activePlayers.Clear();
|
_activePlayers.Clear();
|
||||||
_actorsByHash.Clear();
|
_actorsByHash.Clear();
|
||||||
_actorsByName.Clear();
|
_actorsByName.Clear();
|
||||||
Volatile.Write(ref _playerCharacterSnapshot, Array.Empty<ActorDescriptor>());
|
_ownedTracker.Reset();
|
||||||
Volatile.Write(ref _playerAddressSnapshot, Array.Empty<nint>());
|
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
|
||||||
Volatile.Write(ref _renderedPlayerSnapshot, Array.Empty<nint>());
|
|
||||||
Volatile.Write(ref _renderedCompanionSnapshot, Array.Empty<nint>());
|
|
||||||
Volatile.Write(ref _ownedObjectSnapshot, Array.Empty<nint>());
|
|
||||||
Volatile.Write(ref _ownedObjectMapSnapshot, new Dictionary<nint, LightlessObjectKind>());
|
|
||||||
Volatile.Write(ref _localPlayerAddress, nint.Zero);
|
|
||||||
Volatile.Write(ref _localPetAddress, nint.Zero);
|
|
||||||
Volatile.Write(ref _localMinionMountAddress, nint.Zero);
|
|
||||||
Volatile.Write(ref _localCompanionAddress, nint.Zero);
|
|
||||||
_renderedPlayers.Clear();
|
|
||||||
_renderedCompanions.Clear();
|
|
||||||
_ownedObjects.Clear();
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeHooks()
|
private unsafe void InitializeHooks()
|
||||||
{
|
{
|
||||||
if (_hooksActive)
|
if (_hooksActive)
|
||||||
return;
|
return;
|
||||||
@@ -271,7 +333,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCharacterInitialized(Character* chara)
|
private unsafe void OnCharacterInitialized(Character* chara)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -285,7 +347,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)chara));
|
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)chara));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCharacterTerminated(Character* chara)
|
private unsafe void OnCharacterTerminated(Character* chara)
|
||||||
{
|
{
|
||||||
var address = (nint)chara;
|
var address = (nint)chara;
|
||||||
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
||||||
@@ -299,7 +361,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GameObject* OnCharacterDisposed(Character* chara, byte freeMemory)
|
private unsafe GameObject* OnCharacterDisposed(Character* chara, byte freeMemory)
|
||||||
{
|
{
|
||||||
var address = (nint)chara;
|
var address = (nint)chara;
|
||||||
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
||||||
@@ -314,7 +376,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TrackGameObject(GameObject* gameObject)
|
private unsafe void TrackGameObject(GameObject* gameObject)
|
||||||
{
|
{
|
||||||
if (gameObject == null)
|
if (gameObject == null)
|
||||||
return;
|
return;
|
||||||
@@ -332,14 +394,10 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
|
|
||||||
if (_activePlayers.TryGetValue(descriptor.Address, out var existing))
|
if (_activePlayers.TryGetValue(descriptor.Address, out var existing))
|
||||||
{
|
{
|
||||||
RemoveDescriptorFromIndexes(existing);
|
RemoveDescriptor(existing);
|
||||||
RemoveDescriptorFromCollections(existing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_activePlayers[descriptor.Address] = descriptor;
|
AddDescriptor(descriptor);
|
||||||
IndexDescriptor(descriptor);
|
|
||||||
AddDescriptorToCollections(descriptor);
|
|
||||||
RebuildSnapshots();
|
|
||||||
|
|
||||||
if (_logger.IsEnabled(LogLevel.Debug))
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
{
|
{
|
||||||
@@ -355,16 +413,16 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
_mediator.Publish(new ActorTrackedMessage(descriptor));
|
_mediator.Publish(new ActorTrackedMessage(descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActorDescriptor? BuildDescriptor(GameObject* gameObject, DalamudObjectKind objectKind)
|
private unsafe ActorDescriptor? BuildDescriptor(GameObject* gameObject, DalamudObjectKind objectKind)
|
||||||
{
|
{
|
||||||
if (gameObject == null)
|
if (gameObject == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var address = (nint)gameObject;
|
var address = (nint)gameObject;
|
||||||
string name = string.Empty;
|
string name = string.Empty;
|
||||||
ushort objectIndex = (ushort)gameObject->ObjectIndex;
|
ushort objectIndex = gameObject->ObjectIndex;
|
||||||
bool isInGpose = objectIndex >= 200;
|
bool isInGpose = objectIndex >= 200;
|
||||||
bool isLocal = _clientState.LocalPlayer?.Address == address;
|
bool isLocal = _objectTable.LocalPlayer?.Address == address;
|
||||||
string hashedCid = string.Empty;
|
string hashedCid = string.Empty;
|
||||||
|
|
||||||
if (_objectTable.CreateObjectReference(address) is IPlayerCharacter playerCharacter)
|
if (_objectTable.CreateObjectReference(address) is IPlayerCharacter playerCharacter)
|
||||||
@@ -372,7 +430,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
name = playerCharacter.Name.TextValue ?? string.Empty;
|
name = playerCharacter.Name.TextValue ?? string.Empty;
|
||||||
objectIndex = playerCharacter.ObjectIndex;
|
objectIndex = playerCharacter.ObjectIndex;
|
||||||
isInGpose = objectIndex >= 200;
|
isInGpose = objectIndex >= 200;
|
||||||
isLocal = playerCharacter.Address == _clientState.LocalPlayer?.Address;
|
isLocal = playerCharacter.Address == _objectTable.LocalPlayer?.Address;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -389,7 +447,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return new ActorDescriptor(name, hashedCid, address, objectIndex, isLocal, isInGpose, objectKind, ownedKind, ownerEntityId);
|
return new ActorDescriptor(name, hashedCid, address, objectIndex, isLocal, isInGpose, objectKind, ownedKind, ownerEntityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (LightlessObjectKind? OwnedKind, uint OwnerEntityId) DetermineOwnedKind(GameObject* gameObject, DalamudObjectKind objectKind, bool isLocalPlayer)
|
private unsafe (LightlessObjectKind? OwnedKind, uint OwnerEntityId) DetermineOwnedKind(GameObject* gameObject, DalamudObjectKind objectKind, bool isLocalPlayer)
|
||||||
{
|
{
|
||||||
if (gameObject == null)
|
if (gameObject == null)
|
||||||
return (null, 0);
|
return (null, 0);
|
||||||
@@ -406,7 +464,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return (LightlessObjectKind.Player, entityId);
|
return (LightlessObjectKind.Player, entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_clientState.LocalPlayer is not { } localPlayer)
|
if (_objectTable.LocalPlayer is not { } localPlayer)
|
||||||
return (null, 0);
|
return (null, 0);
|
||||||
|
|
||||||
var ownerId = gameObject->OwnerId;
|
var ownerId = gameObject->OwnerId;
|
||||||
@@ -453,9 +511,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
|
|
||||||
if (_activePlayers.TryRemove(address, out var descriptor))
|
if (_activePlayers.TryRemove(address, out var descriptor))
|
||||||
{
|
{
|
||||||
RemoveDescriptorFromIndexes(descriptor);
|
RemoveDescriptor(descriptor);
|
||||||
RemoveDescriptorFromCollections(descriptor);
|
|
||||||
RebuildSnapshots();
|
|
||||||
if (_logger.IsEnabled(LogLevel.Debug))
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Actor untracked: {Name} addr={Address:X} idx={Index} owned={OwnedKind}",
|
_logger.LogDebug("Actor untracked: {Name} addr={Address:X} idx={Index} owned={OwnedKind}",
|
||||||
@@ -469,7 +525,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshTrackedActorsInternal()
|
private unsafe void RefreshTrackedActorsInternal()
|
||||||
{
|
{
|
||||||
var addresses = EnumerateActiveCharacterAddresses();
|
var addresses = EnumerateActiveCharacterAddresses();
|
||||||
HashSet<nint> seen = new(addresses.Count);
|
HashSet<nint> seen = new(addresses.Count);
|
||||||
@@ -524,7 +580,10 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return candidate.ObjectIndex < current.ObjectIndex;
|
return candidate.ObjectIndex < current.ObjectIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCompanionInitialized(Companion* companion)
|
private bool TryGetDescriptor(nint address, out ActorDescriptor descriptor)
|
||||||
|
=> _activePlayers.TryGetValue(address, out descriptor);
|
||||||
|
|
||||||
|
private unsafe void OnCompanionInitialized(Companion* companion)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -538,7 +597,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)companion));
|
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)companion));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCompanionTerminated(Companion* companion)
|
private unsafe void OnCompanionTerminated(Companion* companion)
|
||||||
{
|
{
|
||||||
var address = (nint)companion;
|
var address = (nint)companion;
|
||||||
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
||||||
@@ -559,107 +618,46 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
_actorsByHash.TryRemove(descriptor.HashedContentId, out _);
|
_actorsByHash.TryRemove(descriptor.HashedContentId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor.ObjectKind == DalamudObjectKind.Player && !string.IsNullOrEmpty(descriptor.Name))
|
if (descriptor.ObjectKind == DalamudObjectKind.Player
|
||||||
|
&& !string.IsNullOrEmpty(descriptor.Name)
|
||||||
|
&& _actorsByName.TryGetValue(descriptor.Name, out var bucket))
|
||||||
{
|
{
|
||||||
if (_actorsByName.TryGetValue(descriptor.Name, out var bucket))
|
bucket.TryRemove(descriptor.Address, out _);
|
||||||
|
if (bucket.IsEmpty)
|
||||||
{
|
{
|
||||||
bucket.TryRemove(descriptor.Address, out _);
|
_actorsByName.TryRemove(descriptor.Name, out _);
|
||||||
if (bucket.IsEmpty)
|
|
||||||
{
|
|
||||||
_actorsByName.TryRemove(descriptor.Name, out _);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddDescriptorToCollections(ActorDescriptor descriptor)
|
private void AddDescriptor(ActorDescriptor descriptor)
|
||||||
{
|
{
|
||||||
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
_activePlayers[descriptor.Address] = descriptor;
|
||||||
{
|
IndexDescriptor(descriptor);
|
||||||
_renderedPlayers.Add(descriptor.Address);
|
_ownedTracker.OnDescriptorAdded(descriptor);
|
||||||
if (descriptor.IsLocalPlayer)
|
PublishSnapshot();
|
||||||
{
|
|
||||||
Volatile.Write(ref _localPlayerAddress, descriptor.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (descriptor.ObjectKind == DalamudObjectKind.Companion)
|
|
||||||
{
|
|
||||||
_renderedCompanions.Add(descriptor.Address);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.OwnedKind is { } ownedKind)
|
|
||||||
{
|
|
||||||
_ownedObjects[descriptor.Address] = ownedKind;
|
|
||||||
switch (ownedKind)
|
|
||||||
{
|
|
||||||
case LightlessObjectKind.Player:
|
|
||||||
Volatile.Write(ref _localPlayerAddress, descriptor.Address);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.Pet:
|
|
||||||
Volatile.Write(ref _localPetAddress, descriptor.Address);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.MinionOrMount:
|
|
||||||
Volatile.Write(ref _localMinionMountAddress, descriptor.Address);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.Companion:
|
|
||||||
Volatile.Write(ref _localCompanionAddress, descriptor.Address);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveDescriptorFromCollections(ActorDescriptor descriptor)
|
private void RemoveDescriptor(ActorDescriptor descriptor)
|
||||||
{
|
{
|
||||||
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
RemoveDescriptorFromIndexes(descriptor);
|
||||||
{
|
_ownedTracker.OnDescriptorRemoved(descriptor);
|
||||||
_renderedPlayers.Remove(descriptor.Address);
|
PublishSnapshot();
|
||||||
if (descriptor.IsLocalPlayer && Volatile.Read(ref _localPlayerAddress) == descriptor.Address)
|
|
||||||
{
|
|
||||||
Volatile.Write(ref _localPlayerAddress, nint.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (descriptor.ObjectKind == DalamudObjectKind.Companion)
|
|
||||||
{
|
|
||||||
_renderedCompanions.Remove(descriptor.Address);
|
|
||||||
if (Volatile.Read(ref _localCompanionAddress) == descriptor.Address)
|
|
||||||
{
|
|
||||||
Volatile.Write(ref _localCompanionAddress, nint.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.OwnedKind is { } ownedKind)
|
|
||||||
{
|
|
||||||
_ownedObjects.Remove(descriptor.Address);
|
|
||||||
switch (ownedKind)
|
|
||||||
{
|
|
||||||
case LightlessObjectKind.Player when Volatile.Read(ref _localPlayerAddress) == descriptor.Address:
|
|
||||||
Volatile.Write(ref _localPlayerAddress, nint.Zero);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.Pet when Volatile.Read(ref _localPetAddress) == descriptor.Address:
|
|
||||||
Volatile.Write(ref _localPetAddress, nint.Zero);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.MinionOrMount when Volatile.Read(ref _localMinionMountAddress) == descriptor.Address:
|
|
||||||
Volatile.Write(ref _localMinionMountAddress, nint.Zero);
|
|
||||||
break;
|
|
||||||
case LightlessObjectKind.Companion when Volatile.Read(ref _localCompanionAddress) == descriptor.Address:
|
|
||||||
Volatile.Write(ref _localCompanionAddress, nint.Zero);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RebuildSnapshots()
|
private void PublishSnapshot()
|
||||||
{
|
{
|
||||||
var playerDescriptors = _activePlayers.Values
|
var playerDescriptors = _activePlayers.Values
|
||||||
.Where(descriptor => descriptor.ObjectKind == DalamudObjectKind.Player)
|
.Where(descriptor => descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
var playerAddresses = new nint[playerDescriptors.Length];
|
||||||
|
for (var i = 0; i < playerDescriptors.Length; i++)
|
||||||
|
playerAddresses[i] = playerDescriptors[i].Address;
|
||||||
|
|
||||||
Volatile.Write(ref _playerCharacterSnapshot, playerDescriptors);
|
var ownedSnapshot = _ownedTracker.CreateSnapshot();
|
||||||
Volatile.Write(ref _playerAddressSnapshot, playerDescriptors.Select(d => d.Address).ToArray());
|
var nextGeneration = Snapshot.Generation + 1;
|
||||||
Volatile.Write(ref _renderedPlayerSnapshot, _renderedPlayers.ToArray());
|
var snapshot = new ActorSnapshot(playerDescriptors, playerAddresses, ownedSnapshot, nextGeneration);
|
||||||
Volatile.Write(ref _renderedCompanionSnapshot, _renderedCompanions.ToArray());
|
Volatile.Write(ref _snapshot, snapshot);
|
||||||
Volatile.Write(ref _ownedObjectSnapshot, _ownedObjects.Keys.ToArray());
|
|
||||||
Volatile.Write(ref _ownedObjectMapSnapshot, new Dictionary<nint, LightlessObjectKind>(_ownedObjects));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QueueFrameworkUpdate(Action action)
|
private void QueueFrameworkUpdate(Action action)
|
||||||
@@ -673,7 +671,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_framework.RunOnFrameworkThread(action);
|
_ = _framework.RunOnFrameworkThread(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeHooks()
|
private void DisposeHooks()
|
||||||
@@ -723,7 +721,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
or DalamudObjectKind.Companion
|
or DalamudObjectKind.Companion
|
||||||
or DalamudObjectKind.MountType;
|
or DalamudObjectKind.MountType;
|
||||||
|
|
||||||
private static List<nint> EnumerateActiveCharacterAddresses()
|
private static unsafe List<nint> EnumerateActiveCharacterAddresses()
|
||||||
{
|
{
|
||||||
var results = new List<nint>(64);
|
var results = new List<nint>(64);
|
||||||
var manager = GameObjectManager.Instance();
|
var manager = GameObjectManager.Instance();
|
||||||
@@ -751,4 +749,170 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static unsafe bool IsObjectFullyLoaded(nint address)
|
||||||
|
{
|
||||||
|
if (address == nint.Zero)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var gameObject = (GameObject*)address;
|
||||||
|
if (gameObject == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var drawObject = gameObject->DrawObject;
|
||||||
|
if (drawObject == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (gameObject->RenderFlags == 2048)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var characterBase = (CharacterBase*)drawObject;
|
||||||
|
if (characterBase == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (characterBase->HasModelInSlotLoaded != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (characterBase->HasModelFilesInSlotLoaded != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class OwnedObjectTracker
|
||||||
|
{
|
||||||
|
private readonly HashSet<nint> _renderedPlayers = new();
|
||||||
|
private readonly HashSet<nint> _renderedCompanions = new();
|
||||||
|
private readonly Dictionary<nint, LightlessObjectKind> _ownedObjects = new();
|
||||||
|
private nint _localPlayerAddress = nint.Zero;
|
||||||
|
private nint _localPetAddress = nint.Zero;
|
||||||
|
private nint _localMinionMountAddress = nint.Zero;
|
||||||
|
private nint _localCompanionAddress = nint.Zero;
|
||||||
|
|
||||||
|
public void OnDescriptorAdded(ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||||
|
{
|
||||||
|
_renderedPlayers.Add(descriptor.Address);
|
||||||
|
if (descriptor.IsLocalPlayer)
|
||||||
|
_localPlayerAddress = descriptor.Address;
|
||||||
|
}
|
||||||
|
else if (descriptor.ObjectKind == DalamudObjectKind.Companion)
|
||||||
|
{
|
||||||
|
_renderedCompanions.Add(descriptor.Address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.OwnedKind is { } ownedKind)
|
||||||
|
{
|
||||||
|
_ownedObjects[descriptor.Address] = ownedKind;
|
||||||
|
switch (ownedKind)
|
||||||
|
{
|
||||||
|
case LightlessObjectKind.Player:
|
||||||
|
_localPlayerAddress = descriptor.Address;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.Pet:
|
||||||
|
_localPetAddress = descriptor.Address;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.MinionOrMount:
|
||||||
|
_localMinionMountAddress = descriptor.Address;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.Companion:
|
||||||
|
_localCompanionAddress = descriptor.Address;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDescriptorRemoved(ActorDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||||
|
{
|
||||||
|
_renderedPlayers.Remove(descriptor.Address);
|
||||||
|
if (descriptor.IsLocalPlayer && _localPlayerAddress == descriptor.Address)
|
||||||
|
_localPlayerAddress = nint.Zero;
|
||||||
|
}
|
||||||
|
else if (descriptor.ObjectKind == DalamudObjectKind.Companion)
|
||||||
|
{
|
||||||
|
_renderedCompanions.Remove(descriptor.Address);
|
||||||
|
if (_localCompanionAddress == descriptor.Address)
|
||||||
|
_localCompanionAddress = nint.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.OwnedKind is { } ownedKind)
|
||||||
|
{
|
||||||
|
_ownedObjects.Remove(descriptor.Address);
|
||||||
|
switch (ownedKind)
|
||||||
|
{
|
||||||
|
case LightlessObjectKind.Player when _localPlayerAddress == descriptor.Address:
|
||||||
|
_localPlayerAddress = nint.Zero;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.Pet when _localPetAddress == descriptor.Address:
|
||||||
|
_localPetAddress = nint.Zero;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.MinionOrMount when _localMinionMountAddress == descriptor.Address:
|
||||||
|
_localMinionMountAddress = nint.Zero;
|
||||||
|
break;
|
||||||
|
case LightlessObjectKind.Companion when _localCompanionAddress == descriptor.Address:
|
||||||
|
_localCompanionAddress = nint.Zero;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OwnedObjectSnapshot CreateSnapshot()
|
||||||
|
=> new(
|
||||||
|
_renderedPlayers.ToArray(),
|
||||||
|
_renderedCompanions.ToArray(),
|
||||||
|
_ownedObjects.Keys.ToArray(),
|
||||||
|
new Dictionary<nint, LightlessObjectKind>(_ownedObjects),
|
||||||
|
_localPlayerAddress,
|
||||||
|
_localPetAddress,
|
||||||
|
_localMinionMountAddress,
|
||||||
|
_localCompanionAddress);
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_renderedPlayers.Clear();
|
||||||
|
_renderedCompanions.Clear();
|
||||||
|
_ownedObjects.Clear();
|
||||||
|
_localPlayerAddress = nint.Zero;
|
||||||
|
_localPetAddress = nint.Zero;
|
||||||
|
_localMinionMountAddress = nint.Zero;
|
||||||
|
_localCompanionAddress = nint.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record OwnedObjectSnapshot(
|
||||||
|
IReadOnlyList<nint> RenderedPlayers,
|
||||||
|
IReadOnlyList<nint> RenderedCompanions,
|
||||||
|
IReadOnlyList<nint> OwnedAddresses,
|
||||||
|
IReadOnlyDictionary<nint, LightlessObjectKind> Map,
|
||||||
|
nint LocalPlayer,
|
||||||
|
nint LocalPet,
|
||||||
|
nint LocalMinionOrMount,
|
||||||
|
nint LocalCompanion)
|
||||||
|
{
|
||||||
|
public static OwnedObjectSnapshot Empty { get; } = new(
|
||||||
|
Array.Empty<nint>(),
|
||||||
|
Array.Empty<nint>(),
|
||||||
|
Array.Empty<nint>(),
|
||||||
|
new Dictionary<nint, LightlessObjectKind>(),
|
||||||
|
nint.Zero,
|
||||||
|
nint.Zero,
|
||||||
|
nint.Zero,
|
||||||
|
nint.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record ActorSnapshot(
|
||||||
|
IReadOnlyList<ActorDescriptor> PlayerDescriptors,
|
||||||
|
IReadOnlyList<nint> PlayerAddresses,
|
||||||
|
OwnedObjectSnapshot OwnedObjects,
|
||||||
|
int Generation)
|
||||||
|
{
|
||||||
|
public static ActorSnapshot Empty { get; } = new(
|
||||||
|
Array.Empty<ActorDescriptor>(),
|
||||||
|
Array.Empty<nint>(),
|
||||||
|
OwnedObjectSnapshot.Empty,
|
||||||
|
0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using LightlessSync.API.Dto;
|
|
||||||
using LightlessSync.API.Dto.Chat;
|
using LightlessSync.API.Dto.Chat;
|
||||||
using LightlessSync.Services.ActorTracking;
|
using LightlessSync.Services.ActorTracking;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
@@ -21,12 +20,11 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
private const int MaxReportContextLength = 1000;
|
private const int MaxReportContextLength = 1000;
|
||||||
|
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly ChatConfigService _chatConfigService;
|
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
private readonly ActorObjectService _actorObjectService;
|
private readonly ActorObjectService _actorObjectService;
|
||||||
private readonly PairUiService _pairUiService;
|
private readonly PairUiService _pairUiService;
|
||||||
|
|
||||||
private readonly object _sync = new();
|
private readonly Lock _sync = new();
|
||||||
|
|
||||||
private readonly Dictionary<string, ChatChannelState> _channels = new(StringComparer.Ordinal);
|
private readonly Dictionary<string, ChatChannelState> _channels = new(StringComparer.Ordinal);
|
||||||
private readonly List<string> _channelOrder = new();
|
private readonly List<string> _channelOrder = new();
|
||||||
@@ -55,7 +53,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
PairUiService pairUiService)
|
PairUiService pairUiService)
|
||||||
: base(logger, mediator)
|
: base(logger, mediator)
|
||||||
{
|
{
|
||||||
_chatConfigService = chatConfigService;
|
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_actorObjectService = actorObjectService;
|
_actorObjectService = actorObjectService;
|
||||||
@@ -63,12 +60,12 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
_isLoggedIn = _dalamudUtilService.IsLoggedIn;
|
_isLoggedIn = _dalamudUtilService.IsLoggedIn;
|
||||||
_isConnected = _apiController.IsConnected;
|
_isConnected = _apiController.IsConnected;
|
||||||
_chatEnabled = _chatConfigService.Current.AutoEnableChatOnLogin;
|
_chatEnabled = chatConfigService.Current.AutoEnableChatOnLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<ChatChannelSnapshot> GetChannelsSnapshot()
|
public IReadOnlyList<ChatChannelSnapshot> GetChannelsSnapshot()
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
var snapshots = new List<ChatChannelSnapshot>(_channelOrder.Count);
|
var snapshots = new List<ChatChannelSnapshot>(_channelOrder.Count);
|
||||||
foreach (var key in _channelOrder)
|
foreach (var key in _channelOrder)
|
||||||
@@ -107,7 +104,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
return _chatEnabled;
|
return _chatEnabled;
|
||||||
}
|
}
|
||||||
@@ -118,7 +115,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
return _chatEnabled && _isConnected;
|
return _chatEnabled && _isConnected;
|
||||||
}
|
}
|
||||||
@@ -127,7 +124,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
public void SetActiveChannel(string? key)
|
public void SetActiveChannel(string? key)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_activeChannelKey = key;
|
_activeChannelKey = key;
|
||||||
if (key is not null && _channels.TryGetValue(key, out var state))
|
if (key is not null && _channels.TryGetValue(key, out var state))
|
||||||
@@ -145,7 +142,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
private async Task EnableChatAsync()
|
private async Task EnableChatAsync()
|
||||||
{
|
{
|
||||||
bool wasEnabled;
|
bool wasEnabled;
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
wasEnabled = _chatEnabled;
|
wasEnabled = _chatEnabled;
|
||||||
if (!wasEnabled)
|
if (!wasEnabled)
|
||||||
@@ -170,7 +167,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
List<ChatChannelDescriptor> groupDescriptors;
|
List<ChatChannelDescriptor> groupDescriptors;
|
||||||
ChatChannelDescriptor? zoneDescriptor;
|
ChatChannelDescriptor? zoneDescriptor;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
wasEnabled = _chatEnabled;
|
wasEnabled = _chatEnabled;
|
||||||
if (!wasEnabled)
|
if (!wasEnabled)
|
||||||
@@ -259,7 +256,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
return Task.FromResult(new ChatReportResult(false, "Please describe why you are reporting this message."));
|
return Task.FromResult(new ChatReportResult(false, "Please describe why you are reporting this message."));
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
if (!_chatEnabled)
|
if (!_chatEnabled)
|
||||||
{
|
{
|
||||||
@@ -311,8 +308,8 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
Mediator.Subscribe<DalamudLogoutMessage>(this, _ => HandleLogout());
|
Mediator.Subscribe<DalamudLogoutMessage>(this, _ => HandleLogout());
|
||||||
Mediator.Subscribe<ZoneSwitchEndMessage>(this, _ => ScheduleZonePresenceUpdate());
|
Mediator.Subscribe<ZoneSwitchEndMessage>(this, _ => ScheduleZonePresenceUpdate());
|
||||||
Mediator.Subscribe<WorldChangedMessage>(this, _ => ScheduleZonePresenceUpdate(force: true));
|
Mediator.Subscribe<WorldChangedMessage>(this, _ => ScheduleZonePresenceUpdate(force: true));
|
||||||
Mediator.Subscribe<ConnectedMessage>(this, msg => HandleConnected(msg.Connection));
|
Mediator.Subscribe<ConnectedMessage>(this, _ => HandleConnected());
|
||||||
Mediator.Subscribe<HubReconnectedMessage>(this, _ => HandleConnected(null));
|
Mediator.Subscribe<HubReconnectedMessage>(this, _ => HandleConnected());
|
||||||
Mediator.Subscribe<HubReconnectingMessage>(this, _ => HandleReconnecting());
|
Mediator.Subscribe<HubReconnectingMessage>(this, _ => HandleReconnecting());
|
||||||
Mediator.Subscribe<DisconnectedMessage>(this, _ => HandleReconnecting());
|
Mediator.Subscribe<DisconnectedMessage>(this, _ => HandleReconnecting());
|
||||||
Mediator.Subscribe<PairUiUpdatedMessage>(this, _ => RefreshGroupsFromPairManager());
|
Mediator.Subscribe<PairUiUpdatedMessage>(this, _ => RefreshGroupsFromPairManager());
|
||||||
@@ -371,11 +368,11 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleConnected(ConnectionDto? connection)
|
private void HandleConnected()
|
||||||
{
|
{
|
||||||
_isConnected = true;
|
_isConnected = true;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_selfTokens.Clear();
|
_selfTokens.Clear();
|
||||||
_pendingSelfMessages.Clear();
|
_pendingSelfMessages.Clear();
|
||||||
@@ -410,7 +407,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
_isConnected = false;
|
_isConnected = false;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_selfTokens.Clear();
|
_selfTokens.Clear();
|
||||||
_pendingSelfMessages.Clear();
|
_pendingSelfMessages.Clear();
|
||||||
@@ -475,7 +472,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
private void UpdateChannelsForDisabledState()
|
private void UpdateChannelsForDisabledState()
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
foreach (var state in _channels.Values)
|
foreach (var state in _channels.Values)
|
||||||
{
|
{
|
||||||
@@ -513,7 +510,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
string? zoneKey;
|
string? zoneKey;
|
||||||
ZoneChannelDefinition? definition = null;
|
ZoneChannelDefinition? definition = null;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_territoryToZoneKey.TryGetValue(territoryId, out zoneKey);
|
_territoryToZoneKey.TryGetValue(territoryId, out zoneKey);
|
||||||
if (zoneKey is not null)
|
if (zoneKey is not null)
|
||||||
@@ -538,7 +535,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
bool shouldForceSend;
|
bool shouldForceSend;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
var state = EnsureZoneStateLocked();
|
var state = EnsureZoneStateLocked();
|
||||||
state.DisplayName = definition.Value.DisplayName;
|
state.DisplayName = definition.Value.DisplayName;
|
||||||
@@ -566,7 +563,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
ChatChannelDescriptor? descriptor = null;
|
ChatChannelDescriptor? descriptor = null;
|
||||||
bool clearedHistory = false;
|
bool clearedHistory = false;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
descriptor = _lastZoneDescriptor;
|
descriptor = _lastZoneDescriptor;
|
||||||
_lastZoneDescriptor = null;
|
_lastZoneDescriptor = null;
|
||||||
@@ -590,9 +587,9 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
state.DisplayName = "Zone Chat";
|
state.DisplayName = "Zone Chat";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_activeChannelKey == ZoneChannelKey)
|
if (string.Equals(_activeChannelKey, ZoneChannelKey, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
_activeChannelKey = _channelOrder.FirstOrDefault(key => key != ZoneChannelKey);
|
_activeChannelKey = _channelOrder.FirstOrDefault(key => !string.Equals(key, ZoneChannelKey, StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +624,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
var infoList = infos ?? Array.Empty<ZoneChatChannelInfoDto>();
|
var infoList = infos ?? Array.Empty<ZoneChatChannelInfoDto>();
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_zoneDefinitions.Clear();
|
_zoneDefinitions.Clear();
|
||||||
_territoryToZoneKey.Clear();
|
_territoryToZoneKey.Clear();
|
||||||
@@ -657,7 +654,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
if (def.TerritoryNames.Contains(variant))
|
if (def.TerritoryNames.Contains(variant))
|
||||||
{
|
{
|
||||||
_territoryToZoneKey[(uint)kvp.Key] = def.Key;
|
_territoryToZoneKey[kvp.Key] = def.Key;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -689,7 +686,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
var descriptorsToJoin = new List<ChatChannelDescriptor>();
|
var descriptorsToJoin = new List<ChatChannelDescriptor>();
|
||||||
var descriptorsToLeave = new List<ChatChannelDescriptor>();
|
var descriptorsToLeave = new List<ChatChannelDescriptor>();
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
var remainingGroups = new HashSet<string>(_groupDefinitions.Keys, StringComparer.OrdinalIgnoreCase);
|
var remainingGroups = new HashSet<string>(_groupDefinitions.Keys, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
@@ -807,7 +804,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
List<ChatChannelDescriptor> descriptors;
|
List<ChatChannelDescriptor> descriptors;
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
descriptors = _channels.Values
|
descriptors = _channels.Values
|
||||||
.Where(state => state.Type == ChatChannelType.Group)
|
.Where(state => state.Type == ChatChannelType.Group)
|
||||||
@@ -832,7 +829,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
var presenceKey = BuildPresenceKey(descriptor);
|
var presenceKey = BuildPresenceKey(descriptor);
|
||||||
bool stateMatches;
|
bool stateMatches;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
stateMatches = !force
|
stateMatches = !force
|
||||||
&& _lastPresenceStates.TryGetValue(presenceKey, out var lastState)
|
&& _lastPresenceStates.TryGetValue(presenceKey, out var lastState)
|
||||||
@@ -846,7 +843,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
await _apiController.UpdateChatPresence(new ChatPresenceUpdateDto(descriptor, territoryId, isActive)).ConfigureAwait(false);
|
await _apiController.UpdateChatPresence(new ChatPresenceUpdateDto(descriptor, territoryId, isActive)).ConfigureAwait(false);
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
if (isActive)
|
if (isActive)
|
||||||
{
|
{
|
||||||
@@ -870,7 +867,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
var key = normalized.Type == ChatChannelType.Zone ? ZoneChannelKey : BuildChannelKey(normalized);
|
var key = normalized.Type == ChatChannelType.Zone ? ZoneChannelKey : BuildChannelKey(normalized);
|
||||||
var pending = new PendingSelfMessage(key, message);
|
var pending = new PendingSelfMessage(key, message);
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_pendingSelfMessages.Add(pending);
|
_pendingSelfMessages.Add(pending);
|
||||||
while (_pendingSelfMessages.Count > 20)
|
while (_pendingSelfMessages.Count > 20)
|
||||||
@@ -884,7 +881,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
private void RemovePendingSelfMessage(PendingSelfMessage pending)
|
private void RemovePendingSelfMessage(PendingSelfMessage pending)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
var index = _pendingSelfMessages.FindIndex(p =>
|
var index = _pendingSelfMessages.FindIndex(p =>
|
||||||
string.Equals(p.ChannelKey, pending.ChannelKey, StringComparison.Ordinal) &&
|
string.Equals(p.ChannelKey, pending.ChannelKey, StringComparison.Ordinal) &&
|
||||||
@@ -905,7 +902,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
var message = BuildMessage(dto, fromSelf);
|
var message = BuildMessage(dto, fromSelf);
|
||||||
bool publishChannelList = false;
|
bool publishChannelList = false;
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
if (!_channels.TryGetValue(key, out var state))
|
if (!_channels.TryGetValue(key, out var state))
|
||||||
{
|
{
|
||||||
@@ -960,7 +957,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
if (publishChannelList)
|
if (publishChannelList)
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
UpdateChannelOrderLocked();
|
UpdateChannelOrderLocked();
|
||||||
}
|
}
|
||||||
@@ -973,7 +970,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
if (dto.Sender.User?.UID is { } uid && string.Equals(uid, _apiController.UID, StringComparison.Ordinal))
|
if (dto.Sender.User?.UID is { } uid && string.Equals(uid, _apiController.UID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
_selfTokens[channelKey] = dto.Sender.Token;
|
_selfTokens[channelKey] = dto.Sender.Token;
|
||||||
}
|
}
|
||||||
@@ -981,7 +978,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_sync)
|
using (_sync.EnterScope())
|
||||||
{
|
{
|
||||||
if (_selfTokens.TryGetValue(channelKey, out var token) &&
|
if (_selfTokens.TryGetValue(channelKey, out var token) &&
|
||||||
string.Equals(token, dto.Sender.Token, StringComparison.Ordinal))
|
string.Equals(token, dto.Sender.Token, StringComparison.Ordinal))
|
||||||
@@ -1014,7 +1011,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
var isZone = dto.Channel.Type == ChatChannelType.Zone;
|
var isZone = dto.Channel.Type == ChatChannelType.Zone;
|
||||||
if (!string.IsNullOrEmpty(dto.Sender.HashedCid) &&
|
if (!string.IsNullOrEmpty(dto.Sender.HashedCid) &&
|
||||||
_actorObjectService.TryGetActorByHash(dto.Sender.HashedCid, out var descriptor) &&
|
_actorObjectService.TryGetValidatedActorByHash(dto.Sender.HashedCid, out var descriptor) &&
|
||||||
!string.IsNullOrWhiteSpace(descriptor.Name))
|
!string.IsNullOrWhiteSpace(descriptor.Name))
|
||||||
{
|
{
|
||||||
return descriptor.Name;
|
return descriptor.Name;
|
||||||
@@ -1065,7 +1062,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
_activeChannelKey = _channelOrder[0];
|
_activeChannelKey = _channelOrder[0];
|
||||||
}
|
}
|
||||||
else if (_activeChannelKey is not null && !_channelOrder.Contains(_activeChannelKey))
|
else if (_activeChannelKey is not null && !_channelOrder.Contains(_activeChannelKey, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
_activeChannelKey = _channelOrder.Count > 0 ? _channelOrder[0] : null;
|
_activeChannelKey = _channelOrder.Count > 0 ? _channelOrder[0] : null;
|
||||||
}
|
}
|
||||||
@@ -1108,7 +1105,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
private static bool ChannelDescriptorsMatch(ChatChannelDescriptor left, ChatChannelDescriptor right)
|
private static bool ChannelDescriptorsMatch(ChatChannelDescriptor left, ChatChannelDescriptor right)
|
||||||
=> left.Type == right.Type
|
=> left.Type == right.Type
|
||||||
&& NormalizeKey(left.CustomKey) == NormalizeKey(right.CustomKey)
|
&& string.Equals(NormalizeKey(left.CustomKey), NormalizeKey(right.CustomKey), StringComparison.Ordinal)
|
||||||
&& left.WorldId == right.WorldId;
|
&& left.WorldId == right.WorldId;
|
||||||
|
|
||||||
private ChatChannelState EnsureZoneStateLocked()
|
private ChatChannelState EnsureZoneStateLocked()
|
||||||
@@ -1180,6 +1177,3 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
private readonly record struct PendingSelfMessage(string ChannelKey, string Message);
|
private readonly record struct PendingSelfMessage(string ChannelKey, string Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName)
|
public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName)
|
||||||
{
|
{
|
||||||
if (_actorObjectService.TryGetActorByHash(characterName, out var actor))
|
if (_actorObjectService.TryGetValidatedActorByHash(characterName, out var actor))
|
||||||
return actor.Address;
|
return actor.Address;
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
@@ -639,7 +639,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
internal (string Name, nint Address) FindPlayerByNameHash(string ident)
|
internal (string Name, nint Address) FindPlayerByNameHash(string ident)
|
||||||
{
|
{
|
||||||
if (_actorObjectService.TryGetActorByHash(ident, out var descriptor))
|
if (_actorObjectService.TryGetValidatedActorByHash(ident, out var descriptor))
|
||||||
{
|
{
|
||||||
return (descriptor.Name, descriptor.Address);
|
return (descriptor.Name, descriptor.Address);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,249 +1,692 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
|
using LightlessSync.Services.Rendering;
|
||||||
using LightlessSync.UI;
|
using LightlessSync.UI;
|
||||||
using Microsoft.Extensions.Hosting;
|
using LightlessSync.UI.Services;
|
||||||
|
using LightlessSync.Utils;
|
||||||
|
using LightlessSync.UtilsEnum.Enum;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Pictomancy;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Task = System.Threading.Tasks.Task;
|
||||||
|
|
||||||
namespace LightlessSync.Services.LightFinder
|
namespace LightlessSync.Services.LightFinder;
|
||||||
|
|
||||||
|
public unsafe class LightFinderPlateHandler : IHostedService, IMediatorSubscriber
|
||||||
{
|
{
|
||||||
public class LightFinderPlateHandler : IHostedService, IMediatorSubscriber
|
private readonly ILogger<LightFinderPlateHandler> _logger;
|
||||||
|
private readonly IAddonLifecycle _addonLifecycle;
|
||||||
|
private readonly IGameGui _gameGui;
|
||||||
|
private readonly IObjectTable _objectTable;
|
||||||
|
private readonly LightlessConfigService _configService;
|
||||||
|
private readonly PairUiService _pairUiService;
|
||||||
|
private readonly LightlessMediator _mediator;
|
||||||
|
public LightlessMediator Mediator => _mediator;
|
||||||
|
|
||||||
|
private readonly IUiBuilder _uiBuilder;
|
||||||
|
private bool _mEnabled;
|
||||||
|
private bool _needsLabelRefresh;
|
||||||
|
private bool _drawSubscribed;
|
||||||
|
private AddonNamePlate* _mpNameplateAddon;
|
||||||
|
private readonly object _labelLock = new();
|
||||||
|
private readonly NameplateBuffers _buffers = new();
|
||||||
|
private int _labelRenderCount;
|
||||||
|
|
||||||
|
private const string DefaultLabelText = "LightFinder";
|
||||||
|
private const SeIconChar DefaultIcon = SeIconChar.Hyadelyn;
|
||||||
|
private static readonly string DefaultIconGlyph = SeIconCharExtensions.ToIconString(DefaultIcon);
|
||||||
|
private static readonly Vector2 DefaultPivot = new(0.5f, 1f);
|
||||||
|
|
||||||
|
private ImmutableHashSet<string> _activeBroadcastingCids = [];
|
||||||
|
|
||||||
|
public LightFinderPlateHandler(
|
||||||
|
ILogger<LightFinderPlateHandler> logger,
|
||||||
|
IAddonLifecycle addonLifecycle,
|
||||||
|
IGameGui gameGui,
|
||||||
|
LightlessConfigService configService,
|
||||||
|
LightlessMediator mediator,
|
||||||
|
IObjectTable objectTable,
|
||||||
|
PairUiService pairUiService,
|
||||||
|
IDalamudPluginInterface pluginInterface,
|
||||||
|
PictomancyService pictomancyService)
|
||||||
{
|
{
|
||||||
private readonly ILogger<LightFinderPlateHandler> _logger;
|
_logger = logger;
|
||||||
private readonly LightlessConfigService _configService;
|
_addonLifecycle = addonLifecycle;
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
_gameGui = gameGui;
|
||||||
private readonly IObjectTable _gameObjects;
|
_configService = configService;
|
||||||
private readonly IGameGui _gameGui;
|
_mediator = mediator;
|
||||||
|
_objectTable = objectTable;
|
||||||
|
_pairUiService = pairUiService;
|
||||||
|
_uiBuilder = pluginInterface.UiBuilder ?? throw new ArgumentNullException(nameof(pluginInterface));
|
||||||
|
_ = pictomancyService ?? throw new ArgumentNullException(nameof(pictomancyService));
|
||||||
|
|
||||||
private const float _defaultNameplateDistance = 15.0f;
|
}
|
||||||
private ImmutableHashSet<string> _activeBroadcastingCids = [];
|
|
||||||
private readonly Dictionary<IGameObject, Vector3> _smoothed = [];
|
|
||||||
private readonly float _defaultHeightOffset = 0f;
|
|
||||||
|
|
||||||
public LightlessMediator Mediator { get; }
|
internal void Init()
|
||||||
|
{
|
||||||
public LightFinderPlateHandler(
|
if (!_drawSubscribed)
|
||||||
ILogger<LightFinderPlateHandler> logger,
|
|
||||||
LightlessMediator mediator,
|
|
||||||
IDalamudPluginInterface dalamudPluginInterface,
|
|
||||||
LightlessConfigService configService,
|
|
||||||
IObjectTable gameObjects,
|
|
||||||
IGameGui gameGui)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_uiBuilder.Draw += OnUiBuilderDraw;
|
||||||
Mediator = mediator;
|
_drawSubscribed = true;
|
||||||
_pluginInterface = dalamudPluginInterface;
|
|
||||||
_configService = configService;
|
|
||||||
_gameObjects = gameObjects;
|
|
||||||
_gameGui = gameGui;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
EnableNameplate();
|
||||||
|
_mediator.Subscribe<PriorityFrameworkUpdateMessage>(this, OnTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Uninit()
|
||||||
|
{
|
||||||
|
DisableNameplate();
|
||||||
|
if (_drawSubscribed)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Starting LightFinderPlateHandler...");
|
_uiBuilder.Draw -= OnUiBuilderDraw;
|
||||||
|
_drawSubscribed = false;
|
||||||
_pluginInterface.UiBuilder.Draw += OnDraw;
|
|
||||||
|
|
||||||
_logger.LogInformation("LightFinderPlateHandler started.");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
ClearLabelBuffer();
|
||||||
|
_mediator.Unsubscribe<PriorityFrameworkUpdateMessage>(this);
|
||||||
|
_mpNameplateAddon = null;
|
||||||
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
internal void EnableNameplate()
|
||||||
|
{
|
||||||
|
if (!_mEnabled)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Stopping LightFinderPlateHandler...");
|
try
|
||||||
|
|
||||||
_pluginInterface.UiBuilder.Draw -= OnDraw;
|
|
||||||
|
|
||||||
_logger.LogInformation("LightFinderPlateHandler stopped.");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void OnDraw()
|
|
||||||
{
|
|
||||||
if (!_configService.Current.BroadcastEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_activeBroadcastingCids.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var drawList = ImGui.GetForegroundDrawList();
|
|
||||||
|
|
||||||
foreach (var obj in _gameObjects.PlayerObjects.OfType<IPlayerCharacter>())
|
|
||||||
{
|
{
|
||||||
//Double check to be sure, should always be true due to OfType filter above
|
_addonLifecycle.RegisterListener(AddonEvent.PostDraw, "NamePlate", NameplateDrawDetour);
|
||||||
if (obj is not IPlayerCharacter player)
|
_mEnabled = true;
|
||||||
continue;
|
}
|
||||||
|
catch (Exception e)
|
||||||
if (player.Address == IntPtr.Zero)
|
{
|
||||||
continue;
|
_logger.LogError(e, "Unknown error while trying to enable nameplate.");
|
||||||
|
DisableNameplate();
|
||||||
var hashedCID = DalamudUtilService.GetHashedCIDFromPlayerPointer(player.Address);
|
|
||||||
if (!_activeBroadcastingCids.Contains(hashedCID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
//Approximate check if nameplate should be visible (at short distances)
|
|
||||||
if (!ShouldApproximateNameplateVisible(player))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!TryGetApproxNameplateScreenPos(player, out var rawScreenPos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var rawVector3 = new Vector3(rawScreenPos.X, rawScreenPos.Y, 0f);
|
|
||||||
|
|
||||||
if (rawVector3 == Vector3.Zero)
|
|
||||||
{
|
|
||||||
_smoothed.Remove(obj);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Possible have to rework this. Currently just a simple distance check to avoid jitter.
|
|
||||||
Vector3 smoothedVector3;
|
|
||||||
|
|
||||||
if (_smoothed.TryGetValue(obj, out var lastVector3))
|
|
||||||
{
|
|
||||||
var deltaVector2 = new Vector2(rawVector3.X - lastVector3.X, rawVector3.Y - lastVector3.Y);
|
|
||||||
if (deltaVector2.Length() < 1f)
|
|
||||||
smoothedVector3 = lastVector3;
|
|
||||||
else
|
|
||||||
smoothedVector3 = rawVector3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
smoothedVector3 = rawVector3;
|
|
||||||
}
|
|
||||||
|
|
||||||
_smoothed[obj] = smoothedVector3;
|
|
||||||
|
|
||||||
var screenPos = new Vector2(smoothedVector3.X, smoothedVector3.Y);
|
|
||||||
|
|
||||||
var radiusWorld = Math.Max(player.HitboxRadius, 0.5f);
|
|
||||||
var radiusPx = radiusWorld * 8.0f;
|
|
||||||
var offsetPx = GetScreenOffset(player);
|
|
||||||
var drawPos = new Vector2(screenPos.X, screenPos.Y - offsetPx);
|
|
||||||
|
|
||||||
var fillColor = ImGui.GetColorU32(UiSharedService.Color(UIColors.Get("Lightfinder")));
|
|
||||||
var outlineColor = ImGui.GetColorU32(UiSharedService.Color(UIColors.Get("LightfinderEdge")));
|
|
||||||
|
|
||||||
drawList.AddCircleFilled(drawPos, radiusPx, fillColor);
|
|
||||||
drawList.AddCircle(drawPos, radiusPx, outlineColor, 0, 2.0f);
|
|
||||||
|
|
||||||
var label = "LightFinder";
|
|
||||||
var icon = FontAwesomeIcon.Bullseye.ToIconString();
|
|
||||||
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
var iconSize = ImGui.CalcTextSize(icon);
|
|
||||||
var iconPos = new Vector2(drawPos.X - iconSize.X / 2f, drawPos.Y - radiusPx - iconSize.Y - 2f);
|
|
||||||
drawList.AddText(iconPos, fillColor, icon);
|
|
||||||
ImGui.PopFont();
|
|
||||||
|
|
||||||
/* var scale = 1.4f;
|
|
||||||
var font = ImGui.GetFont();
|
|
||||||
var baseFontSize = ImGui.GetFontSize();
|
|
||||||
var fontSize = baseFontSize * scale;
|
|
||||||
|
|
||||||
var baseTextSize = ImGui.CalcTextSize(label);
|
|
||||||
var textSize = baseTextSize * scale;
|
|
||||||
|
|
||||||
var textPos = new Vector2(
|
|
||||||
drawPos.X - textSize.X / 2f,
|
|
||||||
drawPos.Y - radiusPx - textSize.Y - 2f
|
|
||||||
);
|
|
||||||
|
|
||||||
drawList.AddText(font, fontSize, textPos, fillColor, label); */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get screen offset based on distance to local player (to scale size appropriately)
|
internal void DisableNameplate()
|
||||||
// I need to fine tune these values still
|
{
|
||||||
private float GetScreenOffset(IPlayerCharacter player)
|
if (_mEnabled)
|
||||||
{
|
{
|
||||||
var local = _gameObjects.LocalPlayer;
|
try
|
||||||
if (local == null)
|
{
|
||||||
return 32.1f;
|
_addonLifecycle.UnregisterListener(NameplateDrawDetour);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Unknown error while unregistering nameplate listener.");
|
||||||
|
}
|
||||||
|
|
||||||
var delta = player.Position - local.Position;
|
_mEnabled = false;
|
||||||
var dist = MathF.Sqrt(delta.X * delta.X + delta.Z * delta.Z);
|
ClearNameplateCaches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const float minDist = 2.1f;
|
private void NameplateDrawDetour(AddonEvent type, AddonArgs args)
|
||||||
const float maxDist = 30.4f;
|
{
|
||||||
dist = Math.Clamp(dist, minDist, maxDist);
|
if (args.Addon.Address == nint.Zero)
|
||||||
|
{
|
||||||
var t = 1f - (dist - minDist) / (maxDist - minDist);
|
if (_logger.IsEnabled(LogLevel.Warning))
|
||||||
|
_logger.LogWarning("Nameplate draw detour received a null addon address, skipping update.");
|
||||||
const float minOffset = 24.4f;
|
return;
|
||||||
const float maxOffset = 56.4f;
|
|
||||||
return minOffset + (maxOffset - minOffset) * t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetApproxNameplateScreenPos(IPlayerCharacter player, out Vector2 screenPos)
|
var pNameplateAddon = (AddonNamePlate*)args.Addon.Address;
|
||||||
|
|
||||||
|
if (_mpNameplateAddon != pNameplateAddon)
|
||||||
{
|
{
|
||||||
screenPos = default;
|
ClearNameplateCaches();
|
||||||
|
_mpNameplateAddon = pNameplateAddon;
|
||||||
|
}
|
||||||
|
|
||||||
var worldPos = player.Position;
|
UpdateNameplateNodes();
|
||||||
|
}
|
||||||
|
|
||||||
var visualHeight = GetVisualHeight(player);
|
private void UpdateNameplateNodes()
|
||||||
|
{
|
||||||
|
var currentHandle = _gameGui.GetAddonByName("NamePlate");
|
||||||
|
if (currentHandle.Address == nint.Zero)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("NamePlate addon unavailable during update, skipping label refresh.");
|
||||||
|
ClearLabelBuffer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
worldPos.Y += (visualHeight + 1.2f) + _defaultHeightOffset;
|
var currentAddon = (AddonNamePlate*)currentHandle.Address;
|
||||||
|
if (_mpNameplateAddon == null || currentAddon == null || currentAddon != _mpNameplateAddon)
|
||||||
|
{
|
||||||
|
if (_mpNameplateAddon != null && _logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("Cached NamePlate addon pointer differs from current: waiting for new hook (cached {Cached}, current {Current}).", (IntPtr)_mpNameplateAddon, (IntPtr)currentAddon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_gameGui.WorldToScreen(worldPos, out var raw))
|
var framework = Framework.Instance();
|
||||||
return false;
|
if (framework == null)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("Framework instance unavailable during nameplate update, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
screenPos = raw;
|
var uiModule = framework->GetUIModule();
|
||||||
|
if (uiModule == null)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("UI module unavailable during nameplate update, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ui3DModule = uiModule->GetUI3DModule();
|
||||||
|
if (ui3DModule == null)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("UI3D module unavailable during nameplate update, skipping.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var vec = ui3DModule->NamePlateObjectInfoPointers;
|
||||||
|
if (vec.IsEmpty)
|
||||||
|
{
|
||||||
|
ClearLabelBuffer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibleUserIdsSnapshot = VisibleUserIds;
|
||||||
|
var safeCount = System.Math.Min(ui3DModule->NamePlateObjectInfoCount, vec.Length);
|
||||||
|
var currentConfig = _configService.Current;
|
||||||
|
var labelColor = UIColors.Get("Lightfinder");
|
||||||
|
var edgeColor = UIColors.Get("LightfinderEdge");
|
||||||
|
var scratchCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < safeCount; ++i)
|
||||||
|
{
|
||||||
|
var objectInfoPtr = vec[i];
|
||||||
|
if (objectInfoPtr == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var objectInfo = objectInfoPtr.Value;
|
||||||
|
if (objectInfo == null || objectInfo->GameObject == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var nameplateIndex = objectInfo->NamePlateIndex;
|
||||||
|
if (nameplateIndex < 0 || nameplateIndex >= AddonNamePlate.NumNamePlateObjects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var gameObject = objectInfo->GameObject;
|
||||||
|
if ((ObjectKind)gameObject->ObjectKind != ObjectKind.Player)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// CID gating
|
||||||
|
var cid = DalamudUtilService.GetHashedCIDFromPlayerPointer((nint)gameObject);
|
||||||
|
if (cid == null || !_activeBroadcastingCids.Contains(cid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var local = _objectTable.LocalPlayer;
|
||||||
|
if (!currentConfig.LightfinderLabelShowOwn && local != null &&
|
||||||
|
objectInfo->GameObject->GetGameObjectId() == local.GameObjectId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var hidePaired = !currentConfig.LightfinderLabelShowPaired;
|
||||||
|
var goId = gameObject->GetGameObjectId();
|
||||||
|
if (hidePaired && visibleUserIdsSnapshot.Contains(goId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var nameplateObject = _mpNameplateAddon->NamePlateObjectArray[nameplateIndex];
|
||||||
|
var root = nameplateObject.RootComponentNode;
|
||||||
|
var nameContainer = nameplateObject.NameContainer;
|
||||||
|
var nameText = nameplateObject.NameText;
|
||||||
|
var marker = nameplateObject.MarkerIcon;
|
||||||
|
|
||||||
|
if (root == null || root->Component == null || nameContainer == null || nameText == null)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("Nameplate {Index} missing required nodes during update, skipping.", nameplateIndex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
root->Component->UldManager.UpdateDrawNodeList();
|
||||||
|
|
||||||
|
bool isVisible =
|
||||||
|
(marker != null && marker->AtkResNode.IsVisible()) ||
|
||||||
|
(nameContainer->IsVisible() && nameText->AtkResNode.IsVisible()) ||
|
||||||
|
currentConfig.LightfinderLabelShowHidden;
|
||||||
|
|
||||||
|
if (!isVisible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var scaleMultiplier = System.Math.Clamp(currentConfig.LightfinderLabelScale, 0.5f, 2.0f);
|
||||||
|
var baseScale = currentConfig.LightfinderLabelUseIcon ? 1.0f : 0.5f;
|
||||||
|
var effectiveScale = baseScale * scaleMultiplier;
|
||||||
|
var baseFontSize = currentConfig.LightfinderLabelUseIcon ? 36f : 24f;
|
||||||
|
var targetFontSize = (int)System.Math.Round(baseFontSize * scaleMultiplier);
|
||||||
|
var labelContent = currentConfig.LightfinderLabelUseIcon
|
||||||
|
? NormalizeIconGlyph(currentConfig.LightfinderLabelIconGlyph)
|
||||||
|
: DefaultLabelText;
|
||||||
|
|
||||||
|
if (!currentConfig.LightfinderLabelUseIcon && (string.IsNullOrWhiteSpace(labelContent) || string.Equals(labelContent, "-", StringComparison.Ordinal)))
|
||||||
|
labelContent = DefaultLabelText;
|
||||||
|
|
||||||
|
var nodeWidth = (int)System.Math.Round(AtkNodeHelpers.DefaultTextNodeWidth * effectiveScale);
|
||||||
|
var nodeHeight = (int)System.Math.Round(AtkNodeHelpers.DefaultTextNodeHeight * effectiveScale);
|
||||||
|
AlignmentType alignment;
|
||||||
|
|
||||||
|
var textScaleY = nameText->AtkResNode.ScaleY;
|
||||||
|
if (textScaleY <= 0f)
|
||||||
|
textScaleY = 1f;
|
||||||
|
|
||||||
|
var blockHeight = ResolveCache(
|
||||||
|
_buffers.TextHeights,
|
||||||
|
nameplateIndex,
|
||||||
|
System.Math.Abs((int)nameplateObject.TextH),
|
||||||
|
() => GetScaledTextHeight(nameText),
|
||||||
|
nodeHeight);
|
||||||
|
|
||||||
|
var containerHeight = ResolveCache(
|
||||||
|
_buffers.ContainerHeights,
|
||||||
|
nameplateIndex,
|
||||||
|
(int)nameContainer->Height,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var computed = blockHeight + (int)System.Math.Round(8 * textScaleY);
|
||||||
|
return computed <= blockHeight ? blockHeight + 1 : computed;
|
||||||
|
},
|
||||||
|
blockHeight + 1);
|
||||||
|
|
||||||
|
var blockTop = containerHeight - blockHeight;
|
||||||
|
if (blockTop < 0)
|
||||||
|
blockTop = 0;
|
||||||
|
var verticalPadding = (int)System.Math.Round(4 * effectiveScale);
|
||||||
|
|
||||||
|
var positionY = blockTop - verticalPadding;
|
||||||
|
|
||||||
|
var rawTextWidth = (int)nameplateObject.TextW;
|
||||||
|
var textWidth = ResolveCache(
|
||||||
|
_buffers.TextWidths,
|
||||||
|
nameplateIndex,
|
||||||
|
System.Math.Abs(rawTextWidth),
|
||||||
|
() => GetScaledTextWidth(nameText),
|
||||||
|
nodeWidth);
|
||||||
|
|
||||||
|
var textOffset = (int)System.Math.Round(nameText->AtkResNode.X);
|
||||||
|
var hasValidOffset = TryCacheTextOffset(nameplateIndex, rawTextWidth, textOffset);
|
||||||
|
|
||||||
|
if (nameContainer == null)
|
||||||
|
{
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("Nameplate {Index} container became unavailable during update, skipping.", nameplateIndex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float finalX;
|
||||||
|
if (currentConfig.LightfinderAutoAlign)
|
||||||
|
{
|
||||||
|
var measuredWidth = System.Math.Max(1, textWidth > 0 ? textWidth : nodeWidth);
|
||||||
|
var measuredWidthF = (float)measuredWidth;
|
||||||
|
var alignmentType = currentConfig.LabelAlignment;
|
||||||
|
|
||||||
|
var containerScale = nameContainer->ScaleX;
|
||||||
|
if (containerScale <= 0f)
|
||||||
|
containerScale = 1f;
|
||||||
|
var containerWidthRaw = (float)nameContainer->Width;
|
||||||
|
if (containerWidthRaw <= 0f)
|
||||||
|
containerWidthRaw = measuredWidthF;
|
||||||
|
var containerWidth = containerWidthRaw * containerScale;
|
||||||
|
if (containerWidth <= 0f)
|
||||||
|
containerWidth = measuredWidthF;
|
||||||
|
|
||||||
|
var containerLeft = nameContainer->ScreenX;
|
||||||
|
var containerRight = containerLeft + containerWidth;
|
||||||
|
var containerCenter = containerLeft + (containerWidth * 0.5f);
|
||||||
|
|
||||||
|
var iconMargin = currentConfig.LightfinderLabelUseIcon
|
||||||
|
? System.Math.Min(containerWidth * 0.1f, 14f * containerScale)
|
||||||
|
: 0f;
|
||||||
|
|
||||||
|
switch (alignmentType)
|
||||||
|
{
|
||||||
|
case LabelAlignment.Left:
|
||||||
|
finalX = containerLeft + iconMargin;
|
||||||
|
alignment = AlignmentType.BottomLeft;
|
||||||
|
break;
|
||||||
|
case LabelAlignment.Right:
|
||||||
|
finalX = containerRight - iconMargin;
|
||||||
|
alignment = AlignmentType.BottomRight;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
finalX = containerCenter;
|
||||||
|
alignment = AlignmentType.Bottom;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalX += currentConfig.LightfinderLabelOffsetX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var cachedTextOffset = _buffers.TextOffsets[nameplateIndex];
|
||||||
|
var hasCachedOffset = cachedTextOffset != int.MinValue;
|
||||||
|
var baseOffsetX = (!currentConfig.LightfinderLabelUseIcon && hasValidOffset && hasCachedOffset) ? cachedTextOffset : 0;
|
||||||
|
finalX = nameContainer->ScreenX + baseOffsetX + 58 + currentConfig.LightfinderLabelOffsetX;
|
||||||
|
alignment = AlignmentType.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
positionY += currentConfig.LightfinderLabelOffsetY;
|
||||||
|
alignment = (AlignmentType)System.Math.Clamp((int)alignment, 0, 8);
|
||||||
|
|
||||||
|
var finalPosition = new Vector2(finalX, nameContainer->ScreenY + positionY);
|
||||||
|
var pivot = (currentConfig.LightfinderAutoAlign || currentConfig.LightfinderLabelUseIcon)
|
||||||
|
? AlignmentToPivot(alignment)
|
||||||
|
: DefaultPivot;
|
||||||
|
var textColorPacked = PackColor(labelColor);
|
||||||
|
var edgeColorPacked = PackColor(edgeColor);
|
||||||
|
|
||||||
|
_buffers.LabelScratch[scratchCount++] = new NameplateLabelInfo(
|
||||||
|
finalPosition,
|
||||||
|
labelContent,
|
||||||
|
textColorPacked,
|
||||||
|
edgeColorPacked,
|
||||||
|
targetFontSize,
|
||||||
|
pivot,
|
||||||
|
currentConfig.LightfinderLabelUseIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_labelLock)
|
||||||
|
{
|
||||||
|
if (scratchCount == 0)
|
||||||
|
{
|
||||||
|
_labelRenderCount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Array.Copy(_buffers.LabelScratch, _buffers.LabelRender, scratchCount);
|
||||||
|
_labelRenderCount = scratchCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUiBuilderDraw()
|
||||||
|
{
|
||||||
|
if (!_mEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int copyCount;
|
||||||
|
lock (_labelLock)
|
||||||
|
{
|
||||||
|
copyCount = _labelRenderCount;
|
||||||
|
if (copyCount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Array.Copy(_buffers.LabelRender, _buffers.LabelCopy, copyCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var drawList = PictoService.Draw();
|
||||||
|
if (drawList == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < copyCount; ++i)
|
||||||
|
{
|
||||||
|
ref var info = ref _buffers.LabelCopy[i];
|
||||||
|
var font = default(ImFontPtr);
|
||||||
|
if (info.UseIcon)
|
||||||
|
{
|
||||||
|
var ioFonts = ImGui.GetIO().Fonts;
|
||||||
|
font = ioFonts.Fonts.Size > 1 ? new ImFontPtr(ioFonts.Fonts[1]) : ImGui.GetFont();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawList.AddScreenText(info.ScreenPosition, info.Text, info.TextColor, info.FontSize, info.Pivot, info.EdgeColor, font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2 AlignmentToPivot(AlignmentType alignment) => alignment switch
|
||||||
|
{
|
||||||
|
AlignmentType.BottomLeft => new Vector2(0f, 1f),
|
||||||
|
AlignmentType.BottomRight => new Vector2(1f, 1f),
|
||||||
|
AlignmentType.TopLeft => new Vector2(0f, 0f),
|
||||||
|
AlignmentType.TopRight => new Vector2(1f, 0f),
|
||||||
|
AlignmentType.Top => new Vector2(0.5f, 0f),
|
||||||
|
AlignmentType.Left => new Vector2(0f, 0.5f),
|
||||||
|
AlignmentType.Right => new Vector2(1f, 0.5f),
|
||||||
|
_ => DefaultPivot
|
||||||
|
};
|
||||||
|
|
||||||
|
private static uint PackColor(Vector4 color)
|
||||||
|
{
|
||||||
|
var r = (byte)System.Math.Clamp(color.X * 255f, 0f, 255f);
|
||||||
|
var g = (byte)System.Math.Clamp(color.Y * 255f, 0f, 255f);
|
||||||
|
var b = (byte)System.Math.Clamp(color.Z * 255f, 0f, 255f);
|
||||||
|
var a = (byte)System.Math.Clamp(color.W * 255f, 0f, 255f);
|
||||||
|
return (uint)((a << 24) | (b << 16) | (g << 8) | r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearLabelBuffer()
|
||||||
|
{
|
||||||
|
lock (_labelLock)
|
||||||
|
{
|
||||||
|
_labelRenderCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe int GetScaledTextHeight(AtkTextNode* node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var resNode = &node->AtkResNode;
|
||||||
|
var rawHeight = (int)resNode->GetHeight();
|
||||||
|
if (rawHeight <= 0 && node->LineSpacing > 0)
|
||||||
|
rawHeight = node->LineSpacing;
|
||||||
|
if (rawHeight <= 0)
|
||||||
|
rawHeight = AtkNodeHelpers.DefaultTextNodeHeight;
|
||||||
|
|
||||||
|
var scale = resNode->ScaleY;
|
||||||
|
if (scale <= 0f)
|
||||||
|
scale = 1f;
|
||||||
|
|
||||||
|
var computed = (int)System.Math.Round(rawHeight * scale);
|
||||||
|
return System.Math.Max(1, computed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe int GetScaledTextWidth(AtkTextNode* node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var resNode = &node->AtkResNode;
|
||||||
|
var rawWidth = (int)resNode->GetWidth();
|
||||||
|
if (rawWidth <= 0)
|
||||||
|
rawWidth = AtkNodeHelpers.DefaultTextNodeWidth;
|
||||||
|
|
||||||
|
var scale = resNode->ScaleX;
|
||||||
|
if (scale <= 0f)
|
||||||
|
scale = 1f;
|
||||||
|
|
||||||
|
var computed = (int)System.Math.Round(rawWidth * scale);
|
||||||
|
return System.Math.Max(1, computed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ResolveCache(
|
||||||
|
int[] cache,
|
||||||
|
int index,
|
||||||
|
int rawValue,
|
||||||
|
Func<int> fallback,
|
||||||
|
int fallbackWhenZero)
|
||||||
|
{
|
||||||
|
if (rawValue > 0)
|
||||||
|
{
|
||||||
|
cache[index] = rawValue;
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cachedValue = cache[index];
|
||||||
|
if (cachedValue > 0)
|
||||||
|
return cachedValue;
|
||||||
|
|
||||||
|
var computed = fallback();
|
||||||
|
if (computed <= 0)
|
||||||
|
computed = fallbackWhenZero;
|
||||||
|
|
||||||
|
cache[index] = computed;
|
||||||
|
return computed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryCacheTextOffset(int nameplateIndex, int measuredTextWidth, int textOffset)
|
||||||
|
{
|
||||||
|
if (System.Math.Abs(measuredTextWidth) > 0 || textOffset != 0)
|
||||||
|
{
|
||||||
|
_buffers.TextOffsets[nameplateIndex] = textOffset;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approximate check to see if nameplate would be visible based on distance and screen position
|
return false;
|
||||||
// Also has to be fine tuned still
|
}
|
||||||
private bool ShouldApproximateNameplateVisible(IPlayerCharacter player)
|
|
||||||
|
internal static string NormalizeIconGlyph(string? rawInput)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(rawInput))
|
||||||
|
return DefaultIconGlyph;
|
||||||
|
|
||||||
|
var trimmed = rawInput.Trim();
|
||||||
|
|
||||||
|
if (Enum.TryParse<SeIconChar>(trimmed, true, out var iconEnum))
|
||||||
|
return SeIconCharExtensions.ToIconString(iconEnum);
|
||||||
|
|
||||||
|
var hexCandidate = trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? trimmed[2..]
|
||||||
|
: trimmed;
|
||||||
|
|
||||||
|
if (ushort.TryParse(hexCandidate, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var hexValue))
|
||||||
|
return char.ConvertFromUtf32(hexValue);
|
||||||
|
|
||||||
|
var enumerator = trimmed.EnumerateRunes();
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
return enumerator.Current.ToString();
|
||||||
|
|
||||||
|
return DefaultIconGlyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ToIconEditorString(string? rawInput)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeIconGlyph(rawInput);
|
||||||
|
var runeEnumerator = normalized.EnumerateRunes();
|
||||||
|
return runeEnumerator.MoveNext()
|
||||||
|
? runeEnumerator.Current.Value.ToString("X4", CultureInfo.InvariantCulture)
|
||||||
|
: DefaultIconGlyph;
|
||||||
|
}
|
||||||
|
private readonly struct NameplateLabelInfo
|
||||||
|
{
|
||||||
|
public NameplateLabelInfo(
|
||||||
|
Vector2 screenPosition,
|
||||||
|
string text,
|
||||||
|
uint textColor,
|
||||||
|
uint edgeColor,
|
||||||
|
float fontSize,
|
||||||
|
Vector2 pivot,
|
||||||
|
bool useIcon)
|
||||||
{
|
{
|
||||||
var local = _gameObjects.LocalPlayer;
|
ScreenPosition = screenPosition;
|
||||||
if (local == null)
|
Text = text;
|
||||||
return false;
|
TextColor = textColor;
|
||||||
|
EdgeColor = edgeColor;
|
||||||
var delta = player.Position - local.Position;
|
FontSize = fontSize;
|
||||||
var distance2D = MathF.Sqrt(delta.X * delta.X + delta.Z * delta.Z);
|
Pivot = pivot;
|
||||||
if (distance2D > _defaultNameplateDistance)
|
UseIcon = useIcon;
|
||||||
return false;
|
|
||||||
|
|
||||||
var verticalDelta = MathF.Abs(delta.Y);
|
|
||||||
if (verticalDelta > 3.4f)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return TryGetApproxNameplateScreenPos(player, out _);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe float GetVisualHeight(IPlayerCharacter player)
|
public Vector2 ScreenPosition { get; }
|
||||||
|
public string Text { get; }
|
||||||
|
public uint TextColor { get; }
|
||||||
|
public uint EdgeColor { get; }
|
||||||
|
public float FontSize { get; }
|
||||||
|
public Vector2 Pivot { get; }
|
||||||
|
public bool UseIcon { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<ulong> VisibleUserIds
|
||||||
|
=> [.. _pairUiService.GetSnapshot().PairsByUid.Values
|
||||||
|
.Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue)
|
||||||
|
.Select(u => (ulong)u.PlayerCharacterId)];
|
||||||
|
|
||||||
|
public void FlagRefresh()
|
||||||
|
{
|
||||||
|
_needsLabelRefresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnTick(PriorityFrameworkUpdateMessage _)
|
||||||
|
{
|
||||||
|
if (_needsLabelRefresh)
|
||||||
{
|
{
|
||||||
var gameObject = (GameObject*)player.Address;
|
UpdateNameplateNodes();
|
||||||
if (gameObject == null)
|
_needsLabelRefresh = false;
|
||||||
return Math.Max(player.HitboxRadius * 2.0f, 1.7f); // fallback
|
|
||||||
|
|
||||||
// This should account for transformations (sitting, crouching, etc.)
|
|
||||||
var radius = gameObject->GetRadius(adjustByTransformation: true);
|
|
||||||
if (radius <= 0)
|
|
||||||
radius = Math.Max(player.HitboxRadius * 2.0f, 1.7f);
|
|
||||||
|
|
||||||
return radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the set of active broadcasting CIDs (Same uses as in NameplateHnadler before)
|
|
||||||
public void UpdateBroadcastingCids(IEnumerable<string> cids)
|
|
||||||
{
|
|
||||||
var newSet = cids.ToImmutableHashSet(StringComparer.Ordinal);
|
|
||||||
if (ReferenceEquals(_activeBroadcastingCids, newSet) || _activeBroadcastingCids.SetEquals(newSet))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_activeBroadcastingCids = newSet;
|
|
||||||
if (_logger.IsEnabled(LogLevel.Information))
|
|
||||||
_logger.LogInformation("Active broadcast CIDs: {Cids}", string.Join(',', _activeBroadcastingCids));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateBroadcastingCids(IEnumerable<string> cids)
|
||||||
|
{
|
||||||
|
var newSet = cids.ToImmutableHashSet(StringComparer.Ordinal);
|
||||||
|
if (ReferenceEquals(_activeBroadcastingCids, newSet) || _activeBroadcastingCids.SetEquals(newSet))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_activeBroadcastingCids = newSet;
|
||||||
|
if (_logger.IsEnabled(LogLevel.Information))
|
||||||
|
_logger.LogInformation("Active broadcast CIDs: {Cids}", string.Join(',', _activeBroadcastingCids));
|
||||||
|
FlagRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearNameplateCaches()
|
||||||
|
{
|
||||||
|
_buffers.Clear();
|
||||||
|
ClearLabelBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class NameplateBuffers
|
||||||
|
{
|
||||||
|
public NameplateBuffers()
|
||||||
|
{
|
||||||
|
TextOffsets = new int[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
System.Array.Fill(TextOffsets, int.MinValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] TextWidths { get; } = new int[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
public int[] TextHeights { get; } = new int[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
public int[] ContainerHeights { get; } = new int[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
public int[] TextOffsets { get; }
|
||||||
|
public NameplateLabelInfo[] LabelScratch { get; } = new NameplateLabelInfo[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
public NameplateLabelInfo[] LabelRender { get; } = new NameplateLabelInfo[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
public NameplateLabelInfo[] LabelCopy { get; } = new NameplateLabelInfo[AddonNamePlate.NumNamePlateObjects];
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
System.Array.Clear(TextWidths, 0, TextWidths.Length);
|
||||||
|
System.Array.Clear(TextHeights, 0, TextHeights.Length);
|
||||||
|
System.Array.Clear(ContainerHeights, 0, ContainerHeights.Length);
|
||||||
|
System.Array.Fill(TextOffsets, int.MinValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Uninit();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
82
LightlessSync/Services/Rendering/PctDrawListExtensions.cs
Normal file
82
LightlessSync/Services/Rendering/PctDrawListExtensions.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Pictomancy;
|
||||||
|
|
||||||
|
namespace LightlessSync.Services.Rendering;
|
||||||
|
|
||||||
|
internal static class PctDrawListExtensions
|
||||||
|
{
|
||||||
|
private static readonly FieldInfo? DrawListField = typeof(PctDrawList).GetField("_drawList", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
private static bool TryGetImDrawList(PctDrawList drawList, out ImDrawListPtr ptr)
|
||||||
|
{
|
||||||
|
ptr = default;
|
||||||
|
if (DrawListField == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (DrawListField.GetValue(drawList) is ImDrawListPtr list)
|
||||||
|
{
|
||||||
|
ptr = list;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddScreenText(this PctDrawList drawList, Vector2 screenPosition, string text, uint color, float fontSize, Vector2? pivot = null, uint? outlineColor = null, ImFontPtr fontOverride = default)
|
||||||
|
{
|
||||||
|
if (drawList == null || string.IsNullOrEmpty(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryGetImDrawList(drawList, out var imDrawList))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var font = fontOverride.IsNull ? ImGui.GetFont() : fontOverride;
|
||||||
|
if (font.IsNull)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var size = MathF.Max(1f, fontSize);
|
||||||
|
var pivotValue = pivot ?? new Vector2(0.5f, 0.5f);
|
||||||
|
|
||||||
|
Vector2 measured;
|
||||||
|
float calcFontSize;
|
||||||
|
if (!fontOverride.IsNull)
|
||||||
|
{
|
||||||
|
ImGui.PushFont(font);
|
||||||
|
measured = ImGui.CalcTextSize(text);
|
||||||
|
calcFontSize = ImGui.GetFontSize();
|
||||||
|
ImGui.PopFont();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
measured = ImGui.CalcTextSize(text);
|
||||||
|
calcFontSize = ImGui.GetFontSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calcFontSize > 0f && MathF.Abs(size - calcFontSize) > 0.001f)
|
||||||
|
{
|
||||||
|
measured *= size / calcFontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
var drawPos = screenPosition - measured * pivotValue;
|
||||||
|
if (outlineColor.HasValue)
|
||||||
|
{
|
||||||
|
var thickness = MathF.Max(1f, size / 24f);
|
||||||
|
Span<Vector2> offsets = stackalloc Vector2[4]
|
||||||
|
{
|
||||||
|
new Vector2(1f, 0f),
|
||||||
|
new Vector2(-1f, 0f),
|
||||||
|
new Vector2(0f, 1f),
|
||||||
|
new Vector2(0f, -1f)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var offset in offsets)
|
||||||
|
{
|
||||||
|
imDrawList.AddText(font, size, drawPos + offset * thickness, outlineColor.Value, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imDrawList.AddText(font, size, drawPos, color, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
LightlessSync/Services/Rendering/PictomancyService.cs
Normal file
47
LightlessSync/Services/Rendering/PictomancyService.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Dalamud.Plugin;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Pictomancy;
|
||||||
|
|
||||||
|
namespace LightlessSync.Services.Rendering;
|
||||||
|
|
||||||
|
public sealed class PictomancyService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger<PictomancyService> _logger;
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
|
public PictomancyService(ILogger<PictomancyService> logger, IDalamudPluginInterface pluginInterface)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PictoService.Initialize(pluginInterface);
|
||||||
|
_initialized = true;
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
_logger.LogDebug("Pictomancy initialized");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to initialize Pictomancy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PictoService.Dispose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to dispose Pictomancy");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Buffers;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using OtterTex;
|
using OtterTex;
|
||||||
using OtterImage = OtterTex.Image;
|
using OtterImage = OtterTex.Image;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
@@ -11,7 +12,6 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using SixLabors.ImageSharp.Processing;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OtterTex made by Ottermandias
|
* OtterTex made by Ottermandias
|
||||||
@@ -33,6 +33,7 @@ public sealed class TextureDownscaleService
|
|||||||
|
|
||||||
private readonly ConcurrentDictionary<string, Task> _activeJobs = new(StringComparer.OrdinalIgnoreCase);
|
private readonly ConcurrentDictionary<string, Task> _activeJobs = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly ConcurrentDictionary<string, string> _downscaledPaths = new(StringComparer.OrdinalIgnoreCase);
|
private readonly ConcurrentDictionary<string, string> _downscaledPaths = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly SemaphoreSlim _downscaleSemaphore = new(4);
|
||||||
private static readonly IReadOnlyDictionary<int, TextureCompressionTarget> BlockCompressedFormatMap =
|
private static readonly IReadOnlyDictionary<int, TextureCompressionTarget> BlockCompressedFormatMap =
|
||||||
new Dictionary<int, TextureCompressionTarget>
|
new Dictionary<int, TextureCompressionTarget>
|
||||||
{
|
{
|
||||||
@@ -80,7 +81,10 @@ public sealed class TextureDownscaleService
|
|||||||
if (!filePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase)) return;
|
if (!filePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase)) return;
|
||||||
if (_activeJobs.ContainsKey(hash)) return;
|
if (_activeJobs.ContainsKey(hash)) return;
|
||||||
|
|
||||||
_activeJobs[hash] = Task.Run(() => DownscaleInternalAsync(hash, filePath, mapKind), CancellationToken.None);
|
_activeJobs[hash] = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await DownscaleInternalAsync(hash, filePath, mapKind).ConfigureAwait(false);
|
||||||
|
}, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetPreferredPath(string hash, string originalPath)
|
public string GetPreferredPath(string hash, string originalPath)
|
||||||
@@ -108,6 +112,7 @@ public sealed class TextureDownscaleService
|
|||||||
bool onlyDownscaleUncompressed = false;
|
bool onlyDownscaleUncompressed = false;
|
||||||
bool? isIndexTexture = null;
|
bool? isIndexTexture = null;
|
||||||
|
|
||||||
|
await _downscaleSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!File.Exists(sourcePath))
|
if (!File.Exists(sourcePath))
|
||||||
@@ -157,6 +162,15 @@ public sealed class TextureDownscaleService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (headerInfo is { } headerValue &&
|
||||||
|
headerValue.Width <= targetMaxDimension &&
|
||||||
|
headerValue.Height <= targetMaxDimension)
|
||||||
|
{
|
||||||
|
_downscaledPaths[hash] = sourcePath;
|
||||||
|
_logger.LogTrace("Skipping downscale for index texture {Hash}; header dimensions {Width}x{Height} within target.", hash, headerValue.Width, headerValue.Height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (onlyDownscaleUncompressed && headerInfo.HasValue && IsBlockCompressedFormat(headerInfo.Value.Format))
|
if (onlyDownscaleUncompressed && headerInfo.HasValue && IsBlockCompressedFormat(headerInfo.Value.Format))
|
||||||
{
|
{
|
||||||
_downscaledPaths[hash] = sourcePath;
|
_downscaledPaths[hash] = sourcePath;
|
||||||
@@ -172,21 +186,20 @@ public sealed class TextureDownscaleService
|
|||||||
var height = rgbaInfo.Meta.Height;
|
var height = rgbaInfo.Meta.Height;
|
||||||
var requiredLength = width * height * bytesPerPixel;
|
var requiredLength = width * height * bytesPerPixel;
|
||||||
|
|
||||||
var rgbaPixels = rgbaScratch.Pixels[..requiredLength].ToArray();
|
var rgbaPixels = rgbaScratch.Pixels.Slice(0, requiredLength);
|
||||||
using var originalImage = SixLabors.ImageSharp.Image.LoadPixelData<Rgba32>(rgbaPixels, width, height);
|
using var originalImage = SixLabors.ImageSharp.Image.LoadPixelData<Rgba32>(rgbaPixels, width, height);
|
||||||
|
|
||||||
var targetSize = CalculateTargetSize(originalImage.Width, originalImage.Height, targetMaxDimension);
|
var targetSize = CalculateTargetSize(originalImage.Width, originalImage.Height, targetMaxDimension);
|
||||||
if (targetSize.width == originalImage.Width && targetSize.height == originalImage.Height)
|
if (targetSize.width == originalImage.Width && targetSize.height == originalImage.Height)
|
||||||
{
|
{
|
||||||
|
_downscaledPaths[hash] = sourcePath;
|
||||||
|
_logger.LogTrace("Skipping downscale for index texture {Hash}; already within bounds.", hash);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var resized = IndexDownscaler.Downscale(originalImage, targetSize.width, targetSize.height, BlockMultiple);
|
using var resized = IndexDownscaler.Downscale(originalImage, targetSize.width, targetSize.height, BlockMultiple);
|
||||||
|
|
||||||
var resizedPixels = new byte[targetSize.width * targetSize.height * 4];
|
using var resizedScratch = CreateScratchImage(resized, targetSize.width, targetSize.height);
|
||||||
resized.CopyPixelDataTo(resizedPixels);
|
|
||||||
|
|
||||||
using var resizedScratch = ScratchImage.FromRGBA(resizedPixels, targetSize.width, targetSize.height, out var creationInfo).ThrowIfError(creationInfo);
|
|
||||||
using var finalScratch = resizedScratch.Convert(DXGIFormat.B8G8R8A8UNorm);
|
using var finalScratch = resizedScratch.Convert(DXGIFormat.B8G8R8A8UNorm);
|
||||||
|
|
||||||
TexFileHelper.Save(destination, finalScratch);
|
TexFileHelper.Save(destination, finalScratch);
|
||||||
@@ -209,6 +222,7 @@ public sealed class TextureDownscaleService
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_downscaleSemaphore.Release();
|
||||||
_activeJobs.TryRemove(hash, out _);
|
_activeJobs.TryRemove(hash, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,6 +241,41 @@ public sealed class TextureDownscaleService
|
|||||||
return (resultWidth, resultHeight);
|
return (resultWidth, resultHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ScratchImage CreateScratchImage(Image<Rgba32> image, int width, int height)
|
||||||
|
{
|
||||||
|
const int BytesPerPixel = 4;
|
||||||
|
var requiredLength = width * height * BytesPerPixel;
|
||||||
|
|
||||||
|
static ScratchImage Create(ReadOnlySpan<byte> pixels, int width, int height)
|
||||||
|
{
|
||||||
|
var scratchResult = ScratchImage.FromRGBA(pixels, width, height, out var creationInfo);
|
||||||
|
return scratchResult.ThrowIfError(creationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.DangerousTryGetSinglePixelMemory(out var pixelMemory))
|
||||||
|
{
|
||||||
|
var byteSpan = MemoryMarshal.AsBytes(pixelMemory.Span);
|
||||||
|
if (byteSpan.Length < requiredLength)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Image buffer shorter than expected ({byteSpan.Length} < {requiredLength}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Create(byteSpan.Slice(0, requiredLength), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rented = ArrayPool<byte>.Shared.Rent(requiredLength);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rentedSpan = rented.AsSpan(0, requiredLength);
|
||||||
|
image.CopyPixelDataTo(rentedSpan);
|
||||||
|
return Create(rentedSpan, width, height);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(rented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsIndexMap(TextureMapKind kind)
|
private static bool IsIndexMap(TextureMapKind kind)
|
||||||
=> kind is TextureMapKind.Mask
|
=> kind is TextureMapKind.Mask
|
||||||
or TextureMapKind.Index;
|
or TextureMapKind.Index;
|
||||||
@@ -420,21 +469,6 @@ public sealed class TextureDownscaleService
|
|||||||
private static int ReduceDimension(int value)
|
private static int ReduceDimension(int value)
|
||||||
=> value <= 1 ? 1 : Math.Max(1, value / 2);
|
=> value <= 1 ? 1 : Math.Max(1, value / 2);
|
||||||
|
|
||||||
private static Image<Rgba32> ReduceLinearTexture(Image<Rgba32> source, int targetWidth, int targetHeight)
|
|
||||||
{
|
|
||||||
var clone = source.Clone();
|
|
||||||
|
|
||||||
while (clone.Width > targetWidth || clone.Height > targetHeight)
|
|
||||||
{
|
|
||||||
var nextWidth = Math.Max(targetWidth, Math.Max(BlockMultiple, clone.Width / 2));
|
|
||||||
var nextHeight = Math.Max(targetHeight, Math.Max(BlockMultiple, clone.Height / 2));
|
|
||||||
clone.Mutate(ctx => ctx.Resize(nextWidth, nextHeight, KnownResamplers.Lanczos3));
|
|
||||||
}
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static bool ShouldTrim(in TexMeta meta, int targetMaxDimension)
|
private static bool ShouldTrim(in TexMeta meta, int targetMaxDimension)
|
||||||
{
|
{
|
||||||
var depth = meta.Dimension == TexDimension.Tex3D ? Math.Max(1, meta.Depth) : 1;
|
var depth = meta.Dimension == TexDimension.Tex3D ? Math.Max(1, meta.Depth) : 1;
|
||||||
@@ -443,12 +477,7 @@ public sealed class TextureDownscaleService
|
|||||||
|
|
||||||
private static bool ShouldTrimDimensions(int width, int height, int depth, int targetMaxDimension)
|
private static bool ShouldTrimDimensions(int width, int height, int depth, int targetMaxDimension)
|
||||||
{
|
{
|
||||||
if (width <= targetMaxDimension || height <= targetMaxDimension)
|
if (width <= targetMaxDimension && height <= targetMaxDimension && depth <= targetMaxDimension)
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth > 1 && depth <= targetMaxDimension)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,10 +51,11 @@ public class IdDisplayHandler
|
|||||||
(bool textIsUid, string playerText) = GetGroupText(group);
|
(bool textIsUid, string playerText) = GetGroupText(group);
|
||||||
if (!string.Equals(_editEntry, group.GID, StringComparison.Ordinal))
|
if (!string.Equals(_editEntry, group.GID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
|
|
||||||
using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid))
|
using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid))
|
||||||
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(playerText);
|
ImGui.TextUnformatted(playerText);
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
@@ -121,108 +122,113 @@ public class IdDisplayHandler
|
|||||||
|
|
||||||
if (!string.Equals(_editEntry, pair.UserData.UID, StringComparison.Ordinal))
|
if (!string.Equals(_editEntry, pair.UserData.UID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
var rowStart = ImGui.GetCursorScreenPos();
|
|
||||||
var rowWidth = MathF.Max(editBoxWidth.Invoke(), 0f);
|
|
||||||
var rowRightLimit = rowStart.X + rowWidth;
|
|
||||||
|
|
||||||
var font = textIsUid ? UiBuilder.MonoFont : ImGui.GetFont();
|
var font = textIsUid ? UiBuilder.MonoFont : ImGui.GetFont();
|
||||||
|
var rowWidth = MathF.Max(editBoxWidth.Invoke(), 0f);
|
||||||
|
float rowRightLimit = 0f;
|
||||||
|
Vector2 nameRectMin = Vector2.Zero;
|
||||||
|
Vector2 nameRectMax = Vector2.Zero;
|
||||||
|
float rowTopForStats = 0f;
|
||||||
|
float frameHeightForStats = 0f;
|
||||||
|
|
||||||
Vector4? textColor = null;
|
|
||||||
Vector4? glowColor = null;
|
|
||||||
|
|
||||||
if (pair.UserData.HasVanity)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(pair.UserData.TextColorHex))
|
|
||||||
{
|
|
||||||
textColor = UIColors.HexToRgba(pair.UserData.TextColorHex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(pair.UserData.TextGlowColorHex))
|
|
||||||
{
|
|
||||||
glowColor = UIColors.HexToRgba(pair.UserData.TextGlowColorHex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var useVanityColors = _lightlessConfigService.Current.useColoredUIDs && (textColor != null || glowColor != null);
|
|
||||||
var seString = useVanityColors
|
|
||||||
? SeStringUtils.BuildFormattedPlayerName(playerText, textColor, glowColor)
|
|
||||||
: SeStringUtils.BuildPlain(playerText);
|
|
||||||
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
|
||||||
bool useHighlight = false;
|
|
||||||
float highlightPadX = 0f;
|
|
||||||
float highlightPadY = 0f;
|
|
||||||
|
|
||||||
if (useVanityColors)
|
|
||||||
{
|
|
||||||
float boost = Luminance.ComputeHighlight(textColor, glowColor);
|
|
||||||
|
|
||||||
if (boost > 0f)
|
|
||||||
{
|
|
||||||
var style = ImGui.GetStyle();
|
|
||||||
useHighlight = true;
|
|
||||||
highlightPadX = MathF.Max(style.FramePadding.X * 0.6f, 2f * ImGuiHelpers.GlobalScale);
|
|
||||||
highlightPadY = MathF.Max(style.FramePadding.Y * 0.55f, 1.25f * ImGuiHelpers.GlobalScale);
|
|
||||||
drawList.ChannelsSplit(2);
|
|
||||||
drawList.ChannelsSetCurrent(1);
|
|
||||||
|
|
||||||
_highlightBoost = boost;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_highlightBoost = 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 itemMin;
|
|
||||||
Vector2 itemMax;
|
|
||||||
using (ImRaii.PushFont(font, textIsUid))
|
using (ImRaii.PushFont(font, textIsUid))
|
||||||
{
|
{
|
||||||
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, font, pair.UserData.UID);
|
ImGui.AlignTextToFramePadding();
|
||||||
itemMin = ImGui.GetItemRectMin();
|
var rowStart = ImGui.GetCursorScreenPos();
|
||||||
itemMax = ImGui.GetItemRectMax();
|
rowRightLimit = rowStart.X + rowWidth;
|
||||||
}
|
|
||||||
|
|
||||||
if (useHighlight)
|
Vector4? textColor = null;
|
||||||
|
Vector4? glowColor = null;
|
||||||
|
|
||||||
|
if (pair.UserData.HasVanity)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(pair.UserData.TextColorHex))
|
||||||
|
{
|
||||||
|
textColor = UIColors.HexToRgba(pair.UserData.TextColorHex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(pair.UserData.TextGlowColorHex))
|
||||||
|
{
|
||||||
|
glowColor = UIColors.HexToRgba(pair.UserData.TextGlowColorHex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var useVanityColors = _lightlessConfigService.Current.useColoredUIDs && (textColor != null || glowColor != null);
|
||||||
|
var seString = useVanityColors
|
||||||
|
? SeStringUtils.BuildFormattedPlayerName(playerText, textColor, glowColor)
|
||||||
|
: SeStringUtils.BuildPlain(playerText);
|
||||||
|
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
bool useHighlight = false;
|
||||||
|
float highlightPadX = 0f;
|
||||||
|
float highlightPadY = 0f;
|
||||||
|
|
||||||
|
if (useVanityColors)
|
||||||
|
{
|
||||||
|
float boost = Luminance.ComputeHighlight(textColor, glowColor);
|
||||||
|
|
||||||
|
if (boost > 0f)
|
||||||
|
{
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
useHighlight = true;
|
||||||
|
highlightPadX = MathF.Max(style.FramePadding.X * 0.6f, 2f * ImGuiHelpers.GlobalScale);
|
||||||
|
highlightPadY = MathF.Max(style.FramePadding.Y * 0.55f, 1.25f * ImGuiHelpers.GlobalScale);
|
||||||
|
drawList.ChannelsSplit(2);
|
||||||
|
drawList.ChannelsSetCurrent(1);
|
||||||
|
|
||||||
|
_highlightBoost = boost;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_highlightBoost = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, font, pair.UserData.UID);
|
||||||
|
nameRectMin = ImGui.GetItemRectMin();
|
||||||
|
nameRectMax = ImGui.GetItemRectMax();
|
||||||
|
|
||||||
|
if (useHighlight)
|
||||||
|
{
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
|
var rowTop = rowStart.Y - style.FramePadding.Y;
|
||||||
|
var rowBottom = rowTop + frameHeight;
|
||||||
|
|
||||||
|
var highlightMin = new Vector2(nameRectMin.X - highlightPadX, rowTop - highlightPadY);
|
||||||
|
var highlightMax = new Vector2(nameRectMax.X + highlightPadX, rowBottom + highlightPadY);
|
||||||
|
|
||||||
|
var windowPos = ImGui.GetWindowPos();
|
||||||
|
var contentMin = windowPos + ImGui.GetWindowContentRegionMin();
|
||||||
|
var contentMax = windowPos + ImGui.GetWindowContentRegionMax();
|
||||||
|
highlightMin.X = MathF.Max(highlightMin.X, contentMin.X);
|
||||||
|
highlightMax.X = MathF.Min(highlightMax.X, contentMax.X);
|
||||||
|
highlightMin.Y = MathF.Max(highlightMin.Y, contentMin.Y);
|
||||||
|
highlightMax.Y = MathF.Min(highlightMax.Y, contentMax.Y);
|
||||||
|
|
||||||
|
var highlightColor = new Vector4(
|
||||||
|
0.25f + _highlightBoost,
|
||||||
|
0.25f + _highlightBoost,
|
||||||
|
0.25f + _highlightBoost,
|
||||||
|
1f
|
||||||
|
);
|
||||||
|
|
||||||
|
highlightColor = Luminance.BackgroundContrast(textColor, glowColor, highlightColor, ref _currentBg);
|
||||||
|
|
||||||
|
float rounding = style.FrameRounding > 0f ? style.FrameRounding : 5f * ImGuiHelpers.GlobalScale;
|
||||||
|
drawList.ChannelsSetCurrent(0);
|
||||||
|
drawList.AddRectFilled(highlightMin, highlightMax, ImGui.GetColorU32(highlightColor), rounding);
|
||||||
|
|
||||||
|
var borderColor = style.Colors[(int)ImGuiCol.Border];
|
||||||
|
borderColor.W *= 0.25f;
|
||||||
|
drawList.AddRect(highlightMin, highlightMax, ImGui.GetColorU32(borderColor), rounding);
|
||||||
|
drawList.ChannelsMerge();
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
var frameHeight = ImGui.GetFrameHeight();
|
frameHeightForStats = ImGui.GetFrameHeight();
|
||||||
var rowTop = rowStart.Y - style.FramePadding.Y;
|
rowTopForStats = nameRectMin.Y - style.FramePadding.Y;
|
||||||
var rowBottom = rowTop + frameHeight;
|
|
||||||
|
|
||||||
var highlightMin = new Vector2(itemMin.X - highlightPadX, rowTop - highlightPadY);
|
|
||||||
var highlightMax = new Vector2(itemMax.X + highlightPadX, rowBottom + highlightPadY);
|
|
||||||
|
|
||||||
var windowPos = ImGui.GetWindowPos();
|
|
||||||
var contentMin = windowPos + ImGui.GetWindowContentRegionMin();
|
|
||||||
var contentMax = windowPos + ImGui.GetWindowContentRegionMax();
|
|
||||||
highlightMin.X = MathF.Max(highlightMin.X, contentMin.X);
|
|
||||||
highlightMax.X = MathF.Min(highlightMax.X, contentMax.X);
|
|
||||||
highlightMin.Y = MathF.Max(highlightMin.Y, contentMin.Y);
|
|
||||||
highlightMax.Y = MathF.Min(highlightMax.Y, contentMax.Y);
|
|
||||||
|
|
||||||
var highlightColor = new Vector4(
|
|
||||||
0.25f + _highlightBoost,
|
|
||||||
0.25f + _highlightBoost,
|
|
||||||
0.25f + _highlightBoost,
|
|
||||||
1f
|
|
||||||
);
|
|
||||||
|
|
||||||
highlightColor = Luminance.BackgroundContrast(textColor, glowColor, highlightColor, ref _currentBg);
|
|
||||||
|
|
||||||
float rounding = style.FrameRounding > 0f ? style.FrameRounding : 5f * ImGuiHelpers.GlobalScale;
|
|
||||||
drawList.ChannelsSetCurrent(0);
|
|
||||||
drawList.AddRectFilled(highlightMin, highlightMax, ImGui.GetColorU32(highlightColor), rounding);
|
|
||||||
|
|
||||||
var borderColor = style.Colors[(int)ImGuiCol.Border];
|
|
||||||
borderColor.W *= 0.25f;
|
|
||||||
drawList.AddRect(highlightMin, highlightMax, ImGui.GetColorU32(borderColor), rounding);
|
|
||||||
drawList.ChannelsMerge();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameRectMin = ImGui.GetItemRectMin();
|
|
||||||
var nameRectMax = ImGui.GetItemRectMax();
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
if (!string.Equals(_lastMouseOverUid, id, StringComparison.Ordinal))
|
if (!string.Equals(_lastMouseOverUid, id, StringComparison.Ordinal))
|
||||||
@@ -292,12 +298,9 @@ public class IdDisplayHandler
|
|||||||
const float compactFontScale = 0.85f;
|
const float compactFontScale = 0.85f;
|
||||||
ImGui.SetWindowFontScale(compactFontScale);
|
ImGui.SetWindowFontScale(compactFontScale);
|
||||||
var compactHeight = ImGui.GetTextLineHeight();
|
var compactHeight = ImGui.GetTextLineHeight();
|
||||||
var nameHeight = nameRectMax.Y - nameRectMin.Y;
|
|
||||||
var targetPos = ImGui.GetCursorScreenPos();
|
var targetPos = ImGui.GetCursorScreenPos();
|
||||||
var availableWidth = MathF.Max(rowRightLimit - targetPos.X, 0f);
|
var availableWidth = MathF.Max(rowRightLimit - targetPos.X, 0f);
|
||||||
var centeredY = nameRectMin.Y + MathF.Max((nameHeight - compactHeight) * 0.5f, 0f);
|
var centeredY = rowTopForStats + MathF.Max((frameHeightForStats - compactHeight) * 0.5f, 0f);
|
||||||
float verticalOffset = 1f * ImGuiHelpers.GlobalScale;
|
|
||||||
centeredY += verticalOffset;
|
|
||||||
ImGui.SetCursorScreenPos(new Vector2(targetPos.X, centeredY));
|
ImGui.SetCursorScreenPos(new Vector2(targetPos.X, centeredY));
|
||||||
|
|
||||||
var performanceText = string.Empty;
|
var performanceText = string.Empty;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
private readonly ImGuiWindowFlags _unpinnedWindowFlags;
|
private readonly ImGuiWindowFlags _unpinnedWindowFlags;
|
||||||
private float _currentWindowOpacity = DefaultWindowOpacity;
|
private float _currentWindowOpacity = DefaultWindowOpacity;
|
||||||
private bool _isWindowPinned;
|
private bool _isWindowPinned;
|
||||||
private bool _showRulesOverlay = true;
|
private bool _showRulesOverlay;
|
||||||
|
|
||||||
private string? _selectedChannelKey;
|
private string? _selectedChannelKey;
|
||||||
private bool _scrollToBottom = true;
|
private bool _scrollToBottom = true;
|
||||||
@@ -165,7 +165,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawHeader(ChatChannelSnapshot channel)
|
private static void DrawHeader(ChatChannelSnapshot channel)
|
||||||
{
|
{
|
||||||
var prefix = channel.Type == ChatChannelType.Zone ? "Zone" : "Syncshell";
|
var prefix = channel.Type == ChatChannelType.Zone ? "Zone" : "Syncshell";
|
||||||
Vector4 color;
|
Vector4 color;
|
||||||
@@ -577,12 +577,10 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
ImGui.TextUnformatted("Reason (required)");
|
ImGui.TextUnformatted("Reason (required)");
|
||||||
if (ImGui.InputTextMultiline("##chat_report_reason", ref _reportReason, ReportReasonMaxLength, new Vector2(-1, 80f * ImGuiHelpers.GlobalScale)))
|
if (ImGui.InputTextMultiline("##chat_report_reason", ref _reportReason, ReportReasonMaxLength, new Vector2(-1, 80f * ImGuiHelpers.GlobalScale))
|
||||||
|
&& _reportReason.Length > ReportReasonMaxLength)
|
||||||
{
|
{
|
||||||
if (_reportReason.Length > ReportReasonMaxLength)
|
_reportReason = _reportReason[..ReportReasonMaxLength];
|
||||||
{
|
|
||||||
_reportReason = _reportReason[..(int)ReportReasonMaxLength];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
||||||
@@ -591,12 +589,10 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
ImGui.TextUnformatted("Additional context (optional)");
|
ImGui.TextUnformatted("Additional context (optional)");
|
||||||
if (ImGui.InputTextMultiline("##chat_report_context", ref _reportAdditionalContext, ReportContextMaxLength, new Vector2(-1, 120f * ImGuiHelpers.GlobalScale)))
|
if (ImGui.InputTextMultiline("##chat_report_context", ref _reportAdditionalContext, ReportContextMaxLength, new Vector2(-1, 120f * ImGuiHelpers.GlobalScale))
|
||||||
|
&& _reportAdditionalContext.Length > ReportContextMaxLength)
|
||||||
{
|
{
|
||||||
if (_reportAdditionalContext.Length > ReportContextMaxLength)
|
_reportAdditionalContext = _reportAdditionalContext[..ReportContextMaxLength];
|
||||||
{
|
|
||||||
_reportAdditionalContext = _reportAdditionalContext[..(int)ReportContextMaxLength];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
||||||
@@ -768,7 +764,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryCreateCopyMessageAction(ChatMessageEntry message, out ChatMessageContextAction action)
|
private static bool TryCreateCopyMessageAction(ChatMessageEntry message, out ChatMessageContextAction action)
|
||||||
{
|
{
|
||||||
var text = message.Payload.Message;
|
var text = message.Payload.Message;
|
||||||
if (string.IsNullOrEmpty(text))
|
if (string.IsNullOrEmpty(text))
|
||||||
@@ -920,7 +916,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private void EnsureSelectedChannel(IReadOnlyList<ChatChannelSnapshot> channels)
|
private void EnsureSelectedChannel(IReadOnlyList<ChatChannelSnapshot> channels)
|
||||||
{
|
{
|
||||||
if (_selectedChannelKey is not null && channels.Any(channel => channel.Key == _selectedChannelKey))
|
if (_selectedChannelKey is not null && channels.Any(channel => string.Equals(channel.Key, _selectedChannelKey, StringComparison.Ordinal)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_selectedChannelKey = channels.Count > 0 ? channels[0].Key : null;
|
_selectedChannelKey = channels.Count > 0 ? channels[0].Key : null;
|
||||||
@@ -1264,11 +1260,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
var isSelected = string.Equals(channel.Key, _selectedChannelKey, StringComparison.Ordinal);
|
var isSelected = string.Equals(channel.Key, _selectedChannelKey, StringComparison.Ordinal);
|
||||||
var showBadge = !isSelected && channel.UnreadCount > 0;
|
var showBadge = !isSelected && channel.UnreadCount > 0;
|
||||||
var isZoneChannel = channel.Type == ChatChannelType.Zone;
|
var isZoneChannel = channel.Type == ChatChannelType.Zone;
|
||||||
var badgeText = string.Empty;
|
(string Text, Vector2 TextSize, float Width, float Height)? badgeMetrics = null;
|
||||||
var badgePadding = Vector2.Zero;
|
|
||||||
var badgeTextSize = Vector2.Zero;
|
|
||||||
float badgeWidth = 0f;
|
|
||||||
float badgeHeight = 0f;
|
|
||||||
|
|
||||||
var normal = isSelected ? UIColors.Get("LightlessPurpleDefault") : UIColors.Get("ButtonDefault");
|
var normal = isSelected ? UIColors.Get("LightlessPurpleDefault") : UIColors.Get("ButtonDefault");
|
||||||
var hovered = isSelected
|
var hovered = isSelected
|
||||||
@@ -1285,15 +1277,16 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
if (showBadge)
|
if (showBadge)
|
||||||
{
|
{
|
||||||
var badgeSpacing = 4f * ImGuiHelpers.GlobalScale;
|
var badgeSpacing = 4f * ImGuiHelpers.GlobalScale;
|
||||||
badgePadding = new Vector2(4f, 1.5f) * ImGuiHelpers.GlobalScale;
|
var badgePadding = new Vector2(4f, 1.5f) * ImGuiHelpers.GlobalScale;
|
||||||
badgeText = channel.UnreadCount > MaxBadgeDisplay
|
var badgeText = channel.UnreadCount > MaxBadgeDisplay
|
||||||
? $"{MaxBadgeDisplay}+"
|
? $"{MaxBadgeDisplay}+"
|
||||||
: channel.UnreadCount.ToString(CultureInfo.InvariantCulture);
|
: channel.UnreadCount.ToString(CultureInfo.InvariantCulture);
|
||||||
badgeTextSize = ImGui.CalcTextSize(badgeText);
|
var badgeTextSize = ImGui.CalcTextSize(badgeText);
|
||||||
badgeWidth = badgeTextSize.X + badgePadding.X * 2f;
|
var badgeWidth = badgeTextSize.X + badgePadding.X * 2f;
|
||||||
badgeHeight = badgeTextSize.Y + badgePadding.Y * 2f;
|
var badgeHeight = badgeTextSize.Y + badgePadding.Y * 2f;
|
||||||
var customPadding = new Vector2(baseFramePadding.X + badgeWidth + badgeSpacing, baseFramePadding.Y);
|
var customPadding = new Vector2(baseFramePadding.X + badgeWidth + badgeSpacing, baseFramePadding.Y);
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, customPadding);
|
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, customPadding);
|
||||||
|
badgeMetrics = (badgeText, badgeTextSize, badgeWidth, badgeHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
var clicked = ImGui.Button($"{channel.DisplayName}##chat_channel_{channel.Key}");
|
var clicked = ImGui.Button($"{channel.DisplayName}##chat_channel_{channel.Key}");
|
||||||
@@ -1324,20 +1317,20 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
drawList.AddRect(itemMin, itemMax, borderColorU32, style.FrameRounding, ImDrawFlags.None, borderThickness);
|
drawList.AddRect(itemMin, itemMax, borderColorU32, style.FrameRounding, ImDrawFlags.None, borderThickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBadge)
|
if (showBadge && badgeMetrics is { } metrics)
|
||||||
{
|
{
|
||||||
var buttonSizeY = itemMax.Y - itemMin.Y;
|
var buttonSizeY = itemMax.Y - itemMin.Y;
|
||||||
var badgeMin = new Vector2(
|
var badgeMin = new Vector2(
|
||||||
itemMin.X + baseFramePadding.X,
|
itemMin.X + baseFramePadding.X,
|
||||||
itemMin.Y + (buttonSizeY - badgeHeight) * 0.5f);
|
itemMin.Y + (buttonSizeY - metrics.Height) * 0.5f);
|
||||||
var badgeMax = badgeMin + new Vector2(badgeWidth, badgeHeight);
|
var badgeMax = badgeMin + new Vector2(metrics.Width, metrics.Height);
|
||||||
var badgeColor = UIColors.Get("DimRed");
|
var badgeColor = UIColors.Get("DimRed");
|
||||||
var badgeColorU32 = ImGui.ColorConvertFloat4ToU32(badgeColor);
|
var badgeColorU32 = ImGui.ColorConvertFloat4ToU32(badgeColor);
|
||||||
drawList.AddRectFilled(badgeMin, badgeMax, badgeColorU32, badgeHeight * 0.5f);
|
drawList.AddRectFilled(badgeMin, badgeMax, badgeColorU32, metrics.Height * 0.5f);
|
||||||
var textPos = new Vector2(
|
var textPos = new Vector2(
|
||||||
badgeMin.X + (badgeWidth - badgeTextSize.X) * 0.5f,
|
badgeMin.X + (metrics.Width - metrics.TextSize.X) * 0.5f,
|
||||||
badgeMin.Y + (badgeHeight - badgeTextSize.Y) * 0.5f);
|
badgeMin.Y + (metrics.Height - metrics.TextSize.Y) * 0.5f);
|
||||||
drawList.AddText(textPos, ImGui.ColorConvertFloat4ToU32(ImGuiColors.DalamudWhite), badgeText);
|
drawList.AddText(textPos, ImGui.ColorConvertFloat4ToU32(ImGuiColors.DalamudWhite), metrics.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
|||||||
@@ -565,19 +565,30 @@ public static class SeStringUtils
|
|||||||
public static Vector2 RenderSeStringWithHitbox(DalamudSeString seString, Vector2 position, ImFontPtr? font = null, string? id = null)
|
public static Vector2 RenderSeStringWithHitbox(DalamudSeString seString, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var usedFont = font ?? UiBuilder.MonoFont;
|
||||||
var drawParams = new SeStringDrawParams
|
var drawParams = new SeStringDrawParams
|
||||||
{
|
{
|
||||||
Font = font ?? UiBuilder.MonoFont,
|
Font = usedFont,
|
||||||
Color = 0xFFFFFFFF,
|
Color = 0xFFFFFFFF,
|
||||||
WrapWidth = float.MaxValue,
|
WrapWidth = float.MaxValue,
|
||||||
TargetDrawList = drawList
|
TargetDrawList = drawList
|
||||||
};
|
};
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(position);
|
|
||||||
ImGuiHelpers.SeStringWrapped(seString.Encode(), drawParams);
|
|
||||||
|
|
||||||
var textSize = ImGui.CalcTextSize(seString.TextValue);
|
var textSize = ImGui.CalcTextSize(seString.TextValue);
|
||||||
|
if (textSize.Y <= 0f)
|
||||||
|
{
|
||||||
|
textSize.Y = usedFont.FontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
var fontHeight = usedFont.FontSize > 0f ? usedFont.FontSize : ImGui.GetFontSize();
|
||||||
|
var frameHeight = fontHeight + style.FramePadding.Y * 2f;
|
||||||
|
var hitboxHeight = MathF.Max(frameHeight, textSize.Y);
|
||||||
|
var verticalOffset = MathF.Max((hitboxHeight - textSize.Y) * 0.5f, 0f);
|
||||||
|
|
||||||
|
var drawPos = new Vector2(position.X, position.Y + verticalOffset);
|
||||||
|
ImGui.SetCursorScreenPos(drawPos);
|
||||||
|
ImGuiHelpers.SeStringWrapped(seString.Encode(), drawParams);
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(position);
|
ImGui.SetCursorScreenPos(position);
|
||||||
if (id is not null)
|
if (id is not null)
|
||||||
@@ -591,30 +602,52 @@ public static class SeStringUtils
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ImGui.InvisibleButton("##hitbox", textSize);
|
ImGui.InvisibleButton("##hitbox", new Vector2(textSize.X, hitboxHeight));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
return textSize;
|
return new Vector2(textSize.X, hitboxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2 RenderIconWithHitbox(int iconId, Vector2 position, ImFontPtr? font = null, string? id = null)
|
public static Vector2 RenderIconWithHitbox(int iconId, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var usedFont = font ?? UiBuilder.MonoFont;
|
||||||
|
var iconMacro = $"<icon({iconId})>";
|
||||||
|
|
||||||
var drawParams = new SeStringDrawParams
|
var measureParams = new SeStringDrawParams
|
||||||
{
|
{
|
||||||
Font = font ?? UiBuilder.MonoFont,
|
Font = usedFont,
|
||||||
Color = 0xFFFFFFFF,
|
Color = 0xFFFFFFFF,
|
||||||
WrapWidth = float.MaxValue,
|
WrapWidth = float.MaxValue
|
||||||
TargetDrawList = drawList
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var iconMacro = $"<icon({iconId})>";
|
var measureResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, measureParams);
|
||||||
var drawResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, drawParams);
|
var iconSize = measureResult.Size;
|
||||||
|
if (iconSize.Y <= 0f)
|
||||||
|
{
|
||||||
|
iconSize.Y = usedFont.FontSize > 0f ? usedFont.FontSize : ImGui.GetFontSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
var fontHeight = usedFont.FontSize > 0f ? usedFont.FontSize : ImGui.GetFontSize();
|
||||||
|
var frameHeight = fontHeight + style.FramePadding.Y * 2f;
|
||||||
|
var hitboxHeight = MathF.Max(frameHeight, iconSize.Y);
|
||||||
|
var verticalOffset = MathF.Max((hitboxHeight - iconSize.Y) * 0.5f, 0f);
|
||||||
|
|
||||||
|
var drawPos = new Vector2(position.X, position.Y + verticalOffset);
|
||||||
|
var drawParams = new SeStringDrawParams
|
||||||
|
{
|
||||||
|
Font = usedFont,
|
||||||
|
Color = 0xFFFFFFFF,
|
||||||
|
WrapWidth = float.MaxValue,
|
||||||
|
TargetDrawList = drawList,
|
||||||
|
ScreenOffset = drawPos
|
||||||
|
};
|
||||||
|
ImGuiHelpers.CompileSeStringWrapped(iconMacro, drawParams);
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(position);
|
ImGui.SetCursorScreenPos(position);
|
||||||
if (id is not null)
|
if (id is not null)
|
||||||
@@ -628,14 +661,14 @@ public static class SeStringUtils
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ImGui.InvisibleButton("##iconHitbox", drawResult.Size);
|
ImGui.InvisibleButton("##iconHitbox", new Vector2(iconSize.X, hitboxHeight));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
return drawResult.Size;
|
return new Vector2(iconSize.X, hitboxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Internal Payloads
|
#region Internal Payloads
|
||||||
|
|||||||
@@ -177,7 +177,8 @@ public static class VariousExtensions
|
|||||||
if (objectKind != ObjectKind.Player) continue;
|
if (objectKind != ObjectKind.Player) continue;
|
||||||
|
|
||||||
bool manipDataDifferent = !string.Equals(oldData.ManipulationData, newData.ManipulationData, StringComparison.Ordinal);
|
bool manipDataDifferent = !string.Equals(oldData.ManipulationData, newData.ManipulationData, StringComparison.Ordinal);
|
||||||
if (manipDataDifferent || forceApplyMods)
|
var hasManipulationData = !string.IsNullOrEmpty(newData.ManipulationData);
|
||||||
|
if (manipDataDifferent || (forceApplyMods && hasManipulationData))
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ using System.Collections.Concurrent;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.Services.PairProcessing;
|
|
||||||
|
|
||||||
namespace LightlessSync.WebAPI.Files;
|
namespace LightlessSync.WebAPI.Files;
|
||||||
|
|
||||||
@@ -22,12 +21,10 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly FileCompactor _fileCompactor;
|
private readonly FileCompactor _fileCompactor;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
private readonly FileTransferOrchestrator _orchestrator;
|
private readonly FileTransferOrchestrator _orchestrator;
|
||||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
|
||||||
private readonly LightlessConfigService _configService;
|
private readonly LightlessConfigService _configService;
|
||||||
private readonly TextureDownscaleService _textureDownscaleService;
|
private readonly TextureDownscaleService _textureDownscaleService;
|
||||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||||
private readonly ConcurrentDictionary<ThrottledStream, byte> _activeDownloadStreams;
|
private readonly ConcurrentDictionary<ThrottledStream, byte> _activeDownloadStreams;
|
||||||
private static readonly TimeSpan DownloadStallTimeout = TimeSpan.FromSeconds(30);
|
|
||||||
private volatile bool _disableDirectDownloads;
|
private volatile bool _disableDirectDownloads;
|
||||||
private int _consecutiveDirectDownloadFailures;
|
private int _consecutiveDirectDownloadFailures;
|
||||||
private bool _lastConfigDirectDownloadsState;
|
private bool _lastConfigDirectDownloadsState;
|
||||||
@@ -38,7 +35,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
FileTransferOrchestrator orchestrator,
|
FileTransferOrchestrator orchestrator,
|
||||||
FileCacheManager fileCacheManager,
|
FileCacheManager fileCacheManager,
|
||||||
FileCompactor fileCompactor,
|
FileCompactor fileCompactor,
|
||||||
PairProcessingLimiter pairProcessingLimiter,
|
|
||||||
LightlessConfigService configService,
|
LightlessConfigService configService,
|
||||||
TextureDownscaleService textureDownscaleService, TextureMetadataHelper textureMetadataHelper) : base(logger, mediator)
|
TextureDownscaleService textureDownscaleService, TextureMetadataHelper textureMetadataHelper) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
@@ -46,7 +42,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
_orchestrator = orchestrator;
|
_orchestrator = orchestrator;
|
||||||
_fileDbManager = fileCacheManager;
|
_fileDbManager = fileCacheManager;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
_pairProcessingLimiter = pairProcessingLimiter;
|
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_textureDownscaleService = textureDownscaleService;
|
_textureDownscaleService = textureDownscaleService;
|
||||||
_textureMetadataHelper = textureMetadataHelper;
|
_textureMetadataHelper = textureMetadataHelper;
|
||||||
@@ -282,42 +277,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
int bytesRead;
|
int bytesRead;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var readCancellation = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), ct).ConfigureAwait(false);
|
||||||
var readTask = stream.ReadAsync(buffer.AsMemory(0, buffer.Length), readCancellation.Token).AsTask();
|
|
||||||
while (!readTask.IsCompleted)
|
|
||||||
{
|
|
||||||
var completedTask = await Task.WhenAny(readTask, Task.Delay(DownloadStallTimeout)).ConfigureAwait(false);
|
|
||||||
if (completedTask == readTask)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var snapshot = _pairProcessingLimiter.GetSnapshot();
|
|
||||||
if (snapshot.Waiting > 0)
|
|
||||||
{
|
|
||||||
readCancellation.Cancel();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await readTask.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
// expected when cancelling the read due to timeout
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogDebug(ex, "Error finishing read task after stall detection for {requestUrl}", requestUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TimeoutException($"No data received for {DownloadStallTimeout.TotalSeconds} seconds while downloading {requestUrl} (waiting: {snapshot.Waiting})");
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogTrace("Download stalled for {requestUrl} but no queued pairs, continuing to wait", requestUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead = await readTask.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException ex)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
@@ -340,11 +300,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("{requestUrl} downloaded to {destination}", requestUrl, destinationFilename);
|
Logger.LogDebug("{requestUrl} downloaded to {destination}", requestUrl, destinationFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (TimeoutException ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(ex, "Detected stalled download for {requestUrl}, aborting transfer", requestUrl);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
|
|||||||
@@ -521,26 +521,979 @@
|
|||||||
"resolved": "17.6.3",
|
"resolved": "17.6.3",
|
||||||
"contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA=="
|
"contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA=="
|
||||||
},
|
},
|
||||||
|
"Microsoft.NETCore.Platforms": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.1.0",
|
||||||
|
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||||
|
},
|
||||||
|
"Microsoft.NETCore.Targets": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.1.0",
|
||||||
|
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
|
||||||
|
},
|
||||||
|
"Microsoft.Win32.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NETStandard.Library": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.6.1",
|
||||||
|
"contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.Win32.Primitives": "4.3.0",
|
||||||
|
"System.AppContext": "4.3.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Collections.Concurrent": "4.3.0",
|
||||||
|
"System.Console": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tools": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Calendars": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.Compression": "4.3.0",
|
||||||
|
"System.IO.Compression.ZipFile": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.Linq.Expressions": "4.3.0",
|
||||||
|
"System.Net.Http": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Net.Sockets": "4.3.0",
|
||||||
|
"System.ObjectModel": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Text.Encoding.Extensions": "4.3.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"System.Threading.Timer": "4.3.0",
|
||||||
|
"System.Xml.ReaderWriter": "4.3.0",
|
||||||
|
"System.Xml.XDocument": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q=="
|
||||||
|
},
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA=="
|
||||||
|
},
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw=="
|
||||||
|
},
|
||||||
|
"runtime.native.System": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.IO.Compression": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Net.Http": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Security.Cryptography.Apple": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==",
|
||||||
|
"dependencies": {
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A=="
|
||||||
|
},
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ=="
|
||||||
|
},
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ=="
|
||||||
|
},
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g=="
|
||||||
|
},
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg=="
|
||||||
|
},
|
||||||
|
"SharpDX": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "3pv0LFMvfK/dv1qISJnn8xBeeT6R/FRvr0EV4KI2DGsL84Qlv6P7isWqxGyU0LCwlSVCJN3jgHJ4Bl0KI2PJww==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.D3DCompiler": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "Rnsd6Ilp127xbXqhTit8WKFQUrXwWxqVGpglyWDNkIBCk0tWXNQEjrJpsl0KAObzyZaa33+EXAikLVt5fnd3GA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Direct2D1": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "Qs8LzDMaQf1u3KB8ArHu9pDv6itZ++QXs99a/bVAG+nKr0Hx5NG4mcN5vsfE0mVR2TkeHfeUm4PksRah6VUPtA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0",
|
||||||
|
"SharpDX.DXGI": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Direct3D11": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "oTm/iT5X/IIuJ8kNYP+DTC/MhBhqtRF5dbgPPFgLBdQv0BKzNTzXQQXd7SveBFjQg6hXEAJ2jGCAzNYvGFc9LA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0",
|
||||||
|
"SharpDX.DXGI": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.DXGI": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "UjKqkgWc8U+SP+j3LBzFP6OB6Ntapjih7Xo+g1rLcsGbIb5KwewBrBChaUu7sil8rWoeVU/k0EJd3SMN4VqNZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Mathematics": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "R2pcKLgdsP9p5WyTjHmGOZ0ka0zASAZYc6P4L6rSvjYhf6klGYbent7MiVwbkwkt9dD44p5brjy5IwAnVONWGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.AppContext": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Buffers": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Collections": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Collections.Concurrent": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Console": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.Debug": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.DiagnosticSource": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Diagnostics.EventLog": {
|
"System.Diagnostics.EventLog": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.3",
|
"resolved": "9.0.3",
|
||||||
"contentHash": "0nDJBZ06DVdTG2vvCZ4XjazLVaFawdT0pnji23ISX8I8fEOlRJyzH2I0kWiAbCtFwry2Zir4qE4l/GStLATfFw=="
|
"contentHash": "0nDJBZ06DVdTG2vvCZ4XjazLVaFawdT0pnji23ISX8I8fEOlRJyzH2I0kWiAbCtFwry2Zir4qE4l/GStLATfFw=="
|
||||||
},
|
},
|
||||||
|
"System.Diagnostics.Tools": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.Tracing": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization.Calendars": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.Compression": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Buffers": "4.3.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.IO.Compression": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.Compression.ZipFile": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Buffers": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.Compression": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.FileSystem": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.FileSystem.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Linq": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Linq.Expressions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.ObjectModel": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Emit.Lightweight": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Reflection.TypeExtensions": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Memory": {
|
"System.Memory": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.5",
|
"resolved": "4.5.5",
|
||||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
|
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
|
||||||
},
|
},
|
||||||
|
"System.Net.Http": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Extensions": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.Net.Http": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Net.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Net.ServerSentEvents": {
|
"System.Net.ServerSentEvents": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.3",
|
"resolved": "9.0.3",
|
||||||
"contentHash": "Vs/C2V27bjtwLqYag9ATzHilcUn8VQTICre4jSBMGFUeSTxEZffTjb+xZwjcmPsVAjmSZmBI5N7Ezq8UFvqQQg=="
|
"contentHash": "Vs/C2V27bjtwLqYag9ATzHilcUn8VQTICre4jSBMGFUeSTxEZffTjb+xZwjcmPsVAjmSZmBI5N7Ezq8UFvqQQg=="
|
||||||
},
|
},
|
||||||
|
"System.Net.Sockets": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.ObjectModel": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit.ILGeneration": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit.Lightweight": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.TypeExtensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Resources.ResourceManager": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Handles": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.InteropServices": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Numerics": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Algorithms": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.Apple": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Cng": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Csp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Encoding": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Collections.Concurrent": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.X509Certificates": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Calendars": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Cng": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Csp": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.Net.Http": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.Encoding": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.Encoding.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.RegularExpressions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Threading.Channels": {
|
"System.Threading.Channels": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.3",
|
"resolved": "9.0.3",
|
||||||
"contentHash": "Ao0iegVONKYVw0eWxJv0ArtMVfkFjgyyYKtUXru6xX5H95flSZWW3QCavD4PAgwpc0ETP38kGHaYbPzSE7sw2w=="
|
"contentHash": "Ao0iegVONKYVw0eWxJv0ArtMVfkFjgyyYKtUXru6xX5H95flSZWW3QCavD4PAgwpc0ETP38kGHaYbPzSE7sw2w=="
|
||||||
},
|
},
|
||||||
|
"System.Threading.Tasks": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Tasks.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Timer": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Xml.ReaderWriter": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Text.Encoding.Extensions": "4.3.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"System.Threading.Tasks.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Xml.XDocument": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tools": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Xml.ReaderWriter": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lightlesssync.api": {
|
"lightlesssync.api": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -569,6 +1522,15 @@
|
|||||||
},
|
},
|
||||||
"penumbra.string": {
|
"penumbra.string": {
|
||||||
"type": "Project"
|
"type": "Project"
|
||||||
|
},
|
||||||
|
"pictomancy": {
|
||||||
|
"type": "Project",
|
||||||
|
"dependencies": {
|
||||||
|
"SharpDX.D3DCompiler": "[4.2.0, )",
|
||||||
|
"SharpDX.Direct2D1": "[4.2.0, )",
|
||||||
|
"SharpDX.Direct3D11": "[4.2.0, )",
|
||||||
|
"SharpDX.Mathematics": "[4.2.0, )"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
970
Pictomancy/Pictomancy/packages.lock.json
Normal file
970
Pictomancy/Pictomancy/packages.lock.json
Normal file
@@ -0,0 +1,970 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net9.0-windows7.0": {
|
||||||
|
"DotNet.ReproducibleBuilds": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[1.2.25, )",
|
||||||
|
"resolved": "1.2.25",
|
||||||
|
"contentHash": "xCXiw7BCxHJ8pF6wPepRUddlh2dlQlbr81gXA72hdk4FLHkKXas7EH/n+fk5UCA/YfMqG1Z6XaPiUjDbUNBUzg=="
|
||||||
|
},
|
||||||
|
"SharpDX.D3DCompiler": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[4.2.0, )",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "Rnsd6Ilp127xbXqhTit8WKFQUrXwWxqVGpglyWDNkIBCk0tWXNQEjrJpsl0KAObzyZaa33+EXAikLVt5fnd3GA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Direct2D1": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[4.2.0, )",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "Qs8LzDMaQf1u3KB8ArHu9pDv6itZ++QXs99a/bVAG+nKr0Hx5NG4mcN5vsfE0mVR2TkeHfeUm4PksRah6VUPtA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0",
|
||||||
|
"SharpDX.DXGI": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Direct3D11": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[4.2.0, )",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "oTm/iT5X/IIuJ8kNYP+DTC/MhBhqtRF5dbgPPFgLBdQv0BKzNTzXQQXd7SveBFjQg6hXEAJ2jGCAzNYvGFc9LA==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0",
|
||||||
|
"SharpDX.DXGI": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.Mathematics": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[4.2.0, )",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "R2pcKLgdsP9p5WyTjHmGOZ0ka0zASAZYc6P4L6rSvjYhf6klGYbent7MiVwbkwkt9dD44p5brjy5IwAnVONWGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.NETCore.Platforms": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.1.0",
|
||||||
|
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||||
|
},
|
||||||
|
"Microsoft.NETCore.Targets": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.1.0",
|
||||||
|
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
|
||||||
|
},
|
||||||
|
"Microsoft.Win32.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NETStandard.Library": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.6.1",
|
||||||
|
"contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.Win32.Primitives": "4.3.0",
|
||||||
|
"System.AppContext": "4.3.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Collections.Concurrent": "4.3.0",
|
||||||
|
"System.Console": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tools": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Calendars": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.Compression": "4.3.0",
|
||||||
|
"System.IO.Compression.ZipFile": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.Linq.Expressions": "4.3.0",
|
||||||
|
"System.Net.Http": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Net.Sockets": "4.3.0",
|
||||||
|
"System.ObjectModel": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Text.Encoding.Extensions": "4.3.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"System.Threading.Timer": "4.3.0",
|
||||||
|
"System.Xml.ReaderWriter": "4.3.0",
|
||||||
|
"System.Xml.XDocument": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q=="
|
||||||
|
},
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA=="
|
||||||
|
},
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw=="
|
||||||
|
},
|
||||||
|
"runtime.native.System": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.IO.Compression": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Net.Http": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Security.Cryptography.Apple": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==",
|
||||||
|
"dependencies": {
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A=="
|
||||||
|
},
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ=="
|
||||||
|
},
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ=="
|
||||||
|
},
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g=="
|
||||||
|
},
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A=="
|
||||||
|
},
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg=="
|
||||||
|
},
|
||||||
|
"SharpDX": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "3pv0LFMvfK/dv1qISJnn8xBeeT6R/FRvr0EV4KI2DGsL84Qlv6P7isWqxGyU0LCwlSVCJN3jgHJ4Bl0KI2PJww==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SharpDX.DXGI": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.2.0",
|
||||||
|
"contentHash": "UjKqkgWc8U+SP+j3LBzFP6OB6Ntapjih7Xo+g1rLcsGbIb5KwewBrBChaUu7sil8rWoeVU/k0EJd3SMN4VqNZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.6.1",
|
||||||
|
"SharpDX": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.AppContext": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Buffers": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Collections": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Collections.Concurrent": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Console": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.Debug": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.DiagnosticSource": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.Tools": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Diagnostics.Tracing": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization.Calendars": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Globalization.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.Compression": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Buffers": "4.3.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.IO.Compression": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.Compression.ZipFile": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Buffers": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.Compression": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.FileSystem": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.FileSystem.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Linq": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Linq.Expressions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.ObjectModel": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Emit.Lightweight": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Reflection.TypeExtensions": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Net.Http": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "4.3.0",
|
||||||
|
"System.Diagnostics.Tracing": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Extensions": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.Net.Http": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Net.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Net.Sockets": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Net.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.ObjectModel": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit.ILGeneration": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Emit.Lightweight": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Reflection.TypeExtensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Resources.ResourceManager": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Handles": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.InteropServices": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Primitives": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.InteropServices.RuntimeInformation": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Reflection.Extensions": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.Numerics": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Algorithms": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.Apple": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Cng": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Csp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Encoding": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Collections.Concurrent": "4.3.0",
|
||||||
|
"System.Linq": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.OpenSsl": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.Primitives": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Security.Cryptography.X509Certificates": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.Globalization.Calendars": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.Handles": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Runtime.Numerics": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Algorithms": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Cng": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Csp": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Encoding": "4.3.0",
|
||||||
|
"System.Security.Cryptography.OpenSsl": "4.3.0",
|
||||||
|
"System.Security.Cryptography.Primitives": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"runtime.native.System": "4.3.0",
|
||||||
|
"runtime.native.System.Net.Http": "4.3.0",
|
||||||
|
"runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.Encoding": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.Encoding.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Text.RegularExpressions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Tasks": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Tasks.Extensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Timer": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||||
|
"Microsoft.NETCore.Targets": "1.1.0",
|
||||||
|
"System.Runtime": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Xml.ReaderWriter": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.IO.FileSystem": "4.3.0",
|
||||||
|
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Runtime.InteropServices": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Text.Encoding.Extensions": "4.3.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"System.Threading.Tasks": "4.3.0",
|
||||||
|
"System.Threading.Tasks.Extensions": "4.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Xml.XDocument": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.3.0",
|
||||||
|
"contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections": "4.3.0",
|
||||||
|
"System.Diagnostics.Debug": "4.3.0",
|
||||||
|
"System.Diagnostics.Tools": "4.3.0",
|
||||||
|
"System.Globalization": "4.3.0",
|
||||||
|
"System.IO": "4.3.0",
|
||||||
|
"System.Reflection": "4.3.0",
|
||||||
|
"System.Resources.ResourceManager": "4.3.0",
|
||||||
|
"System.Runtime": "4.3.0",
|
||||||
|
"System.Runtime.Extensions": "4.3.0",
|
||||||
|
"System.Text.Encoding": "4.3.0",
|
||||||
|
"System.Threading": "4.3.0",
|
||||||
|
"System.Xml.ReaderWriter": "4.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
ffxiv_pictomancy
Submodule
1
ffxiv_pictomancy
Submodule
Submodule ffxiv_pictomancy added at 788bc339a6
Reference in New Issue
Block a user