2.0.0 #92
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,3 +13,6 @@
|
||||
[submodule "OtterGui"]
|
||||
path = 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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OtterGui", "OtterGui\OtterGui.csproj", "{C77A2833-3FE4-405B-811D-439B1FF859D9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pictomancy", "ffxiv_pictomancy\Pictomancy\Pictomancy.csproj", "{825F17D8-2704-24F6-DF8B-2542AC92C765}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|x86.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -426,7 +426,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
||||
() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(descriptor.HashedContentId) &&
|
||||
_actorObjectService.TryGetActorByHash(descriptor.HashedContentId, out var current) &&
|
||||
_actorObjectService.TryGetValidatedActorByHash(descriptor.HashedContentId, out var current) &&
|
||||
current.OwnedKind == kind)
|
||||
{
|
||||
return current.Address;
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
|
||||
<ProjectReference Include="..\ffxiv_pictomancy\Pictomancy\Pictomancy.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using LightlessSync.FileCache;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
using LightlessSync.WebAPI.Files;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -15,7 +14,6 @@ public class FileDownloadManagerFactory
|
||||
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly FileCompactor _fileCompactor;
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||
@@ -26,7 +24,6 @@ public class FileDownloadManagerFactory
|
||||
FileTransferOrchestrator fileTransferOrchestrator,
|
||||
FileCacheManager fileCacheManager,
|
||||
FileCompactor fileCompactor,
|
||||
PairProcessingLimiter pairProcessingLimiter,
|
||||
LightlessConfigService configService,
|
||||
TextureDownscaleService textureDownscaleService,
|
||||
TextureMetadataHelper textureMetadataHelper)
|
||||
@@ -36,7 +33,6 @@ public class FileDownloadManagerFactory
|
||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||
_fileCacheManager = fileCacheManager;
|
||||
_fileCompactor = fileCompactor;
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_configService = configService;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_textureMetadataHelper = textureMetadataHelper;
|
||||
@@ -50,7 +46,6 @@ public class FileDownloadManagerFactory
|
||||
_fileTransferOrchestrator,
|
||||
_fileCacheManager,
|
||||
_fileCompactor,
|
||||
_pairProcessingLimiter,
|
||||
_configService,
|
||||
_textureDownscaleService,
|
||||
_textureMetadataHelper);
|
||||
|
||||
@@ -94,6 +94,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
public DrawCondition CurrentDrawCondition { get; set; } = DrawCondition.None;
|
||||
public byte Gender { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public uint EntityId { get; private set; } = uint.MaxValue;
|
||||
public ObjectKind ObjectKind { get; }
|
||||
public byte RaceId { get; private set; }
|
||||
public byte TribeId { get; private set; }
|
||||
@@ -142,6 +143,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
{
|
||||
Address = IntPtr.Zero;
|
||||
DrawObjectAddress = IntPtr.Zero;
|
||||
EntityId = uint.MaxValue;
|
||||
_haltProcessing = false;
|
||||
}
|
||||
|
||||
@@ -171,13 +173,16 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighP
|
||||
Address = _getAddress();
|
||||
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;
|
||||
EntityId = gameObject->EntityId;
|
||||
CurrentDrawCondition = DrawCondition.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawObjectAddress = IntPtr.Zero;
|
||||
EntityId = uint.MaxValue;
|
||||
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.WebAPI.Files;
|
||||
using LightlessSync.WebAPI.Files.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||
using FileReplacementDataComparer = LightlessSync.PlayerData.Data.FileReplacementDataComparer;
|
||||
|
||||
namespace LightlessSync.PlayerData.Pairs;
|
||||
|
||||
/// <summary>
|
||||
/// orchestrates the lifecycle of a paired character
|
||||
/// handles lifecycle, visibility, queued data, character data for a paired user
|
||||
/// </summary>
|
||||
public interface IPairHandlerAdapter : IDisposable, IPairPerformanceSubject
|
||||
{
|
||||
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
|
||||
internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPairHandlerAdapter
|
||||
{
|
||||
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 PairPerformanceMetricsCache _performanceMetricsCache;
|
||||
private readonly PairManager _pairManager;
|
||||
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
||||
private CancellationTokenSource? _applicationCancellationTokenSource;
|
||||
private Guid _applicationId;
|
||||
private Task? _applicationTask;
|
||||
private CharacterData? _cachedData = null;
|
||||
private GameObjectHandler? _charaHandler;
|
||||
private readonly Dictionary<ObjectKind, Guid?> _customizeIds = [];
|
||||
private CombatData? _dataReceivedInDowntime;
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource;
|
||||
private bool _forceApplyMods = false;
|
||||
private bool _forceFullReapply;
|
||||
private Dictionary<(string GamePath, string? Hash), string>? _lastAppliedModdedPaths;
|
||||
@@ -86,6 +60,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
private Guid _penumbraCollection;
|
||||
private readonly object _collectionGate = new();
|
||||
private bool _redrawOnNextApplication = false;
|
||||
private bool _explicitRedrawQueued;
|
||||
private readonly object _initializationGate = new();
|
||||
private readonly object _pauseLock = new();
|
||||
private Task _pauseTransitionTask = Task.CompletedTask;
|
||||
@@ -183,7 +158,6 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
return;
|
||||
}
|
||||
|
||||
var user = GetPrimaryUserData();
|
||||
if (LastAppliedDataBytes < 0 || LastAppliedDataTris < 0
|
||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||
{
|
||||
@@ -441,9 +415,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
return combined;
|
||||
}
|
||||
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
||||
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
|
||||
? uint.MaxValue
|
||||
: ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_charaHandler!.Address)->EntityId;
|
||||
public uint PlayerCharacterId => _charaHandler?.EntityId ?? uint.MaxValue;
|
||||
public string? PlayerName { get; private set; }
|
||||
public string PlayerNameHash => Ident;
|
||||
|
||||
@@ -490,14 +462,14 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
if (shouldForce)
|
||||
{
|
||||
_forceApplyMods = true;
|
||||
_cachedData = null;
|
||||
_forceFullReapply = true;
|
||||
LastAppliedDataBytes = -1;
|
||||
LastAppliedDataTris = -1;
|
||||
LastAppliedApproximateVRAMBytes = -1;
|
||||
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
||||
}
|
||||
|
||||
var sanitized = CloneAndSanitizeLastReceived(out var dataHash);
|
||||
var sanitized = CloneAndSanitizeLastReceived(out _);
|
||||
if (sanitized is null)
|
||||
{
|
||||
Logger.LogTrace("Sanitized data null for {Ident}", Ident);
|
||||
@@ -746,7 +718,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
if (characterData is null)
|
||||
{
|
||||
Logger.LogWarning("[BASE-{appBase}] Received null character data, skipping application for {handler}", applicationBase, GetLogIdentifier());
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -757,7 +729,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"Cannot apply character data: you are in combat, deferring application")));
|
||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -767,7 +739,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"Cannot apply character data: you are performing music, deferring application")));
|
||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is performing", applicationBase);
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -777,7 +749,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"Cannot apply character data: you are in an instance, deferring application")));
|
||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in instance", applicationBase);
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -787,7 +759,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"Cannot apply character data: you are in a cutscene, deferring application")));
|
||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in a cutscene", applicationBase);
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -797,7 +769,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"Cannot apply character data: you are in GPose, deferring application")));
|
||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in GPose", applicationBase);
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -807,7 +779,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
"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());
|
||||
_dataReceivedInDowntime = new(applicationBase, characterData, forceApplyCustomization);
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
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);
|
||||
}
|
||||
|
||||
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}] 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;
|
||||
}
|
||||
|
||||
_explicitRedrawQueued = false;
|
||||
|
||||
if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player))
|
||||
{
|
||||
player.Add(PlayerChanges.ForcedRedraw);
|
||||
_redrawOnNextApplication = false;
|
||||
_explicitRedrawQueued = true;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
var forceFullReapply = _forceFullReapply || forceApplyCustomization
|
||||
var forceFullReapply = _forceFullReapply
|
||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedDataTris < 0;
|
||||
|
||||
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")}";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
SetUploading(isUploading: false);
|
||||
SetUploading(false);
|
||||
var name = PlayerName;
|
||||
var user = GetPrimaryUserDataSafe();
|
||||
var alias = GetPrimaryAliasOrUidSafe();
|
||||
@@ -1046,6 +1021,11 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
break;
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
var result = new Dictionary<ObjectKind, HashSet<PlayerChanges>>();
|
||||
@@ -1126,6 +1145,39 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
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)
|
||||
{
|
||||
if (!updatedData.Any())
|
||||
@@ -1165,7 +1217,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
|
||||
_downloadCancellationTokenSource = _downloadCancellationTokenSource?.CancelRecreate() ?? new CancellationTokenSource();
|
||||
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;
|
||||
@@ -1173,107 +1226,114 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
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)
|
||||
{
|
||||
await using var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
||||
bool skipDownscaleForPair = ShouldSkipDownscale();
|
||||
var user = GetPrimaryUserData();
|
||||
Dictionary<(string GamePath, string? Hash), string> moddedPaths;
|
||||
|
||||
if (updateModdedPaths)
|
||||
var concurrencyLease = await _pairProcessingLimiter.AcquireAsync(downloadToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
moddedPaths = cachedModdedPaths is not null
|
||||
? 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);
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
moddedPaths = cachedModdedPaths is not null
|
||||
? new Dictionary<(string GamePath, string? Hash), string>(cachedModdedPaths, cachedModdedPaths.Comparer)
|
||||
: [];
|
||||
await concurrencyLease.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -1416,6 +1476,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
{
|
||||
try
|
||||
{
|
||||
_forceFullReapply = true;
|
||||
ApplyCharacterData(appData, cachedData!, forceApplyCustomization: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1432,6 +1493,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
{
|
||||
try
|
||||
{
|
||||
_forceFullReapply = true;
|
||||
ApplyLastReceivedData(forced: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1468,21 +1530,37 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_serverConfigManager.AutoPopulateNoteForUid(user.UID, name);
|
||||
}
|
||||
|
||||
Mediator.Subscribe<HonorificReadyMessage>(this, async (_) =>
|
||||
Mediator.Subscribe<HonorificReadyMessage>(this, _message =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachedData?.HonorificData)) return;
|
||||
Logger.LogTrace("Reapplying Honorific data for {handler}", GetLogIdentifier());
|
||||
await _ipcManager.Honorific.SetTitleAsync(PlayerCharacter, _cachedData.HonorificData).ConfigureAwait(false);
|
||||
var honorificData = _cachedData?.HonorificData;
|
||||
if (string.IsNullOrEmpty(honorificData))
|
||||
return;
|
||||
|
||||
_ = ReapplyHonorificAsync(honorificData!);
|
||||
});
|
||||
|
||||
Mediator.Subscribe<PetNamesReadyMessage>(this, async (_) =>
|
||||
Mediator.Subscribe<PetNamesReadyMessage>(this, _message =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachedData?.PetNamesData)) return;
|
||||
Logger.LogTrace("Reapplying Pet Names data for {handler}", GetLogIdentifier());
|
||||
await _ipcManager.PetNames.SetPlayerData(PlayerCharacter, _cachedData.PetNamesData).ConfigureAwait(false);
|
||||
var petNamesData = _cachedData?.PetNamesData;
|
||||
if (string.IsNullOrEmpty(petNamesData))
|
||||
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)
|
||||
{
|
||||
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(Ident);
|
||||
@@ -1572,14 +1650,11 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
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);
|
||||
fileCache = null;
|
||||
}
|
||||
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);
|
||||
fileCache = null;
|
||||
}
|
||||
|
||||
if (fileCache != null)
|
||||
@@ -1701,7 +1776,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
if (penumbraCollection != Guid.Empty)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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.Events;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.Rendering;
|
||||
using LightlessSync.Services.ServerConfiguration;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
using LightlessSync.UI;
|
||||
@@ -33,8 +34,6 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NReco.Logging.File;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using OtterTex;
|
||||
@@ -178,6 +177,10 @@ public sealed class Plugin : IDalamudPlugin
|
||||
sp.GetRequiredService<CharaDataManager>(),
|
||||
sp.GetRequiredService<LightlessMediator>()));
|
||||
|
||||
services.AddSingleton(sp => new PictomancyService(
|
||||
sp.GetRequiredService<ILogger<PictomancyService>>(),
|
||||
pluginInterface));
|
||||
|
||||
// Tag (Groups) UIs
|
||||
services.AddSingleton<SelectPairForTagUi>();
|
||||
services.AddSingleton<RenamePairTagUi>();
|
||||
@@ -260,11 +263,14 @@ public sealed class Plugin : IDalamudPlugin
|
||||
|
||||
services.AddSingleton(sp => new LightFinderPlateHandler(
|
||||
sp.GetRequiredService<ILogger<LightFinderPlateHandler>>(),
|
||||
sp.GetRequiredService<LightlessMediator>(),
|
||||
pluginInterface,
|
||||
addonLifecycle,
|
||||
gameGui,
|
||||
sp.GetRequiredService<LightlessConfigService>(),
|
||||
sp.GetRequiredService<LightlessMediator>(),
|
||||
objectTable,
|
||||
gameGui));
|
||||
sp.GetRequiredService<PairUiService>(),
|
||||
pluginInterface,
|
||||
sp.GetRequiredService<PictomancyService>()));
|
||||
|
||||
services.AddSingleton(sp => new LightFinderScannerService(
|
||||
sp.GetRequiredService<ILogger<LightFinderScannerService>>(),
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
using LightlessSync;
|
||||
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 System.Collections.Concurrent;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.Interop;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
using FFXIVClientStructs.Interop;
|
||||
using System.Threading;
|
||||
using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||
|
||||
namespace LightlessSync.Services.ActorTracking;
|
||||
|
||||
public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
public sealed class ActorObjectService : IHostedService, IDisposable
|
||||
{
|
||||
public readonly record struct ActorDescriptor(
|
||||
string Name,
|
||||
@@ -38,25 +31,13 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
private readonly IFramework _framework;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
private readonly IObjectTable _objectTable;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly LightlessMediator _mediator;
|
||||
|
||||
private readonly ConcurrentDictionary<nint, ActorDescriptor> _activePlayers = new();
|
||||
private readonly ConcurrentDictionary<string, ActorDescriptor> _actorsByHash = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<nint, ActorDescriptor>> _actorsByName = new(StringComparer.Ordinal);
|
||||
private ActorDescriptor[] _playerCharacterSnapshot = Array.Empty<ActorDescriptor>();
|
||||
private nint[] _playerAddressSnapshot = Array.Empty<nint>();
|
||||
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 readonly OwnedObjectTracker _ownedTracker = new();
|
||||
private ActorSnapshot _snapshot = ActorSnapshot.Empty;
|
||||
|
||||
private Hook<Character.Delegates.OnInitialize>? _onInitializeHook;
|
||||
private Hook<Character.Delegates.Terminate>? _onTerminateHook;
|
||||
@@ -80,16 +61,30 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
_framework = framework;
|
||||
_interop = interop;
|
||||
_objectTable = objectTable;
|
||||
_clientState = clientState;
|
||||
_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 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 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)
|
||||
{
|
||||
descriptor = default;
|
||||
@@ -100,6 +95,9 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
ActorDescriptor? best = null;
|
||||
foreach (var candidate in entries.Values)
|
||||
{
|
||||
if (!ValidateDescriptorThreadSafe(candidate))
|
||||
continue;
|
||||
|
||||
if (best is null || IsBetterNameMatch(candidate, best.Value))
|
||||
{
|
||||
best = candidate;
|
||||
@@ -115,23 +113,54 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
return false;
|
||||
}
|
||||
public bool HooksActive => _hooksActive;
|
||||
public IReadOnlyList<nint> RenderedPlayerAddresses => Volatile.Read(ref _renderedPlayerSnapshot);
|
||||
public IReadOnlyList<nint> RenderedCompanionAddresses => Volatile.Read(ref _renderedCompanionSnapshot);
|
||||
public IReadOnlyList<nint> OwnedObjectAddresses => Volatile.Read(ref _ownedObjectSnapshot);
|
||||
public IReadOnlyDictionary<nint, LightlessObjectKind> OwnedObjects => Volatile.Read(ref _ownedObjectMapSnapshot);
|
||||
public nint LocalPlayerAddress => Volatile.Read(ref _localPlayerAddress);
|
||||
public nint LocalPetAddress => Volatile.Read(ref _localPetAddress);
|
||||
public nint LocalMinionOrMountAddress => Volatile.Read(ref _localMinionMountAddress);
|
||||
public nint LocalCompanionAddress => Volatile.Read(ref _localCompanionAddress);
|
||||
public IReadOnlyList<nint> RenderedPlayerAddresses => Snapshot.OwnedObjects.RenderedPlayers;
|
||||
public IReadOnlyList<nint> RenderedCompanionAddresses => Snapshot.OwnedObjects.RenderedCompanions;
|
||||
public IReadOnlyList<nint> OwnedObjectAddresses => Snapshot.OwnedObjects.OwnedAddresses;
|
||||
public IReadOnlyDictionary<nint, LightlessObjectKind> OwnedObjects => Snapshot.OwnedObjects.Map;
|
||||
public nint LocalPlayerAddress => Snapshot.OwnedObjects.LocalPlayer;
|
||||
public nint LocalPetAddress => Snapshot.OwnedObjects.LocalPet;
|
||||
public nint LocalMinionOrMountAddress => Snapshot.OwnedObjects.LocalMinionOrMount;
|
||||
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)
|
||||
{
|
||||
var ownedSnapshot = Snapshot.OwnedObjects;
|
||||
address = kind switch
|
||||
{
|
||||
LightlessObjectKind.Player => Volatile.Read(ref _localPlayerAddress),
|
||||
LightlessObjectKind.Pet => Volatile.Read(ref _localPetAddress),
|
||||
LightlessObjectKind.MinionOrMount => Volatile.Read(ref _localMinionMountAddress),
|
||||
LightlessObjectKind.Companion => Volatile.Read(ref _localCompanionAddress),
|
||||
LightlessObjectKind.Player => ownedSnapshot.LocalPlayer,
|
||||
LightlessObjectKind.Pet => ownedSnapshot.LocalPet,
|
||||
LightlessObjectKind.MinionOrMount => ownedSnapshot.LocalMinionOrMount,
|
||||
LightlessObjectKind.Companion => ownedSnapshot.LocalCompanion,
|
||||
_ => nint.Zero
|
||||
};
|
||||
|
||||
@@ -158,7 +187,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
|
||||
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;
|
||||
return true;
|
||||
@@ -168,6 +197,50 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
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)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
@@ -185,7 +258,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
_framework.RunOnFrameworkThread(RefreshTrackedActorsInternal);
|
||||
_ = _framework.RunOnFrameworkThread(RefreshTrackedActorsInternal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,23 +284,12 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
_activePlayers.Clear();
|
||||
_actorsByHash.Clear();
|
||||
_actorsByName.Clear();
|
||||
Volatile.Write(ref _playerCharacterSnapshot, Array.Empty<ActorDescriptor>());
|
||||
Volatile.Write(ref _playerAddressSnapshot, Array.Empty<nint>());
|
||||
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();
|
||||
_ownedTracker.Reset();
|
||||
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void InitializeHooks()
|
||||
private unsafe void InitializeHooks()
|
||||
{
|
||||
if (_hooksActive)
|
||||
return;
|
||||
@@ -271,7 +333,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCharacterInitialized(Character* chara)
|
||||
private unsafe void OnCharacterInitialized(Character* chara)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -285,7 +347,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)chara));
|
||||
}
|
||||
|
||||
private void OnCharacterTerminated(Character* chara)
|
||||
private unsafe void OnCharacterTerminated(Character* chara)
|
||||
{
|
||||
var address = (nint)chara;
|
||||
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;
|
||||
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)
|
||||
return;
|
||||
@@ -332,14 +394,10 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
|
||||
if (_activePlayers.TryGetValue(descriptor.Address, out var existing))
|
||||
{
|
||||
RemoveDescriptorFromIndexes(existing);
|
||||
RemoveDescriptorFromCollections(existing);
|
||||
RemoveDescriptor(existing);
|
||||
}
|
||||
|
||||
_activePlayers[descriptor.Address] = descriptor;
|
||||
IndexDescriptor(descriptor);
|
||||
AddDescriptorToCollections(descriptor);
|
||||
RebuildSnapshots();
|
||||
AddDescriptor(descriptor);
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
@@ -355,16 +413,16 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
_mediator.Publish(new ActorTrackedMessage(descriptor));
|
||||
}
|
||||
|
||||
private ActorDescriptor? BuildDescriptor(GameObject* gameObject, DalamudObjectKind objectKind)
|
||||
private unsafe ActorDescriptor? BuildDescriptor(GameObject* gameObject, DalamudObjectKind objectKind)
|
||||
{
|
||||
if (gameObject == null)
|
||||
return null;
|
||||
|
||||
var address = (nint)gameObject;
|
||||
string name = string.Empty;
|
||||
ushort objectIndex = (ushort)gameObject->ObjectIndex;
|
||||
ushort objectIndex = gameObject->ObjectIndex;
|
||||
bool isInGpose = objectIndex >= 200;
|
||||
bool isLocal = _clientState.LocalPlayer?.Address == address;
|
||||
bool isLocal = _objectTable.LocalPlayer?.Address == address;
|
||||
string hashedCid = string.Empty;
|
||||
|
||||
if (_objectTable.CreateObjectReference(address) is IPlayerCharacter playerCharacter)
|
||||
@@ -372,7 +430,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
name = playerCharacter.Name.TextValue ?? string.Empty;
|
||||
objectIndex = playerCharacter.ObjectIndex;
|
||||
isInGpose = objectIndex >= 200;
|
||||
isLocal = playerCharacter.Address == _clientState.LocalPlayer?.Address;
|
||||
isLocal = playerCharacter.Address == _objectTable.LocalPlayer?.Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -389,7 +447,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
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)
|
||||
return (null, 0);
|
||||
@@ -406,7 +464,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
return (LightlessObjectKind.Player, entityId);
|
||||
}
|
||||
|
||||
if (_clientState.LocalPlayer is not { } localPlayer)
|
||||
if (_objectTable.LocalPlayer is not { } localPlayer)
|
||||
return (null, 0);
|
||||
|
||||
var ownerId = gameObject->OwnerId;
|
||||
@@ -453,9 +511,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
|
||||
if (_activePlayers.TryRemove(address, out var descriptor))
|
||||
{
|
||||
RemoveDescriptorFromIndexes(descriptor);
|
||||
RemoveDescriptorFromCollections(descriptor);
|
||||
RebuildSnapshots();
|
||||
RemoveDescriptor(descriptor);
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
_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();
|
||||
HashSet<nint> seen = new(addresses.Count);
|
||||
@@ -524,7 +580,10 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
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
|
||||
{
|
||||
@@ -538,7 +597,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
QueueFrameworkUpdate(() => TrackGameObject((GameObject*)companion));
|
||||
}
|
||||
|
||||
private void OnCompanionTerminated(Companion* companion)
|
||||
private unsafe void OnCompanionTerminated(Companion* companion)
|
||||
{
|
||||
var address = (nint)companion;
|
||||
QueueFrameworkUpdate(() => UntrackGameObject(address));
|
||||
@@ -559,107 +618,46 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
_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 _);
|
||||
if (bucket.IsEmpty)
|
||||
{
|
||||
_actorsByName.TryRemove(descriptor.Name, out _);
|
||||
}
|
||||
_actorsByName.TryRemove(descriptor.Name, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDescriptorToCollections(ActorDescriptor descriptor)
|
||||
private void AddDescriptor(ActorDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||
{
|
||||
_renderedPlayers.Add(descriptor.Address);
|
||||
if (descriptor.IsLocalPlayer)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
_activePlayers[descriptor.Address] = descriptor;
|
||||
IndexDescriptor(descriptor);
|
||||
_ownedTracker.OnDescriptorAdded(descriptor);
|
||||
PublishSnapshot();
|
||||
}
|
||||
|
||||
private void RemoveDescriptorFromCollections(ActorDescriptor descriptor)
|
||||
private void RemoveDescriptor(ActorDescriptor descriptor)
|
||||
{
|
||||
if (descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||
{
|
||||
_renderedPlayers.Remove(descriptor.Address);
|
||||
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;
|
||||
}
|
||||
}
|
||||
RemoveDescriptorFromIndexes(descriptor);
|
||||
_ownedTracker.OnDescriptorRemoved(descriptor);
|
||||
PublishSnapshot();
|
||||
}
|
||||
|
||||
private void RebuildSnapshots()
|
||||
private void PublishSnapshot()
|
||||
{
|
||||
var playerDescriptors = _activePlayers.Values
|
||||
.Where(descriptor => descriptor.ObjectKind == DalamudObjectKind.Player)
|
||||
.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);
|
||||
Volatile.Write(ref _playerAddressSnapshot, playerDescriptors.Select(d => d.Address).ToArray());
|
||||
Volatile.Write(ref _renderedPlayerSnapshot, _renderedPlayers.ToArray());
|
||||
Volatile.Write(ref _renderedCompanionSnapshot, _renderedCompanions.ToArray());
|
||||
Volatile.Write(ref _ownedObjectSnapshot, _ownedObjects.Keys.ToArray());
|
||||
Volatile.Write(ref _ownedObjectMapSnapshot, new Dictionary<nint, LightlessObjectKind>(_ownedObjects));
|
||||
var ownedSnapshot = _ownedTracker.CreateSnapshot();
|
||||
var nextGeneration = Snapshot.Generation + 1;
|
||||
var snapshot = new ActorSnapshot(playerDescriptors, playerAddresses, ownedSnapshot, nextGeneration);
|
||||
Volatile.Write(ref _snapshot, snapshot);
|
||||
}
|
||||
|
||||
private void QueueFrameworkUpdate(Action action)
|
||||
@@ -673,7 +671,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
_framework.RunOnFrameworkThread(action);
|
||||
_ = _framework.RunOnFrameworkThread(action);
|
||||
}
|
||||
|
||||
private void DisposeHooks()
|
||||
@@ -723,7 +721,7 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
or DalamudObjectKind.Companion
|
||||
or DalamudObjectKind.MountType;
|
||||
|
||||
private static List<nint> EnumerateActiveCharacterAddresses()
|
||||
private static unsafe List<nint> EnumerateActiveCharacterAddresses()
|
||||
{
|
||||
var results = new List<nint>(64);
|
||||
var manager = GameObjectManager.Instance();
|
||||
@@ -751,4 +749,170 @@ public sealed unsafe class ActorObjectService : IHostedService, IDisposable
|
||||
|
||||
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.Services.ActorTracking;
|
||||
using LightlessSync.Services.Mediator;
|
||||
@@ -21,12 +20,11 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
private const int MaxReportContextLength = 1000;
|
||||
|
||||
private readonly ApiController _apiController;
|
||||
private readonly ChatConfigService _chatConfigService;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
private readonly ActorObjectService _actorObjectService;
|
||||
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 List<string> _channelOrder = new();
|
||||
@@ -55,7 +53,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
PairUiService pairUiService)
|
||||
: base(logger, mediator)
|
||||
{
|
||||
_chatConfigService = chatConfigService;
|
||||
_apiController = apiController;
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_actorObjectService = actorObjectService;
|
||||
@@ -63,12 +60,12 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
_isLoggedIn = _dalamudUtilService.IsLoggedIn;
|
||||
_isConnected = _apiController.IsConnected;
|
||||
_chatEnabled = _chatConfigService.Current.AutoEnableChatOnLogin;
|
||||
_chatEnabled = chatConfigService.Current.AutoEnableChatOnLogin;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ChatChannelSnapshot> GetChannelsSnapshot()
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
var snapshots = new List<ChatChannelSnapshot>(_channelOrder.Count);
|
||||
foreach (var key in _channelOrder)
|
||||
@@ -107,7 +104,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
return _chatEnabled;
|
||||
}
|
||||
@@ -118,7 +115,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
return _chatEnabled && _isConnected;
|
||||
}
|
||||
@@ -127,7 +124,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
public void SetActiveChannel(string? key)
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_activeChannelKey = key;
|
||||
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()
|
||||
{
|
||||
bool wasEnabled;
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
wasEnabled = _chatEnabled;
|
||||
if (!wasEnabled)
|
||||
@@ -170,7 +167,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
List<ChatChannelDescriptor> groupDescriptors;
|
||||
ChatChannelDescriptor? zoneDescriptor;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
wasEnabled = _chatEnabled;
|
||||
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."));
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
if (!_chatEnabled)
|
||||
{
|
||||
@@ -311,8 +308,8 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
Mediator.Subscribe<DalamudLogoutMessage>(this, _ => HandleLogout());
|
||||
Mediator.Subscribe<ZoneSwitchEndMessage>(this, _ => ScheduleZonePresenceUpdate());
|
||||
Mediator.Subscribe<WorldChangedMessage>(this, _ => ScheduleZonePresenceUpdate(force: true));
|
||||
Mediator.Subscribe<ConnectedMessage>(this, msg => HandleConnected(msg.Connection));
|
||||
Mediator.Subscribe<HubReconnectedMessage>(this, _ => HandleConnected(null));
|
||||
Mediator.Subscribe<ConnectedMessage>(this, _ => HandleConnected());
|
||||
Mediator.Subscribe<HubReconnectedMessage>(this, _ => HandleConnected());
|
||||
Mediator.Subscribe<HubReconnectingMessage>(this, _ => HandleReconnecting());
|
||||
Mediator.Subscribe<DisconnectedMessage>(this, _ => HandleReconnecting());
|
||||
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;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_selfTokens.Clear();
|
||||
_pendingSelfMessages.Clear();
|
||||
@@ -410,7 +407,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
{
|
||||
_isConnected = false;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_selfTokens.Clear();
|
||||
_pendingSelfMessages.Clear();
|
||||
@@ -475,7 +472,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
private void UpdateChannelsForDisabledState()
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
foreach (var state in _channels.Values)
|
||||
{
|
||||
@@ -513,7 +510,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
string? zoneKey;
|
||||
ZoneChannelDefinition? definition = null;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_territoryToZoneKey.TryGetValue(territoryId, out zoneKey);
|
||||
if (zoneKey is not null)
|
||||
@@ -538,7 +535,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
bool shouldForceSend;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
var state = EnsureZoneStateLocked();
|
||||
state.DisplayName = definition.Value.DisplayName;
|
||||
@@ -566,7 +563,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
ChatChannelDescriptor? descriptor = null;
|
||||
bool clearedHistory = false;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
descriptor = _lastZoneDescriptor;
|
||||
_lastZoneDescriptor = null;
|
||||
@@ -590,9 +587,9 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
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>();
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_zoneDefinitions.Clear();
|
||||
_territoryToZoneKey.Clear();
|
||||
@@ -657,7 +654,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
{
|
||||
if (def.TerritoryNames.Contains(variant))
|
||||
{
|
||||
_territoryToZoneKey[(uint)kvp.Key] = def.Key;
|
||||
_territoryToZoneKey[kvp.Key] = def.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -689,7 +686,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
var descriptorsToJoin = new List<ChatChannelDescriptor>();
|
||||
var descriptorsToLeave = new List<ChatChannelDescriptor>();
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
var remainingGroups = new HashSet<string>(_groupDefinitions.Keys, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -807,7 +804,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
return;
|
||||
|
||||
List<ChatChannelDescriptor> descriptors;
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
descriptors = _channels.Values
|
||||
.Where(state => state.Type == ChatChannelType.Group)
|
||||
@@ -832,7 +829,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
var presenceKey = BuildPresenceKey(descriptor);
|
||||
bool stateMatches;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
stateMatches = !force
|
||||
&& _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);
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
@@ -870,7 +867,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
var key = normalized.Type == ChatChannelType.Zone ? ZoneChannelKey : BuildChannelKey(normalized);
|
||||
var pending = new PendingSelfMessage(key, message);
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_pendingSelfMessages.Add(pending);
|
||||
while (_pendingSelfMessages.Count > 20)
|
||||
@@ -884,7 +881,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
private void RemovePendingSelfMessage(PendingSelfMessage pending)
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
var index = _pendingSelfMessages.FindIndex(p =>
|
||||
string.Equals(p.ChannelKey, pending.ChannelKey, StringComparison.Ordinal) &&
|
||||
@@ -905,7 +902,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
var message = BuildMessage(dto, fromSelf);
|
||||
bool publishChannelList = false;
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
if (!_channels.TryGetValue(key, out var state))
|
||||
{
|
||||
@@ -960,7 +957,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
if (publishChannelList)
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
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))
|
||||
{
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
_selfTokens[channelKey] = dto.Sender.Token;
|
||||
}
|
||||
@@ -981,7 +978,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
return true;
|
||||
}
|
||||
|
||||
lock (_sync)
|
||||
using (_sync.EnterScope())
|
||||
{
|
||||
if (_selfTokens.TryGetValue(channelKey, out var token) &&
|
||||
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;
|
||||
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))
|
||||
{
|
||||
return descriptor.Name;
|
||||
@@ -1065,7 +1062,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
{
|
||||
_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;
|
||||
}
|
||||
@@ -1108,7 +1105,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
private static bool ChannelDescriptorsMatch(ChatChannelDescriptor left, ChatChannelDescriptor right)
|
||||
=> left.Type == right.Type
|
||||
&& NormalizeKey(left.CustomKey) == NormalizeKey(right.CustomKey)
|
||||
&& string.Equals(NormalizeKey(left.CustomKey), NormalizeKey(right.CustomKey), StringComparison.Ordinal)
|
||||
&& left.WorldId == right.WorldId;
|
||||
|
||||
private ChatChannelState EnsureZoneStateLocked()
|
||||
@@ -1180,6 +1177,3 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
||||
|
||||
private readonly record struct PendingSelfMessage(string ChannelKey, string Message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
|
||||
public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName)
|
||||
{
|
||||
if (_actorObjectService.TryGetActorByHash(characterName, out var actor))
|
||||
if (_actorObjectService.TryGetValidatedActorByHash(characterName, out var actor))
|
||||
return actor.Address;
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
@@ -639,7 +639,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,249 +1,692 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Plugin;
|
||||
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.Services.Mediator;
|
||||
using LightlessSync.Services.Rendering;
|
||||
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.Hosting;
|
||||
using Pictomancy;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
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;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
private readonly IObjectTable _gameObjects;
|
||||
private readonly IGameGui _gameGui;
|
||||
_logger = logger;
|
||||
_addonLifecycle = addonLifecycle;
|
||||
_gameGui = gameGui;
|
||||
_configService = configService;
|
||||
_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; }
|
||||
|
||||
public LightFinderPlateHandler(
|
||||
ILogger<LightFinderPlateHandler> logger,
|
||||
LightlessMediator mediator,
|
||||
IDalamudPluginInterface dalamudPluginInterface,
|
||||
LightlessConfigService configService,
|
||||
IObjectTable gameObjects,
|
||||
IGameGui gameGui)
|
||||
internal void Init()
|
||||
{
|
||||
if (!_drawSubscribed)
|
||||
{
|
||||
_logger = logger;
|
||||
Mediator = mediator;
|
||||
_pluginInterface = dalamudPluginInterface;
|
||||
_configService = configService;
|
||||
_gameObjects = gameObjects;
|
||||
_gameGui = gameGui;
|
||||
_uiBuilder.Draw += OnUiBuilderDraw;
|
||||
_drawSubscribed = true;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
EnableNameplate();
|
||||
_mediator.Subscribe<PriorityFrameworkUpdateMessage>(this, OnTick);
|
||||
}
|
||||
|
||||
internal void Uninit()
|
||||
{
|
||||
DisableNameplate();
|
||||
if (_drawSubscribed)
|
||||
{
|
||||
_logger.LogInformation("Starting LightFinderPlateHandler...");
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += OnDraw;
|
||||
|
||||
_logger.LogInformation("LightFinderPlateHandler started.");
|
||||
return Task.CompletedTask;
|
||||
_uiBuilder.Draw -= OnUiBuilderDraw;
|
||||
_drawSubscribed = false;
|
||||
}
|
||||
ClearLabelBuffer();
|
||||
_mediator.Unsubscribe<PriorityFrameworkUpdateMessage>(this);
|
||||
_mpNameplateAddon = null;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
internal void EnableNameplate()
|
||||
{
|
||||
if (!_mEnabled)
|
||||
{
|
||||
_logger.LogInformation("Stopping LightFinderPlateHandler...");
|
||||
|
||||
_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>())
|
||||
try
|
||||
{
|
||||
//Double check to be sure, should always be true due to OfType filter above
|
||||
if (obj is not IPlayerCharacter player)
|
||||
continue;
|
||||
|
||||
if (player.Address == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
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); */
|
||||
_addonLifecycle.RegisterListener(AddonEvent.PostDraw, "NamePlate", NameplateDrawDetour);
|
||||
_mEnabled = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unknown error while trying to enable nameplate.");
|
||||
DisableNameplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get screen offset based on distance to local player (to scale size appropriately)
|
||||
// I need to fine tune these values still
|
||||
private float GetScreenOffset(IPlayerCharacter player)
|
||||
internal void DisableNameplate()
|
||||
{
|
||||
if (_mEnabled)
|
||||
{
|
||||
var local = _gameObjects.LocalPlayer;
|
||||
if (local == null)
|
||||
return 32.1f;
|
||||
try
|
||||
{
|
||||
_addonLifecycle.UnregisterListener(NameplateDrawDetour);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unknown error while unregistering nameplate listener.");
|
||||
}
|
||||
|
||||
var delta = player.Position - local.Position;
|
||||
var dist = MathF.Sqrt(delta.X * delta.X + delta.Z * delta.Z);
|
||||
_mEnabled = false;
|
||||
ClearNameplateCaches();
|
||||
}
|
||||
}
|
||||
|
||||
const float minDist = 2.1f;
|
||||
const float maxDist = 30.4f;
|
||||
dist = Math.Clamp(dist, minDist, maxDist);
|
||||
|
||||
var t = 1f - (dist - minDist) / (maxDist - minDist);
|
||||
|
||||
const float minOffset = 24.4f;
|
||||
const float maxOffset = 56.4f;
|
||||
return minOffset + (maxOffset - minOffset) * t;
|
||||
private void NameplateDrawDetour(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
if (args.Addon.Address == nint.Zero)
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Warning))
|
||||
_logger.LogWarning("Nameplate draw detour received a null addon address, skipping update.");
|
||||
return;
|
||||
}
|
||||
|
||||
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))
|
||||
return false;
|
||||
var framework = Framework.Instance();
|
||||
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;
|
||||
}
|
||||
|
||||
// Approximate check to see if nameplate would be visible based on distance and screen position
|
||||
// Also has to be fine tuned still
|
||||
private bool ShouldApproximateNameplateVisible(IPlayerCharacter player)
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
if (local == null)
|
||||
return false;
|
||||
|
||||
var delta = player.Position - local.Position;
|
||||
var distance2D = MathF.Sqrt(delta.X * delta.X + delta.Z * delta.Z);
|
||||
if (distance2D > _defaultNameplateDistance)
|
||||
return false;
|
||||
|
||||
var verticalDelta = MathF.Abs(delta.Y);
|
||||
if (verticalDelta > 3.4f)
|
||||
return false;
|
||||
|
||||
return TryGetApproxNameplateScreenPos(player, out _);
|
||||
ScreenPosition = screenPosition;
|
||||
Text = text;
|
||||
TextColor = textColor;
|
||||
EdgeColor = edgeColor;
|
||||
FontSize = fontSize;
|
||||
Pivot = pivot;
|
||||
UseIcon = useIcon;
|
||||
}
|
||||
|
||||
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;
|
||||
if (gameObject == null)
|
||||
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));
|
||||
UpdateNameplateNodes();
|
||||
_needsLabelRefresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
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.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using OtterTex;
|
||||
using OtterImage = OtterTex.Image;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
@@ -11,7 +12,6 @@ using Microsoft.Extensions.Logging;
|
||||
using Lumina.Data.Files;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
/*
|
||||
* 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, string> _downscaledPaths = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly SemaphoreSlim _downscaleSemaphore = new(4);
|
||||
private static readonly IReadOnlyDictionary<int, TextureCompressionTarget> BlockCompressedFormatMap =
|
||||
new Dictionary<int, TextureCompressionTarget>
|
||||
{
|
||||
@@ -80,7 +81,10 @@ public sealed class TextureDownscaleService
|
||||
if (!filePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase)) 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)
|
||||
@@ -108,6 +112,7 @@ public sealed class TextureDownscaleService
|
||||
bool onlyDownscaleUncompressed = false;
|
||||
bool? isIndexTexture = null;
|
||||
|
||||
await _downscaleSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!File.Exists(sourcePath))
|
||||
@@ -157,6 +162,15 @@ public sealed class TextureDownscaleService
|
||||
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))
|
||||
{
|
||||
_downscaledPaths[hash] = sourcePath;
|
||||
@@ -172,21 +186,20 @@ public sealed class TextureDownscaleService
|
||||
var height = rgbaInfo.Meta.Height;
|
||||
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);
|
||||
|
||||
var targetSize = CalculateTargetSize(originalImage.Width, originalImage.Height, targetMaxDimension);
|
||||
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;
|
||||
}
|
||||
|
||||
using var resized = IndexDownscaler.Downscale(originalImage, targetSize.width, targetSize.height, BlockMultiple);
|
||||
|
||||
var resizedPixels = new byte[targetSize.width * targetSize.height * 4];
|
||||
resized.CopyPixelDataTo(resizedPixels);
|
||||
|
||||
using var resizedScratch = ScratchImage.FromRGBA(resizedPixels, targetSize.width, targetSize.height, out var creationInfo).ThrowIfError(creationInfo);
|
||||
using var resizedScratch = CreateScratchImage(resized, targetSize.width, targetSize.height);
|
||||
using var finalScratch = resizedScratch.Convert(DXGIFormat.B8G8R8A8UNorm);
|
||||
|
||||
TexFileHelper.Save(destination, finalScratch);
|
||||
@@ -209,6 +222,7 @@ public sealed class TextureDownscaleService
|
||||
}
|
||||
finally
|
||||
{
|
||||
_downscaleSemaphore.Release();
|
||||
_activeJobs.TryRemove(hash, out _);
|
||||
}
|
||||
}
|
||||
@@ -227,6 +241,41 @@ public sealed class TextureDownscaleService
|
||||
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)
|
||||
=> kind is TextureMapKind.Mask
|
||||
or TextureMapKind.Index;
|
||||
@@ -420,21 +469,6 @@ public sealed class TextureDownscaleService
|
||||
private static int ReduceDimension(int value)
|
||||
=> 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (width <= targetMaxDimension || height <= targetMaxDimension)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (depth > 1 && depth <= targetMaxDimension)
|
||||
if (width <= targetMaxDimension && height <= targetMaxDimension && depth <= targetMaxDimension)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,10 +51,11 @@ public class IdDisplayHandler
|
||||
(bool textIsUid, string playerText) = GetGroupText(group);
|
||||
if (!string.Equals(_editEntry, group.GID, StringComparison.Ordinal))
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
|
||||
using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid))
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(playerText);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
@@ -121,108 +122,113 @@ public class IdDisplayHandler
|
||||
|
||||
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 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))
|
||||
{
|
||||
SeStringUtils.RenderSeStringWithHitbox(seString, rowStart, font, pair.UserData.UID);
|
||||
itemMin = ImGui.GetItemRectMin();
|
||||
itemMax = ImGui.GetItemRectMax();
|
||||
}
|
||||
ImGui.AlignTextToFramePadding();
|
||||
var rowStart = ImGui.GetCursorScreenPos();
|
||||
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 frameHeight = ImGui.GetFrameHeight();
|
||||
var rowTop = rowStart.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();
|
||||
frameHeightForStats = ImGui.GetFrameHeight();
|
||||
rowTopForStats = nameRectMin.Y - style.FramePadding.Y;
|
||||
}
|
||||
|
||||
var nameRectMin = ImGui.GetItemRectMin();
|
||||
var nameRectMax = ImGui.GetItemRectMax();
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
if (!string.Equals(_lastMouseOverUid, id, StringComparison.Ordinal))
|
||||
@@ -292,12 +298,9 @@ public class IdDisplayHandler
|
||||
const float compactFontScale = 0.85f;
|
||||
ImGui.SetWindowFontScale(compactFontScale);
|
||||
var compactHeight = ImGui.GetTextLineHeight();
|
||||
var nameHeight = nameRectMax.Y - nameRectMin.Y;
|
||||
var targetPos = ImGui.GetCursorScreenPos();
|
||||
var availableWidth = MathF.Max(rowRightLimit - targetPos.X, 0f);
|
||||
var centeredY = nameRectMin.Y + MathF.Max((nameHeight - compactHeight) * 0.5f, 0f);
|
||||
float verticalOffset = 1f * ImGuiHelpers.GlobalScale;
|
||||
centeredY += verticalOffset;
|
||||
var centeredY = rowTopForStats + MathF.Max((frameHeightForStats - compactHeight) * 0.5f, 0f);
|
||||
ImGui.SetCursorScreenPos(new Vector2(targetPos.X, centeredY));
|
||||
|
||||
var performanceText = string.Empty;
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
private readonly ImGuiWindowFlags _unpinnedWindowFlags;
|
||||
private float _currentWindowOpacity = DefaultWindowOpacity;
|
||||
private bool _isWindowPinned;
|
||||
private bool _showRulesOverlay = true;
|
||||
private bool _showRulesOverlay;
|
||||
|
||||
private string? _selectedChannelKey;
|
||||
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";
|
||||
Vector4 color;
|
||||
@@ -577,12 +577,10 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
ImGui.Separator();
|
||||
|
||||
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[..(int)ReportReasonMaxLength];
|
||||
}
|
||||
_reportReason = _reportReason[..ReportReasonMaxLength];
|
||||
}
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
||||
@@ -591,12 +589,10 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
|
||||
ImGui.Spacing();
|
||||
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[..(int)ReportContextMaxLength];
|
||||
}
|
||||
_reportAdditionalContext = _reportAdditionalContext[..ReportContextMaxLength];
|
||||
}
|
||||
|
||||
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;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
@@ -920,7 +916,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
|
||||
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;
|
||||
|
||||
_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 showBadge = !isSelected && channel.UnreadCount > 0;
|
||||
var isZoneChannel = channel.Type == ChatChannelType.Zone;
|
||||
var badgeText = string.Empty;
|
||||
var badgePadding = Vector2.Zero;
|
||||
var badgeTextSize = Vector2.Zero;
|
||||
float badgeWidth = 0f;
|
||||
float badgeHeight = 0f;
|
||||
(string Text, Vector2 TextSize, float Width, float Height)? badgeMetrics = null;
|
||||
|
||||
var normal = isSelected ? UIColors.Get("LightlessPurpleDefault") : UIColors.Get("ButtonDefault");
|
||||
var hovered = isSelected
|
||||
@@ -1285,15 +1277,16 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||
if (showBadge)
|
||||
{
|
||||
var badgeSpacing = 4f * ImGuiHelpers.GlobalScale;
|
||||
badgePadding = new Vector2(4f, 1.5f) * ImGuiHelpers.GlobalScale;
|
||||
badgeText = channel.UnreadCount > MaxBadgeDisplay
|
||||
var badgePadding = new Vector2(4f, 1.5f) * ImGuiHelpers.GlobalScale;
|
||||
var badgeText = channel.UnreadCount > MaxBadgeDisplay
|
||||
? $"{MaxBadgeDisplay}+"
|
||||
: channel.UnreadCount.ToString(CultureInfo.InvariantCulture);
|
||||
badgeTextSize = ImGui.CalcTextSize(badgeText);
|
||||
badgeWidth = badgeTextSize.X + badgePadding.X * 2f;
|
||||
badgeHeight = badgeTextSize.Y + badgePadding.Y * 2f;
|
||||
var badgeTextSize = ImGui.CalcTextSize(badgeText);
|
||||
var badgeWidth = badgeTextSize.X + badgePadding.X * 2f;
|
||||
var badgeHeight = badgeTextSize.Y + badgePadding.Y * 2f;
|
||||
var customPadding = new Vector2(baseFramePadding.X + badgeWidth + badgeSpacing, baseFramePadding.Y);
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, customPadding);
|
||||
badgeMetrics = (badgeText, badgeTextSize, badgeWidth, badgeHeight);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (showBadge)
|
||||
if (showBadge && badgeMetrics is { } metrics)
|
||||
{
|
||||
var buttonSizeY = itemMax.Y - itemMin.Y;
|
||||
var badgeMin = new Vector2(
|
||||
itemMin.X + baseFramePadding.X,
|
||||
itemMin.Y + (buttonSizeY - badgeHeight) * 0.5f);
|
||||
var badgeMax = badgeMin + new Vector2(badgeWidth, badgeHeight);
|
||||
itemMin.Y + (buttonSizeY - metrics.Height) * 0.5f);
|
||||
var badgeMax = badgeMin + new Vector2(metrics.Width, metrics.Height);
|
||||
var badgeColor = UIColors.Get("DimRed");
|
||||
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(
|
||||
badgeMin.X + (badgeWidth - badgeTextSize.X) * 0.5f,
|
||||
badgeMin.Y + (badgeHeight - badgeTextSize.Y) * 0.5f);
|
||||
drawList.AddText(textPos, ImGui.ColorConvertFloat4ToU32(ImGuiColors.DalamudWhite), badgeText);
|
||||
badgeMin.X + (metrics.Width - metrics.TextSize.X) * 0.5f,
|
||||
badgeMin.Y + (metrics.Height - metrics.TextSize.Y) * 0.5f);
|
||||
drawList.AddText(textPos, ImGui.ColorConvertFloat4ToU32(ImGuiColors.DalamudWhite), metrics.Text);
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
||||
@@ -565,19 +565,30 @@ public static class SeStringUtils
|
||||
public static Vector2 RenderSeStringWithHitbox(DalamudSeString seString, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||
{
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
var usedFont = font ?? UiBuilder.MonoFont;
|
||||
var drawParams = new SeStringDrawParams
|
||||
{
|
||||
Font = font ?? UiBuilder.MonoFont,
|
||||
Font = usedFont,
|
||||
Color = 0xFFFFFFFF,
|
||||
WrapWidth = float.MaxValue,
|
||||
TargetDrawList = drawList
|
||||
};
|
||||
|
||||
ImGui.SetCursorScreenPos(position);
|
||||
ImGuiHelpers.SeStringWrapped(seString.Encode(), drawParams);
|
||||
|
||||
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);
|
||||
if (id is not null)
|
||||
@@ -591,30 +602,52 @@ public static class SeStringUtils
|
||||
|
||||
try
|
||||
{
|
||||
ImGui.InvisibleButton("##hitbox", textSize);
|
||||
ImGui.InvisibleButton("##hitbox", new Vector2(textSize.X, hitboxHeight));
|
||||
}
|
||||
finally
|
||||
{
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
return textSize;
|
||||
return new Vector2(textSize.X, hitboxHeight);
|
||||
}
|
||||
|
||||
public static Vector2 RenderIconWithHitbox(int iconId, Vector2 position, ImFontPtr? font = null, string? id = null)
|
||||
{
|
||||
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,
|
||||
WrapWidth = float.MaxValue,
|
||||
TargetDrawList = drawList
|
||||
WrapWidth = float.MaxValue
|
||||
};
|
||||
|
||||
var iconMacro = $"<icon({iconId})>";
|
||||
var drawResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, drawParams);
|
||||
var measureResult = ImGuiHelpers.CompileSeStringWrapped(iconMacro, measureParams);
|
||||
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);
|
||||
if (id is not null)
|
||||
@@ -628,14 +661,14 @@ public static class SeStringUtils
|
||||
|
||||
try
|
||||
{
|
||||
ImGui.InvisibleButton("##iconHitbox", drawResult.Size);
|
||||
ImGui.InvisibleButton("##iconHitbox", new Vector2(iconSize.X, hitboxHeight));
|
||||
}
|
||||
finally
|
||||
{
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
return drawResult.Size;
|
||||
return new Vector2(iconSize.X, hitboxHeight);
|
||||
}
|
||||
|
||||
#region Internal Payloads
|
||||
|
||||
@@ -177,7 +177,8 @@ public static class VariousExtensions
|
||||
if (objectKind != ObjectKind.Player) continue;
|
||||
|
||||
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);
|
||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
|
||||
|
||||
@@ -12,7 +12,6 @@ using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
|
||||
namespace LightlessSync.WebAPI.Files;
|
||||
|
||||
@@ -22,12 +21,10 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
private readonly FileCompactor _fileCompactor;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly FileTransferOrchestrator _orchestrator;
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||
private readonly ConcurrentDictionary<ThrottledStream, byte> _activeDownloadStreams;
|
||||
private static readonly TimeSpan DownloadStallTimeout = TimeSpan.FromSeconds(30);
|
||||
private volatile bool _disableDirectDownloads;
|
||||
private int _consecutiveDirectDownloadFailures;
|
||||
private bool _lastConfigDirectDownloadsState;
|
||||
@@ -38,7 +35,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
FileTransferOrchestrator orchestrator,
|
||||
FileCacheManager fileCacheManager,
|
||||
FileCompactor fileCompactor,
|
||||
PairProcessingLimiter pairProcessingLimiter,
|
||||
LightlessConfigService configService,
|
||||
TextureDownscaleService textureDownscaleService, TextureMetadataHelper textureMetadataHelper) : base(logger, mediator)
|
||||
{
|
||||
@@ -46,7 +42,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
_orchestrator = orchestrator;
|
||||
_fileDbManager = fileCacheManager;
|
||||
_fileCompactor = fileCompactor;
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_configService = configService;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_textureMetadataHelper = textureMetadataHelper;
|
||||
@@ -282,42 +277,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
int bytesRead;
|
||||
try
|
||||
{
|
||||
using var readCancellation = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
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);
|
||||
bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), ct).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
@@ -340,11 +300,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
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)
|
||||
{
|
||||
throw;
|
||||
|
||||
@@ -521,26 +521,979 @@
|
||||
"resolved": "17.6.3",
|
||||
"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": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.3",
|
||||
"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": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
"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": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.3",
|
||||
"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": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.3",
|
||||
"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": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
@@ -569,6 +1522,15 @@
|
||||
},
|
||||
"penumbra.string": {
|
||||
"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