102
.editorconfig
Normal file
102
.editorconfig
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# MA0046: Use EventHandler<T> to declare events
|
||||||
|
dotnet_diagnostic.MA0046.severity = silent
|
||||||
|
csharp_indent_labels = one_less_than_current
|
||||||
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
|
csharp_prefer_braces = true:silent
|
||||||
|
csharp_style_namespace_declarations = block_scoped:silent
|
||||||
|
csharp_style_prefer_method_group_conversion = true:silent
|
||||||
|
csharp_style_prefer_top_level_statements = true:silent
|
||||||
|
csharp_style_expression_bodied_methods = false:silent
|
||||||
|
csharp_style_expression_bodied_constructors = false:silent
|
||||||
|
csharp_style_expression_bodied_operators = false:silent
|
||||||
|
csharp_style_expression_bodied_properties = true:silent
|
||||||
|
csharp_style_expression_bodied_indexers = true:silent
|
||||||
|
csharp_style_expression_bodied_accessors = true:silent
|
||||||
|
csharp_style_expression_bodied_lambdas = true:silent
|
||||||
|
csharp_style_expression_bodied_local_functions = false:silent
|
||||||
|
csharp_style_throw_expression = true:suggestion
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||||
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_prefer_index_operator = true:suggestion
|
||||||
|
csharp_style_prefer_range_operator = true:suggestion
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||||
|
csharp_style_prefer_tuple_swap = true:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
dotnet_diagnostic.MA0016.severity = suggestion
|
||||||
|
dotnet_diagnostic.MA0051.severity = suggestion
|
||||||
|
|
||||||
|
# MA0009: Add regex evaluation timeout
|
||||||
|
dotnet_diagnostic.MA0009.severity = silent
|
||||||
|
|
||||||
|
[*.{cs,vb}]
|
||||||
|
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||||
|
tab_width = 4
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = crlf
|
||||||
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
|
dotnet_style_null_propagation = true:suggestion
|
||||||
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||||
|
dotnet_style_prefer_auto_properties = true:silent
|
||||||
|
dotnet_style_object_initializer = true:suggestion
|
||||||
|
dotnet_style_collection_initializer = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||||
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
|
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||||
|
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||||
|
dotnet_style_namespace_match_folder = true:suggestion
|
||||||
|
[*.{cs,vb}]
|
||||||
|
#### Naming styles ####
|
||||||
|
|
||||||
|
# Naming rules
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
# Symbol specifications
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
# Naming styles
|
||||||
|
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
2
MareAPI
2
MareAPI
Submodule MareAPI updated: 9dc1e901aa...2d5d9d9d1c
@@ -9,6 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "MareA
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumbra\Penumbra.GameData\Penumbra.GameData.csproj", "{89DD407C-B2B7-4BB3-BF26-C550BA1841F8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumbra\Penumbra.GameData\Penumbra.GameData.csproj", "{89DD407C-B2B7-4BB3-BF26-C550BA1841F8}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{585B740D-BA2C-429B-9CF3-B2D223423748}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ using System.Linq;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
|
|
||||||
namespace MareSynchronos
|
namespace MareSynchronos;
|
||||||
{
|
|
||||||
public static class ConfigurationExtensions
|
public static class ConfigurationExtensions
|
||||||
{
|
{
|
||||||
public static bool HasValidSetup(this Configuration configuration)
|
public static bool HasValidSetup(this Configuration configuration)
|
||||||
@@ -23,14 +23,31 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
return configuration.UidServerComments.ContainsKey(configuration.ApiUri)
|
return configuration.UidServerComments.ContainsKey(configuration.ApiUri)
|
||||||
? configuration.UidServerComments[configuration.ApiUri]
|
? configuration.UidServerComments[configuration.ApiUri]
|
||||||
: new Dictionary<string, string>();
|
: new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, string> GetCurrentServerGidComments(this Configuration configuration)
|
||||||
|
{
|
||||||
|
return configuration.GidServerComments.ContainsKey(configuration.ApiUri)
|
||||||
|
? configuration.GidServerComments[configuration.ApiUri]
|
||||||
|
: new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetCurrentServerGidComment(this Configuration configuration, string gid, string comment)
|
||||||
|
{
|
||||||
|
if (!configuration.GidServerComments.ContainsKey(configuration.ApiUri))
|
||||||
|
{
|
||||||
|
configuration.GidServerComments[configuration.ApiUri] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.GidServerComments[configuration.ApiUri][gid] = comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetCurrentServerUidComment(this Configuration configuration, string uid, string comment)
|
public static void SetCurrentServerUidComment(this Configuration configuration, string uid, string comment)
|
||||||
{
|
{
|
||||||
if (!configuration.UidServerComments.ContainsKey(configuration.ApiUri))
|
if (!configuration.UidServerComments.ContainsKey(configuration.ApiUri))
|
||||||
{
|
{
|
||||||
configuration.UidServerComments[configuration.ApiUri] = new Dictionary<string, string>();
|
configuration.UidServerComments[configuration.ApiUri] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration.UidServerComments[configuration.ApiUri][uid] = comment;
|
configuration.UidServerComments[configuration.ApiUri][uid] = comment;
|
||||||
@@ -54,10 +71,10 @@ namespace MareSynchronos
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string CacheFolder { get; set; } = string.Empty;
|
public string CacheFolder { get; set; } = string.Empty;
|
||||||
public Dictionary<string, string> ClientSecret { get; set; } = new();
|
public Dictionary<string, string> ClientSecret { get; set; } = new(StringComparer.Ordinal);
|
||||||
public Dictionary<string, string> CustomServerList { get; set; } = new();
|
public Dictionary<string, string> CustomServerList { get; set; } = new(StringComparer.Ordinal);
|
||||||
public int MaxLocalCacheInGiB { get; set; } = 20;
|
public int MaxLocalCacheInGiB { get; set; } = 20;
|
||||||
public bool ReverseUserSort { get; set; } = true;
|
public bool ReverseUserSort { get; set; } = false;
|
||||||
|
|
||||||
public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
|
public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
|
||||||
public bool FileScanPaused { get; set; } = false;
|
public bool FileScanPaused { get; set; } = false;
|
||||||
@@ -65,9 +82,10 @@ namespace MareSynchronos
|
|||||||
public bool InitialScanComplete { get; set; } = false;
|
public bool InitialScanComplete { get; set; } = false;
|
||||||
|
|
||||||
public bool FullPause { get; set; } = false;
|
public bool FullPause { get; set; } = false;
|
||||||
public Dictionary<string, Dictionary<string, string>> UidServerComments { get; set; } = new();
|
public Dictionary<string, Dictionary<string, string>> UidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||||
|
public Dictionary<string, Dictionary<string, string>> GidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
public Dictionary<string, string> UidComments { get; set; } = new();
|
public Dictionary<string, string> UidComments { get; set; } = new(StringComparer.Ordinal);
|
||||||
public int Version { get; set; } = 5;
|
public int Version { get; set; } = 5;
|
||||||
|
|
||||||
public bool ShowTransferWindow { get; set; } = true;
|
public bool ShowTransferWindow { get; set; } = true;
|
||||||
@@ -96,10 +114,10 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
Logger.Debug("Migrating Configuration from V0 to V1");
|
Logger.Debug("Migrating Configuration from V0 to V1");
|
||||||
Version = 1;
|
Version = 1;
|
||||||
ApiUri = ApiUri.Replace("https", "wss");
|
ApiUri = ApiUri.Replace("https", "wss", StringComparison.Ordinal);
|
||||||
foreach (var kvp in ClientSecret.ToList())
|
foreach (var kvp in ClientSecret.ToList())
|
||||||
{
|
{
|
||||||
var newKey = kvp.Key.Replace("https", "wss");
|
var newKey = kvp.Key.Replace("https", "wss", StringComparison.Ordinal);
|
||||||
ClientSecret.Remove(kvp.Key);
|
ClientSecret.Remove(kvp.Key);
|
||||||
if (ClientSecret.ContainsKey(newKey))
|
if (ClientSecret.ContainsKey(newKey))
|
||||||
{
|
{
|
||||||
@@ -110,7 +128,7 @@ namespace MareSynchronos
|
|||||||
ClientSecret.Add(newKey, kvp.Value);
|
ClientSecret.Add(newKey, kvp.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UidServerComments.Add(ApiUri, UidComments.ToDictionary(k => k.Key, k => k.Value));
|
UidServerComments.Add(ApiUri, UidComments.ToDictionary(k => k.Key, k => k.Value, StringComparer.Ordinal));
|
||||||
UidComments.Clear();
|
UidComments.Clear();
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
@@ -118,10 +136,10 @@ namespace MareSynchronos
|
|||||||
if (Version == 1)
|
if (Version == 1)
|
||||||
{
|
{
|
||||||
Logger.Debug("Migrating Configuration from V1 to V2");
|
Logger.Debug("Migrating Configuration from V1 to V2");
|
||||||
ApiUri = ApiUri.Replace("5001", "5000");
|
ApiUri = ApiUri.Replace("5001", "5000", StringComparison.Ordinal);
|
||||||
foreach (var kvp in ClientSecret.ToList())
|
foreach (var kvp in ClientSecret.ToList())
|
||||||
{
|
{
|
||||||
var newKey = kvp.Key.Replace("5001", "5000");
|
var newKey = kvp.Key.Replace("5001", "5000", StringComparison.Ordinal);
|
||||||
ClientSecret.Remove(kvp.Key);
|
ClientSecret.Remove(kvp.Key);
|
||||||
if (ClientSecret.ContainsKey(newKey))
|
if (ClientSecret.ContainsKey(newKey))
|
||||||
{
|
{
|
||||||
@@ -135,7 +153,7 @@ namespace MareSynchronos
|
|||||||
|
|
||||||
foreach (var kvp in UidServerComments.ToList())
|
foreach (var kvp in UidServerComments.ToList())
|
||||||
{
|
{
|
||||||
var newKey = kvp.Key.Replace("5001", "5000");
|
var newKey = kvp.Key.Replace("5001", "5000", StringComparison.Ordinal);
|
||||||
UidServerComments.Remove(kvp.Key);
|
UidServerComments.Remove(kvp.Key);
|
||||||
UidServerComments.Add(newKey, kvp.Value);
|
UidServerComments.Add(newKey, kvp.Value);
|
||||||
}
|
}
|
||||||
@@ -159,10 +177,10 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
Logger.Debug("Migrating Configuration from V3 to V4");
|
Logger.Debug("Migrating Configuration from V3 to V4");
|
||||||
|
|
||||||
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872");
|
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||||
foreach (var kvp in ClientSecret.ToList())
|
foreach (var kvp in ClientSecret.ToList())
|
||||||
{
|
{
|
||||||
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872");
|
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||||
ClientSecret.Remove(kvp.Key);
|
ClientSecret.Remove(kvp.Key);
|
||||||
if (ClientSecret.ContainsKey(newKey))
|
if (ClientSecret.ContainsKey(newKey))
|
||||||
{
|
{
|
||||||
@@ -176,7 +194,7 @@ namespace MareSynchronos
|
|||||||
|
|
||||||
foreach (var kvp in UidServerComments.ToList())
|
foreach (var kvp in UidServerComments.ToList())
|
||||||
{
|
{
|
||||||
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872");
|
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||||
UidServerComments.Remove(kvp.Key);
|
UidServerComments.Remove(kvp.Key);
|
||||||
UidServerComments.Add(newKey, kvp.Value);
|
UidServerComments.Add(newKey, kvp.Value);
|
||||||
}
|
}
|
||||||
@@ -189,7 +207,7 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
Logger.Debug("Migrating Configuration from V4 to V5");
|
Logger.Debug("Migrating Configuration from V4 to V5");
|
||||||
|
|
||||||
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6872", "wss://maresynchronos.com");
|
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6872", "wss://maresynchronos.com", StringComparison.Ordinal);
|
||||||
ClientSecret.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
ClientSecret.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
||||||
UidServerComments.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
UidServerComments.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
||||||
|
|
||||||
@@ -198,4 +216,3 @@ namespace MareSynchronos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class CharacterDataFactory
|
|||||||
|
|
||||||
if (cache.FileReplacements.ContainsKey(objectKind))
|
if (cache.FileReplacements.ContainsKey(objectKind))
|
||||||
{
|
{
|
||||||
if (cache.FileReplacements[objectKind].Any(c => c.ResolvedPath.Contains(mtrlPath)))
|
if (cache.FileReplacements[objectKind].Any(c => c.ResolvedPath.Contains(mtrlPath, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ public class CharacterDataFactory
|
|||||||
|
|
||||||
if (cache.FileReplacements.ContainsKey(objectKind))
|
if (cache.FileReplacements.ContainsKey(objectKind))
|
||||||
{
|
{
|
||||||
if (cache.FileReplacements[objectKind].Any(c => c.GamePaths.Contains(varPath)))
|
if (cache.FileReplacements[objectKind].Any(c => c.GamePaths.Contains(varPath, StringComparer.Ordinal)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ public class CharacterDataFactory
|
|||||||
|
|
||||||
if (cache.FileReplacements.ContainsKey(objectKind))
|
if (cache.FileReplacements.ContainsKey(objectKind))
|
||||||
{
|
{
|
||||||
if (cache.FileReplacements[objectKind].Any(c => c.GamePaths.Contains(texPath)))
|
if (cache.FileReplacements[objectKind].Any(c => c.GamePaths.Contains(texPath, StringComparer.Ordinal)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ public class CharacterDataFactory
|
|||||||
|
|
||||||
cache.AddFileReplacement(objectKind, texFileReplacement);
|
cache.AddFileReplacement(objectKind, texFileReplacement);
|
||||||
|
|
||||||
if (texPath.Contains("/--")) return;
|
if (texPath.Contains("/--", StringComparison.Ordinal)) return;
|
||||||
|
|
||||||
var texDx11Replacement =
|
var texDx11Replacement =
|
||||||
CreateFileReplacement(texPath.Insert(texPath.LastIndexOf('/') + 1, "--"), doNotReverseResolve);
|
CreateFileReplacement(texPath.Insert(texPath.LastIndexOf('/') + 1, "--"), doNotReverseResolve);
|
||||||
@@ -322,13 +322,13 @@ public class CharacterDataFactory
|
|||||||
previousData.FileReplacements.Add(objectKind, new());
|
previousData.FileReplacements.Add(objectKind, new());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!previousData.FileReplacements[objectKind].Any(k => k.GamePaths.Any(p => item.GamePaths.Select(p => p.ToLowerInvariant()).Contains(p.ToLowerInvariant()))))
|
if (!previousData.FileReplacements[objectKind].Any(k => k.GamePaths.Any(p => item.GamePaths.Contains(p, StringComparer.OrdinalIgnoreCase))))
|
||||||
{
|
{
|
||||||
var penumResolve = _ipcManager.PenumbraResolvePath(item.GamePaths.First()).ToLowerInvariant();
|
var penumResolve = _ipcManager.PenumbraResolvePath(item.GamePaths.First()).ToLowerInvariant();
|
||||||
var gamePath = item.GamePaths.First().ToLowerInvariant();
|
var gamePath = item.GamePaths.First().ToLowerInvariant();
|
||||||
if (penumResolve == gamePath)
|
if (string.Equals(penumResolve, gamePath, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
Logger.Debug("PenumResolve was same as GamePath, not adding " + item);
|
Logger.Verbose("PenumResolve was same as GamePath, not adding " + item);
|
||||||
transientResourceManager.RemoveTransientResource(charaPointer, item);
|
transientResourceManager.RemoveTransientResource(charaPointer, item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ using System.Globalization;
|
|||||||
|
|
||||||
namespace MareSynchronos.FileCache;
|
namespace MareSynchronos.FileCache;
|
||||||
|
|
||||||
public class FileCache
|
public class FileCacheEntity
|
||||||
{
|
{
|
||||||
public string ResolvedFilepath { get; private set; } = string.Empty;
|
public string ResolvedFilepath { get; private set; } = string.Empty;
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
public string PrefixedFilePath { get; init; }
|
public string PrefixedFilePath { get; init; }
|
||||||
public string LastModifiedDateTicks { get; set; }
|
public string LastModifiedDateTicks { get; set; }
|
||||||
|
|
||||||
public FileCache(string hash, string path, string lastModifiedDateTicks)
|
public FileCacheEntity(string hash, string path, string lastModifiedDateTicks)
|
||||||
{
|
{
|
||||||
Hash = hash;
|
Hash = hash;
|
||||||
PrefixedFilePath = path;
|
PrefixedFilePath = path;
|
||||||
@@ -22,7 +22,7 @@ public class FileCache
|
|||||||
|
|
||||||
public void SetResolvedFilePath(string filePath)
|
public void SetResolvedFilePath(string filePath)
|
||||||
{
|
{
|
||||||
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\");
|
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\", System.StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CsvEntry => $"{Hash}{FileCacheManager.CsvSplit}{PrefixedFilePath}{FileCacheManager.CsvSplit}{LastModifiedDateTicks.ToString(CultureInfo.InvariantCulture)}";
|
public string CsvEntry => $"{Hash}{FileCacheManager.CsvSplit}{PrefixedFilePath}{FileCacheManager.CsvSplit}{LastModifiedDateTicks.ToString(CultureInfo.InvariantCulture)}";
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ public class FileCacheManager : IDisposable
|
|||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly string CsvPath;
|
private readonly string CsvPath;
|
||||||
private string CsvBakPath => CsvPath + ".bak";
|
private string CsvBakPath => CsvPath + ".bak";
|
||||||
private readonly ConcurrentDictionary<string, FileCache> FileCaches = new();
|
private readonly ConcurrentDictionary<string, FileCacheEntity> FileCaches = new(StringComparer.Ordinal);
|
||||||
public const string CsvSplit = "|";
|
public const string CsvSplit = "|";
|
||||||
private object _fileWriteLock = new object();
|
private object _fileWriteLock = new();
|
||||||
|
|
||||||
public FileCacheManager(IpcManager ipcManager, Configuration configuration, string configDirectoryName)
|
public FileCacheManager(IpcManager ipcManager, Configuration configuration, string configDirectoryName)
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ public class FileCacheManager : IDisposable
|
|||||||
var hash = splittedEntry[0];
|
var hash = splittedEntry[0];
|
||||||
var path = splittedEntry[1];
|
var path = splittedEntry[1];
|
||||||
var time = splittedEntry[2];
|
var time = splittedEntry[2];
|
||||||
FileCaches[path] = new FileCache(hash, path, time);
|
FileCaches[path] = new FileCacheEntity(hash, path, time);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -64,7 +64,7 @@ public class FileCacheManager : IDisposable
|
|||||||
|
|
||||||
public void WriteOutFullCsv()
|
public void WriteOutFullCsv()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
foreach (var entry in FileCaches.OrderBy(f => f.Value.PrefixedFilePath))
|
foreach (var entry in FileCaches.OrderBy(f => f.Value.PrefixedFilePath))
|
||||||
{
|
{
|
||||||
sb.AppendLine(entry.Value.CsvEntry);
|
sb.AppendLine(entry.Value.CsvEntry);
|
||||||
@@ -87,19 +87,19 @@ public class FileCacheManager : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileCache> GetAllFileCaches() => FileCaches.Values.ToList();
|
public List<FileCacheEntity> GetAllFileCaches() => FileCaches.Values.ToList();
|
||||||
|
|
||||||
public FileCache? GetFileCacheByHash(string hash)
|
public FileCacheEntity? GetFileCacheByHash(string hash)
|
||||||
{
|
{
|
||||||
if (FileCaches.Any(f => f.Value.Hash == hash))
|
if (FileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
return GetValidatedFileCache(FileCaches.FirstOrDefault(f => f.Value.Hash == hash).Value);
|
return GetValidatedFileCache(FileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (FileState, FileCache) ValidateFileCacheEntity(FileCache fileCache)
|
public (FileState, FileCacheEntity) ValidateFileCacheEntity(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
fileCache = ReplacePathPrefixes(fileCache);
|
fileCache = ReplacePathPrefixes(fileCache);
|
||||||
FileInfo fi = new(fileCache.ResolvedFilepath);
|
FileInfo fi = new(fileCache.ResolvedFilepath);
|
||||||
@@ -107,7 +107,7 @@ public class FileCacheManager : IDisposable
|
|||||||
{
|
{
|
||||||
return (FileState.RequireDeletion, fileCache);
|
return (FileState.RequireDeletion, fileCache);
|
||||||
}
|
}
|
||||||
if (fi.LastWriteTimeUtc.Ticks.ToString() != fileCache.LastModifiedDateTicks)
|
if (!string.Equals(fi.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture), fileCache.LastModifiedDateTicks, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return (FileState.RequireUpdate, fileCache);
|
return (FileState.RequireUpdate, fileCache);
|
||||||
}
|
}
|
||||||
@@ -115,10 +115,10 @@ public class FileCacheManager : IDisposable
|
|||||||
return (FileState.Valid, fileCache);
|
return (FileState.Valid, fileCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileCache? GetFileCacheByPath(string path)
|
public FileCacheEntity? GetFileCacheByPath(string path)
|
||||||
{
|
{
|
||||||
var cleanedPath = path.Replace("/", "\\").ToLowerInvariant().Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), "");
|
var cleanedPath = path.Replace("/", "\\", StringComparison.Ordinal).ToLowerInvariant().Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), "", StringComparison.Ordinal);
|
||||||
var entry = FileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath)).Value;
|
var entry = FileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.Ordinal)).Value;
|
||||||
|
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
{
|
{
|
||||||
@@ -131,35 +131,35 @@ public class FileCacheManager : IDisposable
|
|||||||
return validatedCacheEntry;
|
return validatedCacheEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileCache? CreateCacheEntry(string path)
|
public FileCacheEntity? CreateCacheEntry(string path)
|
||||||
{
|
{
|
||||||
Logger.Debug("Creating cache entry for " + path);
|
Logger.Verbose("Creating cache entry for " + path);
|
||||||
FileInfo fi = new(path);
|
FileInfo fi = new(path);
|
||||||
if (!fi.Exists) return null;
|
if (!fi.Exists) return null;
|
||||||
var fullName = fi.FullName.ToLowerInvariant();
|
var fullName = fi.FullName.ToLowerInvariant();
|
||||||
if (!fullName.Contains(_configuration.CacheFolder.ToLowerInvariant())) return null;
|
if (!fullName.Contains(_configuration.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
|
||||||
string prefixedPath = fullName.Replace(_configuration.CacheFolder.ToLowerInvariant(), CachePrefix + "\\").Replace("\\\\", "\\");
|
string prefixedPath = fullName.Replace(_configuration.CacheFolder.ToLowerInvariant(), CachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||||
return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper());
|
return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileCache? CreateFileEntry(string path)
|
public FileCacheEntity? CreateFileEntry(string path)
|
||||||
{
|
{
|
||||||
Logger.Debug("Creating file entry for " + path);
|
Logger.Verbose("Creating file entry for " + path);
|
||||||
FileInfo fi = new(path);
|
FileInfo fi = new(path);
|
||||||
if (!fi.Exists) return null;
|
if (!fi.Exists) return null;
|
||||||
var fullName = fi.FullName.ToLowerInvariant();
|
var fullName = fi.FullName.ToLowerInvariant();
|
||||||
if (!fullName.Contains(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant())) return null;
|
if (!fullName.Contains(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), StringComparison.Ordinal)) return null;
|
||||||
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), PenumbraPrefix + "\\").Replace("\\\\", "\\");
|
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), PenumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||||
return CreateFileCacheEntity(fi, prefixedPath);
|
return CreateFileCacheEntity(fi, prefixedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileCache? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath, string? hash = null)
|
private FileCacheEntity? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath, string? hash = null)
|
||||||
{
|
{
|
||||||
if (hash == null)
|
if (hash == null)
|
||||||
{
|
{
|
||||||
hash = Crypto.GetFileHash(fileInfo.FullName);
|
hash = Crypto.GetFileHash(fileInfo.FullName);
|
||||||
}
|
}
|
||||||
var entity = new FileCache(hash, prefixedPath, fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture));
|
var entity = new FileCacheEntity(hash, prefixedPath, fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture));
|
||||||
entity = ReplacePathPrefixes(entity);
|
entity = ReplacePathPrefixes(entity);
|
||||||
FileCaches[prefixedPath] = entity;
|
FileCaches[prefixedPath] = entity;
|
||||||
lock (_fileWriteLock)
|
lock (_fileWriteLock)
|
||||||
@@ -171,14 +171,14 @@ public class FileCacheManager : IDisposable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileCache? GetValidatedFileCache(FileCache fileCache)
|
private FileCacheEntity? GetValidatedFileCache(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
var resulingFileCache = ReplacePathPrefixes(fileCache);
|
var resulingFileCache = ReplacePathPrefixes(fileCache);
|
||||||
resulingFileCache = Validate(resulingFileCache);
|
resulingFileCache = Validate(resulingFileCache);
|
||||||
return resulingFileCache;
|
return resulingFileCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileCache? Validate(FileCache fileCache)
|
private FileCacheEntity? Validate(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
var file = new FileInfo(fileCache.ResolvedFilepath);
|
var file = new FileInfo(fileCache.ResolvedFilepath);
|
||||||
if (!file.Exists)
|
if (!file.Exists)
|
||||||
@@ -187,7 +187,7 @@ public class FileCacheManager : IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.LastWriteTimeUtc.Ticks.ToString() != fileCache.LastModifiedDateTicks)
|
if (!string.Equals(file.LastWriteTimeUtc.Ticks.ToString(), fileCache.LastModifiedDateTicks, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
UpdateHash(fileCache);
|
UpdateHash(fileCache);
|
||||||
}
|
}
|
||||||
@@ -195,12 +195,12 @@ public class FileCacheManager : IDisposable
|
|||||||
return fileCache;
|
return fileCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveHash(FileCache entity)
|
public void RemoveHash(FileCacheEntity entity)
|
||||||
{
|
{
|
||||||
FileCaches.Remove(entity.Hash, out _);
|
FileCaches.Remove(entity.Hash, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHash(FileCache fileCache)
|
public void UpdateHash(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
Logger.Debug("Updating hash for " + fileCache.ResolvedFilepath);
|
Logger.Debug("Updating hash for " + fileCache.ResolvedFilepath);
|
||||||
fileCache.Hash = Crypto.GetFileHash(fileCache.ResolvedFilepath);
|
fileCache.Hash = Crypto.GetFileHash(fileCache.ResolvedFilepath);
|
||||||
@@ -209,15 +209,15 @@ public class FileCacheManager : IDisposable
|
|||||||
FileCaches[fileCache.PrefixedFilePath] = fileCache;
|
FileCaches[fileCache.PrefixedFilePath] = fileCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileCache ReplacePathPrefixes(FileCache fileCache)
|
private FileCacheEntity ReplacePathPrefixes(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix))
|
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory()));
|
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory(), StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix))
|
else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(CachePrefix, _configuration.CacheFolder));
|
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(CachePrefix, _configuration.CacheFolder, StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileCache;
|
return fileCache;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -19,7 +20,7 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
private readonly DalamudUtil _dalamudUtil;
|
private readonly DalamudUtil _dalamudUtil;
|
||||||
private CancellationTokenSource? _scanCancellationTokenSource;
|
private CancellationTokenSource? _scanCancellationTokenSource;
|
||||||
private Task? _fileScannerTask = null;
|
private Task? _fileScannerTask = null;
|
||||||
public ConcurrentDictionary<string, int> haltScanLocks = new();
|
public ConcurrentDictionary<string, int> haltScanLocks = new(StringComparer.Ordinal);
|
||||||
public PeriodicFileScanner(IpcManager ipcManager, Configuration pluginConfiguration, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
|
public PeriodicFileScanner(IpcManager ipcManager, Configuration pluginConfiguration, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
|
||||||
{
|
{
|
||||||
Logger.Verbose("Creating " + nameof(PeriodicFileScanner));
|
Logger.Verbose("Creating " + nameof(PeriodicFileScanner));
|
||||||
@@ -127,7 +128,7 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
{
|
{
|
||||||
while (haltScanLocks.Any(f => f.Value > 0))
|
while (haltScanLocks.Any(f => f.Value > 0))
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
isForced |= RecalculateFileCacheSize();
|
isForced |= RecalculateFileCacheSize();
|
||||||
@@ -143,7 +144,7 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
_timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans);
|
_timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans);
|
||||||
while (_timeUntilNextScan.TotalSeconds >= 0)
|
while (_timeUntilNextScan.TotalSeconds >= 0)
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
|
||||||
_timeUntilNextScan -= TimeSpan.FromSeconds(1);
|
_timeUntilNextScan -= TimeSpan.FromSeconds(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,13 +204,16 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder);
|
Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder);
|
||||||
string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp" };
|
string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp" };
|
||||||
|
|
||||||
var scannedFiles = Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories)
|
var scannedFiles = new ConcurrentDictionary<string, bool>(Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories)
|
||||||
.Select(s => s.ToLowerInvariant())
|
.Select(s => s.ToLowerInvariant())
|
||||||
.Where(f => ext.Any(e => f.EndsWith(e)) && !f.Contains(@"\bg\") && !f.Contains(@"\bgcommon\") && !f.Contains(@"\ui\"))
|
.Where(f => ext.Any(e => f.EndsWith(e, StringComparison.OrdinalIgnoreCase))
|
||||||
|
&& !f.Contains(@"\bg\", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !f.Contains(@"\bgcommon\", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !f.Contains(@"\ui\", StringComparison.OrdinalIgnoreCase))
|
||||||
.Concat(Directory.EnumerateFiles(_pluginConfiguration.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
|
.Concat(Directory.EnumerateFiles(_pluginConfiguration.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
|
||||||
.Where(f => new FileInfo(f).Name.Length == 40)
|
.Where(f => new FileInfo(f).Name.Length == 40)
|
||||||
.Select(s => s.ToLowerInvariant()).ToList())
|
.Select(s => s.ToLowerInvariant()).ToList())
|
||||||
.ToDictionary(c => c, c => false);
|
.Select(c => new KeyValuePair<string, bool>(c, false)), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
TotalFiles = scannedFiles.Count;
|
TotalFiles = scannedFiles.Count;
|
||||||
|
|
||||||
@@ -217,8 +221,8 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
var cpuCount = (int)(Environment.ProcessorCount / 2.0f);
|
var cpuCount = (int)(Environment.ProcessorCount / 2.0f);
|
||||||
Task[] dbTasks = Enumerable.Range(0, cpuCount).Select(c => Task.CompletedTask).ToArray();
|
Task[] dbTasks = Enumerable.Range(0, cpuCount).Select(c => Task.CompletedTask).ToArray();
|
||||||
|
|
||||||
ConcurrentBag<FileCache> entitiesToRemove = new();
|
ConcurrentBag<FileCacheEntity> entitiesToRemove = new();
|
||||||
ConcurrentBag<FileCache> entitiesToUpdate = new();
|
ConcurrentBag<FileCacheEntity> entitiesToUpdate = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var cache in _fileDbManager.GetAllFileCaches())
|
foreach (var cache in _fileDbManager.GetAllFileCaches())
|
||||||
@@ -280,7 +284,7 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
_fileDbManager.WriteOutFullCsv();
|
_fileDbManager.WriteOutFullCsv();
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Debug("Scanner validated existing db files");
|
Logger.Verbose("Scanner validated existing db files");
|
||||||
|
|
||||||
if (ct.IsCancellationRequested) return;
|
if (ct.IsCancellationRequested) return;
|
||||||
|
|
||||||
@@ -311,7 +315,7 @@ public class PeriodicFileScanner : IDisposable
|
|||||||
|
|
||||||
Task.WaitAll(dbTasks);
|
Task.WaitAll(dbTasks);
|
||||||
|
|
||||||
Logger.Debug("Scanner added new files to db");
|
Logger.Verbose("Scanner added new files to db");
|
||||||
|
|
||||||
Logger.Debug("Scan complete");
|
Logger.Debug("Scan complete");
|
||||||
TotalFiles = 0;
|
TotalFiles = 0;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
|
||||||
namespace MareSynchronos.Interop
|
namespace MareSynchronos.Interop;
|
||||||
{
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
public unsafe struct Weapon
|
public unsafe struct Weapon
|
||||||
{
|
{
|
||||||
@@ -35,4 +35,3 @@ namespace MareSynchronos.Interop
|
|||||||
[FieldOffset(0x0)] public Character Character;
|
[FieldOffset(0x0)] public Character Character;
|
||||||
[FieldOffset(0x650)] public Character* Mount;
|
[FieldOffset(0x650)] public Character* Mount;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
|
|
||||||
namespace MareSynchronos.Localization
|
namespace MareSynchronos.Localization;
|
||||||
{
|
|
||||||
public static class Strings
|
public static class Strings
|
||||||
{
|
{
|
||||||
public class ToSStrings
|
public class ToSStrings
|
||||||
@@ -65,4 +65,3 @@ namespace MareSynchronos.Localization
|
|||||||
|
|
||||||
public static ToSStrings ToS { get; set; } = new();
|
public static ToSStrings ToS { get; set; } = new();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -71,7 +71,7 @@ public class CachedPlayer
|
|||||||
if (characterData.GetHashCode() == _cachedData.GetHashCode()) return;
|
if (characterData.GetHashCode() == _cachedData.GetHashCode()) return;
|
||||||
|
|
||||||
bool updateModdedPaths = false;
|
bool updateModdedPaths = false;
|
||||||
List<ObjectKind> charaDataToUpdate = new List<ObjectKind>();
|
List<ObjectKind> charaDataToUpdate = new();
|
||||||
foreach (var objectKind in Enum.GetValues<ObjectKind>())
|
foreach (var objectKind in Enum.GetValues<ObjectKind>())
|
||||||
{
|
{
|
||||||
_cachedData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
|
_cachedData.FileReplacements.TryGetValue(objectKind, out var existingFileReplacements);
|
||||||
@@ -108,7 +108,7 @@ public class CachedPlayer
|
|||||||
|
|
||||||
if (hasNewAndOldGlamourerData)
|
if (hasNewAndOldGlamourerData)
|
||||||
{
|
{
|
||||||
bool glamourerDataDifferent = _cachedData.GlamourerData[objectKind] != characterData.GlamourerData[objectKind];
|
bool glamourerDataDifferent = !string.Equals(_cachedData.GlamourerData[objectKind], characterData.GlamourerData[objectKind], StringComparison.Ordinal);
|
||||||
if (glamourerDataDifferent)
|
if (glamourerDataDifferent)
|
||||||
{
|
{
|
||||||
Logger.Debug("Updating " + objectKind);
|
Logger.Debug("Updating " + objectKind);
|
||||||
@@ -159,7 +159,7 @@ public class CachedPlayer
|
|||||||
Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind);
|
Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind);
|
||||||
if (toDownloadReplacements.Any())
|
if (toDownloadReplacements.Any())
|
||||||
{
|
{
|
||||||
await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken);
|
await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
||||||
_apiController.CancelDownload(downloadId);
|
_apiController.CancelDownload(downloadId);
|
||||||
}
|
}
|
||||||
if (downloadToken.IsCancellationRequested)
|
if (downloadToken.IsCancellationRequested)
|
||||||
@@ -168,7 +168,7 @@ public class CachedPlayer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((TryCalculateModdedDictionary(out moddedPaths)).All(c => _apiController.ForbiddenTransfers.Any(f => f.Hash == c.Hash)))
|
if ((TryCalculateModdedDictionary(out moddedPaths)).All(c => _apiController.ForbiddenTransfers.Any(f => string.Equals(f.Hash, c.Hash, StringComparison.Ordinal))))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ public class CachedPlayer
|
|||||||
private List<FileReplacementDto> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
private List<FileReplacementDto> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
||||||
{
|
{
|
||||||
List<FileReplacementDto> missingFiles = new();
|
List<FileReplacementDto> missingFiles = new();
|
||||||
moddedDictionary = new Dictionary<string, string>();
|
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||||
@@ -454,7 +454,7 @@ public class CachedPlayer
|
|||||||
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
||||||
{
|
{
|
||||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
||||||
if (player == null || player.Name.ToString() != PlayerName) return;
|
if (player == null || !string.Equals(player.Name.ToString(), PlayerName, StringComparison.OrdinalIgnoreCase)) return;
|
||||||
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
||||||
|
|
||||||
_penumbraRedrawEventTask = Task.Run(() =>
|
_penumbraRedrawEventTask = Task.Run(() =>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ using MareSynchronos.WebAPI;
|
|||||||
using Action = System.Action;
|
using Action = System.Action;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace MareSynchronos.Managers
|
namespace MareSynchronos.Managers;
|
||||||
{
|
|
||||||
public delegate void PenumbraRedrawEvent(IntPtr address, int objTblIdx);
|
public delegate void PenumbraRedrawEvent(IntPtr address, int objTblIdx);
|
||||||
public delegate void HeelsOffsetChange(float change);
|
public delegate void HeelsOffsetChange(float change);
|
||||||
public delegate void PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath);
|
public delegate void PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath);
|
||||||
@@ -158,7 +158,7 @@ namespace MareSynchronos.Managers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _heelsGetApiVersion.InvokeFunc() == "1.0.1";
|
return string.Equals(_heelsGetApiVersion.InvokeFunc(), "1.0.1", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -398,4 +398,3 @@ namespace MareSynchronos.Managers
|
|||||||
actionQueue.Clear();
|
actionQueue.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ public class OnlinePlayerManager : IDisposable
|
|||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly PlayerManager _playerManager;
|
private readonly PlayerManager _playerManager;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
private readonly ConcurrentDictionary<string, CachedPlayer> _onlineCachedPlayers = new();
|
private readonly ConcurrentDictionary<string, CachedPlayer> _onlineCachedPlayers = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentDictionary<string, CharacterCacheDto> _temporaryStoredCharacterCache = new();
|
private readonly ConcurrentDictionary<string, CharacterCacheDto> _temporaryStoredCharacterCache = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentDictionary<CachedPlayer, CancellationTokenSource> _playerTokenDisposal = new();
|
private readonly ConcurrentDictionary<CachedPlayer, CancellationTokenSource> _playerTokenDisposal = new();
|
||||||
|
|
||||||
private List<string> OnlineVisiblePlayerHashes => _onlineCachedPlayers.Select(p => p.Value).Where(p => p.PlayerCharacter != IntPtr.Zero)
|
private List<string> OnlineVisiblePlayerHashes => _onlineCachedPlayers.Select(p => p.Value).Where(p => p.PlayerCharacter != IntPtr.Zero)
|
||||||
@@ -37,8 +37,6 @@ public class OnlinePlayerManager : IDisposable
|
|||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
_apiController.PairedClientOnline += ApiControllerOnPairedClientOnline;
|
_apiController.PairedClientOnline += ApiControllerOnPairedClientOnline;
|
||||||
_apiController.PairedClientOffline += ApiControllerOnPairedClientOffline;
|
_apiController.PairedClientOffline += ApiControllerOnPairedClientOffline;
|
||||||
_apiController.PairedWithOther += ApiControllerOnPairedWithOther;
|
|
||||||
_apiController.UnpairedFromOther += ApiControllerOnUnpairedFromOther;
|
|
||||||
_apiController.Connected += ApiControllerOnConnected;
|
_apiController.Connected += ApiControllerOnConnected;
|
||||||
_apiController.Disconnected += ApiControllerOnDisconnected;
|
_apiController.Disconnected += ApiControllerOnDisconnected;
|
||||||
_apiController.CharacterReceived += ApiControllerOnCharacterReceived;
|
_apiController.CharacterReceived += ApiControllerOnCharacterReceived;
|
||||||
@@ -137,8 +135,6 @@ public class OnlinePlayerManager : IDisposable
|
|||||||
|
|
||||||
_apiController.PairedClientOnline -= ApiControllerOnPairedClientOnline;
|
_apiController.PairedClientOnline -= ApiControllerOnPairedClientOnline;
|
||||||
_apiController.PairedClientOffline -= ApiControllerOnPairedClientOffline;
|
_apiController.PairedClientOffline -= ApiControllerOnPairedClientOffline;
|
||||||
_apiController.PairedWithOther -= ApiControllerOnPairedWithOther;
|
|
||||||
_apiController.UnpairedFromOther -= ApiControllerOnUnpairedFromOther;
|
|
||||||
_apiController.Disconnected -= ApiControllerOnDisconnected;
|
_apiController.Disconnected -= ApiControllerOnDisconnected;
|
||||||
_apiController.Connected -= ApiControllerOnConnected;
|
_apiController.Connected -= ApiControllerOnConnected;
|
||||||
|
|
||||||
@@ -169,20 +165,6 @@ public class OnlinePlayerManager : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApiControllerOnPairedWithOther(string charHash)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(charHash)) return;
|
|
||||||
Logger.Debug("Pairing with " + charHash);
|
|
||||||
AddPlayer(charHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApiControllerOnUnpairedFromOther(string? characterHash)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(characterHash)) return;
|
|
||||||
Logger.Debug("Unpairing from " + characterHash);
|
|
||||||
RemovePlayer(characterHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddPlayer(string characterNameHash)
|
private void AddPlayer(string characterNameHash)
|
||||||
{
|
{
|
||||||
if (_onlineCachedPlayers.TryGetValue(characterNameHash, out var cachedPlayer))
|
if (_onlineCachedPlayers.TryGetValue(characterNameHash, out var cachedPlayer))
|
||||||
@@ -244,7 +226,7 @@ public class OnlinePlayerManager : IDisposable
|
|||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData,
|
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData,
|
||||||
visiblePlayers);
|
visiblePlayers).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ using MareSynchronos.FileCache;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace MareSynchronos.Managers
|
namespace MareSynchronos.Managers;
|
||||||
{
|
|
||||||
public delegate void PlayerHasChanged(CharacterCacheDto characterCache);
|
public delegate void PlayerHasChanged(CharacterCacheDto characterCache);
|
||||||
|
|
||||||
public class PlayerManager : IDisposable
|
public class PlayerManager : IDisposable
|
||||||
@@ -34,7 +34,7 @@ namespace MareSynchronos.Managers
|
|||||||
private CancellationTokenSource? _playerChangedCts = new();
|
private CancellationTokenSource? _playerChangedCts = new();
|
||||||
private CancellationTokenSource _transientUpdateCts = new();
|
private CancellationTokenSource _transientUpdateCts = new();
|
||||||
|
|
||||||
private List<PlayerRelatedObject> playerRelatedObjects = new List<PlayerRelatedObject>();
|
private List<PlayerRelatedObject> playerRelatedObjects = new();
|
||||||
|
|
||||||
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
|
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
|
||||||
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager,
|
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager,
|
||||||
@@ -88,7 +88,7 @@ namespace MareSynchronos.Managers
|
|||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Logger.Debug("Delaying transient resource load update");
|
Logger.Debug("Delaying transient resource load update");
|
||||||
await Task.Delay(750, token);
|
await Task.Delay(750, token).ConfigureAwait(false);
|
||||||
if (obj.HasUnprocessedUpdate || token.IsCancellationRequested) return;
|
if (obj.HasUnprocessedUpdate || token.IsCancellationRequested) return;
|
||||||
Logger.Debug("Firing transient resource load update");
|
Logger.Debug("Firing transient resource load update");
|
||||||
obj.HasTransientsUpdate = true;
|
obj.HasTransientsUpdate = true;
|
||||||
@@ -169,7 +169,7 @@ namespace MareSynchronos.Managers
|
|||||||
while (!PermanentDataCache.IsReady && !token.IsCancellationRequested)
|
while (!PermanentDataCache.IsReady && !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger.Verbose("Waiting until cache is ready");
|
Logger.Verbose("Waiting until cache is ready");
|
||||||
await Task.Delay(50, token);
|
await Task.Delay(50, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.IsCancellationRequested) return null;
|
if (token.IsCancellationRequested) return null;
|
||||||
@@ -215,7 +215,7 @@ namespace MareSynchronos.Managers
|
|||||||
var token = _playerChangedCts.Token;
|
var token = _playerChangedCts.Token;
|
||||||
|
|
||||||
// fix for redraw from anamnesis
|
// fix for redraw from anamnesis
|
||||||
while ((!_dalamudUtil.IsPlayerPresent || _dalamudUtil.PlayerName == "--") && !token.IsCancellationRequested)
|
while ((!_dalamudUtil.IsPlayerPresent || string.Equals(_dalamudUtil.PlayerName, "--", StringComparison.Ordinal)) && !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger.Debug("Waiting Until Player is Present");
|
Logger.Debug("Waiting Until Player is Present");
|
||||||
Thread.Sleep(100);
|
Thread.Sleep(100);
|
||||||
@@ -234,6 +234,9 @@ namespace MareSynchronos.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
CharacterCacheDto? cacheDto = null;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_periodicFileScanner.HaltScan("Character creation");
|
_periodicFileScanner.HaltScan("Character creation");
|
||||||
foreach (var item in unprocessedObjects)
|
foreach (var item in unprocessedObjects)
|
||||||
@@ -241,8 +244,13 @@ namespace MareSynchronos.Managers
|
|||||||
_dalamudUtil.WaitWhileCharacterIsDrawing("self " + item.ObjectKind.ToString(), item.Address, 10000, token);
|
_dalamudUtil.WaitWhileCharacterIsDrawing("self " + item.ObjectKind.ToString(), item.Address, 10000, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterCacheDto? cacheDto = (await CreateFullCharacterCacheDto(token));
|
cacheDto = (await CreateFullCharacterCacheDto(token).ConfigureAwait(false));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
_periodicFileScanner.ResumeScan("Character creation");
|
_periodicFileScanner.ResumeScan("Character creation");
|
||||||
|
}
|
||||||
if (cacheDto == null || token.IsCancellationRequested) return;
|
if (cacheDto == null || token.IsCancellationRequested) return;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -268,4 +276,3 @@ namespace MareSynchronos.Managers
|
|||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MareSynchronos.Managers
|
namespace MareSynchronos.Managers;
|
||||||
{
|
|
||||||
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
|
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
|
||||||
|
|
||||||
public class TransientResourceManager : IDisposable
|
public class TransientResourceManager : IDisposable
|
||||||
@@ -82,7 +82,7 @@ namespace MareSynchronos.Managers
|
|||||||
|
|
||||||
private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath)
|
private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath)
|
||||||
{
|
{
|
||||||
if (!FileTypesToHandle.Any(type => gamePath.ToLowerInvariant().EndsWith(type)))
|
if (!FileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,25 +93,25 @@ namespace MareSynchronos.Managers
|
|||||||
|
|
||||||
if (!TransientResources.ContainsKey(gameObject))
|
if (!TransientResources.ContainsKey(gameObject))
|
||||||
{
|
{
|
||||||
TransientResources[gameObject] = new();
|
TransientResources[gameObject] = new(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filePath.StartsWith("|"))
|
if (filePath.StartsWith("|", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
filePath = filePath.Split("|")[2];
|
filePath = filePath.Split("|")[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
filePath = filePath.ToLowerInvariant().Replace("\\", "/");
|
filePath = filePath.ToLowerInvariant().Replace("\\", "/", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var replacedGamePath = gamePath.ToLowerInvariant().Replace("\\", "/");
|
var replacedGamePath = gamePath.ToLowerInvariant().Replace("\\", "/", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (TransientResources[gameObject].Contains(replacedGamePath) ||
|
if (TransientResources[gameObject].Contains(replacedGamePath) ||
|
||||||
SemiTransientResources.Any(r => r.Value.Any(f => f.GamePaths.First().ToLowerInvariant() == replacedGamePath
|
SemiTransientResources.Any(r => r.Value.Any(f => string.Equals(f.GamePaths.First(), replacedGamePath , StringComparison.OrdinalIgnoreCase)
|
||||||
&& f.ResolvedPath.ToLowerInvariant() == filePath)))
|
&& string.Equals(f.ResolvedPath, filePath, StringComparison.OrdinalIgnoreCase))))
|
||||||
{
|
{
|
||||||
Logger.Debug("Not adding " + replacedGamePath + ":" + filePath);
|
Logger.Verbose("Not adding " + replacedGamePath + ":" + filePath);
|
||||||
Logger.Verbose("SemiTransientAny: " + SemiTransientResources.Any(r => r.Value.Any(f => f.GamePaths.First().ToLowerInvariant() == replacedGamePath
|
Logger.Verbose("SemiTransientAny: " + SemiTransientResources.Any(r => r.Value.Any(f => string.Equals(f.GamePaths.First(), replacedGamePath, StringComparison.OrdinalIgnoreCase)
|
||||||
&& f.ResolvedPath.ToLowerInvariant() == filePath)).ToString() + ", TransientAny: " + TransientResources[gameObject].Contains(replacedGamePath));
|
&& string.Equals(f.ResolvedPath, filePath, StringComparison.OrdinalIgnoreCase))).ToString() + ", TransientAny: " + TransientResources[gameObject].Contains(replacedGamePath));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -125,7 +125,7 @@ namespace MareSynchronos.Managers
|
|||||||
{
|
{
|
||||||
if (TransientResources.ContainsKey(gameObject))
|
if (TransientResources.ContainsKey(gameObject))
|
||||||
{
|
{
|
||||||
TransientResources[gameObject].RemoveWhere(f => fileReplacement.GamePaths.Any(g => g.ToLowerInvariant() == f.ToLowerInvariant()));
|
TransientResources[gameObject].RemoveWhere(f => fileReplacement.GamePaths.Any(g => string.Equals(g, f, StringComparison.OrdinalIgnoreCase)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,11 +145,11 @@ namespace MareSynchronos.Managers
|
|||||||
Logger.Debug("Persisting " + transientResources.Count + " transient resources");
|
Logger.Debug("Persisting " + transientResources.Count + " transient resources");
|
||||||
foreach (var gamePath in transientResources)
|
foreach (var gamePath in transientResources)
|
||||||
{
|
{
|
||||||
var existingResource = SemiTransientResources[objectKind].Any(f => f.GamePaths.First().ToLowerInvariant() == gamePath.ToLowerInvariant());
|
var existingResource = SemiTransientResources[objectKind].Any(f => string.Equals(f.GamePaths.First(), gamePath, StringComparison.OrdinalIgnoreCase));
|
||||||
if (existingResource)
|
if (existingResource)
|
||||||
{
|
{
|
||||||
Logger.Debug("Semi Transient resource replaced: " + gamePath);
|
Logger.Debug("Semi Transient resource replaced: " + gamePath);
|
||||||
SemiTransientResources[objectKind].RemoveWhere(f => f.GamePaths.First().ToLowerInvariant() == gamePath.ToLowerInvariant());
|
SemiTransientResources[objectKind].RemoveWhere(f => string.Equals(f.GamePaths.First(), gamePath, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -196,10 +196,9 @@ namespace MareSynchronos.Managers
|
|||||||
SemiTransientResources[objectKind] = new HashSet<FileReplacement>();
|
SemiTransientResources[objectKind] = new HashSet<FileReplacement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SemiTransientResources[objectKind].Any(f => f.ResolvedPath.ToLowerInvariant() == item.ResolvedPath.ToLowerInvariant()))
|
if (!SemiTransientResources[objectKind].Any(f => string.Equals(f.ResolvedPath, item.ResolvedPath, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
SemiTransientResources[objectKind].Add(item);
|
SemiTransientResources[objectKind].Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors></Authors>
|
<Authors></Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>0.4.24</Version>
|
<Version>0.5.0</Version>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>
|
||||||
@@ -28,6 +28,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DalamudPackager" Version="2.1.8" />
|
<PackageReference Include="DalamudPackager" Version="2.1.8" />
|
||||||
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
||||||
|
<PackageReference Include="Meziantou.Analyzer" Version="1.0.733">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -86,4 +90,8 @@
|
|||||||
<EmbeddedResource Include="Localization\fr.json" />
|
<EmbeddedResource Include="Localization\fr.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ using MareSynchronos.API;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
namespace MareSynchronos.Models
|
namespace MareSynchronos.Models;
|
||||||
{
|
|
||||||
[JsonObject(MemberSerialization.OptIn)]
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class CharacterData
|
public class CharacterData
|
||||||
{
|
{
|
||||||
@@ -31,10 +31,10 @@ namespace MareSynchronos.Models
|
|||||||
|
|
||||||
if (!FileReplacements.ContainsKey(objectKind)) FileReplacements.Add(objectKind, new List<FileReplacement>());
|
if (!FileReplacements.ContainsKey(objectKind)) FileReplacements.Add(objectKind, new List<FileReplacement>());
|
||||||
|
|
||||||
var existingReplacement = FileReplacements[objectKind].SingleOrDefault(f => f.ResolvedPath == fileReplacement.ResolvedPath);
|
var existingReplacement = FileReplacements[objectKind].SingleOrDefault(f => string.Equals(f.ResolvedPath, fileReplacement.ResolvedPath, System.StringComparison.OrdinalIgnoreCase));
|
||||||
if (existingReplacement != null)
|
if (existingReplacement != null)
|
||||||
{
|
{
|
||||||
existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e)));
|
existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e, System.StringComparer.OrdinalIgnoreCase)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -44,11 +44,11 @@ namespace MareSynchronos.Models
|
|||||||
|
|
||||||
public CharacterCacheDto ToCharacterCacheDto()
|
public CharacterCacheDto ToCharacterCacheDto()
|
||||||
{
|
{
|
||||||
var fileReplacements = FileReplacements.ToDictionary(k => k.Key, k => k.Value.Where(f => f.HasFileReplacement && !f.IsFileSwap).GroupBy(f => f.Hash).Select(g =>
|
var fileReplacements = FileReplacements.ToDictionary(k => k.Key, k => k.Value.Where(f => f.HasFileReplacement && !f.IsFileSwap).GroupBy(f => f.Hash, System.StringComparer.OrdinalIgnoreCase).Select(g =>
|
||||||
{
|
{
|
||||||
return new FileReplacementDto()
|
return new FileReplacementDto()
|
||||||
{
|
{
|
||||||
GamePaths = g.SelectMany(f => f.GamePaths).Distinct().ToArray(),
|
GamePaths = g.SelectMany(f => f.GamePaths).Distinct(System.StringComparer.OrdinalIgnoreCase).ToArray(),
|
||||||
Hash = g.First().Hash,
|
Hash = g.First().Hash,
|
||||||
};
|
};
|
||||||
}).ToList());
|
}).ToList());
|
||||||
@@ -85,4 +85,3 @@ namespace MareSynchronos.Models
|
|||||||
return stringBuilder.ToString();
|
return stringBuilder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ using MareSynchronos.API;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
|
||||||
namespace MareSynchronos.Models
|
namespace MareSynchronos.Models;
|
||||||
{
|
|
||||||
public class FileReplacement
|
public class FileReplacement
|
||||||
{
|
{
|
||||||
private readonly FileCacheManager fileDbManager;
|
private readonly FileCacheManager fileDbManager;
|
||||||
@@ -21,9 +21,9 @@ namespace MareSynchronos.Models
|
|||||||
|
|
||||||
public List<string> GamePaths { get; set; } = new();
|
public List<string> GamePaths { get; set; } = new();
|
||||||
|
|
||||||
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => p != ResolvedPath);
|
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => !string.Equals(p, ResolvedPath, System.StringComparison.Ordinal));
|
||||||
|
|
||||||
public bool IsFileSwap => !Regex.IsMatch(ResolvedPath, @"^[a-zA-Z]:(/|\\)", RegexOptions.ECMAScript) && GamePaths.First() != ResolvedPath;
|
public bool IsFileSwap => !Regex.IsMatch(ResolvedPath, @"^[a-zA-Z]:(/|\\)", RegexOptions.ECMAScript) && !string.Equals(GamePaths.First(), ResolvedPath, System.StringComparison.Ordinal);
|
||||||
|
|
||||||
public string Hash { get; set; } = string.Empty;
|
public string Hash { get; set; } = string.Empty;
|
||||||
|
|
||||||
@@ -57,4 +57,3 @@ namespace MareSynchronos.Models
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ using System.Runtime.InteropServices;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.ByteString;
|
||||||
|
|
||||||
namespace MareSynchronos.Models
|
namespace MareSynchronos.Models;
|
||||||
{
|
|
||||||
public class PlayerRelatedObject
|
public class PlayerRelatedObject
|
||||||
{
|
{
|
||||||
private readonly Func<IntPtr> getAddress;
|
private readonly Func<IntPtr> getAddress;
|
||||||
@@ -61,7 +61,7 @@ namespace MareSynchronos.Models
|
|||||||
bool equip = CompareAndUpdateByteData(chara->EquipSlotData, chara->CustomizeData);
|
bool equip = CompareAndUpdateByteData(chara->EquipSlotData, chara->CustomizeData);
|
||||||
bool drawObj = (IntPtr)chara->GameObject.DrawObject != DrawObjectAddress;
|
bool drawObj = (IntPtr)chara->GameObject.DrawObject != DrawObjectAddress;
|
||||||
var name = new Utf8String(chara->GameObject.Name).ToString();
|
var name = new Utf8String(chara->GameObject.Name).ToString();
|
||||||
bool nameChange = (name != _name);
|
bool nameChange = (!string.Equals(name, _name, StringComparison.Ordinal));
|
||||||
if (addr || equip || drawObj || nameChange)
|
if (addr || equip || drawObj || nameChange)
|
||||||
{
|
{
|
||||||
_name = name;
|
_name = name;
|
||||||
@@ -133,4 +133,3 @@ namespace MareSynchronos.Models
|
|||||||
return hasChanges;
|
return hasChanges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ using MareSynchronos.UI;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
using Dalamud.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos;
|
||||||
|
|
||||||
namespace MareSynchronos
|
|
||||||
{
|
|
||||||
public sealed class Plugin : IDalamudPlugin
|
public sealed class Plugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
private const string CommandName = "/mare";
|
private const string CommandName = "/mare";
|
||||||
@@ -174,7 +175,7 @@ namespace MareSynchronos
|
|||||||
{
|
{
|
||||||
while (!_dalamudUtil.IsPlayerPresent)
|
while (!_dalamudUtil.IsPlayerPresent)
|
||||||
{
|
{
|
||||||
await Task.Delay(100);
|
await Task.Delay(100).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -236,4 +237,3 @@ namespace MareSynchronos
|
|||||||
_introUi.Toggle();
|
_introUi.Toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -14,26 +15,31 @@ using MareSynchronos.API;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
|
|
||||||
namespace MareSynchronos.UI
|
namespace MareSynchronos.UI;
|
||||||
{
|
|
||||||
public class CompactUi : Window, IDisposable
|
public class CompactUi : Window, IDisposable
|
||||||
{
|
{
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly Dictionary<string, bool> _showUidForEntry = new();
|
public readonly Dictionary<string, bool> ShowUidForEntry = new(StringComparer.Ordinal);
|
||||||
private readonly UiShared _uiShared;
|
private readonly UiShared _uiShared;
|
||||||
private readonly WindowSystem _windowSystem;
|
private readonly WindowSystem _windowSystem;
|
||||||
private string _characterOrCommentFilter = string.Empty;
|
private string _characterOrCommentFilter = string.Empty;
|
||||||
|
|
||||||
private string _editCharComment = string.Empty;
|
public string EditUserComment = string.Empty;
|
||||||
private string _editNickEntry = string.Empty;
|
public string EditNickEntry = string.Empty;
|
||||||
|
|
||||||
private string _pairToAdd = string.Empty;
|
private string _pairToAdd = string.Empty;
|
||||||
|
|
||||||
private readonly Stopwatch _timeout = new();
|
private readonly Stopwatch _timeout = new();
|
||||||
private bool _buttonState;
|
private bool _buttonState;
|
||||||
|
|
||||||
private float _transferPartHeight = 0;
|
public float TransferPartHeight = 0;
|
||||||
|
public float _windowContentWidth = 0;
|
||||||
|
|
||||||
private float _windowContentWidth = 0;
|
|
||||||
|
private bool showSyncShells = false;
|
||||||
|
private GroupPanel groupPanel;
|
||||||
|
|
||||||
public CompactUi(WindowSystem windowSystem,
|
public CompactUi(WindowSystem windowSystem,
|
||||||
UiShared uiShared, Configuration configuration, ApiController apiController) : base("###MareSynchronosMainUI")
|
UiShared uiShared, Configuration configuration, ApiController apiController) : base("###MareSynchronosMainUI")
|
||||||
@@ -54,7 +60,7 @@ namespace MareSynchronos.UI
|
|||||||
this.WindowName = "Mare Synchronos " + dateTime + "###MareSynchronosMainUI";
|
this.WindowName = "Mare Synchronos " + dateTime + "###MareSynchronosMainUI";
|
||||||
Toggle();
|
Toggle();
|
||||||
#else
|
#else
|
||||||
this.WindowName = "Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version;
|
this.WindowName = "Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version + "###MareSynchronosMainUI";
|
||||||
#endif
|
#endif
|
||||||
Logger.Verbose("Creating " + nameof(CompactUi));
|
Logger.Verbose("Creating " + nameof(CompactUi));
|
||||||
|
|
||||||
@@ -63,10 +69,12 @@ namespace MareSynchronos.UI
|
|||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
|
|
||||||
|
groupPanel = new(this, uiShared, configuration, apiController);
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
MinimumSize = new Vector2(300, 400),
|
MinimumSize = new Vector2(350, 400),
|
||||||
MaximumSize = new Vector2(300, 2000),
|
MaximumSize = new Vector2(350, 2000),
|
||||||
};
|
};
|
||||||
|
|
||||||
windowSystem.AddWindow(this);
|
windowSystem.AddWindow(this);
|
||||||
@@ -88,29 +96,74 @@ namespace MareSynchronos.UI
|
|||||||
|
|
||||||
if (_apiController.ServerState is ServerState.Connected)
|
if (_apiController.ServerState is ServerState.Connected)
|
||||||
{
|
{
|
||||||
|
var hasShownSyncShells = showSyncShells;
|
||||||
|
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
if (!hasShownSyncShells)
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Button, ImGui.GetStyle().Colors[(int)ImGuiCol.ButtonHovered]);
|
||||||
|
}
|
||||||
|
if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), new Vector2((UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30)))
|
||||||
|
{
|
||||||
|
showSyncShells = false;
|
||||||
|
}
|
||||||
|
if (!hasShownSyncShells)
|
||||||
|
{
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip("Individual pairs");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
if (hasShownSyncShells)
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Button, ImGui.GetStyle().Colors[(int)ImGuiCol.ButtonHovered]);
|
||||||
|
}
|
||||||
|
if (ImGui.Button(FontAwesomeIcon.UserFriends.ToIconString(), new Vector2((UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30)))
|
||||||
|
{
|
||||||
|
showSyncShells = true;
|
||||||
|
}
|
||||||
|
if (hasShownSyncShells)
|
||||||
|
{
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui.PopFont();
|
||||||
|
|
||||||
|
UiShared.AttachToolTip("Syncshells");
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
if (!hasShownSyncShells)
|
||||||
|
{
|
||||||
UiShared.DrawWithID("pairlist", DrawPairList);
|
UiShared.DrawWithID("pairlist", DrawPairList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UiShared.DrawWithID("syncshells", groupPanel.DrawSyncshells);
|
||||||
|
|
||||||
|
}
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiShared.DrawWithID("transfers", DrawTransfers);
|
UiShared.DrawWithID("transfers", DrawTransfers);
|
||||||
_transferPartHeight = ImGui.GetCursorPosY() - _transferPartHeight;
|
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnClose()
|
public override void OnClose()
|
||||||
{
|
{
|
||||||
_editNickEntry = string.Empty;
|
EditNickEntry = string.Empty;
|
||||||
_editCharComment = string.Empty;
|
EditUserComment = string.Empty;
|
||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
private void DrawAddPair()
|
private void DrawAddPair()
|
||||||
{
|
{
|
||||||
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
||||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X);
|
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X);
|
||||||
ImGui.InputTextWithHint("##otheruid", "Other players UID", ref _pairToAdd, 10);
|
ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20);
|
||||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X);
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X);
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||||
{
|
{
|
||||||
if (_apiController.PairedClients.All(w => w.OtherUID != _pairToAdd))
|
if (_apiController.PairedClients.All(w => !string.Equals(w.OtherUID, _pairToAdd, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
_ = _apiController.SendPairedClientAddition(_pairToAdd);
|
_ = _apiController.SendPairedClientAddition(_pairToAdd);
|
||||||
_pairToAdd = string.Empty;
|
_pairToAdd = string.Empty;
|
||||||
@@ -132,7 +185,7 @@ namespace MareSynchronos.UI
|
|||||||
_configuration.ReverseUserSort = true;
|
_configuration.ReverseUserSort = true;
|
||||||
_configuration.Save();
|
_configuration.Save();
|
||||||
}
|
}
|
||||||
UiShared.AttachToolTip("Sort by newest additions first");
|
UiShared.AttachToolTip("Sort by name descending");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -141,7 +194,7 @@ namespace MareSynchronos.UI
|
|||||||
_configuration.ReverseUserSort = false;
|
_configuration.ReverseUserSort = false;
|
||||||
_configuration.Save();
|
_configuration.Save();
|
||||||
}
|
}
|
||||||
UiShared.AttachToolTip("Sort by oldest additions first");
|
UiShared.AttachToolTip("Sort by name ascending");
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
@@ -246,7 +299,7 @@ namespace MareSynchronos.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
var textIsUid = true;
|
var textIsUid = true;
|
||||||
_showUidForEntry.TryGetValue(entry.OtherUID, out var showUidInsteadOfName);
|
ShowUidForEntry.TryGetValue(entry.OtherUID, out var showUidInsteadOfName);
|
||||||
if (!showUidInsteadOfName && _configuration.GetCurrentServerUidComments().TryGetValue(entry.OtherUID, out var playerText))
|
if (!showUidInsteadOfName && _configuration.GetCurrentServerUidComments().TryGetValue(entry.OtherUID, out var playerText))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(playerText))
|
if (string.IsNullOrEmpty(playerText))
|
||||||
@@ -264,7 +317,7 @@ namespace MareSynchronos.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_editNickEntry != entry.OtherUID)
|
if (!string.Equals(EditNickEntry, entry.OtherUID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosY(textPos);
|
ImGui.SetCursorPosY(textPos);
|
||||||
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||||
@@ -275,22 +328,22 @@ namespace MareSynchronos.UI
|
|||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||||
{
|
{
|
||||||
var prevState = textIsUid;
|
var prevState = textIsUid;
|
||||||
if (_showUidForEntry.ContainsKey(entry.OtherUID))
|
if (ShowUidForEntry.ContainsKey(entry.OtherUID))
|
||||||
{
|
{
|
||||||
prevState = _showUidForEntry[entry.OtherUID];
|
prevState = ShowUidForEntry[entry.OtherUID];
|
||||||
}
|
}
|
||||||
|
|
||||||
_showUidForEntry[entry.OtherUID] = !prevState;
|
ShowUidForEntry[entry.OtherUID] = !prevState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
{
|
{
|
||||||
_configuration.SetCurrentServerUidComment(_editNickEntry, _editCharComment);
|
_configuration.SetCurrentServerUidComment(EditNickEntry, EditUserComment);
|
||||||
_configuration.Save();
|
_configuration.Save();
|
||||||
_editCharComment = _configuration.GetCurrentServerUidComments().ContainsKey(entry.OtherUID)
|
EditUserComment = _configuration.GetCurrentServerUidComments().ContainsKey(entry.OtherUID)
|
||||||
? _configuration.GetCurrentServerUidComments()[entry.OtherUID]
|
? _configuration.GetCurrentServerUidComments()[entry.OtherUID]
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
_editNickEntry = entry.OtherUID;
|
EditNickEntry = entry.OtherUID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -298,16 +351,16 @@ namespace MareSynchronos.UI
|
|||||||
ImGui.SetCursorPosY(originalY);
|
ImGui.SetCursorPosY(originalY);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
||||||
if (ImGui.InputTextWithHint("", "Nick/Notes", ref _editCharComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||||
{
|
{
|
||||||
_configuration.SetCurrentServerUidComment(entry.OtherUID, _editCharComment);
|
_configuration.SetCurrentServerUidComment(entry.OtherUID, EditUserComment);
|
||||||
_configuration.Save();
|
_configuration.Save();
|
||||||
_editNickEntry = string.Empty;
|
EditNickEntry = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
{
|
{
|
||||||
_editNickEntry = string.Empty;
|
EditNickEntry = string.Empty;
|
||||||
}
|
}
|
||||||
UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
||||||
}
|
}
|
||||||
@@ -319,7 +372,6 @@ namespace MareSynchronos.UI
|
|||||||
if (UiShared.CtrlPressed())
|
if (UiShared.CtrlPressed())
|
||||||
{
|
{
|
||||||
_ = _apiController.SendPairedClientRemoval(entry.OtherUID);
|
_ = _apiController.SendPairedClientRemoval(entry.OtherUID);
|
||||||
_apiController.PairedClients.Remove(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UiShared.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID);
|
UiShared.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID);
|
||||||
@@ -342,17 +394,18 @@ namespace MareSynchronos.UI
|
|||||||
{
|
{
|
||||||
UiShared.DrawWithID("addpair", DrawAddPair);
|
UiShared.DrawWithID("addpair", DrawAddPair);
|
||||||
UiShared.DrawWithID("pairs", DrawPairs);
|
UiShared.DrawWithID("pairs", DrawPairs);
|
||||||
_transferPartHeight = ImGui.GetCursorPosY();
|
TransferPartHeight = ImGui.GetCursorPosY();
|
||||||
UiShared.DrawWithID("filter", DrawFilter);
|
UiShared.DrawWithID("filter", DrawFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPairs()
|
private void DrawPairs()
|
||||||
{
|
{
|
||||||
var ySize = _transferPartHeight == 0
|
var ySize = TransferPartHeight == 0
|
||||||
? 1
|
? 1
|
||||||
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _transferPartHeight - ImGui.GetCursorPosY();
|
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
|
||||||
var users = GetFilteredUsers();
|
var users = GetFilteredUsers();
|
||||||
|
|
||||||
|
users = users.OrderBy(u => _configuration.GetCurrentServerUidComments().ContainsKey(u.OtherUID) ? _configuration.GetCurrentServerUidComments()[u.OtherUID] : !string.IsNullOrEmpty(u.VanityUID) ? u.VanityUID : u.OtherUID);
|
||||||
if (_configuration.ReverseUserSort) users = users.Reverse();
|
if (_configuration.ReverseUserSort) users = users.Reverse();
|
||||||
|
|
||||||
ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), false);
|
ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), false);
|
||||||
@@ -363,6 +416,8 @@ namespace MareSynchronos.UI
|
|||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private IEnumerable<ClientPairDto> GetFilteredUsers()
|
private IEnumerable<ClientPairDto> GetFilteredUsers()
|
||||||
{
|
{
|
||||||
return _apiController.PairedClients.Where(p =>
|
return _apiController.PairedClients.Where(p =>
|
||||||
@@ -370,17 +425,23 @@ namespace MareSynchronos.UI
|
|||||||
if (_characterOrCommentFilter.IsNullOrEmpty()) return true;
|
if (_characterOrCommentFilter.IsNullOrEmpty()) return true;
|
||||||
_configuration.GetCurrentServerUidComments().TryGetValue(p.OtherUID, out var comment);
|
_configuration.GetCurrentServerUidComments().TryGetValue(p.OtherUID, out var comment);
|
||||||
var uid = p.VanityUID.IsNullOrEmpty() ? p.OtherUID : p.VanityUID;
|
var uid = p.VanityUID.IsNullOrEmpty() ? p.OtherUID : p.VanityUID;
|
||||||
return uid.ToLowerInvariant().Contains(_characterOrCommentFilter.ToLowerInvariant()) ||
|
return uid.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ||
|
||||||
(comment?.ToLowerInvariant().Contains(_characterOrCommentFilter.ToLowerInvariant()) ?? false);
|
(comment?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawServerStatus()
|
private void DrawServerStatus()
|
||||||
{
|
{
|
||||||
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Link);
|
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Link);
|
||||||
var userCount = _apiController.OnlineUsers.ToString();
|
var userCount = _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture);
|
||||||
var userSize = ImGui.CalcTextSize(userCount);
|
var userSize = ImGui.CalcTextSize(userCount);
|
||||||
var textSize = ImGui.CalcTextSize("Users Online");
|
var textSize = ImGui.CalcTextSize("Users Online");
|
||||||
|
#if DEBUG
|
||||||
|
string shardConnection = $"Shard: {_apiController.ServerInfo.ShardName}";
|
||||||
|
#else
|
||||||
|
string shardConnection = string.Equals(_apiController.ServerInfo.ShardName, "Main", StringComparison.OrdinalIgnoreCase) ? string.Empty : $"Shard: {_apiController.ServerInfo.ShardName}";
|
||||||
|
#endif
|
||||||
|
var shardTextSize = ImGui.CalcTextSize(shardConnection);
|
||||||
|
|
||||||
if (_apiController.ServerState is ServerState.Connected)
|
if (_apiController.ServerState is ServerState.Connected)
|
||||||
{
|
{
|
||||||
@@ -410,6 +471,12 @@ namespace MareSynchronos.UI
|
|||||||
}
|
}
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
UiShared.AttachToolTip(!_configuration.FullPause ? "Disconnect from " + _apiController.ServerDictionary[_configuration.ApiUri] : "Connect to " + _apiController.ServerDictionary[_configuration.ApiUri]);
|
UiShared.AttachToolTip(!_configuration.FullPause ? "Disconnect from " + _apiController.ServerDictionary[_configuration.ApiUri] : "Connect to " + _apiController.ServerDictionary[_configuration.ApiUri]);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_apiController.ServerInfo.ShardName))
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth()) / 2 - shardTextSize.X / 2);
|
||||||
|
ImGui.TextUnformatted(shardConnection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTransfers()
|
private void DrawTransfers()
|
||||||
@@ -560,4 +627,3 @@ namespace MareSynchronos.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
544
MareSynchronos/UI/GroupPanel.cs
Normal file
544
MareSynchronos/UI/GroupPanel.cs
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using ImGuiNET;
|
||||||
|
using MareSynchronos.API;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace MareSynchronos.UI
|
||||||
|
{
|
||||||
|
internal class GroupPanel
|
||||||
|
{
|
||||||
|
private readonly CompactUi _mainUi;
|
||||||
|
private UiShared _uiShared;
|
||||||
|
private Configuration _configuration;
|
||||||
|
private ApiController _apiController;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal);
|
||||||
|
private string _editGroupEntry = string.Empty;
|
||||||
|
private string _editGroupComment = string.Empty;
|
||||||
|
private string _syncShellPassword = string.Empty;
|
||||||
|
private string _syncShellToJoin = string.Empty;
|
||||||
|
|
||||||
|
private bool _showModalEnterPassword;
|
||||||
|
private bool _showModalCreateGroup;
|
||||||
|
private bool _showModalChangePassword;
|
||||||
|
private string _newSyncShellPassword = string.Empty;
|
||||||
|
private bool _isPasswordValid;
|
||||||
|
private bool _errorGroupJoin;
|
||||||
|
private bool _errorGroupCreate = false;
|
||||||
|
private GroupCreatedDto? _lastCreatedGroup = null;
|
||||||
|
private readonly Dictionary<string, bool> ExpandedGroupState = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public GroupPanel(CompactUi mainUi, UiShared uiShared, Configuration configuration, ApiController apiController)
|
||||||
|
{
|
||||||
|
_mainUi = mainUi;
|
||||||
|
_uiShared = uiShared;
|
||||||
|
_configuration = configuration;
|
||||||
|
_apiController = apiController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawSyncshells()
|
||||||
|
{
|
||||||
|
UiShared.DrawWithID("addsyncshell", DrawAddSyncshell);
|
||||||
|
UiShared.DrawWithID("syncshelllist", DrawSyncshellList);
|
||||||
|
_mainUi.TransferPartHeight = ImGui.GetCursorPosY();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAddSyncshell()
|
||||||
|
{
|
||||||
|
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
||||||
|
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X);
|
||||||
|
ImGui.InputTextWithHint("##syncshellid", "Syncshell GID/Alias", ref _syncShellToJoin, 20);
|
||||||
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X);
|
||||||
|
|
||||||
|
bool userCanJoinMoreGroups = _apiController.Groups.Count < _apiController.ServerInfo.MaxGroupsJoinedByUser;
|
||||||
|
bool userCanCreateMoreGroups = _apiController.Groups.Count(u => string.Equals(u.OwnedBy, _apiController.UID, StringComparison.Ordinal)) < _apiController.ServerInfo.MaxGroupsCreatedByUser;
|
||||||
|
|
||||||
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||||
|
{
|
||||||
|
if (_apiController.Groups.All(w => !string.Equals(w.GID, _syncShellToJoin, StringComparison.Ordinal)) && !string.IsNullOrEmpty(_syncShellToJoin))
|
||||||
|
{
|
||||||
|
if (userCanJoinMoreGroups)
|
||||||
|
{
|
||||||
|
_errorGroupJoin = false;
|
||||||
|
_showModalEnterPassword = true;
|
||||||
|
ImGui.OpenPopup("Enter Syncshell Password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (userCanCreateMoreGroups)
|
||||||
|
{
|
||||||
|
_lastCreatedGroup = null;
|
||||||
|
_errorGroupCreate = false;
|
||||||
|
_showModalCreateGroup = true;
|
||||||
|
ImGui.OpenPopup("Create Syncshell");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip(_syncShellToJoin.IsNullOrEmpty()
|
||||||
|
? (userCanCreateMoreGroups ? "Create Syncshell" : $"You cannot create more than {_apiController.ServerInfo.MaxGroupsCreatedByUser} Syncshells")
|
||||||
|
: (userCanJoinMoreGroups ? "Join Syncshell" + _syncShellToJoin : $"You cannot join more than {_apiController.ServerInfo.MaxGroupsJoinedByUser} Syncshells"));
|
||||||
|
|
||||||
|
if (ImGui.BeginPopupModal("Enter Syncshell Password", ref _showModalEnterPassword, ImGuiWindowFlags.AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
UiShared.TextWrapped("Before joining any Syncshells please be aware that you will be automatically paired with everyone in the Syncshell.");
|
||||||
|
ImGui.Separator();
|
||||||
|
UiShared.TextWrapped("Enter the password for Syncshell " + _syncShellToJoin + ":");
|
||||||
|
ImGui.InputTextWithHint("##password", _syncShellToJoin + " Password", ref _syncShellPassword, 255, ImGuiInputTextFlags.Password);
|
||||||
|
if (_errorGroupJoin)
|
||||||
|
{
|
||||||
|
UiShared.ColorTextWrapped($"An error occured during joining of this Syncshell: you either have joined the maximum amount of Syncshells ({_apiController.ServerInfo.MaxGroupsJoinedByUser}), " +
|
||||||
|
$"it does not exist, the password you entered is wrong, you already joined the Syncshell, the Syncshell is full ({_apiController.ServerInfo.MaxGroupUserCount} users) or the Syncshell has closed invites.",
|
||||||
|
new Vector4(1, 0, 0, 1));
|
||||||
|
}
|
||||||
|
if (ImGui.Button("Join " + _syncShellToJoin))
|
||||||
|
{
|
||||||
|
var shell = _syncShellToJoin;
|
||||||
|
var pw = _syncShellPassword;
|
||||||
|
_errorGroupJoin = !_apiController.SendGroupJoin(shell, pw).Result;
|
||||||
|
if (!_errorGroupJoin)
|
||||||
|
{
|
||||||
|
_syncShellToJoin = string.Empty;
|
||||||
|
_showModalEnterPassword = false;
|
||||||
|
}
|
||||||
|
_syncShellPassword = string.Empty;
|
||||||
|
}
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginPopupModal("Create Syncshell", ref _showModalCreateGroup))
|
||||||
|
{
|
||||||
|
ImGui.SetWindowSize(new(400, 200));
|
||||||
|
UiShared.TextWrapped("Press the button below to create a new Syncshell.");
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
if (ImGui.Button("Create Syncshell"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_lastCreatedGroup = _apiController.CreateGroup().Result;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_lastCreatedGroup = null;
|
||||||
|
_errorGroupCreate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lastCreatedGroup != null)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
_errorGroupCreate = false;
|
||||||
|
ImGui.TextUnformatted("Syncshell ID: " + _lastCreatedGroup.GID);
|
||||||
|
ImGui.TextUnformatted("Syncshell Password: " + _lastCreatedGroup.Password);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Copy))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(_lastCreatedGroup.Password);
|
||||||
|
}
|
||||||
|
UiShared.TextWrapped("You can change the Syncshell password later at any time.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_errorGroupCreate)
|
||||||
|
{
|
||||||
|
UiShared.ColorTextWrapped("You are already owner of the maximum amount of Syncshells (3) or joined the maximum amount of Syncshells (6). Relinquish ownership of your own Syncshells to someone else or leave existing Syncshells.",
|
||||||
|
new Vector4(1, 0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSyncshellList()
|
||||||
|
{
|
||||||
|
var ySize = _mainUi.TransferPartHeight == 0
|
||||||
|
? 1
|
||||||
|
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _mainUi.TransferPartHeight - ImGui.GetCursorPosY();
|
||||||
|
ImGui.BeginChild("list", new Vector2(_mainUi._windowContentWidth, ySize), false);
|
||||||
|
foreach (var entry in _apiController.Groups.OrderBy(g => string.IsNullOrEmpty(g.Alias) ? g.GID : g.Alias).ToList())
|
||||||
|
{
|
||||||
|
UiShared.DrawWithID(entry.GID, () => DrawSyncshell(entry));
|
||||||
|
}
|
||||||
|
ImGui.EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSyncshell(GroupDto group)
|
||||||
|
{
|
||||||
|
var name = group.Alias ?? group.GID;
|
||||||
|
var pairsInGroup = _apiController.GroupPairedClients.Where(p => string.Equals(p.GroupGID, group.GID, StringComparison.Ordinal)).ToList();
|
||||||
|
if (!ExpandedGroupState.TryGetValue(group.GID, out bool isExpanded))
|
||||||
|
{
|
||||||
|
isExpanded = false;
|
||||||
|
ExpandedGroupState.Add(group.GID, isExpanded);
|
||||||
|
}
|
||||||
|
var icon = isExpanded ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
|
||||||
|
var collapseButton = UiShared.GetIconButtonSize(icon);
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0));
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0, 0, 0, 0));
|
||||||
|
if (ImGuiComponents.IconButton(icon))
|
||||||
|
{
|
||||||
|
ExpandedGroupState[group.GID] = !ExpandedGroupState[group.GID];
|
||||||
|
}
|
||||||
|
ImGui.PopStyleColor(2);
|
||||||
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + collapseButton.X);
|
||||||
|
var pauseIcon = (group.IsPaused ?? false) ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||||
|
if (ImGuiComponents.IconButton(pauseIcon))
|
||||||
|
{
|
||||||
|
_ = _apiController.SendPauseGroup(group.GID, !group.IsPaused ?? false);
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip(((group.IsPaused ?? false) ? "Resume" : "Pause") + " pairing with all users in this Syncshell");
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
var groupName = string.IsNullOrEmpty(group.Alias) ? group.GID : group.Alias;
|
||||||
|
var textIsGid = true;
|
||||||
|
|
||||||
|
if (string.Equals(group.OwnedBy, _apiController.UID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
ImGui.Text(FontAwesomeIcon.Crown.ToIconString());
|
||||||
|
ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip("You are the owner of Syncshell " + groupName);
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
_showGidForEntry.TryGetValue(group.GID, out var showGidInsteadOfName);
|
||||||
|
if (!showGidInsteadOfName && _configuration.GetCurrentServerGidComments().TryGetValue(group.GID, out var groupComment))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(groupComment))
|
||||||
|
{
|
||||||
|
groupName = groupComment;
|
||||||
|
textIsGid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals(_editGroupEntry, group.GID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
if (textIsGid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||||
|
ImGui.TextUnformatted(groupName);
|
||||||
|
if (textIsGid) ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip("Left click to switch between GID display and comment" + Environment.NewLine +
|
||||||
|
"Right click to change comment for " + groupName + Environment.NewLine + Environment.NewLine
|
||||||
|
+ "Users: " + (pairsInGroup.Count + 1) + ", Owner: " + group.OwnedBy);
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||||
|
{
|
||||||
|
var prevState = textIsGid;
|
||||||
|
if (_showGidForEntry.ContainsKey(group.GID))
|
||||||
|
{
|
||||||
|
prevState = _showGidForEntry[group.GID];
|
||||||
|
}
|
||||||
|
|
||||||
|
_showGidForEntry[group.GID] = !prevState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
|
{
|
||||||
|
_configuration.SetCurrentServerGidComment(_editGroupEntry, _editGroupComment);
|
||||||
|
_configuration.Save();
|
||||||
|
_editGroupComment = _configuration.GetCurrentServerGidComments().ContainsKey(group.GID)
|
||||||
|
? _configuration.GetCurrentServerGidComments()[group.GID]
|
||||||
|
: string.Empty;
|
||||||
|
_editGroupEntry = group.GID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX());
|
||||||
|
if (ImGui.InputTextWithHint("", "Comment/Notes", ref _editGroupComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||||
|
{
|
||||||
|
_configuration.SetCurrentServerGidComment(group.GID, _editGroupComment);
|
||||||
|
_configuration.Save();
|
||||||
|
_editGroupEntry = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
|
{
|
||||||
|
_editGroupEntry = string.Empty;
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
UiShared.DrawWithID(group.GID + "settings", () => DrawSyncShellButtons(group, name));
|
||||||
|
|
||||||
|
ImGui.Indent(collapseButton.X);
|
||||||
|
if (ExpandedGroupState[group.GID])
|
||||||
|
{
|
||||||
|
pairsInGroup = pairsInGroup.OrderBy(p => string.Equals(p.UserUID, group.OwnedBy, StringComparison.Ordinal) ? 0 : 1).ThenBy(p => p.IsPinned ?? false ? 0 : 1).ThenBy(p => p.UserAlias ?? p.UserUID).ToList();
|
||||||
|
ImGui.Indent(ImGui.GetStyle().ItemSpacing.X / 2);
|
||||||
|
ImGui.Separator();
|
||||||
|
foreach (var pair in pairsInGroup)
|
||||||
|
{
|
||||||
|
UiShared.DrawWithID(group.GID + pair.UserUID, () => DrawSyncshellPairedClient(pair, string.Equals(group.OwnedBy, _apiController.UID, StringComparison.Ordinal), group?.IsPaused ?? false));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.Unindent(ImGui.GetStyle().ItemSpacing.X / 2);
|
||||||
|
}
|
||||||
|
ImGui.Unindent(collapseButton.X);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSyncShellButtons(GroupDto entry, string name)
|
||||||
|
{
|
||||||
|
bool invitesEnabled = entry.InvitesEnabled ?? true;
|
||||||
|
var lockedIcon = invitesEnabled ? FontAwesomeIcon.LockOpen : FontAwesomeIcon.Lock;
|
||||||
|
var iconSize = UiShared.GetIconSize(lockedIcon);
|
||||||
|
var barbuttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||||
|
|
||||||
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - barbuttonSize.X - iconSize.X - ImGui.GetStyle().ItemSpacing.X);
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
ImGui.Text(lockedIcon.ToIconString());
|
||||||
|
ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip(invitesEnabled ? "Syncshell is open for new joiners" : "Syncshell is closed for new joiners");
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup("ShellPopup");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginPopup("ShellPopup"))
|
||||||
|
{
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell"))
|
||||||
|
{
|
||||||
|
if (UiShared.CtrlPressed())
|
||||||
|
{
|
||||||
|
_ = _apiController.SendLeaveGroup(entry.GID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hold CTRL and click to leave this Syncshell" + (!string.Equals(entry.OwnedBy, _apiController.UID, StringComparison.Ordinal) ? string.Empty : Environment.NewLine
|
||||||
|
+ "WARNING: This action is irreverisble" + Environment.NewLine + "Leaving an owned Syncshell will transfer the ownership to a random person in the Syncshell."));
|
||||||
|
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Copy, "Copy ID"))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(string.IsNullOrEmpty(entry.Alias) ? entry.GID : entry.Alias);
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Copy Syncshell ID to Clipboard");
|
||||||
|
|
||||||
|
if (string.Equals(entry.OwnedBy, _apiController.UID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
if (UiShared.IconTextButton(lockedIcon, invitesEnabled ? "Lock Syncshell" : "Unlock Syncshell"))
|
||||||
|
{
|
||||||
|
_ = _apiController.SendGroupChangeInviteState(entry.GID, !entry.InvitesEnabled ?? true);
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Change Syncshell joining permissions" + Environment.NewLine + "Syncshell is currently " + (invitesEnabled ? "open" : "closed") + " for people to join");
|
||||||
|
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Passport, "Change Password"))
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup("Change Syncshell Password");
|
||||||
|
_isPasswordValid = true;
|
||||||
|
_showModalChangePassword = true;
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Change Syncshell Password");
|
||||||
|
|
||||||
|
if (ImGui.BeginPopupModal("Change Syncshell Password", ref _showModalChangePassword, ImGuiWindowFlags.AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
UiShared.TextWrapped("Enter the new Syncshell password for Syncshell " + name + " here.");
|
||||||
|
UiShared.TextWrapped("This action is irreversible");
|
||||||
|
ImGui.InputTextWithHint("##changepw", "New password for " + name, ref _newSyncShellPassword, 255);
|
||||||
|
if (ImGui.Button("Change password"))
|
||||||
|
{
|
||||||
|
var pw = _newSyncShellPassword;
|
||||||
|
_isPasswordValid = _apiController.ChangeGroupPassword(entry.GID, pw).Result;
|
||||||
|
_newSyncShellPassword = string.Empty;
|
||||||
|
if (_isPasswordValid) _showModalChangePassword = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isPasswordValid)
|
||||||
|
{
|
||||||
|
UiShared.ColorTextWrapped("The selected password is too short. It must be at least 10 characters.", new Vector4(1, 0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
|
||||||
|
{
|
||||||
|
if (UiShared.CtrlPressed())
|
||||||
|
{
|
||||||
|
_ = _apiController.SendClearGroup(entry.GID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hold CTRL and click to clear this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible." + Environment.NewLine
|
||||||
|
+ "Clearing the Syncshell will remove all not pinned users from it.");
|
||||||
|
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell"))
|
||||||
|
{
|
||||||
|
if (UiShared.CtrlPressed() && UiShared.ShiftPressed())
|
||||||
|
{
|
||||||
|
_ = _apiController.SendDeleteGroup(entry.GID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSyncshellPairedClient(GroupPairDto entry, bool isOwner, bool isPausedByYou)
|
||||||
|
{
|
||||||
|
var plusButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
||||||
|
var barButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||||
|
var entryUID = string.IsNullOrEmpty(entry.UserAlias) ? entry.UserUID : entry.UserAlias;
|
||||||
|
var textSize = ImGui.CalcTextSize(entryUID);
|
||||||
|
var originalY = ImGui.GetCursorPosY();
|
||||||
|
var buttonSizes = plusButtonSize.Y;
|
||||||
|
|
||||||
|
var textPos = originalY + plusButtonSize.Y / 2 - textSize.Y / 2;
|
||||||
|
ImGui.SetCursorPosY(textPos);
|
||||||
|
if (isPausedByYou || (entry.IsPaused ?? false))
|
||||||
|
{
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
UiShared.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow);
|
||||||
|
ImGui.PopFont();
|
||||||
|
|
||||||
|
UiShared.AttachToolTip("Pairing status with " + entryUID + " is paused");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
UiShared.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen);
|
||||||
|
ImGui.PopFont();
|
||||||
|
|
||||||
|
UiShared.AttachToolTip("You are paired with " + entryUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.IsPinned ?? false)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosY(textPos);
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
ImGui.TextUnformatted(FontAwesomeIcon.Thumbtack.ToIconString());
|
||||||
|
ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip("User is pinned in this Syncshell");
|
||||||
|
}
|
||||||
|
|
||||||
|
var textIsUid = true;
|
||||||
|
_mainUi.ShowUidForEntry.TryGetValue(entry.UserUID, out var showUidInsteadOfName);
|
||||||
|
if (!showUidInsteadOfName && _configuration.GetCurrentServerUidComments().TryGetValue(entry.UserUID, out var playerText))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(playerText))
|
||||||
|
{
|
||||||
|
playerText = entryUID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textIsUid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
playerText = entryUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (!string.Equals(_mainUi.EditNickEntry, entry.UserUID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosY(textPos);
|
||||||
|
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||||
|
ImGui.TextUnformatted(playerText);
|
||||||
|
if (textIsUid) ImGui.PopFont();
|
||||||
|
UiShared.AttachToolTip("Left click to switch between UID display and nick" + Environment.NewLine +
|
||||||
|
"Right click to change nick for " + entryUID);
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||||
|
{
|
||||||
|
var prevState = textIsUid;
|
||||||
|
if (_mainUi.ShowUidForEntry.ContainsKey(entry.UserUID))
|
||||||
|
{
|
||||||
|
prevState = _mainUi.ShowUidForEntry[entry.UserUID];
|
||||||
|
}
|
||||||
|
|
||||||
|
_mainUi.ShowUidForEntry[entry.UserUID] = !prevState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
|
{
|
||||||
|
_configuration.SetCurrentServerUidComment(_mainUi.EditNickEntry, _mainUi.EditUserComment);
|
||||||
|
_configuration.Save();
|
||||||
|
_mainUi.EditUserComment = _configuration.GetCurrentServerUidComments().ContainsKey(entry.UserUID)
|
||||||
|
? _configuration.GetCurrentServerUidComments()[entry.UserUID]
|
||||||
|
: string.Empty;
|
||||||
|
_mainUi.EditNickEntry = entry.UserUID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosY(originalY);
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
||||||
|
if (ImGui.InputTextWithHint("", "Nick/Notes", ref _mainUi.EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||||
|
{
|
||||||
|
_configuration.SetCurrentServerUidComment(entry.UserUID, _mainUi.EditUserComment);
|
||||||
|
_configuration.Save();
|
||||||
|
_mainUi.EditNickEntry = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||||
|
{
|
||||||
|
_mainUi.EditNickEntry = string.Empty;
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plusButtonShown = !_apiController.PairedClients.Any(p => string.Equals(p.OtherUID, entry.UserUID, StringComparison.Ordinal));
|
||||||
|
|
||||||
|
if (plusButtonShown)
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosY(originalY);
|
||||||
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - plusButtonSize.X - (isOwner ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0));
|
||||||
|
|
||||||
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||||
|
{
|
||||||
|
_ = _apiController.SendPairedClientAddition(entry.UserUID);
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Pair with " + entryUID + " individually");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwner)
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPosY(originalY);
|
||||||
|
var subtractedWidth = plusButtonShown ? (plusButtonSize.X) : 0;
|
||||||
|
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - barButtonSize.X);
|
||||||
|
|
||||||
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
|
||||||
|
{
|
||||||
|
ImGui.OpenPopup("Popup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginPopup("Popup"))
|
||||||
|
{
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Thumbtack, "Pin user"))
|
||||||
|
{
|
||||||
|
_ = _apiController.SendChangeUserPinned(entry.GroupGID, entry.UserUID, !entry.IsPinned ?? false);
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean");
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership"))
|
||||||
|
{
|
||||||
|
if (UiShared.CtrlPressed() && UiShared.ShiftPressed())
|
||||||
|
{
|
||||||
|
_ = _apiController.ChangeOwnerOfGroup(entry.GroupGID, entry.UserUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (entry.UserAlias ?? entry.UserUID) + Environment.NewLine + "WARNING: This action is irreversible.");
|
||||||
|
if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Remove user"))
|
||||||
|
{
|
||||||
|
if (UiShared.CtrlPressed())
|
||||||
|
{
|
||||||
|
_ = _apiController.SendRemoveUserFromGroup(entry.GroupGID, entry.UserUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiShared.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAlias ?? entry.UserUID) + " from Syncshell");
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@ using MareSynchronos.Localization;
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
|
||||||
namespace MareSynchronos.UI
|
namespace MareSynchronos.UI;
|
||||||
{
|
|
||||||
internal class IntroUi : Window, IDisposable
|
internal class IntroUi : Window, IDisposable
|
||||||
{
|
{
|
||||||
private readonly UiShared _uiShared;
|
private readonly UiShared _uiShared;
|
||||||
@@ -37,12 +37,12 @@ namespace MareSynchronos.UI
|
|||||||
private Task _timeoutTask;
|
private Task _timeoutTask;
|
||||||
private string _timeoutTime;
|
private string _timeoutTime;
|
||||||
|
|
||||||
private Dictionary<string, string> _languages = new() { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
private Dictionary<string, string> _languages = new(StringComparer.Ordinal) { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
||||||
private int _currentLanguage;
|
private int _currentLanguage;
|
||||||
|
|
||||||
private bool DarkSoulsCaptchaValid => _darkSoulsCaptcha1.Item2 == _enteredDarkSoulsCaptcha1.Trim()
|
private bool DarkSoulsCaptchaValid => string.Equals(_darkSoulsCaptcha1.Item2, _enteredDarkSoulsCaptcha1.Trim()
|
||||||
&& _darkSoulsCaptcha2.Item2 == _enteredDarkSoulsCaptcha2.Trim()
|
, StringComparison.Ordinal) && string.Equals(_darkSoulsCaptcha2.Item2, _enteredDarkSoulsCaptcha2.Trim()
|
||||||
&& _darkSoulsCaptcha3.Item2 == _enteredDarkSoulsCaptcha3.Trim();
|
, StringComparison.Ordinal) && string.Equals(_darkSoulsCaptcha3.Item2, _enteredDarkSoulsCaptcha3.Trim(), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -158,7 +158,7 @@ namespace MareSynchronos.UI
|
|||||||
{
|
{
|
||||||
_timeoutTime = $"{i}s " + Strings.ToS.RemainingLabel;
|
_timeoutTime = $"{i}s " + Strings.ToS.RemainingLabel;
|
||||||
Logger.Debug(_timeoutTime);
|
Logger.Debug(_timeoutTime);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -286,13 +286,12 @@ namespace MareSynchronos.UI
|
|||||||
|
|
||||||
private Tuple<string, string> GetCaptchaTuple()
|
private Tuple<string, string> GetCaptchaTuple()
|
||||||
{
|
{
|
||||||
Random random = new Random();
|
Random random = new();
|
||||||
var paragraphIdx = random.Next(TosParagraphs.Length);
|
var paragraphIdx = random.Next(TosParagraphs.Length);
|
||||||
var splitParagraph = TosParagraphs[paragraphIdx].Split(".", StringSplitOptions.RemoveEmptyEntries).Select(c => c.Trim()).ToArray();
|
var splitParagraph = TosParagraphs[paragraphIdx].Split(".", StringSplitOptions.RemoveEmptyEntries).Select(c => c.Trim()).ToArray();
|
||||||
var sentenceIdx = random.Next(splitParagraph.Length);
|
var sentenceIdx = random.Next(splitParagraph.Length);
|
||||||
var splitSentence = splitParagraph[sentenceIdx].Split(" ").Select(c => c.Trim()).Select(c => c.Replace(".", "").Replace(",", "").Replace("'", "")).ToArray();
|
var splitSentence = splitParagraph[sentenceIdx].Split(" ").Select(c => c.Trim()).Select(c => c.Replace(".", "", StringComparison.Ordinal).Replace(",", "", StringComparison.Ordinal).Replace("'", "", StringComparison.Ordinal)).ToArray();
|
||||||
var wordIdx = random.Next(splitSentence.Length);
|
var wordIdx = random.Next(splitSentence.Length);
|
||||||
return new($"{Strings.ToS.ParagraphLabel} {paragraphIdx + 1}, {Strings.ToS.SentenceLabel} {sentenceIdx + 1}, {Strings.ToS.WordLabel} {wordIdx + 1}", splitSentence[wordIdx]);
|
return new($"{Strings.ToS.ParagraphLabel} {paragraphIdx + 1}, {Strings.ToS.SentenceLabel} {sentenceIdx + 1}, {Strings.ToS.WordLabel} {wordIdx + 1}", splitSentence[wordIdx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ using MareSynchronos.WebAPI.Utils;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace MareSynchronos.UI
|
namespace MareSynchronos.UI;
|
||||||
{
|
|
||||||
public delegate void SwitchUi();
|
public delegate void SwitchUi();
|
||||||
public class SettingsUi : Window, IDisposable
|
public class SettingsUi : Window, IDisposable
|
||||||
{
|
{
|
||||||
@@ -335,7 +335,7 @@ namespace MareSynchronos.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (onlineUser.UID != _apiController.UID && _apiController.IsAdmin)
|
if (!string.Equals(onlineUser.UID, _apiController.UID, StringComparison.Ordinal) && _apiController.IsAdmin)
|
||||||
{
|
{
|
||||||
if (!onlineUser.IsModerator)
|
if (!onlineUser.IsModerator)
|
||||||
{
|
{
|
||||||
@@ -610,4 +610,3 @@ namespace MareSynchronos.UI
|
|||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ using MareSynchronos.Managers;
|
|||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
|
|
||||||
namespace MareSynchronos.UI
|
namespace MareSynchronos.UI;
|
||||||
{
|
|
||||||
public class UiShared : IDisposable
|
public class UiShared : IDisposable
|
||||||
{
|
{
|
||||||
[DllImport("user32")]
|
[DllImport("user32")]
|
||||||
@@ -39,8 +39,8 @@ namespace MareSynchronos.UI
|
|||||||
public bool EditTrackerPosition { get; set; }
|
public bool EditTrackerPosition { get; set; }
|
||||||
public ImFontPtr UidFont { get; private set; }
|
public ImFontPtr UidFont { get; private set; }
|
||||||
public bool UidFontBuilt { get; private set; }
|
public bool UidFontBuilt { get; private set; }
|
||||||
|
|
||||||
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
|
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
|
||||||
|
public static bool ShiftPressed() => (GetKeyState(0xA1) & 0x8000) != 0 || (GetKeyState(0xA0) & 0x8000) != 0;
|
||||||
|
|
||||||
public ApiController ApiController => _apiController;
|
public ApiController ApiController => _apiController;
|
||||||
|
|
||||||
@@ -88,8 +88,8 @@ namespace MareSynchronos.UI
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Debug($"Font failed to load. {fontFile}");
|
Logger.Warn($"Font failed to load. {fontFile}");
|
||||||
Logger.Debug(ex.ToString());
|
Logger.Warn(ex.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -294,7 +294,7 @@ namespace MareSynchronos.UI
|
|||||||
bool isSelected = _serverSelectionIndex == i;
|
bool isSelected = _serverSelectionIndex == i;
|
||||||
if (ImGui.Selectable(comboEntries[i], isSelected))
|
if (ImGui.Selectable(comboEntries[i], isSelected))
|
||||||
{
|
{
|
||||||
_pluginConfiguration.ApiUri = _apiController.ServerDictionary.Single(k => k.Value == comboEntries[i]).Key;
|
_pluginConfiguration.ApiUri = _apiController.ServerDictionary.Single(k => string.Equals(k.Value, comboEntries[i], StringComparison.Ordinal)).Key;
|
||||||
_pluginConfiguration.Save();
|
_pluginConfiguration.Save();
|
||||||
_ = _apiController.CreateConnections();
|
_ = _apiController.CreateConnections();
|
||||||
}
|
}
|
||||||
@@ -451,7 +451,7 @@ namespace MareSynchronos.UI
|
|||||||
{
|
{
|
||||||
if (!success) return;
|
if (!success) return;
|
||||||
|
|
||||||
_isPenumbraDirectory = path.ToLowerInvariant() == _ipcManager.PenumbraModDirectory()?.ToLowerInvariant();
|
_isPenumbraDirectory = string.Equals(path.ToLowerInvariant(), _ipcManager.PenumbraModDirectory()?.ToLowerInvariant(), StringComparison.Ordinal);
|
||||||
_isDirectoryWritable = IsDirectoryWritable(path);
|
_isDirectoryWritable = IsDirectoryWritable(path);
|
||||||
_cacheDirectoryHasOtherFilesThanCache = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Any(f => new FileInfo(f).Name.Length != 40);
|
_cacheDirectoryHasOtherFilesThanCache = Directory.GetFiles(path, "*", SearchOption.AllDirectories).Any(f => new FileInfo(f).Name.Length != 40);
|
||||||
_cacheDirectoryIsValidPath = Regex.IsMatch(path, @"^(?:[a-zA-Z]:\\[\w\s\-\\]+?|\/(?:[\w\s\-\/])+?)$", RegexOptions.ECMAScript);
|
_cacheDirectoryIsValidPath = Regex.IsMatch(path, @"^(?:[a-zA-Z]:\\[\w\s\-\\]+?|\/(?:[\w\s\-\/])+?)$", RegexOptions.ECMAScript);
|
||||||
@@ -548,9 +548,49 @@ namespace MareSynchronos.UI
|
|||||||
DrawHelpText("This allows you to stop the periodic scans of your Penumbra and Mare cache directories. Use this to move the Mare cache and Penumbra mod folders around. If you enable this permanently, run a Force rescan after adding mods to Penumbra.");
|
DrawHelpText("This allows you to stop the periodic scans of your Penumbra and Mare cache directories. Use this to move the Mare cache and Penumbra mod folders around. If you enable this permanently, run a Force rescan after adding mods to Penumbra.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector2 GetIconSize(FontAwesomeIcon icon)
|
||||||
|
{
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
var iconSize = ImGui.CalcTextSize(icon.ToIconString());
|
||||||
|
ImGui.PopFont();
|
||||||
|
return iconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IconTextButton(FontAwesomeIcon icon, string text)
|
||||||
|
{
|
||||||
|
var buttonClicked = false;
|
||||||
|
|
||||||
|
var iconSize = GetIconSize(icon);
|
||||||
|
var textSize = ImGui.CalcTextSize(text);
|
||||||
|
var padding = ImGui.GetStyle().FramePadding;
|
||||||
|
var spacing = ImGui.GetStyle().ItemSpacing;
|
||||||
|
|
||||||
|
var buttonSizeX = iconSize.X + textSize.X + padding.X * 2 + spacing.X;
|
||||||
|
var buttonSizeY = (iconSize.Y > textSize.Y ? iconSize.Y : textSize.Y) + padding.Y * 2;
|
||||||
|
var buttonSize = new Vector2(buttonSizeX, buttonSizeY);
|
||||||
|
|
||||||
|
if (ImGui.BeginChild(icon.ToIconString() + text, buttonSize))
|
||||||
|
{
|
||||||
|
if (ImGui.Button("", buttonSize))
|
||||||
|
{
|
||||||
|
buttonClicked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosX(padding.X);
|
||||||
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
|
ImGui.Text(icon.ToIconString());
|
||||||
|
ImGui.PopFont();
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.Text(text);
|
||||||
|
ImGui.EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttonClicked;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_pluginInterface.UiBuilder.BuildFonts -= BuildFont;
|
_pluginInterface.UiBuilder.BuildFonts -= BuildFont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,32 +4,31 @@ using System.Security.Cryptography;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
|
|
||||||
namespace MareSynchronos.Utils
|
namespace MareSynchronos.Utils;
|
||||||
{
|
|
||||||
public class Crypto
|
public class Crypto
|
||||||
{
|
{
|
||||||
public static string GetFileHash(string filePath)
|
public static string GetFileHash(string filePath)
|
||||||
{
|
{
|
||||||
using SHA1CryptoServiceProvider cryptoProvider = new();
|
using SHA1CryptoServiceProvider cryptoProvider = new();
|
||||||
return BitConverter.ToString(cryptoProvider.ComputeHash(File.ReadAllBytes(filePath))).Replace("-", "");
|
return BitConverter.ToString(cryptoProvider.ComputeHash(File.ReadAllBytes(filePath))).Replace("-", "", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetHash(string stringToHash)
|
public static string GetHash(string stringToHash)
|
||||||
{
|
{
|
||||||
using SHA1CryptoServiceProvider cryptoProvider = new();
|
using SHA1CryptoServiceProvider cryptoProvider = new();
|
||||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "");
|
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetHash256(string stringToHash)
|
public static string GetHash256(string stringToHash)
|
||||||
{
|
{
|
||||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "");
|
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetHash256(PlayerCharacter character)
|
public static string GetHash256(PlayerCharacter character)
|
||||||
{
|
{
|
||||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(character.Name + character.HomeWorld.Id.ToString()))).Replace("-", "");
|
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(character.Name + character.HomeWorld.Id.ToString()))).Replace("-", "", StringComparison.Ordinal);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
MareSynchronos/Utils/DalamudLoggingProvider.cs
Normal file
26
MareSynchronos/Utils/DalamudLoggingProvider.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Utils;
|
||||||
|
|
||||||
|
[ProviderAlias("Dalamud")]
|
||||||
|
public class DalamudLoggingProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, Logger> _loggers =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public DalamudLoggingProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName)
|
||||||
|
{
|
||||||
|
return _loggers.GetOrAdd(categoryName, name => new Logger(categoryName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_loggers.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,8 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||||
|
|
||||||
namespace MareSynchronos.Utils
|
namespace MareSynchronos.Utils;
|
||||||
{
|
|
||||||
public delegate void PlayerChange(Dalamud.Game.ClientState.Objects.Types.Character actor);
|
public delegate void PlayerChange(Dalamud.Game.ClientState.Objects.Types.Character actor);
|
||||||
|
|
||||||
public delegate void LogIn();
|
public delegate void LogIn();
|
||||||
@@ -186,7 +186,7 @@ namespace MareSynchronos.Utils
|
|||||||
{
|
{
|
||||||
return _objectTable.Where(obj =>
|
return _objectTable.Where(obj =>
|
||||||
obj.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player &&
|
obj.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player &&
|
||||||
obj.Name.ToString() != PlayerName).Select(p => (PlayerCharacter)p).ToList();
|
!string.Equals(obj.Name.ToString(), PlayerName, StringComparison.Ordinal)).Select(p => (PlayerCharacter)p).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dalamud.Game.ClientState.Objects.Types.Character? GetCharacterFromObjectTableByIndex(int index)
|
public Dalamud.Game.ClientState.Objects.Types.Character? GetCharacterFromObjectTableByIndex(int index)
|
||||||
@@ -201,7 +201,7 @@ namespace MareSynchronos.Utils
|
|||||||
foreach (var item in _objectTable)
|
foreach (var item in _objectTable)
|
||||||
{
|
{
|
||||||
if (item.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
if (item.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
||||||
if (item.Name.ToString() == characterName) return (PlayerCharacter)item;
|
if (string.Equals(item.Name.ToString(), characterName, StringComparison.Ordinal)) return (PlayerCharacter)item;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -209,7 +209,7 @@ namespace MareSynchronos.Utils
|
|||||||
|
|
||||||
public async Task<T> RunOnFrameworkThread<T>(Func<T> func)
|
public async Task<T> RunOnFrameworkThread<T>(Func<T> func)
|
||||||
{
|
{
|
||||||
return await _framework.RunOnFrameworkThread(func);
|
return await _framework.RunOnFrameworkThread(func).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void WaitWhileCharacterIsDrawing(string name, IntPtr characterAddress, int timeOut = 5000, CancellationToken? ct = null)
|
public unsafe void WaitWhileCharacterIsDrawing(string name, IntPtr characterAddress, int timeOut = 5000, CancellationToken? ct = null)
|
||||||
@@ -239,4 +239,3 @@ namespace MareSynchronos.Utils
|
|||||||
_framework.Update -= FrameworkOnUpdate;
|
_framework.Update -= FrameworkOnUpdate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,32 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MareSynchronos.Utils
|
namespace MareSynchronos.Utils;
|
||||||
{
|
|
||||||
[ProviderAlias("Dalamud")]
|
|
||||||
public class DalamudLoggingProvider : ILoggerProvider
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, Logger> _loggers =
|
|
||||||
new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
public DalamudLoggingProvider()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
|
||||||
{
|
|
||||||
return _loggers.GetOrAdd(categoryName, name => new Logger(categoryName));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_loggers.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class Logger : ILogger
|
internal class Logger : ILogger
|
||||||
{
|
{
|
||||||
@@ -41,7 +19,7 @@ namespace MareSynchronos.Utils
|
|||||||
public static void Debug(string debug, string stringToHighlight = "")
|
public static void Debug(string debug, string stringToHighlight = "")
|
||||||
{
|
{
|
||||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||||
if (debug.Contains(stringToHighlight) && !stringToHighlight.IsNullOrEmpty())
|
if (debug.Contains(stringToHighlight, StringComparison.Ordinal) && !stringToHighlight.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
PluginLog.Warning($"[{caller}] {debug}");
|
PluginLog.Warning($"[{caller}] {debug}");
|
||||||
}
|
}
|
||||||
@@ -109,4 +87,3 @@ namespace MareSynchronos.Utils
|
|||||||
|
|
||||||
public IDisposable BeginScope<TState>(TState state) => default!;
|
public IDisposable BeginScope<TState>(TState state) => default!;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Utils
|
|
||||||
{
|
|
||||||
public static class VariousExtensions
|
|
||||||
{
|
|
||||||
public static DateTime GetLinkerTime(Assembly assembly)
|
|
||||||
{
|
|
||||||
const string BuildVersionMetadataPrefix = "+build";
|
|
||||||
|
|
||||||
var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
|
||||||
if (attribute?.InformationalVersion != null)
|
|
||||||
{
|
|
||||||
var value = attribute.InformationalVersion;
|
|
||||||
var index = value.IndexOf(BuildVersionMetadataPrefix);
|
|
||||||
if (index > 0)
|
|
||||||
{
|
|
||||||
value = value[(index + BuildVersionMetadataPrefix.Length)..];
|
|
||||||
return DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss:fffZ", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
31
MareSynchronos/Utils/VariousExtensions.cs
Normal file
31
MareSynchronos/Utils/VariousExtensions.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Utils;
|
||||||
|
|
||||||
|
public static class VariousExtensions
|
||||||
|
{
|
||||||
|
public static DateTime GetLinkerTime(Assembly assembly)
|
||||||
|
{
|
||||||
|
const string BuildVersionMetadataPrefix = "+build";
|
||||||
|
|
||||||
|
var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||||
|
if (attribute?.InformationalVersion != null)
|
||||||
|
{
|
||||||
|
var value = attribute.InformationalVersion;
|
||||||
|
var index = value.IndexOf(BuildVersionMetadataPrefix, StringComparison.Ordinal);
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
value = value[(index + BuildVersionMetadataPrefix.Length)..];
|
||||||
|
return DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss:fffZ", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,8 @@ using MareSynchronos.Utils;
|
|||||||
using MareSynchronos.WebAPI.Utils;
|
using MareSynchronos.WebAPI.Utils;
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
namespace MareSynchronos.WebAPI;
|
||||||
{
|
|
||||||
public partial class ApiController
|
public partial class ApiController
|
||||||
{
|
{
|
||||||
private readonly HashSet<string> _verifiedUploadedHashes;
|
private readonly HashSet<string> _verifiedUploadedHashes;
|
||||||
@@ -33,7 +33,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
public async Task DeleteAllMyFiles()
|
public async Task DeleteAllMyFiles()
|
||||||
{
|
{
|
||||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> DownloadFile(int downloadId, string hash, Uri downloadUri, CancellationToken ct)
|
private async Task<string> DownloadFile(int downloadId, string hash, Uri downloadUri, CancellationToken ct)
|
||||||
@@ -44,7 +44,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = e.BytesReceived;
|
CurrentDownloads[downloadId].Single(f => string.Equals(f.Hash, hash, StringComparison.Ordinal)).Transferred = e.BytesReceived;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -61,11 +61,11 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await wc.DownloadFileTaskAsync(downloadUri, fileName);
|
await wc.DownloadFileTaskAsync(downloadUri, fileName).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = CurrentDownloads[downloadId].Single(f => f.Hash == hash).Total;
|
CurrentDownloads[downloadId].Single(f => string.Equals(f.Hash, hash, StringComparison.Ordinal)).Transferred = CurrentDownloads[downloadId].Single(f => string.Equals(f.Hash, hash, StringComparison.Ordinal)).Total;
|
||||||
|
|
||||||
wc.DownloadProgressChanged -= progChanged;
|
wc.DownloadProgressChanged -= progChanged;
|
||||||
return fileName;
|
return fileName;
|
||||||
@@ -78,7 +78,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
DownloadStarted?.Invoke();
|
DownloadStarted?.Invoke();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DownloadFilesInternal(currentDownloadId, fileReplacementDto, ct);
|
await DownloadFilesInternal(currentDownloadId, fileReplacementDto, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -94,8 +94,8 @@ namespace MareSynchronos.WebAPI
|
|||||||
{
|
{
|
||||||
Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");
|
Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");
|
||||||
|
|
||||||
List<DownloadFileDto> downloadFileInfoFromService = new List<DownloadFileDto>();
|
List<DownloadFileDto> downloadFileInfoFromService = new();
|
||||||
downloadFileInfoFromService.AddRange(await _mareHub!.InvokeAsync<List<DownloadFileDto>>(Api.InvokeGetFilesSizes, fileReplacementDto.Select(f => f.Hash).ToList(), ct));
|
downloadFileInfoFromService.AddRange(await _mareHub!.InvokeAsync<List<DownloadFileDto>>(Api.InvokeGetFilesSizes, fileReplacementDto.Select(f => f.Hash).ToList(), ct).ConfigureAwait(false));
|
||||||
|
|
||||||
Logger.Debug("Files with size 0 or less: " + string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
|
Logger.Debug("Files with size 0 or less: " + string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
|
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
|
||||||
{
|
{
|
||||||
if (ForbiddenTransfers.All(f => f.Hash != dto.Hash))
|
if (ForbiddenTransfers.All(f => !string.Equals(f.Hash, dto.Hash, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
ForbiddenTransfers.Add(new DownloadFileTransfer(dto));
|
ForbiddenTransfers.Add(new DownloadFileTransfer(dto));
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
async (file, token) =>
|
async (file, token) =>
|
||||||
{
|
{
|
||||||
var hash = file.Hash;
|
var hash = file.Hash;
|
||||||
var tempFile = await DownloadFile(currentDownloadId, file.Hash, file.DownloadUri, token);
|
var tempFile = await DownloadFile(currentDownloadId, file.Hash, file.DownloadUri, token).ConfigureAwait(false);
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
File.Delete(tempFile);
|
File.Delete(tempFile);
|
||||||
@@ -128,16 +128,16 @@ namespace MareSynchronos.WebAPI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempFileData = await File.ReadAllBytesAsync(tempFile, token);
|
var tempFileData = await File.ReadAllBytesAsync(tempFile, token).ConfigureAwait(false);
|
||||||
var extractedFile = LZ4Codec.Unwrap(tempFileData);
|
var extractedFile = LZ4Codec.Unwrap(tempFileData);
|
||||||
File.Delete(tempFile);
|
File.Delete(tempFile);
|
||||||
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash);
|
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash);
|
||||||
await File.WriteAllBytesAsync(filePath, extractedFile, token);
|
await File.WriteAllBytesAsync(filePath, extractedFile, token).ConfigureAwait(false);
|
||||||
var fi = new FileInfo(filePath);
|
var fi = new FileInfo(filePath);
|
||||||
Func<DateTime> RandomDayFunc()
|
Func<DateTime> RandomDayFunc()
|
||||||
{
|
{
|
||||||
DateTime start = new DateTime(1995, 1, 1);
|
DateTime start = new(1995, 1, 1);
|
||||||
Random gen = new Random();
|
Random gen = new();
|
||||||
int range = (DateTime.Today - start).Days;
|
int range = (DateTime.Today - start).Days;
|
||||||
return () => start.AddDays(gen.Next(range));
|
return () => start.AddDays(gen.Next(range));
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
Logger.Warn(ex.Message);
|
Logger.Warn(ex.Message);
|
||||||
Logger.Warn(ex.StackTrace);
|
Logger.Warn(ex.StackTrace);
|
||||||
}
|
}
|
||||||
});
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
Logger.Debug("Download complete, removing " + currentDownloadId);
|
Logger.Debug("Download complete, removing " + currentDownloadId);
|
||||||
CancelDownload(currentDownloadId);
|
CancelDownload(currentDownloadId);
|
||||||
@@ -163,7 +163,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
||||||
{
|
{
|
||||||
if (!IsConnected || SecretKey == "-") return;
|
if (!IsConnected || string.Equals(SecretKey, "-", StringComparison.Ordinal)) return;
|
||||||
Logger.Debug("Sending Character data to service " + ApiUri);
|
Logger.Debug("Sending Character data to service " + ApiUri);
|
||||||
|
|
||||||
CancelUpload();
|
CancelUpload();
|
||||||
@@ -172,7 +172,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
Logger.Verbose("New Token Created");
|
Logger.Verbose("New Token Created");
|
||||||
|
|
||||||
List<string> unverifiedUploadHashes = new();
|
List<string> unverifiedUploadHashes = new();
|
||||||
foreach (var item in character.FileReplacements.SelectMany(c => c.Value.Where(f => string.IsNullOrEmpty(f.FileSwapPath)).Select(v => v.Hash).Distinct()).Distinct().ToList())
|
foreach (var item in character.FileReplacements.SelectMany(c => c.Value.Where(f => string.IsNullOrEmpty(f.FileSwapPath)).Select(v => v.Hash).Distinct(StringComparer.Ordinal)).Distinct(StringComparer.Ordinal).ToList())
|
||||||
{
|
{
|
||||||
if (!_verifiedUploadedHashes.Contains(item))
|
if (!_verifiedUploadedHashes.Contains(item))
|
||||||
{
|
{
|
||||||
@@ -183,7 +183,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
if (unverifiedUploadHashes.Any())
|
if (unverifiedUploadHashes.Any())
|
||||||
{
|
{
|
||||||
Logger.Debug("Verifying " + unverifiedUploadHashes.Count + " files");
|
Logger.Debug("Verifying " + unverifiedUploadHashes.Count + " files");
|
||||||
var filesToUpload = await _mareHub!.InvokeAsync<List<UploadFileDto>>(Api.InvokeFileSendFiles, unverifiedUploadHashes, uploadToken);
|
var filesToUpload = await _mareHub!.InvokeAsync<List<UploadFileDto>>(Api.InvokeFileSendFiles, unverifiedUploadHashes, uploadToken).ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
|
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
foreach (var file in filesToUpload.Where(c => c.IsForbidden))
|
foreach (var file in filesToUpload.Where(c => c.IsForbidden))
|
||||||
{
|
{
|
||||||
if (ForbiddenTransfers.All(f => f.Hash != file.Hash))
|
if (ForbiddenTransfers.All(f => !string.Equals(f.Hash, file.Hash, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
ForbiddenTransfers.Add(new UploadFileTransfer(file)
|
ForbiddenTransfers.Add(new UploadFileTransfer(file)
|
||||||
{
|
{
|
||||||
@@ -217,9 +217,9 @@ namespace MareSynchronos.WebAPI
|
|||||||
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList())
|
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList())
|
||||||
{
|
{
|
||||||
Logger.Debug("Compressing and uploading " + file);
|
Logger.Debug("Compressing and uploading " + file);
|
||||||
var data = await GetCompressedFileData(file.Hash, uploadToken);
|
var data = await GetCompressedFileData(file.Hash, uploadToken).ConfigureAwait(false);
|
||||||
CurrentUploads.Single(e => e.Hash == data.Item1).Total = data.Item2.Length;
|
CurrentUploads.Single(e => string.Equals(e.Hash, data.Item1, StringComparison.Ordinal)).Total = data.Item2.Length;
|
||||||
await UploadFile(data.Item2, file.Hash, uploadToken);
|
await UploadFile(data.Item2, file.Hash, uploadToken).ConfigureAwait(false);
|
||||||
if (!uploadToken.IsCancellationRequested) continue;
|
if (!uploadToken.IsCancellationRequested) continue;
|
||||||
Logger.Warn("Cancel in filesToUpload loop detected");
|
Logger.Warn("Cancel in filesToUpload loop detected");
|
||||||
CurrentUploads.Clear();
|
CurrentUploads.Clear();
|
||||||
@@ -233,12 +233,12 @@ namespace MareSynchronos.WebAPI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.Debug("Upload tasks complete, waiting for server to confirm");
|
Logger.Debug("Upload tasks complete, waiting for server to confirm");
|
||||||
var anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
var anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken).ConfigureAwait(false);
|
||||||
Logger.Debug("Uploads open: " + anyUploadsOpen);
|
Logger.Debug("Uploads open: " + anyUploadsOpen);
|
||||||
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
|
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken).ConfigureAwait(false);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken);
|
await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken).ConfigureAwait(false);
|
||||||
Logger.Debug("Waiting for uploads to finish");
|
Logger.Debug("Waiting for uploads to finish");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
if (!uploadToken.IsCancellationRequested)
|
if (!uploadToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Logger.Info("Pushing character data for " + character.GetHashCode() + " to " + string.Join(", ", visibleCharacterIds));
|
Logger.Info("Pushing character data for " + character.GetHashCode() + " to " + string.Join(", ", visibleCharacterIds));
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new();
|
||||||
foreach (var item in character.FileReplacements)
|
foreach (var item in character.FileReplacements)
|
||||||
{
|
{
|
||||||
sb.AppendLine($"FileReplacements for {item.Key}: {item.Value.Count}");
|
sb.AppendLine($"FileReplacements for {item.Key}: {item.Value.Count}");
|
||||||
@@ -267,7 +267,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||||
}
|
}
|
||||||
Logger.Debug("Chara data contained: " + Environment.NewLine + sb.ToString());
|
Logger.Debug("Chara data contained: " + Environment.NewLine + sb.ToString());
|
||||||
await _mareHub!.InvokeAsync(Api.InvokeUserPushCharacterDataToVisibleClients, character, visibleCharacterIds, uploadToken);
|
await _mareHub!.InvokeAsync(Api.InvokeUserPushCharacterDataToVisibleClients, character, visibleCharacterIds, uploadToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -281,7 +281,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
||||||
{
|
{
|
||||||
var fileCache = _fileDbManager.GetFileCacheByHash(fileHash)!.ResolvedFilepath;
|
var fileCache = _fileDbManager.GetFileCacheByHash(fileHash)!.ResolvedFilepath;
|
||||||
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken), 0,
|
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken).ConfigureAwait(false), 0,
|
||||||
(int)new FileInfo(fileCache).Length));
|
(int)new FileInfo(fileCache).Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,15 +295,15 @@ namespace MareSynchronos.WebAPI
|
|||||||
using var ms = new MemoryStream(compressedFile);
|
using var ms = new MemoryStream(compressedFile);
|
||||||
var buffer = new byte[chunkSize];
|
var buffer = new byte[chunkSize];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
while ((bytesRead = await ms.ReadAsync(buffer, 0, chunkSize, token)) > 0 && !token.IsCancellationRequested)
|
while ((bytesRead = await ms.ReadAsync(buffer, 0, chunkSize, token).ConfigureAwait(false)) > 0 && !token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
CurrentUploads.Single(f => f.Hash == fileHash).Transferred += bytesRead;
|
CurrentUploads.Single(f => string.Equals(f.Hash, fileHash, StringComparison.Ordinal)).Transferred += bytesRead;
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
yield return bytesRead == chunkSize ? buffer.ToArray() : buffer.Take(bytesRead).ToArray();
|
yield return bytesRead == chunkSize ? buffer.ToArray() : buffer.Take(bytesRead).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _mareHub!.SendAsync(Api.SendFileUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken);
|
await _mareHub!.SendAsync(Api.SendFileUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelDownload(int downloadId)
|
public void CancelDownload(int downloadId)
|
||||||
@@ -315,4 +315,3 @@ namespace MareSynchronos.WebAPI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,44 +1,43 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MareSynchronos.API;
|
using MareSynchronos.API;
|
||||||
using MareSynchronos.Utils;
|
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
namespace MareSynchronos.WebAPI;
|
||||||
{
|
|
||||||
public partial class ApiController
|
public partial class ApiController
|
||||||
{
|
{
|
||||||
public async Task DeleteAccount()
|
public async Task DeleteAccount()
|
||||||
{
|
{
|
||||||
_pluginConfiguration.ClientSecret.Remove(ApiUri);
|
_pluginConfiguration.ClientSecret.Remove(ApiUri);
|
||||||
_pluginConfiguration.Save();
|
_pluginConfiguration.Save();
|
||||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles).ConfigureAwait(false);
|
||||||
await _mareHub!.SendAsync(Api.SendUserDeleteAccount);
|
await _mareHub!.SendAsync(Api.SendUserDeleteAccount).ConfigureAwait(false);
|
||||||
await CreateConnections();
|
await CreateConnections().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<string>> GetOnlineCharacters()
|
public async Task<List<string>> GetOnlineCharacters()
|
||||||
{
|
{
|
||||||
return await _mareHub!.InvokeAsync<List<string>>(Api.InvokeUserGetOnlineCharacters);
|
return await _mareHub!.InvokeAsync<List<string>>(Api.InvokeUserGetOnlineCharacters).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendPairedClientAddition(string uid)
|
public async Task SendPairedClientAddition(string uid)
|
||||||
{
|
{
|
||||||
if (!IsConnected || SecretKey == "-") return;
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
await _mareHub!.SendAsync(Api.SendUserPairedClientAddition, uid);
|
await _mareHub!.SendAsync(Api.SendUserPairedClientAddition, uid).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendPairedClientPauseChange(string uid, bool paused)
|
public async Task SendPairedClientPauseChange(string uid, bool paused)
|
||||||
{
|
{
|
||||||
if (!IsConnected || SecretKey == "-") return;
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
await _mareHub!.SendAsync(Api.SendUserPairedClientPauseChange, uid, paused);
|
await _mareHub!.SendAsync(Api.SendUserPairedClientPauseChange, uid, paused).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendPairedClientRemoval(string uid)
|
public async Task SendPairedClientRemoval(string uid)
|
||||||
{
|
{
|
||||||
if (!IsConnected || SecretKey == "-") return;
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
await _mareHub!.SendAsync(Api.SendUserPairedClientRemoval, uid);
|
await _mareHub!.SendAsync(Api.SendUserPairedClientRemoval, uid).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,372 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MareSynchronos.API;
|
|
||||||
using MareSynchronos.FileCache;
|
|
||||||
using MareSynchronos.Utils;
|
|
||||||
using MareSynchronos.WebAPI.Utils;
|
|
||||||
using Microsoft.AspNetCore.Http.Connections;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
|
||||||
{
|
|
||||||
public delegate void SimpleStringDelegate(string str);
|
|
||||||
public enum ServerState
|
|
||||||
{
|
|
||||||
Offline,
|
|
||||||
Disconnected,
|
|
||||||
Connected,
|
|
||||||
Unauthorized,
|
|
||||||
VersionMisMatch,
|
|
||||||
RateLimited
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class ApiController : IDisposable
|
|
||||||
{
|
|
||||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
|
||||||
public const string MainServiceUri = "wss://maresynchronos.com";
|
|
||||||
|
|
||||||
public readonly int[] SupportedServerVersions = { Api.Version };
|
|
||||||
|
|
||||||
private readonly Configuration _pluginConfiguration;
|
|
||||||
private readonly DalamudUtil _dalamudUtil;
|
|
||||||
private readonly FileCacheManager _fileDbManager;
|
|
||||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
|
||||||
|
|
||||||
private HubConnection? _mareHub;
|
|
||||||
|
|
||||||
private CancellationTokenSource? _uploadCancellationTokenSource = new();
|
|
||||||
|
|
||||||
private ConnectionDto? _connectionDto;
|
|
||||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
|
||||||
public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false);
|
|
||||||
|
|
||||||
public bool IsAdmin => _connectionDto?.IsAdmin ?? false;
|
|
||||||
|
|
||||||
public ApiController(Configuration pluginConfiguration, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
|
||||||
{
|
|
||||||
Logger.Verbose("Creating " + nameof(ApiController));
|
|
||||||
|
|
||||||
_pluginConfiguration = pluginConfiguration;
|
|
||||||
_dalamudUtil = dalamudUtil;
|
|
||||||
_fileDbManager = fileDbManager;
|
|
||||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
|
||||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
|
||||||
ServerState = ServerState.Offline;
|
|
||||||
_verifiedUploadedHashes = new();
|
|
||||||
|
|
||||||
if (_dalamudUtil.IsLoggedIn)
|
|
||||||
{
|
|
||||||
DalamudUtilOnLogIn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DalamudUtilOnLogOut()
|
|
||||||
{
|
|
||||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
|
||||||
ServerState = ServerState.Offline;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DalamudUtilOnLogIn()
|
|
||||||
{
|
|
||||||
Task.Run(CreateConnections);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived;
|
|
||||||
|
|
||||||
public event VoidDelegate? Connected;
|
|
||||||
|
|
||||||
public event VoidDelegate? Disconnected;
|
|
||||||
|
|
||||||
public event SimpleStringDelegate? PairedClientOffline;
|
|
||||||
|
|
||||||
public event SimpleStringDelegate? PairedClientOnline;
|
|
||||||
|
|
||||||
public event SimpleStringDelegate? PairedWithOther;
|
|
||||||
|
|
||||||
public event SimpleStringDelegate? UnpairedFromOther;
|
|
||||||
public event VoidDelegate? DownloadStarted;
|
|
||||||
public event VoidDelegate? DownloadFinished;
|
|
||||||
|
|
||||||
public ConcurrentDictionary<int, List<DownloadFileTransfer>> CurrentDownloads { get; } = new();
|
|
||||||
|
|
||||||
public List<FileTransfer> CurrentUploads { get; } = new();
|
|
||||||
|
|
||||||
public List<FileTransfer> ForbiddenTransfers { get; } = new();
|
|
||||||
|
|
||||||
public List<BannedUserDto> AdminBannedUsers { get; private set; } = new();
|
|
||||||
|
|
||||||
public List<ForbiddenFileDto> AdminForbiddenFiles { get; private set; } = new();
|
|
||||||
|
|
||||||
public bool IsConnected => ServerState == ServerState.Connected;
|
|
||||||
|
|
||||||
public bool IsDownloading => CurrentDownloads.Count > 0;
|
|
||||||
|
|
||||||
public bool IsUploading => CurrentUploads.Count > 0;
|
|
||||||
|
|
||||||
public List<ClientPairDto> PairedClients { get; set; } = new();
|
|
||||||
|
|
||||||
public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri)
|
|
||||||
? _pluginConfiguration.ClientSecret[ApiUri] : string.Empty;
|
|
||||||
|
|
||||||
public bool ServerAlive => ServerState is ServerState.Connected or ServerState.RateLimited or ServerState.Unauthorized or ServerState.Disconnected;
|
|
||||||
|
|
||||||
public Dictionary<string, string> ServerDictionary => new Dictionary<string, string>()
|
|
||||||
{ { MainServiceUri, MainServer } }
|
|
||||||
.Concat(_pluginConfiguration.CustomServerList)
|
|
||||||
.ToDictionary(k => k.Key, k => k.Value);
|
|
||||||
|
|
||||||
public string UID => _connectionDto?.UID ?? string.Empty;
|
|
||||||
private string ApiUri => _pluginConfiguration.ApiUri;
|
|
||||||
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
|
||||||
|
|
||||||
private ServerState _serverState;
|
|
||||||
public ServerState ServerState
|
|
||||||
{
|
|
||||||
get => _serverState;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
Logger.Debug($"New ServerState: {value}, prev ServerState: {_serverState}");
|
|
||||||
_serverState = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateConnections()
|
|
||||||
{
|
|
||||||
Logger.Debug("CreateConnections called");
|
|
||||||
|
|
||||||
if (_pluginConfiguration.FullPause)
|
|
||||||
{
|
|
||||||
Logger.Info("Not recreating Connection, paused");
|
|
||||||
ServerState = ServerState.Disconnected;
|
|
||||||
_connectionDto = null;
|
|
||||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
|
||||||
|
|
||||||
Logger.Info("Recreating Connection");
|
|
||||||
|
|
||||||
_connectionCancellationTokenSource.Cancel();
|
|
||||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
var token = _connectionCancellationTokenSource.Token;
|
|
||||||
_verifiedUploadedHashes.Clear();
|
|
||||||
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(SecretKey))
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
await StopConnection(token);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.Debug("Building connection");
|
|
||||||
|
|
||||||
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
Logger.Debug("Player not loaded in yet, waiting");
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token.IsCancellationRequested) break;
|
|
||||||
|
|
||||||
_mareHub = BuildHubConnection(Api.Path);
|
|
||||||
|
|
||||||
await _mareHub.StartAsync(token);
|
|
||||||
|
|
||||||
_mareHub.On<SystemInfoDto>(Api.OnUpdateSystemInfo, (dto) => SystemInfoDto = dto);
|
|
||||||
|
|
||||||
_connectionDto =
|
|
||||||
await _mareHub.InvokeAsync<ConnectionDto>(Api.InvokeHeartbeat, _dalamudUtil.PlayerNameHashed, token);
|
|
||||||
|
|
||||||
ServerState = ServerState.Connected;
|
|
||||||
|
|
||||||
if (_connectionDto.ServerVersion != Api.Version)
|
|
||||||
{
|
|
||||||
ServerState = ServerState.VersionMisMatch;
|
|
||||||
await StopConnection(token);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ServerState is ServerState.Connected) // user is authorized && server is legit
|
|
||||||
{
|
|
||||||
await InitializeData(token);
|
|
||||||
|
|
||||||
_mareHub.Closed += MareHubOnClosed;
|
|
||||||
_mareHub.Reconnecting += MareHubOnReconnecting;
|
|
||||||
_mareHub.Reconnected += MareHubOnReconnected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (HubException ex)
|
|
||||||
{
|
|
||||||
Logger.Warn(ex.GetType().ToString());
|
|
||||||
Logger.Warn(ex.Message);
|
|
||||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
|
||||||
|
|
||||||
ServerState = ServerState.RateLimited;
|
|
||||||
await StopConnection(token);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
Logger.Warn(ex.GetType().ToString());
|
|
||||||
Logger.Warn(ex.Message);
|
|
||||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
|
||||||
|
|
||||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
|
||||||
{
|
|
||||||
ServerState = ServerState.Unauthorized;
|
|
||||||
await StopConnection(token);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ServerState = ServerState.Offline;
|
|
||||||
Logger.Info("Failed to establish connection, retrying");
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.Warn(ex.GetType().ToString());
|
|
||||||
Logger.Warn(ex.Message);
|
|
||||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
|
||||||
Logger.Info("Failed to establish connection, retrying");
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MareHubOnReconnected(string? arg)
|
|
||||||
{
|
|
||||||
_ = Task.Run(CreateConnections);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitializeData(CancellationToken token)
|
|
||||||
{
|
|
||||||
if (_mareHub == null) return;
|
|
||||||
|
|
||||||
Logger.Debug("Initializing data");
|
|
||||||
_mareHub.On<ClientPairDto, string>(Api.OnUserUpdateClientPairs,
|
|
||||||
UpdateLocalClientPairsCallback);
|
|
||||||
_mareHub.On<CharacterCacheDto, string>(Api.OnUserReceiveCharacterData,
|
|
||||||
ReceiveCharacterDataCallback);
|
|
||||||
_mareHub.On<string>(Api.OnUserRemoveOnlinePairedPlayer,
|
|
||||||
(s) => PairedClientOffline?.Invoke(s));
|
|
||||||
_mareHub.On<string>(Api.OnUserAddOnlinePairedPlayer,
|
|
||||||
(s) => PairedClientOnline?.Invoke(s));
|
|
||||||
_mareHub.On(Api.OnAdminForcedReconnect, UserForcedReconnectCallback);
|
|
||||||
|
|
||||||
PairedClients =
|
|
||||||
await _mareHub!.InvokeAsync<List<ClientPairDto>>(Api.InvokeUserGetPairedClients, token);
|
|
||||||
|
|
||||||
if (IsModerator)
|
|
||||||
{
|
|
||||||
AdminForbiddenFiles =
|
|
||||||
await _mareHub.InvokeAsync<List<ForbiddenFileDto>>(Api.InvokeAdminGetForbiddenFiles,
|
|
||||||
token);
|
|
||||||
AdminBannedUsers =
|
|
||||||
await _mareHub.InvokeAsync<List<BannedUserDto>>(Api.InvokeAdminGetBannedUsers,
|
|
||||||
token);
|
|
||||||
_mareHub.On<BannedUserDto>(Api.OnAdminUpdateOrAddBannedUser,
|
|
||||||
UpdateOrAddBannedUserCallback);
|
|
||||||
_mareHub.On<BannedUserDto>(Api.OnAdminDeleteBannedUser, DeleteBannedUserCallback);
|
|
||||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminUpdateOrAddForbiddenFile,
|
|
||||||
UpdateOrAddForbiddenFileCallback);
|
|
||||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminDeleteForbiddenFile,
|
|
||||||
DeleteForbiddenFileCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
Connected?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Logger.Verbose("Disposing " + nameof(ApiController));
|
|
||||||
|
|
||||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
|
||||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
|
||||||
|
|
||||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
|
||||||
_connectionCancellationTokenSource?.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private HubConnection BuildHubConnection(string hubName)
|
|
||||||
{
|
|
||||||
return new HubConnectionBuilder()
|
|
||||||
.WithUrl(ApiUri + hubName, options =>
|
|
||||||
{
|
|
||||||
options.Headers.Add("Authorization", SecretKey);
|
|
||||||
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
|
||||||
})
|
|
||||||
.WithAutomaticReconnect(new ForeverRetryPolicy())
|
|
||||||
.ConfigureLogging(a => {
|
|
||||||
a.ClearProviders().AddProvider(new DalamudLoggingProvider());
|
|
||||||
a.SetMinimumLevel(LogLevel.Warning);
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MareHubOnClosed(Exception? arg)
|
|
||||||
{
|
|
||||||
CurrentUploads.Clear();
|
|
||||||
CurrentDownloads.Clear();
|
|
||||||
_uploadCancellationTokenSource?.Cancel();
|
|
||||||
Disconnected?.Invoke();
|
|
||||||
ServerState = ServerState.Offline;
|
|
||||||
Logger.Info("Connection closed");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MareHubOnReconnecting(Exception? arg)
|
|
||||||
{
|
|
||||||
ServerState = ServerState.Disconnected;
|
|
||||||
Logger.Warn("Connection closed... Reconnecting");
|
|
||||||
Logger.Warn(arg?.Message ?? string.Empty);
|
|
||||||
Logger.Warn(arg?.StackTrace ?? string.Empty);
|
|
||||||
Disconnected?.Invoke();
|
|
||||||
ServerState = ServerState.Offline;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StopConnection(CancellationToken token)
|
|
||||||
{
|
|
||||||
if (_mareHub is not null)
|
|
||||||
{
|
|
||||||
_uploadCancellationTokenSource?.Cancel();
|
|
||||||
Logger.Info("Stopping existing connection");
|
|
||||||
_mareHub.Closed -= MareHubOnClosed;
|
|
||||||
_mareHub.Reconnecting -= MareHubOnReconnecting;
|
|
||||||
_mareHub.Reconnected -= MareHubOnReconnected;
|
|
||||||
await _mareHub.StopAsync(token);
|
|
||||||
await _mareHub.DisposeAsync();
|
|
||||||
CurrentUploads.Clear();
|
|
||||||
CurrentDownloads.Clear();
|
|
||||||
_uploadCancellationTokenSource?.Cancel();
|
|
||||||
Disconnected?.Invoke();
|
|
||||||
_mareHub = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ServerState != ServerState.Disconnected)
|
|
||||||
{
|
|
||||||
while (ServerState != ServerState.Offline)
|
|
||||||
{
|
|
||||||
await Task.Delay(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +1,36 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MareSynchronos.API;
|
using MareSynchronos.API;
|
||||||
using Microsoft.AspNetCore.SignalR.Client;
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
namespace MareSynchronos.WebAPI;
|
||||||
{
|
|
||||||
public partial class ApiController
|
public partial class ApiController
|
||||||
{
|
{
|
||||||
public async Task AddOrUpdateForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
public async Task AddOrUpdateForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||||
{
|
{
|
||||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddForbiddenFile, forbiddenFile);
|
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddForbiddenFile, forbiddenFile).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
public async Task DeleteForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||||
{
|
{
|
||||||
await _mareHub!.SendAsync(Api.SendAdminDeleteForbiddenFile, forbiddenFile);
|
await _mareHub!.SendAsync(Api.SendAdminDeleteForbiddenFile, forbiddenFile).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddOrUpdateBannedUserEntry(BannedUserDto bannedUser)
|
public async Task AddOrUpdateBannedUserEntry(BannedUserDto bannedUser)
|
||||||
{
|
{
|
||||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddBannedUser, bannedUser);
|
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddBannedUser, bannedUser).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteBannedUserEntry(BannedUserDto bannedUser)
|
public async Task DeleteBannedUserEntry(BannedUserDto bannedUser)
|
||||||
{
|
{
|
||||||
await _mareHub!.SendAsync(Api.SendAdminDeleteBannedUser, bannedUser);
|
await _mareHub!.SendAsync(Api.SendAdminDeleteBannedUser, bannedUser).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshOnlineUsers()
|
public async Task RefreshOnlineUsers()
|
||||||
{
|
{
|
||||||
AdminOnlineUsers = await _mareHub!.InvokeAsync<List<OnlineUserDto>>(Api.InvokeAdminGetOnlineUsers);
|
AdminOnlineUsers = await _mareHub!.InvokeAsync<List<OnlineUserDto>>(Api.InvokeAdminGetOnlineUsers).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<OnlineUserDto> AdminOnlineUsers { get; set; } = new List<OnlineUserDto>();
|
public List<OnlineUserDto> AdminOnlineUsers { get; set; } = new List<OnlineUserDto>();
|
||||||
@@ -44,4 +45,3 @@ namespace MareSynchronos.WebAPI
|
|||||||
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, false);
|
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MareSynchronos.API;
|
using MareSynchronos.API;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using MareSynchronos.WebAPI.Utils;
|
using MareSynchronos.WebAPI.Utils;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
|
||||||
|
namespace MareSynchronos.WebAPI;
|
||||||
|
|
||||||
namespace MareSynchronos.WebAPI
|
|
||||||
{
|
|
||||||
public partial class ApiController
|
public partial class ApiController
|
||||||
{
|
{
|
||||||
private void UserForcedReconnectCallback()
|
private void UserForcedReconnectCallback()
|
||||||
@@ -13,13 +15,12 @@ namespace MareSynchronos.WebAPI
|
|||||||
_ = CreateConnections();
|
_ = CreateConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLocalClientPairsCallback(ClientPairDto dto, string characterIdentifier)
|
private void UpdateLocalClientPairsCallback(ClientPairDto dto)
|
||||||
{
|
{
|
||||||
var entry = PairedClients.SingleOrDefault(e => e.OtherUID == dto.OtherUID);
|
var entry = PairedClients.SingleOrDefault(e => string.Equals(e.OtherUID, dto.OtherUID, System.StringComparison.Ordinal));
|
||||||
if (dto.IsRemoved)
|
if (dto.IsRemoved)
|
||||||
{
|
{
|
||||||
PairedClients.RemoveAll(p => p.OtherUID == dto.OtherUID);
|
PairedClients.RemoveAll(p => string.Equals(p.OtherUID, dto.OtherUID, System.StringComparison.Ordinal));
|
||||||
UnpairedFromOther?.Invoke(characterIdentifier);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
@@ -28,20 +29,9 @@ namespace MareSynchronos.WebAPI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((entry.IsPausedFromOthers != dto.IsPausedFromOthers || entry.IsSynced != dto.IsSynced || entry.IsPaused != dto.IsPaused)
|
|
||||||
&& !dto.IsPaused && dto.IsSynced && !dto.IsPausedFromOthers)
|
|
||||||
{
|
|
||||||
PairedWithOther?.Invoke(characterIdentifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.IsPaused = dto.IsPaused;
|
entry.IsPaused = dto.IsPaused;
|
||||||
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
||||||
entry.IsSynced = dto.IsSynced;
|
entry.IsSynced = dto.IsSynced;
|
||||||
|
|
||||||
if (dto.IsPaused || dto.IsPausedFromOthers || !dto.IsSynced)
|
|
||||||
{
|
|
||||||
UnpairedFromOther?.Invoke(characterIdentifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ReceiveCharacterDataCallback(CharacterCacheDto character, string characterHash)
|
private Task ReceiveCharacterDataCallback(CharacterCacheDto character, string characterHash)
|
||||||
@@ -53,7 +43,7 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
private void UpdateOrAddBannedUserCallback(BannedUserDto obj)
|
private void UpdateOrAddBannedUserCallback(BannedUserDto obj)
|
||||||
{
|
{
|
||||||
var user = AdminBannedUsers.SingleOrDefault(b => b.CharacterHash == obj.CharacterHash);
|
var user = AdminBannedUsers.SingleOrDefault(b => string.Equals(b.CharacterHash, obj.CharacterHash, System.StringComparison.Ordinal));
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
AdminBannedUsers.Add(obj);
|
AdminBannedUsers.Add(obj);
|
||||||
@@ -66,12 +56,12 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
private void DeleteBannedUserCallback(BannedUserDto obj)
|
private void DeleteBannedUserCallback(BannedUserDto obj)
|
||||||
{
|
{
|
||||||
AdminBannedUsers.RemoveAll(a => a.CharacterHash == obj.CharacterHash);
|
AdminBannedUsers.RemoveAll(a => string.Equals(a.CharacterHash, obj.CharacterHash, System.StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateOrAddForbiddenFileCallback(ForbiddenFileDto obj)
|
private void UpdateOrAddForbiddenFileCallback(ForbiddenFileDto obj)
|
||||||
{
|
{
|
||||||
var user = AdminForbiddenFiles.SingleOrDefault(b => b.Hash == obj.Hash);
|
var user = AdminForbiddenFiles.SingleOrDefault(b => string.Equals(b.Hash, obj.Hash, System.StringComparison.Ordinal));
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
AdminForbiddenFiles.Add(obj);
|
AdminForbiddenFiles.Add(obj);
|
||||||
@@ -84,7 +74,48 @@ namespace MareSynchronos.WebAPI
|
|||||||
|
|
||||||
private void DeleteForbiddenFileCallback(ForbiddenFileDto obj)
|
private void DeleteForbiddenFileCallback(ForbiddenFileDto obj)
|
||||||
{
|
{
|
||||||
AdminForbiddenFiles.RemoveAll(f => f.Hash == obj.Hash);
|
AdminForbiddenFiles.RemoveAll(f => string.Equals(f.Hash, obj.Hash, System.StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GroupPairChangedCallback(GroupPairDto dto)
|
||||||
|
{
|
||||||
|
if (dto.IsRemoved.GetValueOrDefault(false))
|
||||||
|
{
|
||||||
|
GroupPairedClients.RemoveAll(g => string.Equals(g.GroupGID, dto.GroupGID, System.StringComparison.Ordinal) && string.Equals(g.UserUID, dto.UserUID, System.StringComparison.Ordinal));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingUser = GroupPairedClients.FirstOrDefault(f => string.Equals(f.GroupGID, dto.GroupGID, System.StringComparison.Ordinal) && string.Equals(f.UserUID, dto.UserUID, System.StringComparison.Ordinal));
|
||||||
|
if (existingUser == null)
|
||||||
|
{
|
||||||
|
GroupPairedClients.Add(dto);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
existingUser.IsPaused = dto.IsPaused ?? existingUser.IsPaused;
|
||||||
|
existingUser.UserAlias = dto.UserAlias ?? existingUser.UserAlias;
|
||||||
|
existingUser.IsPinned = dto.IsPinned ?? existingUser.IsPinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GroupChangedCallback(GroupDto dto)
|
||||||
|
{
|
||||||
|
if (dto.IsDeleted.GetValueOrDefault(false))
|
||||||
|
{
|
||||||
|
Groups.RemoveAll(g => string.Equals(g.GID, dto.GID, System.StringComparison.Ordinal));
|
||||||
|
GroupPairedClients.RemoveAll(g => string.Equals(g.GroupGID, dto.GID, System.StringComparison.Ordinal));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingGroup = Groups.FirstOrDefault(g => string.Equals(g.GID, dto.GID, System.StringComparison.Ordinal));
|
||||||
|
if (existingGroup == null)
|
||||||
|
{
|
||||||
|
Groups.Add(dto);
|
||||||
|
GroupPairedClients.AddRange(await _mareHub!.InvokeAsync<List<GroupPairDto>>(Api.InvokeGroupGetUsersInGroup, dto.GID).ConfigureAwait(false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
existingGroup.OwnedBy = dto.OwnedBy ?? existingGroup.OwnedBy;
|
||||||
|
existingGroup.InvitesEnabled = dto.InvitesEnabled ?? existingGroup.InvitesEnabled;
|
||||||
|
existingGroup.IsPaused = dto.IsPaused ?? existingGroup.IsPaused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
MareSynchronos/WebAPI/ApiController.Functions.Groups.cs
Normal file
87
MareSynchronos/WebAPI/ApiController.Functions.Groups.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using MareSynchronos.API;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MareSynchronos.WebAPI;
|
||||||
|
public partial class ApiController
|
||||||
|
{
|
||||||
|
public async Task<GroupCreatedDto> CreateGroup()
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new GroupCreatedDto();
|
||||||
|
return await _mareHub!.InvokeAsync<GroupCreatedDto>(Api.InvokeGroupCreate).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ChangeGroupPassword(string gid, string newpassword)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return false;
|
||||||
|
return await _mareHub!.InvokeAsync<bool>(Api.InvokeGroupChangePassword, gid, newpassword).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<GroupDto>> GetGroups()
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new List<GroupDto>();
|
||||||
|
return await _mareHub!.InvokeAsync<List<GroupDto>>(Api.InvokeGroupGetGroups).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<GroupPairDto>> GetUsersInGroup(string gid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new List<GroupPairDto>();
|
||||||
|
return await _mareHub!.InvokeAsync<List<GroupPairDto>>(Api.InvokeGroupGetUsersInGroup, gid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SendGroupJoin(string gid, string password)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return false;
|
||||||
|
return await _mareHub!.InvokeAsync<bool>(Api.InvokeGroupJoin, gid, password).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendGroupChangeInviteState(string gid, bool opened)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupChangeInviteState, gid, opened).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendDeleteGroup(string gid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupDelete, gid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendChangeUserPinned(string gid, string uid, bool isPinned)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupChangePinned, gid, uid, isPinned).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendClearGroup(string gid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupClear, gid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendLeaveGroup(string gid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupLeave, gid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendPauseGroup(string gid, bool isPaused)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupPause, gid, isPaused).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendRemoveUserFromGroup(string gid, string uid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupRemoveUser, gid, uid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ChangeOwnerOfGroup(string gid, string uid)
|
||||||
|
{
|
||||||
|
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||||
|
await _mareHub!.SendAsync(Api.SendGroupChangeOwner, gid, uid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
395
MareSynchronos/WebAPI/ApiController.cs
Normal file
395
MareSynchronos/WebAPI/ApiController.cs
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MareSynchronos.API;
|
||||||
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using MareSynchronos.WebAPI.Utils;
|
||||||
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Client;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.WebAPI;
|
||||||
|
|
||||||
|
public delegate void SimpleStringDelegate(string str);
|
||||||
|
|
||||||
|
public partial class ApiController : IDisposable
|
||||||
|
{
|
||||||
|
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||||
|
public const string MainServiceUri = "wss://maresynchronos.com";
|
||||||
|
|
||||||
|
public readonly int[] SupportedServerVersions = { Api.Version };
|
||||||
|
|
||||||
|
private readonly Configuration _pluginConfiguration;
|
||||||
|
private readonly DalamudUtil _dalamudUtil;
|
||||||
|
private readonly FileCacheManager _fileDbManager;
|
||||||
|
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||||
|
|
||||||
|
private HubConnection? _mareHub;
|
||||||
|
|
||||||
|
private CancellationTokenSource? _uploadCancellationTokenSource = new();
|
||||||
|
private CancellationTokenSource? _healthCheckTokenSource = new();
|
||||||
|
|
||||||
|
private ConnectionDto? _connectionDto;
|
||||||
|
public ServerInfoDto ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfoDto();
|
||||||
|
|
||||||
|
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||||
|
public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false);
|
||||||
|
|
||||||
|
public bool IsAdmin => _connectionDto?.IsAdmin ?? false;
|
||||||
|
|
||||||
|
public ApiController(Configuration pluginConfiguration, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||||
|
{
|
||||||
|
Logger.Verbose("Creating " + nameof(ApiController));
|
||||||
|
|
||||||
|
_pluginConfiguration = pluginConfiguration;
|
||||||
|
_dalamudUtil = dalamudUtil;
|
||||||
|
_fileDbManager = fileDbManager;
|
||||||
|
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||||
|
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||||
|
ServerState = ServerState.Offline;
|
||||||
|
_verifiedUploadedHashes = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
if (_dalamudUtil.IsLoggedIn)
|
||||||
|
{
|
||||||
|
DalamudUtilOnLogIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DalamudUtilOnLogOut()
|
||||||
|
{
|
||||||
|
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false));
|
||||||
|
ServerState = ServerState.Offline;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DalamudUtilOnLogIn()
|
||||||
|
{
|
||||||
|
Task.Run(CreateConnections);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived;
|
||||||
|
|
||||||
|
public event VoidDelegate? Connected;
|
||||||
|
|
||||||
|
public event VoidDelegate? Disconnected;
|
||||||
|
|
||||||
|
public event SimpleStringDelegate? PairedClientOffline;
|
||||||
|
|
||||||
|
public event SimpleStringDelegate? PairedClientOnline;
|
||||||
|
public event VoidDelegate? DownloadStarted;
|
||||||
|
public event VoidDelegate? DownloadFinished;
|
||||||
|
|
||||||
|
public ConcurrentDictionary<int, List<DownloadFileTransfer>> CurrentDownloads { get; } = new();
|
||||||
|
|
||||||
|
public List<FileTransfer> CurrentUploads { get; } = new();
|
||||||
|
|
||||||
|
public List<FileTransfer> ForbiddenTransfers { get; } = new();
|
||||||
|
|
||||||
|
public List<BannedUserDto> AdminBannedUsers { get; private set; } = new();
|
||||||
|
|
||||||
|
public List<ForbiddenFileDto> AdminForbiddenFiles { get; private set; } = new();
|
||||||
|
|
||||||
|
public bool IsConnected => ServerState == ServerState.Connected;
|
||||||
|
|
||||||
|
public bool IsDownloading => CurrentDownloads.Count > 0;
|
||||||
|
|
||||||
|
public bool IsUploading => CurrentUploads.Count > 0;
|
||||||
|
|
||||||
|
public List<ClientPairDto> PairedClients { get; set; } = new();
|
||||||
|
public List<GroupPairDto> GroupPairedClients { get; set; } = new();
|
||||||
|
public List<GroupDto> Groups { get; set; } = new();
|
||||||
|
|
||||||
|
public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri)
|
||||||
|
? _pluginConfiguration.ClientSecret[ApiUri] : string.Empty;
|
||||||
|
|
||||||
|
public bool ServerAlive => ServerState is ServerState.Connected or ServerState.RateLimited or ServerState.Unauthorized or ServerState.Disconnected;
|
||||||
|
|
||||||
|
public Dictionary<string, string> ServerDictionary => new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{ { MainServiceUri, MainServer } }
|
||||||
|
.Concat(_pluginConfiguration.CustomServerList)
|
||||||
|
.ToDictionary(k => k.Key, k => k.Value, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public string UID => _connectionDto?.UID ?? string.Empty;
|
||||||
|
public string DisplayName => _connectionDto?.UID ?? string.Empty;
|
||||||
|
private string ApiUri => _pluginConfiguration.ApiUri;
|
||||||
|
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
||||||
|
|
||||||
|
private ServerState _serverState;
|
||||||
|
public ServerState ServerState
|
||||||
|
{
|
||||||
|
get => _serverState;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
Logger.Debug($"New ServerState: {value}, prev ServerState: {_serverState}");
|
||||||
|
_serverState = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateConnections()
|
||||||
|
{
|
||||||
|
Logger.Debug("CreateConnections called");
|
||||||
|
|
||||||
|
if (_pluginConfiguration.FullPause)
|
||||||
|
{
|
||||||
|
Logger.Info("Not recreating Connection, paused");
|
||||||
|
ServerState = ServerState.Disconnected;
|
||||||
|
_connectionDto = null;
|
||||||
|
await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
Logger.Info("Recreating Connection");
|
||||||
|
|
||||||
|
_connectionCancellationTokenSource.Cancel();
|
||||||
|
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var token = _connectionCancellationTokenSource.Token;
|
||||||
|
_verifiedUploadedHashes.Clear();
|
||||||
|
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(SecretKey))
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await StopConnection(token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Debug("Building connection");
|
||||||
|
|
||||||
|
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Logger.Debug("Player not loaded in yet, waiting");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
_mareHub = BuildHubConnection(Api.Path);
|
||||||
|
|
||||||
|
await _mareHub.StartAsync(token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_mareHub.On<SystemInfoDto>(Api.OnUpdateSystemInfo, (dto) => SystemInfoDto = dto);
|
||||||
|
|
||||||
|
_connectionDto =
|
||||||
|
await _mareHub.InvokeAsync<ConnectionDto>(Api.InvokeHeartbeat, _dalamudUtil.PlayerNameHashed, token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
ServerState = ServerState.Connected;
|
||||||
|
|
||||||
|
if (_connectionDto.ServerVersion != Api.Version)
|
||||||
|
{
|
||||||
|
ServerState = ServerState.VersionMisMatch;
|
||||||
|
await StopConnection(token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerState is ServerState.Connected) // user is authorized && server is legit
|
||||||
|
{
|
||||||
|
await InitializeData(token).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_mareHub.Closed += MareHubOnClosed;
|
||||||
|
_mareHub.Reconnecting += MareHubOnReconnecting;
|
||||||
|
_mareHub.Reconnected += MareHubOnReconnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HubException ex)
|
||||||
|
{
|
||||||
|
Logger.Warn(ex.GetType().ToString());
|
||||||
|
Logger.Warn(ex.Message);
|
||||||
|
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||||
|
|
||||||
|
ServerState = ServerState.RateLimited;
|
||||||
|
await StopConnection(token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
Logger.Warn(ex.GetType().ToString());
|
||||||
|
Logger.Warn(ex.Message);
|
||||||
|
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||||
|
|
||||||
|
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
ServerState = ServerState.Unauthorized;
|
||||||
|
await StopConnection(token).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ServerState = ServerState.Offline;
|
||||||
|
Logger.Info("Failed to establish connection, retrying");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Warn(ex.GetType().ToString());
|
||||||
|
Logger.Warn(ex.Message);
|
||||||
|
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||||
|
Logger.Info("Failed to establish connection, retrying");
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task MareHubOnReconnected(string? arg)
|
||||||
|
{
|
||||||
|
_ = Task.Run(CreateConnections);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClientHealthCheck(CancellationToken ct)
|
||||||
|
{
|
||||||
|
while (!ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(30), ct).ConfigureAwait(false);
|
||||||
|
if (ct.IsCancellationRequested) break;
|
||||||
|
var needsRestart = await _mareHub!.InvokeAsync<bool>(Api.InvokeCheckClientHealth, ct).ConfigureAwait(false);
|
||||||
|
Logger.Debug("Checked Client Health State, healthy: " + !needsRestart);
|
||||||
|
if (needsRestart)
|
||||||
|
{
|
||||||
|
_ = CreateConnections();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeData(CancellationToken token)
|
||||||
|
{
|
||||||
|
if (_mareHub == null) return;
|
||||||
|
|
||||||
|
Logger.Debug("Initializing data");
|
||||||
|
_mareHub.On<ClientPairDto>(Api.OnUserUpdateClientPairs,
|
||||||
|
UpdateLocalClientPairsCallback);
|
||||||
|
_mareHub.On<CharacterCacheDto, string>(Api.OnUserReceiveCharacterData,
|
||||||
|
ReceiveCharacterDataCallback);
|
||||||
|
_mareHub.On<string>(Api.OnUserRemoveOnlinePairedPlayer,
|
||||||
|
(s) => PairedClientOffline?.Invoke(s));
|
||||||
|
_mareHub.On<string>(Api.OnUserAddOnlinePairedPlayer,
|
||||||
|
(s) => PairedClientOnline?.Invoke(s));
|
||||||
|
_mareHub.On(Api.OnAdminForcedReconnect, UserForcedReconnectCallback);
|
||||||
|
_mareHub.On<GroupDto>(Api.OnGroupChange, GroupChangedCallback);
|
||||||
|
_mareHub.On<GroupPairDto>(Api.OnGroupUserChange, GroupPairChangedCallback);
|
||||||
|
|
||||||
|
PairedClients =
|
||||||
|
await _mareHub!.InvokeAsync<List<ClientPairDto>>(Api.InvokeUserGetPairedClients, token).ConfigureAwait(false);
|
||||||
|
Groups = await GetGroups().ConfigureAwait(false);
|
||||||
|
GroupPairedClients.Clear();
|
||||||
|
foreach (var group in Groups)
|
||||||
|
{
|
||||||
|
GroupPairedClients.AddRange(await GetUsersInGroup(group.GID).ConfigureAwait(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsModerator)
|
||||||
|
{
|
||||||
|
AdminForbiddenFiles =
|
||||||
|
await _mareHub.InvokeAsync<List<ForbiddenFileDto>>(Api.InvokeAdminGetForbiddenFiles,
|
||||||
|
token).ConfigureAwait(false);
|
||||||
|
AdminBannedUsers =
|
||||||
|
await _mareHub.InvokeAsync<List<BannedUserDto>>(Api.InvokeAdminGetBannedUsers,
|
||||||
|
token).ConfigureAwait(false);
|
||||||
|
_mareHub.On<BannedUserDto>(Api.OnAdminUpdateOrAddBannedUser,
|
||||||
|
UpdateOrAddBannedUserCallback);
|
||||||
|
_mareHub.On<BannedUserDto>(Api.OnAdminDeleteBannedUser, DeleteBannedUserCallback);
|
||||||
|
_mareHub.On<ForbiddenFileDto>(Api.OnAdminUpdateOrAddForbiddenFile,
|
||||||
|
UpdateOrAddForbiddenFileCallback);
|
||||||
|
_mareHub.On<ForbiddenFileDto>(Api.OnAdminDeleteForbiddenFile,
|
||||||
|
DeleteForbiddenFileCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
_healthCheckTokenSource?.Cancel();
|
||||||
|
_healthCheckTokenSource?.Dispose();
|
||||||
|
_healthCheckTokenSource = new CancellationTokenSource();
|
||||||
|
_ = ClientHealthCheck(_healthCheckTokenSource.Token);
|
||||||
|
|
||||||
|
Connected?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Logger.Verbose("Disposing " + nameof(ApiController));
|
||||||
|
|
||||||
|
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||||
|
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||||
|
|
||||||
|
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false));
|
||||||
|
_connectionCancellationTokenSource?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HubConnection BuildHubConnection(string hubName)
|
||||||
|
{
|
||||||
|
return new HubConnectionBuilder()
|
||||||
|
.WithUrl(ApiUri + hubName, options =>
|
||||||
|
{
|
||||||
|
options.Headers.Add("Authorization", SecretKey);
|
||||||
|
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
||||||
|
})
|
||||||
|
.WithAutomaticReconnect(new ForeverRetryPolicy())
|
||||||
|
.ConfigureLogging(a =>
|
||||||
|
{
|
||||||
|
a.ClearProviders().AddProvider(new DalamudLoggingProvider());
|
||||||
|
a.SetMinimumLevel(LogLevel.Warning);
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task MareHubOnClosed(Exception? arg)
|
||||||
|
{
|
||||||
|
CurrentUploads.Clear();
|
||||||
|
CurrentDownloads.Clear();
|
||||||
|
_uploadCancellationTokenSource?.Cancel();
|
||||||
|
Disconnected?.Invoke();
|
||||||
|
ServerState = ServerState.Offline;
|
||||||
|
Logger.Info("Connection closed");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task MareHubOnReconnecting(Exception? arg)
|
||||||
|
{
|
||||||
|
_connectionDto = null;
|
||||||
|
_healthCheckTokenSource?.Cancel();
|
||||||
|
ServerState = ServerState.Disconnected;
|
||||||
|
Logger.Warn("Connection closed... Reconnecting");
|
||||||
|
Logger.Warn(arg?.Message ?? string.Empty);
|
||||||
|
Logger.Warn(arg?.StackTrace ?? string.Empty);
|
||||||
|
Disconnected?.Invoke();
|
||||||
|
ServerState = ServerState.Offline;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StopConnection(CancellationToken token)
|
||||||
|
{
|
||||||
|
if (_mareHub is not null)
|
||||||
|
{
|
||||||
|
_uploadCancellationTokenSource?.Cancel();
|
||||||
|
Logger.Info("Stopping existing connection");
|
||||||
|
_mareHub.Closed -= MareHubOnClosed;
|
||||||
|
_mareHub.Reconnecting -= MareHubOnReconnecting;
|
||||||
|
_mareHub.Reconnected -= MareHubOnReconnected;
|
||||||
|
await _mareHub.StopAsync(token).ConfigureAwait(false);
|
||||||
|
await _mareHub.DisposeAsync().ConfigureAwait(false);
|
||||||
|
CurrentUploads.Clear();
|
||||||
|
CurrentDownloads.Clear();
|
||||||
|
_uploadCancellationTokenSource?.Cancel();
|
||||||
|
Disconnected?.Invoke();
|
||||||
|
_mareHub = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerState != ServerState.Disconnected)
|
||||||
|
{
|
||||||
|
while (ServerState != ServerState.Offline)
|
||||||
|
{
|
||||||
|
await Task.Delay(16).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
MareSynchronos/WebAPI/ServerState.cs
Normal file
11
MareSynchronos/WebAPI/ServerState.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace MareSynchronos.WebAPI;
|
||||||
|
|
||||||
|
public enum ServerState
|
||||||
|
{
|
||||||
|
Offline,
|
||||||
|
Disconnected,
|
||||||
|
Connected,
|
||||||
|
Unauthorized,
|
||||||
|
VersionMisMatch,
|
||||||
|
RateLimited
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user