Client rework for API change and paradigm shift (#39)
* most of the groups refactoring on client * register OnMethods for group stuff * start implementing client (still pretty broken) * finish implementing new api first iteration * idk rework everything for pair shit (still WIP); goal is to remove PairedClients and GroupPairClients from ApiController * move everything to PairManager, remove dictionaries from APiController * remove admin stuff from client, cleanup * adjust reconnection handling, add new settings, todo still to remove access from old stuff that's marked obsolete from config * add back adding servers, fix intro ui * fix obsolete calls * adjust config namespace * add UI for setting animation/sound permissions to syncshells * add ConfigurationService to hot reload config on change from external * move transient data cache to configuration * add deleting service to ui * fix saving of transient resources * fix group pair user assignments * halt scanner when penumbra inactive, add visible/online/offline split to individual pairs and tags * add presence to syncshell ui * move fullpause from config to server config * fixes in code style * more codestyle * show info icon on player in shells, don't show icon when no changes from default state are made, add online notifs * fixes to intro UI --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
313
.editorconfig
313
.editorconfig
@@ -1,37 +1,249 @@
|
||||
[*.cs]
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# 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
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = false
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_collection_initializer = true
|
||||
dotnet_style_explicit_tuple_names = true
|
||||
dotnet_style_namespace_match_folder = true
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_object_initializer = true
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
||||
dotnet_style_prefer_conditional_expression_over_return = true
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
||||
dotnet_style_prefer_inferred_tuple_names = true
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = 0
|
||||
|
||||
# New line preferences
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = false
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_extended_property_pattern = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
csharp_style_prefer_switch_expression = true
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
|
||||
csharp_style_prefer_readonly_struct = true:suggestion
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
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
|
||||
|
||||
# Expression-level preferences
|
||||
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_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||
csharp_style_prefer_range_operator = 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
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# MA0009: Add regex evaluation timeout
|
||||
dotnet_diagnostic.MA0009.severity = silent
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
# New line preferences
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### 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.private_or_internal_field_should_be_fieldstyle.severity = suggestion
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_fieldstyle.symbols = private_or_internal_field
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_fieldstyle.style = fieldstyle
|
||||
|
||||
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.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
|
||||
dotnet_naming_symbols.private_or_internal_field.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.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.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.fieldstyle.required_prefix = _
|
||||
dotnet_naming_style.fieldstyle.required_suffix =
|
||||
dotnet_naming_style.fieldstyle.word_separator =
|
||||
dotnet_naming_style.fieldstyle.capitalization = camel_case
|
||||
dotnet_diagnostic.MA0016.severity = suggestion
|
||||
dotnet_diagnostic.MA0026.severity = warning
|
||||
dotnet_diagnostic.MA0046.severity = suggestion
|
||||
dotnet_diagnostic.MA0051.severity = suggestion
|
||||
dotnet_diagnostic.MA0011.severity = suggestion
|
||||
|
||||
[*.{cs,vb}]
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
@@ -53,50 +265,9 @@ 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
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
|
||||
2
MareAPI
2
MareAPI
Submodule MareAPI updated: 2015496ec0...981f62a071
@@ -1,235 +0,0 @@
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace MareSynchronos;
|
||||
|
||||
public static class ConfigurationExtensions
|
||||
{
|
||||
public static bool HasValidSetup(this Configuration configuration)
|
||||
{
|
||||
return configuration.AcceptedAgreement && configuration.InitialScanComplete
|
||||
&& !string.IsNullOrEmpty(configuration.CacheFolder)
|
||||
&& Directory.Exists(configuration.CacheFolder)
|
||||
&& configuration.ClientSecret.ContainsKey(configuration.ApiUri);
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetCurrentServerUidComments(this Configuration configuration)
|
||||
{
|
||||
return configuration.UidServerComments.ContainsKey(configuration.ApiUri)
|
||||
? configuration.UidServerComments[configuration.ApiUri]
|
||||
: 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)
|
||||
{
|
||||
if (!configuration.UidServerComments.ContainsKey(configuration.ApiUri))
|
||||
{
|
||||
configuration.UidServerComments[configuration.ApiUri] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
configuration.UidServerComments[configuration.ApiUri][uid] = comment;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Configuration : IPluginConfiguration
|
||||
{
|
||||
private string _apiUri = string.Empty;
|
||||
[NonSerialized]
|
||||
private DalamudPluginInterface? _pluginInterface;
|
||||
public bool AcceptedAgreement { get; set; } = false;
|
||||
public string ApiUri
|
||||
{
|
||||
get => string.IsNullOrEmpty(_apiUri) ? ApiController.MainServiceUri : _apiUri;
|
||||
set => _apiUri = value;
|
||||
}
|
||||
|
||||
public string CacheFolder { get; set; } = string.Empty;
|
||||
public Dictionary<string, string> ClientSecret { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, string> CustomServerList { get; set; } = new(StringComparer.Ordinal);
|
||||
public double MaxLocalCacheInGiB { get; set; } = 20;
|
||||
public bool ReverseUserSort { get; set; } = false;
|
||||
|
||||
public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
|
||||
public bool FileScanPaused { get; set; } = false;
|
||||
|
||||
public bool InitialScanComplete { get; set; } = false;
|
||||
|
||||
public bool FullPause { get; set; } = false;
|
||||
public bool HideInfoMessages { get; set; } = false;
|
||||
public bool DisableOptionalPluginWarnings { get; set; } = false;
|
||||
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
||||
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(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Each paired user can have multiple tags. Each tag will create a category, and the user will
|
||||
/// be displayed into that category.
|
||||
/// The dictionary first maps a server URL to a dictionary, and that
|
||||
/// dictionary maps the OtherUID of the <see cref="ClientPairDto"/> to a list of tags.
|
||||
/// </summary>
|
||||
public Dictionary<string, Dictionary<string, List<string>>> UidServerPairedUserTags = new(StringComparer.Ordinal);
|
||||
/// <summary>
|
||||
/// A dictionary that maps a server URL to the tags the user has added for that server.
|
||||
/// </summary>
|
||||
public Dictionary<string, HashSet<string>> ServerAvailablePairTags = new(StringComparer.Ordinal);
|
||||
|
||||
public HashSet<string> OpenPairTags = new(StringComparer.Ordinal);
|
||||
public int Version { get; set; } = 5;
|
||||
|
||||
public bool ShowTransferWindow { get; set; } = true;
|
||||
public bool OpenPopupOnAdd { get; set; } = true;
|
||||
|
||||
// the below exist just to make saving less cumbersome
|
||||
public void Initialize(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
|
||||
if (!Directory.Exists(CacheFolder))
|
||||
{
|
||||
InitialScanComplete = false;
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_pluginInterface!.SavePluginConfig(this);
|
||||
}
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
if (Version == 0)
|
||||
{
|
||||
Logger.Debug("Migrating Configuration from V0 to V1");
|
||||
Version = 1;
|
||||
ApiUri = ApiUri.Replace("https", "wss", StringComparison.Ordinal);
|
||||
foreach (var kvp in ClientSecret.ToList())
|
||||
{
|
||||
var newKey = kvp.Key.Replace("https", "wss", StringComparison.Ordinal);
|
||||
ClientSecret.Remove(kvp.Key);
|
||||
if (ClientSecret.ContainsKey(newKey))
|
||||
{
|
||||
ClientSecret[newKey] = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientSecret.Add(newKey, kvp.Value);
|
||||
}
|
||||
}
|
||||
UidServerComments.Add(ApiUri, UidComments.ToDictionary(k => k.Key, k => k.Value, StringComparer.Ordinal));
|
||||
UidComments.Clear();
|
||||
Save();
|
||||
}
|
||||
|
||||
if (Version == 1)
|
||||
{
|
||||
Logger.Debug("Migrating Configuration from V1 to V2");
|
||||
ApiUri = ApiUri.Replace("5001", "5000", StringComparison.Ordinal);
|
||||
foreach (var kvp in ClientSecret.ToList())
|
||||
{
|
||||
var newKey = kvp.Key.Replace("5001", "5000", StringComparison.Ordinal);
|
||||
ClientSecret.Remove(kvp.Key);
|
||||
if (ClientSecret.ContainsKey(newKey))
|
||||
{
|
||||
ClientSecret[newKey] = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientSecret.Add(newKey, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kvp in UidServerComments.ToList())
|
||||
{
|
||||
var newKey = kvp.Key.Replace("5001", "5000", StringComparison.Ordinal);
|
||||
UidServerComments.Remove(kvp.Key);
|
||||
UidServerComments.Add(newKey, kvp.Value);
|
||||
}
|
||||
|
||||
Version = 2;
|
||||
Save();
|
||||
}
|
||||
|
||||
if (Version == 2)
|
||||
{
|
||||
Logger.Debug("Migrating Configuration from V2 to V3");
|
||||
ApiUri = "wss://v2202207178628194299.powersrv.de:6871";
|
||||
ClientSecret.Clear();
|
||||
UidServerComments.Clear();
|
||||
|
||||
Version = 3;
|
||||
Save();
|
||||
}
|
||||
|
||||
if (Version == 3)
|
||||
{
|
||||
Logger.Debug("Migrating Configuration from V3 to V4");
|
||||
|
||||
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||
foreach (var kvp in ClientSecret.ToList())
|
||||
{
|
||||
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||
ClientSecret.Remove(kvp.Key);
|
||||
if (ClientSecret.ContainsKey(newKey))
|
||||
{
|
||||
ClientSecret[newKey] = kvp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientSecret.Add(newKey, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kvp in UidServerComments.ToList())
|
||||
{
|
||||
var newKey = kvp.Key.Replace("wss://v2202207178628194299.powersrv.de:6871", "wss://v2202207178628194299.powersrv.de:6872", StringComparison.Ordinal);
|
||||
UidServerComments.Remove(kvp.Key);
|
||||
UidServerComments.Add(newKey, kvp.Value);
|
||||
}
|
||||
|
||||
Version = 4;
|
||||
Save();
|
||||
}
|
||||
|
||||
if (Version == 4)
|
||||
{
|
||||
Logger.Debug("Migrating Configuration from V4 to V5");
|
||||
|
||||
ApiUri = ApiUri.Replace("wss://v2202207178628194299.powersrv.de:6872", "wss://maresynchronos.com", StringComparison.Ordinal);
|
||||
ClientSecret.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
||||
UidServerComments.Remove("wss://v2202207178628194299.powersrv.de:6872");
|
||||
|
||||
Version = 5;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
MareSynchronos/Delegates/CharacterDataDelegate.cs
Normal file
3
MareSynchronos/Delegates/CharacterDataDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void CharacterDataDelegate(API.Data.CharacterData characterCache);
|
||||
3
MareSynchronos/Delegates/DrawObjectDelegate.cs
Normal file
3
MareSynchronos/Delegates/DrawObjectDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void DrawObjectDelegate(IntPtr address, int objTblIdx);
|
||||
3
MareSynchronos/Delegates/FloatDelegate.cs
Normal file
3
MareSynchronos/Delegates/FloatDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void FloatDelegate(float change);
|
||||
3
MareSynchronos/Delegates/PenumbraFileResourceDelegate.cs
Normal file
3
MareSynchronos/Delegates/PenumbraFileResourceDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void PenumbraFileResourceDelegate(IntPtr drawObject, string gamePath, string filePath);
|
||||
3
MareSynchronos/Delegates/StringDelegate.cs
Normal file
3
MareSynchronos/Delegates/StringDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void StringDelegate(string str);
|
||||
3
MareSynchronos/Delegates/VoidDelegate.cs
Normal file
3
MareSynchronos/Delegates/VoidDelegate.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Delegates;
|
||||
|
||||
public delegate void VoidDelegate();
|
||||
@@ -1,8 +1,7 @@
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.FileCache;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.Export;
|
||||
@@ -17,7 +16,7 @@ public record MareCharaFileData
|
||||
public List<FileSwap> FileSwaps { get; set; } = new();
|
||||
|
||||
public MareCharaFileData() { }
|
||||
public MareCharaFileData(FileCacheManager manager, string description, CharacterCacheDto dto)
|
||||
public MareCharaFileData(FileCacheManager manager, string description, CharacterData dto)
|
||||
{
|
||||
Description = description;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.FileCache;
|
||||
|
||||
namespace MareSynchronos.Export;
|
||||
@@ -12,7 +12,7 @@ internal class MareCharaFileDataFactory
|
||||
_fileCacheManager = fileCacheManager;
|
||||
}
|
||||
|
||||
public MareCharaFileData Create(string description, CharacterCacheDto characterCacheDto)
|
||||
public MareCharaFileData Create(string description, CharacterData characterCacheDto)
|
||||
{
|
||||
return new MareCharaFileData(_fileCacheManager, description, characterCacheDto);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
using Lumina.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MareSynchronos.Export;
|
||||
namespace MareSynchronos.Export;
|
||||
|
||||
public record MareCharaFileHeader(byte Version, MareCharaFileData CharaFileData)
|
||||
{
|
||||
@@ -11,7 +6,7 @@ public record MareCharaFileHeader(byte Version, MareCharaFileData CharaFileData)
|
||||
|
||||
public byte Version { get; set; } = Version;
|
||||
public MareCharaFileData CharaFileData { get; set; } = CharaFileData;
|
||||
public string FilePath { get; private set; }
|
||||
public string FilePath { get; private set; } = string.Empty;
|
||||
|
||||
public void WriteToStream(BinaryWriter writer)
|
||||
{
|
||||
@@ -28,7 +23,7 @@ public record MareCharaFileHeader(byte Version, MareCharaFileData CharaFileData)
|
||||
public static MareCharaFileHeader? FromBinaryReader(string path, BinaryReader reader)
|
||||
{
|
||||
var chars = new string(reader.ReadChars(4));
|
||||
if (!string.Equals(chars, "MCDF", System.StringComparison.Ordinal)) throw new System.Exception("Not a Mare Chara File");
|
||||
if (!string.Equals(chars, "MCDF", StringComparison.Ordinal)) throw new System.Exception("Not a Mare Chara File");
|
||||
|
||||
MareCharaFileHeader? decoded = null;
|
||||
|
||||
@@ -37,8 +32,10 @@ public record MareCharaFileHeader(byte Version, MareCharaFileData CharaFileData)
|
||||
{
|
||||
var dataLength = reader.ReadInt32();
|
||||
|
||||
decoded = new(version, MareCharaFileData.FromByteArray(reader.ReadBytes(dataLength)));
|
||||
decoded.FilePath = path;
|
||||
decoded = new(version, MareCharaFileData.FromByteArray(reader.ReadBytes(dataLength)))
|
||||
{
|
||||
FilePath = path,
|
||||
};
|
||||
}
|
||||
|
||||
return decoded;
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using LZ4;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
|
||||
namespace MareSynchronos.Export;
|
||||
public class MareCharaFileManager
|
||||
{
|
||||
private readonly FileCacheManager _manager;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly MareCharaFileDataFactory _factory;
|
||||
public MareCharaFileHeader? LoadedCharaFile { get; private set; }
|
||||
public bool CurrentlyWorking { get; private set; } = false;
|
||||
|
||||
public MareCharaFileManager(FileCacheManager manager, IpcManager ipcManager, Configuration configuration, DalamudUtil dalamudUtil)
|
||||
public MareCharaFileManager(FileCacheManager manager, IpcManager ipcManager, ConfigurationService configService, DalamudUtil dalamudUtil)
|
||||
{
|
||||
_factory = new(manager);
|
||||
_manager = manager;
|
||||
_ipcManager = ipcManager;
|
||||
_configuration = configuration;
|
||||
_configService = configService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
}
|
||||
|
||||
@@ -46,44 +42,46 @@ public class MareCharaFileManager
|
||||
using var reader = new BinaryReader(lz4Stream);
|
||||
LoadedCharaFile = MareCharaFileHeader.FromBinaryReader(filePath, reader);
|
||||
Logger.Debug("Read Mare Chara File");
|
||||
Logger.Debug("Version: " + LoadedCharaFile.Version);
|
||||
Logger.Debug("Version: " + (LoadedCharaFile?.Version ?? -1));
|
||||
|
||||
}
|
||||
catch { throw; }
|
||||
finally { CurrentlyWorking = false; }
|
||||
}
|
||||
|
||||
public async Task ApplyMareCharaFile(GameObject charaTarget)
|
||||
public async Task ApplyMareCharaFile(GameObject? charaTarget)
|
||||
{
|
||||
Dictionary<string, string> extractedFiles = new();
|
||||
Dictionary<string, string> extractedFiles = new(StringComparer.Ordinal);
|
||||
CurrentlyWorking = true;
|
||||
try
|
||||
{
|
||||
if (LoadedCharaFile == null || charaTarget == null || !File.Exists(LoadedCharaFile.FilePath)) return;
|
||||
|
||||
using var unwrapped = File.OpenRead(LoadedCharaFile.FilePath);
|
||||
using var lz4Stream = new LZ4Stream(unwrapped, LZ4StreamMode.Decompress, LZ4StreamFlags.HighCompression);
|
||||
using var reader = new BinaryReader(lz4Stream);
|
||||
LoadedCharaFile.AdvanceReaderToData(reader);
|
||||
Logger.Debug("Applying to " + charaTarget.Name.TextValue);
|
||||
extractedFiles = ExtractFilesFromCharaFile(LoadedCharaFile, reader);
|
||||
Dictionary<string, string> fileSwaps = new(StringComparer.Ordinal);
|
||||
foreach (var fileSwap in LoadedCharaFile.CharaFileData.FileSwaps)
|
||||
var unwrapped = File.OpenRead(LoadedCharaFile.FilePath);
|
||||
await using (unwrapped.ConfigureAwait(false))
|
||||
{
|
||||
foreach (var path in fileSwap.GamePaths)
|
||||
using var lz4Stream = new LZ4Stream(unwrapped, LZ4StreamMode.Decompress, LZ4StreamFlags.HighCompression);
|
||||
using var reader = new BinaryReader(lz4Stream);
|
||||
LoadedCharaFile.AdvanceReaderToData(reader);
|
||||
Logger.Debug("Applying to " + charaTarget.Name.TextValue);
|
||||
extractedFiles = ExtractFilesFromCharaFile(LoadedCharaFile, reader);
|
||||
Dictionary<string, string> fileSwaps = new(StringComparer.Ordinal);
|
||||
foreach (var fileSwap in LoadedCharaFile.CharaFileData.FileSwaps)
|
||||
{
|
||||
fileSwaps.Add(path, fileSwap.FileSwapPath);
|
||||
foreach (var path in fileSwap.GamePaths)
|
||||
{
|
||||
fileSwaps.Add(path, fileSwap.FileSwapPath);
|
||||
}
|
||||
}
|
||||
_ipcManager.ToggleGposeQueueMode(on: true);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(charaTarget.Name.TextValue);
|
||||
_ipcManager.PenumbraSetTemporaryMods(charaTarget.Name.TextValue,
|
||||
extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal),
|
||||
LoadedCharaFile.CharaFileData.ManipulationData);
|
||||
_ipcManager.GlamourerApplyAll(LoadedCharaFile.CharaFileData.GlamourerData, charaTarget.Address);
|
||||
_dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(charaTarget.Name.TextValue);
|
||||
_ipcManager.ToggleGposeQueueMode(on: false);
|
||||
}
|
||||
_ipcManager.ToggleGposeQueueMode(true);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(charaTarget.Name.TextValue);
|
||||
_ipcManager.PenumbraSetTemporaryMods(charaTarget.Name.TextValue,
|
||||
extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal),
|
||||
LoadedCharaFile.CharaFileData.ManipulationData);
|
||||
_ipcManager.GlamourerApplyAll(LoadedCharaFile.CharaFileData.GlamourerData, charaTarget.Address);
|
||||
_dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address);
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(charaTarget.Name.TextValue);
|
||||
_ipcManager.ToggleGposeQueueMode(false);
|
||||
}
|
||||
catch { throw; }
|
||||
finally
|
||||
@@ -104,7 +102,7 @@ public class MareCharaFileManager
|
||||
int i = 0;
|
||||
foreach (var fileData in charaFileHeader.CharaFileData.Files)
|
||||
{
|
||||
var fileName = Path.Combine(_configuration.CacheFolder, "mare_" + (i++) + ".tmp");
|
||||
var fileName = Path.Combine(_configService.Current.CacheFolder, "mare_" + (i++) + ".tmp");
|
||||
var length = fileData.Length;
|
||||
var bufferSize = 4 * 1024 * 1024;
|
||||
var buffer = new byte[bufferSize];
|
||||
@@ -127,7 +125,7 @@ public class MareCharaFileManager
|
||||
return gamePathToFilePath;
|
||||
}
|
||||
|
||||
public void SaveMareCharaFile(CharacterCacheDto? dto, string description, string filePath)
|
||||
public void SaveMareCharaFile(CharacterData? dto, string description, string filePath)
|
||||
{
|
||||
CurrentlyWorking = true;
|
||||
try
|
||||
@@ -148,7 +146,7 @@ public class MareCharaFileManager
|
||||
{
|
||||
foreach (var file in replacement.Select(item => _manager.GetFileCacheByHash(item.Hash)).Where(file => file != null))
|
||||
{
|
||||
var length = new FileInfo(file.ResolvedFilepath).Length;
|
||||
var length = new FileInfo(file!.ResolvedFilepath).Length;
|
||||
using var fsRead = File.OpenRead(file.ResolvedFilepath);
|
||||
using var br = new BinaryReader(fsRead);
|
||||
int readBytes = 0;
|
||||
|
||||
26
MareSynchronos/Factories/CachedPlayerFactory.cs
Normal file
26
MareSynchronos/Factories/CachedPlayerFactory.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.Factories;
|
||||
|
||||
public class CachedPlayerFactory
|
||||
{
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
|
||||
public CachedPlayerFactory(IpcManager ipcManager, DalamudUtil dalamudUtil, FileCacheManager fileCacheManager)
|
||||
{
|
||||
_ipcManager = ipcManager;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileCacheManager = fileCacheManager;
|
||||
}
|
||||
|
||||
public CachedPlayer Create(OnlineUserIdentDto dto, ApiController apiController)
|
||||
{
|
||||
return new CachedPlayer(dto, _ipcManager, apiController, _dalamudUtil, _fileCacheManager);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.Interop;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
|
||||
using Penumbra.String;
|
||||
using Weapon = MareSynchronos.Interop.Weapon;
|
||||
@@ -44,7 +39,7 @@ public class CharacterDataFactory
|
||||
{
|
||||
if (!_ipcManager.Initialized)
|
||||
{
|
||||
throw new ArgumentException("Penumbra is not connected");
|
||||
throw new InvalidOperationException("Penumbra is not connected");
|
||||
}
|
||||
|
||||
bool pointerIsZero = true;
|
||||
@@ -231,7 +226,7 @@ public class CharacterDataFactory
|
||||
}
|
||||
}
|
||||
|
||||
var shpkFileReplacement = CreateFileReplacement(shpkPath, true);
|
||||
var shpkFileReplacement = CreateFileReplacement(shpkPath, doNotReverseResolve: true);
|
||||
DebugPrint(shpkFileReplacement, objectKind, "Shader", inheritanceLevel);
|
||||
cache.AddFileReplacement(objectKind, shpkFileReplacement);
|
||||
}
|
||||
@@ -284,7 +279,7 @@ public class CharacterDataFactory
|
||||
}
|
||||
|
||||
var chara = _dalamudUtil.CreateGameObject(charaPointer)!;
|
||||
while (!_dalamudUtil.IsObjectPresent(chara))
|
||||
while (!DalamudUtil.IsObjectPresent(chara))
|
||||
{
|
||||
Logger.Verbose("Character is null but it shouldn't be, waiting");
|
||||
Thread.Sleep(50);
|
||||
@@ -396,7 +391,7 @@ public class CharacterDataFactory
|
||||
foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)weaponObject))
|
||||
{
|
||||
Logger.Verbose("Found transient weapon resource: " + item);
|
||||
AddReplacement(item, objectKind, previousData, 1, true);
|
||||
AddReplacement(item, objectKind, previousData, 1, doNotReverseResolve: true);
|
||||
}
|
||||
|
||||
if (weaponObject->NextSibling != (IntPtr)weaponObject)
|
||||
@@ -413,7 +408,7 @@ public class CharacterDataFactory
|
||||
foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)offHandWeapon))
|
||||
{
|
||||
Logger.Verbose("Found transient offhand weapon resource: " + item);
|
||||
AddReplacement(item, objectKind, previousData, 1, true);
|
||||
AddReplacement(item, objectKind, previousData, 1, doNotReverseResolve: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,7 +416,7 @@ public class CharacterDataFactory
|
||||
AddReplacementSkeleton(((HumanExt*)human)->Human.RaceSexId, objectKind, previousData);
|
||||
try
|
||||
{
|
||||
AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->Decal->FileName()).ToString(), objectKind, previousData, 0, false);
|
||||
AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->Decal->FileName()).ToString(), objectKind, previousData, 0, doNotReverseResolve: false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -429,7 +424,7 @@ public class CharacterDataFactory
|
||||
}
|
||||
try
|
||||
{
|
||||
AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->LegacyBodyDecal->FileName()).ToString(), objectKind, previousData, 0, false);
|
||||
AddReplacementsFromTexture(new ByteString(((HumanExt*)human)->LegacyBodyDecal->FileName()).ToString(), objectKind, previousData, 0, doNotReverseResolve: false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -448,7 +443,7 @@ public class CharacterDataFactory
|
||||
|
||||
string skeletonPath = $"chara/human/c{raceSexIdString}/skeleton/base/b0001/skl_c{raceSexIdString}b0001.sklb";
|
||||
|
||||
var replacement = CreateFileReplacement(skeletonPath, true);
|
||||
var replacement = CreateFileReplacement(skeletonPath, doNotReverseResolve: true);
|
||||
cache.AddFileReplacement(objectKind, replacement);
|
||||
|
||||
DebugPrint(replacement, objectKind, "SKLB", 0);
|
||||
|
||||
22
MareSynchronos/Factories/PairFactory.cs
Normal file
22
MareSynchronos/Factories/PairFactory.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
|
||||
namespace MareSynchronos.Factories;
|
||||
|
||||
public class PairFactory
|
||||
{
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
public PairFactory(ConfigurationService configService, ServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_configService = configService;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
public Pair Create()
|
||||
{
|
||||
return new Pair(_configService, _serverConfigurationManager);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
|
||||
using MareSynchronos;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MareSynchronos.FileCache;
|
||||
@@ -22,7 +21,7 @@ public class FileCacheEntity
|
||||
|
||||
public void SetResolvedFilePath(string filePath)
|
||||
{
|
||||
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\", System.StringComparison.Ordinal);
|
||||
ResolvedFilepath = filePath.ToLowerInvariant().Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public string CsvEntry => $"{Hash}{FileCacheManager.CsvSplit}{PrefixedFilePath}{FileCacheManager.CsvSplit}{LastModifiedDateTicks.ToString(CultureInfo.InvariantCulture)}";
|
||||
@@ -1,44 +1,41 @@
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.FileCache;
|
||||
|
||||
public class FileCacheManager : IDisposable
|
||||
{
|
||||
private const string PenumbraPrefix = "{penumbra}";
|
||||
private const string CachePrefix = "{cache}";
|
||||
private const string _penumbraPrefix = "{penumbra}";
|
||||
private const string _cachePrefix = "{cache}";
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly string CsvPath;
|
||||
private string CsvBakPath => CsvPath + ".bak";
|
||||
private readonly ConcurrentDictionary<string, FileCacheEntity> FileCaches = new(StringComparer.Ordinal);
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly string _csvPath;
|
||||
private string CsvBakPath => _csvPath + ".bak";
|
||||
private readonly ConcurrentDictionary<string, FileCacheEntity> _fileCaches = new(StringComparer.Ordinal);
|
||||
public const string CsvSplit = "|";
|
||||
private object _fileWriteLock = new();
|
||||
private readonly object _fileWriteLock = new();
|
||||
|
||||
public FileCacheManager(IpcManager ipcManager, Configuration configuration, string configDirectoryName)
|
||||
public FileCacheManager(IpcManager ipcManager, ConfigurationService configService)
|
||||
{
|
||||
_ipcManager = ipcManager;
|
||||
_configuration = configuration;
|
||||
CsvPath = Path.Combine(configDirectoryName, "FileCache.csv");
|
||||
_configService = configService;
|
||||
_csvPath = Path.Combine(configService.ConfigurationDirectory, "FileCache.csv");
|
||||
|
||||
lock (_fileWriteLock)
|
||||
{
|
||||
if (File.Exists(CsvBakPath))
|
||||
{
|
||||
File.Move(CsvBakPath, CsvPath, true);
|
||||
File.Move(CsvBakPath, _csvPath, overwrite: true);
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(CsvPath))
|
||||
if (File.Exists(_csvPath))
|
||||
{
|
||||
var entries = File.ReadAllLines(CsvPath);
|
||||
var entries = File.ReadAllLines(_csvPath);
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var splittedEntry = entry.Split(CsvSplit, StringSplitOptions.None);
|
||||
@@ -47,7 +44,7 @@ public class FileCacheManager : IDisposable
|
||||
var hash = splittedEntry[0];
|
||||
var path = splittedEntry[1];
|
||||
var time = splittedEntry[2];
|
||||
FileCaches[path] = ReplacePathPrefixes(new FileCacheEntity(hash, path, time));
|
||||
_fileCaches[path] = ReplacePathPrefixes(new FileCacheEntity(hash, path, time));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -60,19 +57,19 @@ public class FileCacheManager : IDisposable
|
||||
public void WriteOutFullCsv()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
foreach (var entry in FileCaches.OrderBy(f => f.Value.PrefixedFilePath))
|
||||
foreach (var entry in _fileCaches.OrderBy(f => f.Value.PrefixedFilePath, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
sb.AppendLine(entry.Value.CsvEntry);
|
||||
}
|
||||
if (File.Exists(CsvPath))
|
||||
if (File.Exists(_csvPath))
|
||||
{
|
||||
File.Copy(CsvPath, CsvBakPath, true);
|
||||
File.Copy(_csvPath, CsvBakPath, overwrite: true);
|
||||
}
|
||||
lock (_fileWriteLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(CsvPath, sb.ToString());
|
||||
File.WriteAllText(_csvPath, sb.ToString());
|
||||
File.Delete(CsvBakPath);
|
||||
}
|
||||
catch
|
||||
@@ -82,13 +79,13 @@ public class FileCacheManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public List<FileCacheEntity> GetAllFileCaches() => FileCaches.Values.ToList();
|
||||
public List<FileCacheEntity> GetAllFileCaches() => _fileCaches.Values.ToList();
|
||||
|
||||
public FileCacheEntity? GetFileCacheByHash(string hash)
|
||||
{
|
||||
if (FileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)))
|
||||
if (_fileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)))
|
||||
{
|
||||
return GetValidatedFileCache(FileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value);
|
||||
return GetValidatedFileCache(_fileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -113,7 +110,7 @@ public class FileCacheManager : IDisposable
|
||||
public FileCacheEntity? GetFileCacheByPath(string path)
|
||||
{
|
||||
var cleanedPath = path.Replace("/", "\\", StringComparison.OrdinalIgnoreCase).ToLowerInvariant().Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), "", StringComparison.OrdinalIgnoreCase);
|
||||
var entry = FileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
var entry = _fileCaches.FirstOrDefault(f => f.Value.ResolvedFilepath.EndsWith(cleanedPath, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
@@ -132,8 +129,8 @@ public class FileCacheManager : IDisposable
|
||||
FileInfo fi = new(path);
|
||||
if (!fi.Exists) return null;
|
||||
var fullName = fi.FullName.ToLowerInvariant();
|
||||
if (!fullName.Contains(_configuration.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
|
||||
string prefixedPath = fullName.Replace(_configuration.CacheFolder.ToLowerInvariant(), CachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||
if (!fullName.Contains(_configService.Current.CacheFolder.ToLowerInvariant(), StringComparison.Ordinal)) return null;
|
||||
string prefixedPath = fullName.Replace(_configService.Current.CacheFolder.ToLowerInvariant(), _cachePrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||
return CreateFileCacheEntity(fi, prefixedPath, fi.Name.ToUpper(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
@@ -144,22 +141,19 @@ public class FileCacheManager : IDisposable
|
||||
if (!fi.Exists) return null;
|
||||
var fullName = fi.FullName.ToLowerInvariant();
|
||||
if (!fullName.Contains(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), StringComparison.Ordinal)) return null;
|
||||
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), PenumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||
string prefixedPath = fullName.Replace(_ipcManager.PenumbraModDirectory()!.ToLowerInvariant(), _penumbraPrefix + "\\", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
|
||||
return CreateFileCacheEntity(fi, prefixedPath);
|
||||
}
|
||||
|
||||
private FileCacheEntity? CreateFileCacheEntity(FileInfo fileInfo, string prefixedPath, string? hash = null)
|
||||
{
|
||||
if (hash == null)
|
||||
{
|
||||
hash = Crypto.GetFileHash(fileInfo.FullName);
|
||||
}
|
||||
hash ??= Crypto.GetFileHash(fileInfo.FullName);
|
||||
var entity = new FileCacheEntity(hash, prefixedPath, fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture));
|
||||
entity = ReplacePathPrefixes(entity);
|
||||
FileCaches[prefixedPath] = entity;
|
||||
_fileCaches[prefixedPath] = entity;
|
||||
lock (_fileWriteLock)
|
||||
{
|
||||
File.AppendAllLines(CsvPath, new[] { entity.CsvEntry });
|
||||
File.AppendAllLines(_csvPath, new[] { entity.CsvEntry });
|
||||
}
|
||||
var result = GetFileCacheByPath(fileInfo.FullName);
|
||||
Logger.Debug("Creating file cache for " + fileInfo.FullName + " success: " + (result != null));
|
||||
@@ -178,7 +172,7 @@ public class FileCacheManager : IDisposable
|
||||
var file = new FileInfo(fileCache.ResolvedFilepath);
|
||||
if (!file.Exists)
|
||||
{
|
||||
FileCaches.Remove(fileCache.PrefixedFilePath, out _);
|
||||
_fileCaches.Remove(fileCache.PrefixedFilePath, out _);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -193,7 +187,7 @@ public class FileCacheManager : IDisposable
|
||||
public void RemoveHash(FileCacheEntity entity)
|
||||
{
|
||||
Logger.Verbose("Removing " + entity.ResolvedFilepath);
|
||||
FileCaches.Remove(entity.PrefixedFilePath, out _);
|
||||
_fileCaches.Remove(entity.PrefixedFilePath, out _);
|
||||
}
|
||||
|
||||
public void UpdateHash(FileCacheEntity fileCache)
|
||||
@@ -201,19 +195,19 @@ public class FileCacheManager : IDisposable
|
||||
Logger.Verbose("Updating hash for " + fileCache.ResolvedFilepath);
|
||||
fileCache.Hash = Crypto.GetFileHash(fileCache.ResolvedFilepath);
|
||||
fileCache.LastModifiedDateTicks = new FileInfo(fileCache.ResolvedFilepath).LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture);
|
||||
FileCaches.Remove(fileCache.PrefixedFilePath, out _);
|
||||
FileCaches[fileCache.PrefixedFilePath] = fileCache;
|
||||
_fileCaches.Remove(fileCache.PrefixedFilePath, out _);
|
||||
_fileCaches[fileCache.PrefixedFilePath] = fileCache;
|
||||
}
|
||||
|
||||
private FileCacheEntity ReplacePathPrefixes(FileCacheEntity fileCache)
|
||||
{
|
||||
if (fileCache.PrefixedFilePath.StartsWith(PenumbraPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
if (fileCache.PrefixedFilePath.StartsWith(_penumbraPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(PenumbraPrefix, _ipcManager.PenumbraModDirectory(), StringComparison.Ordinal));
|
||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(_penumbraPrefix, _ipcManager.PenumbraModDirectory(), StringComparison.Ordinal));
|
||||
}
|
||||
else if (fileCache.PrefixedFilePath.StartsWith(CachePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
else if (fileCache.PrefixedFilePath.StartsWith(_cachePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(CachePrefix, _configuration.CacheFolder, StringComparison.Ordinal));
|
||||
fileCache.SetResolvedFilePath(fileCache.PrefixedFilePath.Replace(_cachePrefix, _configService.Current.CacheFolder, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
return fileCache;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using MareSynchronos.Models;
|
||||
|
||||
namespace MareSynchronos.FileCache;
|
||||
namespace MareSynchronos.FileCache;
|
||||
|
||||
|
||||
public enum FileState
|
||||
{
|
||||
Valid,
|
||||
RequireUpdate,
|
||||
RequireDeletion
|
||||
RequireDeletion,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
@@ -14,19 +9,19 @@ namespace MareSynchronos.FileCache;
|
||||
public class PeriodicFileScanner : IDisposable
|
||||
{
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private CancellationTokenSource? _scanCancellationTokenSource;
|
||||
private Task? _fileScannerTask = null;
|
||||
public ConcurrentDictionary<string, int> haltScanLocks = new(StringComparer.Ordinal);
|
||||
public PeriodicFileScanner(IpcManager ipcManager, Configuration pluginConfiguration, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
|
||||
public PeriodicFileScanner(IpcManager ipcManager, ConfigurationService configService, FileCacheManager fileDbManager, ApiController apiController, DalamudUtil dalamudUtil)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(PeriodicFileScanner));
|
||||
|
||||
_ipcManager = ipcManager;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_configService = configService;
|
||||
_fileDbManager = fileDbManager;
|
||||
_apiController = apiController;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
@@ -69,10 +64,10 @@ public class PeriodicFileScanner : IDisposable
|
||||
haltScanLocks[source]--;
|
||||
if (haltScanLocks[source] < 0) haltScanLocks[source] = 0;
|
||||
|
||||
if (fileScanWasRunning && haltScanLocks.All(f => f.Value == 0))
|
||||
if (_fileScanWasRunning && haltScanLocks.All(f => f.Value == 0))
|
||||
{
|
||||
fileScanWasRunning = false;
|
||||
InvokeScan(true);
|
||||
_fileScanWasRunning = false;
|
||||
InvokeScan(forced: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +79,13 @@ public class PeriodicFileScanner : IDisposable
|
||||
if (IsScanRunning && haltScanLocks.Any(f => f.Value > 0))
|
||||
{
|
||||
_scanCancellationTokenSource?.Cancel();
|
||||
fileScanWasRunning = true;
|
||||
_fileScanWasRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool fileScanWasRunning = false;
|
||||
private long currentFileProgress = 0;
|
||||
public long CurrentFileProgress => currentFileProgress;
|
||||
private bool _fileScanWasRunning = false;
|
||||
private long _currentFileProgress = 0;
|
||||
public long CurrentFileProgress => _currentFileProgress;
|
||||
|
||||
public long FileCacheSize { get; set; }
|
||||
|
||||
@@ -100,7 +95,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
|
||||
public string TimeUntilNextScan => _timeUntilNextScan.ToString(@"mm\:ss");
|
||||
private TimeSpan _timeUntilNextScan = TimeSpan.Zero;
|
||||
private int timeBetweenScans => _pluginConfiguration.TimeSpanBetweenScansInSeconds;
|
||||
private int TimeBetweenScans => _configService.Current.TimeSpanBetweenScansInSeconds;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -118,7 +113,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
{
|
||||
bool isForced = forced;
|
||||
TotalFiles = 0;
|
||||
currentFileProgress = 0;
|
||||
_currentFileProgress = 0;
|
||||
_scanCancellationTokenSource?.Cancel();
|
||||
_scanCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _scanCancellationTokenSource.Token;
|
||||
@@ -126,22 +121,22 @@ public class PeriodicFileScanner : IDisposable
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
while (haltScanLocks.Any(f => f.Value > 0))
|
||||
while (haltScanLocks.Any(f => f.Value > 0) || !_ipcManager.CheckPenumbraApi())
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
isForced |= RecalculateFileCacheSize();
|
||||
if (!_pluginConfiguration.FileScanPaused || isForced)
|
||||
if (!_configService.Current.FileScanPaused || isForced)
|
||||
{
|
||||
isForced = false;
|
||||
TotalFiles = 0;
|
||||
currentFileProgress = 0;
|
||||
_currentFileProgress = 0;
|
||||
PeriodicFileScan(token);
|
||||
TotalFiles = 0;
|
||||
currentFileProgress = 0;
|
||||
_currentFileProgress = 0;
|
||||
}
|
||||
_timeUntilNextScan = TimeSpan.FromSeconds(timeBetweenScans);
|
||||
_timeUntilNextScan = TimeSpan.FromSeconds(TimeBetweenScans);
|
||||
while (_timeUntilNextScan.TotalSeconds >= 0)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
|
||||
@@ -153,7 +148,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
|
||||
public bool RecalculateFileCacheSize()
|
||||
{
|
||||
FileCacheSize = Directory.EnumerateFiles(_pluginConfiguration.CacheFolder).Sum(f =>
|
||||
FileCacheSize = Directory.EnumerateFiles(_configService.Current.CacheFolder).Sum(f =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -165,13 +160,13 @@ public class PeriodicFileScanner : IDisposable
|
||||
}
|
||||
});
|
||||
|
||||
if (FileCacheSize < (long)_pluginConfiguration.MaxLocalCacheInGiB * 1024 * 1024 * 1024) return false;
|
||||
if (FileCacheSize < (long)_configService.Current.MaxLocalCacheInGiB * 1024 * 1024 * 1024) return false;
|
||||
|
||||
var allFiles = Directory.EnumerateFiles(_pluginConfiguration.CacheFolder)
|
||||
var allFiles = Directory.EnumerateFiles(_configService.Current.CacheFolder)
|
||||
.Select(f => new FileInfo(f)).OrderBy(f => f.LastAccessTime).ToList();
|
||||
while (FileCacheSize > (long)_pluginConfiguration.MaxLocalCacheInGiB * 1024 * 1024 * 1024)
|
||||
while (FileCacheSize > (long)_configService.Current.MaxLocalCacheInGiB * 1024 * 1024 * 1024)
|
||||
{
|
||||
var oldestFile = allFiles.First();
|
||||
var oldestFile = allFiles[0];
|
||||
FileCacheSize -= oldestFile.Length;
|
||||
File.Delete(oldestFile.FullName);
|
||||
allFiles.Remove(oldestFile);
|
||||
@@ -191,7 +186,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
penDirExists = false;
|
||||
Logger.Warn("Penumbra directory is not set or does not exist.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) || !Directory.Exists(_pluginConfiguration.CacheFolder))
|
||||
if (string.IsNullOrEmpty(_configService.Current.CacheFolder) || !Directory.Exists(_configService.Current.CacheFolder))
|
||||
{
|
||||
cacheDirExists = false;
|
||||
Logger.Warn("Mare Cache directory is not set or does not exist.");
|
||||
@@ -201,19 +196,19 @@ public class PeriodicFileScanner : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Debug("Getting files from " + penumbraDir + " and " + _pluginConfiguration.CacheFolder);
|
||||
Logger.Debug("Getting files from " + penumbraDir + " and " + _configService.Current.CacheFolder);
|
||||
string[] ext = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp", ".shpk" };
|
||||
|
||||
var scannedFiles = new ConcurrentDictionary<string, bool>(Directory.EnumerateFiles(penumbraDir, "*.*", SearchOption.AllDirectories)
|
||||
var scannedFiles = new ConcurrentDictionary<string, bool>(Directory.EnumerateFiles(penumbraDir!, "*.*", SearchOption.AllDirectories)
|
||||
.Select(s => s.ToLowerInvariant())
|
||||
.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(_configService.Current.CacheFolder, "*.*", SearchOption.TopDirectoryOnly)
|
||||
.Where(f => new FileInfo(f).Name.Length == 40)
|
||||
.Select(s => s.ToLowerInvariant()).ToList())
|
||||
.Select(c => new KeyValuePair<string, bool>(c, false)), StringComparer.OrdinalIgnoreCase);
|
||||
.Select(c => new KeyValuePair<string, bool>(c, value: false)), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
TotalFiles = scannedFiles.Count;
|
||||
|
||||
@@ -253,7 +248,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
Logger.Warn(ex.StackTrace);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref currentFileProgress);
|
||||
Interlocked.Increment(ref _currentFileProgress);
|
||||
Thread.Sleep(1);
|
||||
}, ct);
|
||||
|
||||
@@ -308,7 +303,7 @@ public class PeriodicFileScanner : IDisposable
|
||||
Logger.Warn(ex.StackTrace);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref currentFileProgress);
|
||||
Interlocked.Increment(ref _currentFileProgress);
|
||||
Thread.Sleep(1);
|
||||
}, ct);
|
||||
|
||||
@@ -321,22 +316,22 @@ public class PeriodicFileScanner : IDisposable
|
||||
|
||||
Logger.Debug("Scan complete");
|
||||
TotalFiles = 0;
|
||||
currentFileProgress = 0;
|
||||
_currentFileProgress = 0;
|
||||
entitiesToRemove.Clear();
|
||||
scannedFiles.Clear();
|
||||
dbTasks = Array.Empty<Task>();
|
||||
|
||||
if (!_pluginConfiguration.InitialScanComplete)
|
||||
if (!_configService.Current.InitialScanComplete)
|
||||
{
|
||||
_pluginConfiguration.InitialScanComplete = true;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.InitialScanComplete = true;
|
||||
_configService.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public void StartScan()
|
||||
{
|
||||
if (!_ipcManager.Initialized || !_pluginConfiguration.HasValidSetup()) return;
|
||||
if (!_ipcManager.Initialized || !_configService.Current.HasValidSetup()) return;
|
||||
Logger.Verbose("Penumbra is active, configuration is valid, scan");
|
||||
InvokeScan(true);
|
||||
InvokeScan(forced: true);
|
||||
}
|
||||
}
|
||||
|
||||
11
MareSynchronos/Interop/CharaExt.cs
Normal file
11
MareSynchronos/Interop/CharaExt.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct CharaExt
|
||||
{
|
||||
[FieldOffset(0x0)] public Character Character;
|
||||
[FieldOffset(0x650)] public Character* Mount;
|
||||
}
|
||||
12
MareSynchronos/Interop/HumanExt.cs
Normal file
12
MareSynchronos/Interop/HumanExt.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct HumanExt
|
||||
{
|
||||
[FieldOffset(0x0)] public Human Human;
|
||||
[FieldOffset(0x9E8)] public ResourceHandle* Decal;
|
||||
[FieldOffset(0x9F0)] public ResourceHandle* LegacyBodyDecal;
|
||||
}
|
||||
@@ -1,18 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct Material
|
||||
{
|
||||
[FieldOffset( 0x10 )]
|
||||
[FieldOffset(0x10)]
|
||||
public ResourceHandle* ResourceHandle;
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
public unsafe struct MaterialData
|
||||
{
|
||||
[FieldOffset( 0x0 )]
|
||||
public byte* Data;
|
||||
}
|
||||
10
MareSynchronos/Interop/MaterialData.cs
Normal file
10
MareSynchronos/Interop/MaterialData.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct MaterialData
|
||||
{
|
||||
[FieldOffset(0x0)]
|
||||
public byte* Data;
|
||||
}
|
||||
@@ -1,29 +1,28 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct MtrlResource
|
||||
{
|
||||
[FieldOffset( 0x00 )]
|
||||
[FieldOffset(0x00)]
|
||||
public ResourceHandle Handle;
|
||||
|
||||
[FieldOffset( 0xD0 )]
|
||||
[FieldOffset(0xD0)]
|
||||
public ushort* TexSpace; // Contains the offsets for the tex files inside the string list.
|
||||
|
||||
[FieldOffset( 0xE0 )]
|
||||
[FieldOffset(0xE0)]
|
||||
public byte* StringList;
|
||||
|
||||
[FieldOffset( 0xF8 )]
|
||||
[FieldOffset(0xF8)]
|
||||
public ushort ShpkOffset;
|
||||
|
||||
[FieldOffset( 0xFA )]
|
||||
[FieldOffset(0xFA)]
|
||||
public byte NumTex;
|
||||
|
||||
public byte* ShpkString
|
||||
=> StringList + ShpkOffset;
|
||||
|
||||
public byte* TexString( int idx )
|
||||
=> StringList + *( TexSpace + 4 + idx * 8 );
|
||||
public byte* TexString(int idx)
|
||||
=> StringList + *(TexSpace + 4 + idx * 8);
|
||||
}
|
||||
@@ -1,42 +1,41 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct RenderModel
|
||||
{
|
||||
[FieldOffset( 0x18 )]
|
||||
[FieldOffset(0x18)]
|
||||
public RenderModel* PreviousModel;
|
||||
|
||||
[FieldOffset( 0x20 )]
|
||||
[FieldOffset(0x20)]
|
||||
public RenderModel* NextModel;
|
||||
|
||||
[FieldOffset( 0x30 )]
|
||||
[FieldOffset(0x30)]
|
||||
public ResourceHandle* ResourceHandle;
|
||||
|
||||
[FieldOffset( 0x40 )]
|
||||
[FieldOffset(0x40)]
|
||||
public Skeleton* Skeleton;
|
||||
|
||||
[FieldOffset( 0x58 )]
|
||||
[FieldOffset(0x58)]
|
||||
public void** BoneList;
|
||||
|
||||
[FieldOffset( 0x60 )]
|
||||
[FieldOffset(0x60)]
|
||||
public int BoneListCount;
|
||||
|
||||
[FieldOffset( 0x70 )]
|
||||
[FieldOffset(0x70)]
|
||||
private void* UnkDXBuffer1;
|
||||
|
||||
[FieldOffset( 0x78 )]
|
||||
[FieldOffset(0x78)]
|
||||
private void* UnkDXBuffer2;
|
||||
|
||||
[FieldOffset( 0x80 )]
|
||||
[FieldOffset(0x80)]
|
||||
private void* UnkDXBuffer3;
|
||||
|
||||
[FieldOffset( 0x98 )]
|
||||
[FieldOffset(0x98)]
|
||||
public void** Materials;
|
||||
|
||||
[FieldOffset( 0xA0 )]
|
||||
[FieldOffset(0xA0)]
|
||||
public int MaterialCount;
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct ResourceHandle
|
||||
{
|
||||
public const int SsoSize = 15;
|
||||
|
||||
public byte* FileName()
|
||||
{
|
||||
if( FileNameLength > SsoSize )
|
||||
if (FileNameLength > SsoSize)
|
||||
{
|
||||
return FileNameData;
|
||||
}
|
||||
|
||||
fixed( byte** name = &FileNameData )
|
||||
fixed (byte** name = &FileNameData)
|
||||
{
|
||||
return ( byte* )name;
|
||||
return (byte*)name;
|
||||
}
|
||||
}
|
||||
|
||||
[FieldOffset( 0x08 )]
|
||||
[FieldOffset(0x08)]
|
||||
public ResourceCategory Category;
|
||||
|
||||
[FieldOffset( 0x48 )]
|
||||
[FieldOffset(0x48)]
|
||||
public byte* FileNameData;
|
||||
|
||||
[FieldOffset( 0x58 )]
|
||||
[FieldOffset(0x58)]
|
||||
public int FileNameLength;
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using Penumbra.Interop.Structs;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
@@ -15,24 +10,3 @@ public unsafe struct Weapon
|
||||
[FieldOffset(0x28)] public IntPtr PreviousSibling;
|
||||
[FieldOffset(0xA8)] public WeaponDrawObject* WeaponRenderModel;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct WeaponDrawObject
|
||||
{
|
||||
[FieldOffset(0x00)] public RenderModel* RenderModel;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct HumanExt
|
||||
{
|
||||
[FieldOffset(0x0)] public Human Human;
|
||||
[FieldOffset(0x9E8)] public Penumbra.Interop.Structs.ResourceHandle* Decal;
|
||||
[FieldOffset(0x9F0)] public Penumbra.Interop.Structs.ResourceHandle* LegacyBodyDecal;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct CharaExt
|
||||
{
|
||||
[FieldOffset(0x0)] public Character Character;
|
||||
[FieldOffset(0x650)] public Character* Mount;
|
||||
}
|
||||
|
||||
9
MareSynchronos/Interop/WeaponDrawObject.cs
Normal file
9
MareSynchronos/Interop/WeaponDrawObject.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MareSynchronos.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct WeaponDrawObject
|
||||
{
|
||||
[FieldOffset(0x00)] public RenderModel* RenderModel;
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Logging;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
@@ -13,21 +10,30 @@ using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class CachedPlayer
|
||||
public class CachedPlayer : IDisposable
|
||||
{
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager fileDbManager;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private API.Data.CharacterData _cachedData = new();
|
||||
private PlayerRelatedObject? _currentCharacterEquipment;
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||
private bool _isVisible;
|
||||
|
||||
public CachedPlayer(string nameHash, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
private string _lastGlamourerData = string.Empty;
|
||||
|
||||
private string _originalGlamourerData = string.Empty;
|
||||
|
||||
private Task? _penumbraRedrawEventTask;
|
||||
|
||||
public CachedPlayer(OnlineUserIdentDto onlineUser, IpcManager ipcManager, ApiController apiController, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
{
|
||||
PlayerNameHash = nameHash;
|
||||
OnlineUser = onlineUser;
|
||||
_ipcManager = ipcManager;
|
||||
_apiController = apiController;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
this.fileDbManager = fileDbManager;
|
||||
_fileDbManager = fileDbManager;
|
||||
}
|
||||
|
||||
public bool IsVisible
|
||||
@@ -40,35 +46,24 @@ public class CachedPlayer
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDisposed = true;
|
||||
private CancellationTokenSource? _downloadCancellationTokenSource = new();
|
||||
|
||||
private string _lastGlamourerData = string.Empty;
|
||||
|
||||
private string _originalGlamourerData = string.Empty;
|
||||
|
||||
public OnlineUserIdentDto OnlineUser { get; set; }
|
||||
public IntPtr PlayerCharacter { get; set; } = IntPtr.Zero;
|
||||
|
||||
public string? PlayerName { get; private set; }
|
||||
|
||||
public string PlayerNameHash { get; }
|
||||
public string PlayerNameHash => OnlineUser.Ident;
|
||||
|
||||
public bool RequestedPenumbraRedraw { get; set; }
|
||||
|
||||
public bool WasVisible { get; private set; }
|
||||
|
||||
private CharacterCacheDto _cachedData = new();
|
||||
|
||||
private PlayerRelatedObject? _currentCharacterEquipment;
|
||||
|
||||
public void ApplyCharacterData(CharacterCacheDto characterData, OptionalPluginWarning warning)
|
||||
public void ApplyCharacterData(API.Data.CharacterData characterData, OptionalPluginWarning warning)
|
||||
{
|
||||
Logger.Debug("Received data for " + this);
|
||||
|
||||
Logger.Debug("Checking for files to download for player " + PlayerName);
|
||||
Logger.Debug("Hash for data is " + characterData.GetHashCode() + ", current cache hash is " + _cachedData.GetHashCode());
|
||||
Logger.Debug("Hash for data is " + characterData.DataHash.Value + ", current cache hash is " + _cachedData.DataHash.Value);
|
||||
|
||||
if (characterData.GetHashCode() == _cachedData.GetHashCode()) return;
|
||||
if (string.Equals(characterData.DataHash.Value, _cachedData.DataHash.Value, StringComparison.Ordinal)) return;
|
||||
|
||||
bool updateModdedPaths = false;
|
||||
List<ObjectKind> charaDataToUpdate = new();
|
||||
@@ -122,13 +117,12 @@ public class CachedPlayer
|
||||
bool heelsOffsetDifferent = _cachedData.HeelsOffset != characterData.HeelsOffset;
|
||||
if (heelsOffsetDifferent)
|
||||
{
|
||||
|
||||
Logger.Debug("Updating " + objectKind);
|
||||
charaDataToUpdate.Add(objectKind);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool customizeDataDifferent = _cachedData.CustomizePlusData != characterData.CustomizePlusData;
|
||||
bool customizeDataDifferent = !string.Equals(_cachedData.CustomizePlusData, characterData.CustomizePlusData, StringComparison.Ordinal);
|
||||
if (customizeDataDifferent)
|
||||
{
|
||||
Logger.Debug("Updating " + objectKind);
|
||||
@@ -160,6 +154,184 @@ public class CachedPlayer
|
||||
DownloadAndApplyCharacter(charaDataToUpdate, updateModdedPaths);
|
||||
}
|
||||
|
||||
public bool CheckExistence()
|
||||
{
|
||||
var curPlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!)?.Address ?? IntPtr.Zero;
|
||||
if (PlayerCharacter == IntPtr.Zero || PlayerCharacter != curPlayerCharacter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentCharacterEquipment?.CheckAndUpdateObject();
|
||||
if (_currentCharacterEquipment?.HasUnprocessedUpdate ?? false)
|
||||
{
|
||||
OnPlayerChanged();
|
||||
}
|
||||
|
||||
IsVisible = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (string.IsNullOrEmpty(PlayerName)) return;
|
||||
Logger.Debug("Disposing " + PlayerName + " (" + OnlineUser + ")");
|
||||
try
|
||||
{
|
||||
Logger.Verbose("Restoring state for " + PlayerName);
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
|
||||
_downloadCancellationTokenSource?.Cancel();
|
||||
_downloadCancellationTokenSource?.Dispose();
|
||||
_downloadCancellationTokenSource = null;
|
||||
if (PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements)
|
||||
{
|
||||
RevertCustomizationData(item.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.Message + Environment.NewLine + ex.StackTrace);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cachedData = new();
|
||||
var tempPlayerName = PlayerName;
|
||||
PlayerName = string.Empty;
|
||||
PlayerCharacter = IntPtr.Zero;
|
||||
IsVisible = false;
|
||||
Logger.Debug("Disposing " + tempPlayerName + " complete");
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(IntPtr character, string name)
|
||||
{
|
||||
IsVisible = true;
|
||||
PlayerName = name;
|
||||
PlayerCharacter = character;
|
||||
Logger.Debug("Initializing Player " + this);
|
||||
|
||||
_ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent;
|
||||
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
_currentCharacterEquipment = new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero,
|
||||
() => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName)?.Address ?? IntPtr.Zero);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return OnlineUser.User.AliasOrUID + ":" + PlayerName + ":HasChar " + (PlayerCharacter != IntPtr.Zero);
|
||||
}
|
||||
|
||||
private void ApplyBaseData(Dictionary<string, string> moddedPaths)
|
||||
{
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName!);
|
||||
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
|
||||
}
|
||||
|
||||
private unsafe void ApplyCustomizationData(ObjectKind objectKind, CancellationToken ct)
|
||||
{
|
||||
if (PlayerCharacter == IntPtr.Zero) return;
|
||||
_cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData);
|
||||
|
||||
switch (objectKind)
|
||||
{
|
||||
case ObjectKind.Player:
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
|
||||
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData);
|
||||
RequestedPenumbraRedraw = true;
|
||||
Logger.Debug(
|
||||
$"Request Redraw for {PlayerName}");
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(PlayerCharacter);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectKind.MinionOrMount:
|
||||
{
|
||||
var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject;
|
||||
if (minionOrMount != null)
|
||||
{
|
||||
Logger.Debug($"Request Redraw for Minion/Mount");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " minion or mount", (IntPtr)minionOrMount, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, (IntPtr)minionOrMount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw((IntPtr)minionOrMount);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectKind.Pet:
|
||||
{
|
||||
int tick = 16;
|
||||
var pet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
if (pet != IntPtr.Zero)
|
||||
{
|
||||
var totalWait = 0;
|
||||
var newPet = IntPtr.Zero;
|
||||
const int maxWait = 3000;
|
||||
Logger.Debug($"Request Redraw for Pet, waiting {maxWait}ms");
|
||||
|
||||
do
|
||||
{
|
||||
Thread.Sleep(tick);
|
||||
totalWait += tick;
|
||||
newPet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
} while (newPet == pet && totalWait < maxWait);
|
||||
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, newPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(newPet);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectKind.Companion:
|
||||
{
|
||||
var companion = _dalamudUtil.GetCompanion(PlayerCharacter);
|
||||
if (companion != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug("Request Redraw for Companion");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " companion", companion, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, companion);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(companion);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadAndApplyCharacter(List<ObjectKind> objectKind, bool updateModdedPaths)
|
||||
{
|
||||
if (!objectKind.Any())
|
||||
@@ -175,7 +347,7 @@ public class CachedPlayer
|
||||
var downloadId = _apiController.GetDownloadId();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
List<FileReplacementDto> toDownloadReplacements;
|
||||
List<FileReplacementData> toDownloadReplacements;
|
||||
|
||||
if (updateModdedPaths)
|
||||
{
|
||||
@@ -213,7 +385,6 @@ public class CachedPlayer
|
||||
{
|
||||
ApplyCustomizationData(kind, downloadToken);
|
||||
}
|
||||
|
||||
}, downloadToken).ContinueWith(task =>
|
||||
{
|
||||
_downloadCancellationTokenSource = null;
|
||||
@@ -225,138 +396,44 @@ public class CachedPlayer
|
||||
});
|
||||
}
|
||||
|
||||
private List<FileReplacementDto> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
||||
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
||||
{
|
||||
List<FileReplacementDto> missingFiles = new();
|
||||
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
try
|
||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
||||
if (player == null || !string.Equals(player.Name.ToString(), PlayerName, StringComparison.OrdinalIgnoreCase)) return;
|
||||
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
||||
|
||||
_penumbraRedrawEventTask = Task.Run(() =>
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
PlayerCharacter = address;
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, cts.Token);
|
||||
cts.Dispose();
|
||||
cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
if (RequestedPenumbraRedraw == false)
|
||||
{
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
var fileCache = fileDbManager.GetFileCacheByHash(item.Hash);
|
||||
if (fileCache != null)
|
||||
{
|
||||
moddedDictionary[gamePath] = fileCache.ResolvedFilepath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Missing file: " + item.Hash);
|
||||
missingFiles.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => !string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
{
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
Logger.Verbose("Adding file swap for " + gamePath + ":" + item.FileSwapPath);
|
||||
moddedDictionary[gamePath] = item.FileSwapPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, "Something went wrong during calculation replacements");
|
||||
}
|
||||
Logger.Debug("ModdedPaths calculated, missing files: " + missingFiles.Count);
|
||||
return missingFiles;
|
||||
}
|
||||
|
||||
private void ApplyBaseData(Dictionary<string, string> moddedPaths)
|
||||
{
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName!);
|
||||
_ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData);
|
||||
}
|
||||
|
||||
private unsafe void ApplyCustomizationData(ObjectKind objectKind, CancellationToken ct)
|
||||
{
|
||||
if (PlayerCharacter == IntPtr.Zero) return;
|
||||
_cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData);
|
||||
|
||||
if (objectKind == ObjectKind.Player)
|
||||
{
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
_ipcManager.HeelsSetOffsetForPlayer(_cachedData.HeelsOffset, PlayerCharacter);
|
||||
_ipcManager.CustomizePlusSetBodyScale(PlayerCharacter, _cachedData.CustomizePlusData);
|
||||
RequestedPenumbraRedraw = true;
|
||||
Logger.Debug(
|
||||
$"Request Redraw for {PlayerName}");
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter);
|
||||
Logger.Debug("Unauthorized character change detected");
|
||||
ApplyCustomizationData(ObjectKind.Player, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(PlayerCharacter);
|
||||
RequestedPenumbraRedraw = false;
|
||||
Logger.Debug(
|
||||
$"Penumbra Redraw done for {PlayerName}");
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.MinionOrMount)
|
||||
{
|
||||
var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject;
|
||||
if (minionOrMount != null)
|
||||
{
|
||||
Logger.Debug($"Request Redraw for Minion/Mount");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " minion or mount", (IntPtr)minionOrMount, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, (IntPtr)minionOrMount);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw((IntPtr)minionOrMount);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Pet)
|
||||
{
|
||||
int tick = 16;
|
||||
var pet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
if (pet != IntPtr.Zero)
|
||||
{
|
||||
var totalWait = 0;
|
||||
var newPet = IntPtr.Zero;
|
||||
const int maxWait = 3000;
|
||||
Logger.Debug($"Request Redraw for Pet, waiting {maxWait}ms");
|
||||
cts.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Thread.Sleep(tick);
|
||||
totalWait += tick;
|
||||
newPet = _dalamudUtil.GetPet(PlayerCharacter);
|
||||
} while (newPet == pet && totalWait < maxWait);
|
||||
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, newPet);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(newPet);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Companion)
|
||||
private void OnPlayerChanged()
|
||||
{
|
||||
Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}");
|
||||
_currentCharacterEquipment!.HasUnprocessedUpdate = false;
|
||||
if (!RequestedPenumbraRedraw && PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
var companion = _dalamudUtil.GetCompanion(PlayerCharacter);
|
||||
if (companion != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug("Request Redraw for Companion");
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName! + " companion", companion, 10000, ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData))
|
||||
{
|
||||
_ipcManager.GlamourerApplyAll(glamourerData, companion);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipcManager.PenumbraRedraw(companion);
|
||||
}
|
||||
}
|
||||
Logger.Debug($"Saving new Glamourer data");
|
||||
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,129 +481,43 @@ public class CachedPlayer
|
||||
}
|
||||
}
|
||||
|
||||
public void DisposePlayer()
|
||||
private List<FileReplacementData> TryCalculateModdedDictionary(out Dictionary<string, string> moddedDictionary)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
if (string.IsNullOrEmpty(PlayerName)) return;
|
||||
Logger.Debug("Disposing " + PlayerName + " (" + PlayerNameHash + ")");
|
||||
_isDisposed = true;
|
||||
List<FileReplacementData> missingFiles = new();
|
||||
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
try
|
||||
{
|
||||
Logger.Verbose("Restoring state for " + PlayerName);
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent;
|
||||
_ipcManager.PenumbraRemoveTemporaryCollection(PlayerName);
|
||||
_downloadCancellationTokenSource?.Cancel();
|
||||
_downloadCancellationTokenSource?.Dispose();
|
||||
_downloadCancellationTokenSource = null;
|
||||
if (PlayerCharacter != IntPtr.Zero)
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
{
|
||||
foreach (var item in _cachedData.FileReplacements)
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
RevertCustomizationData(item.Key);
|
||||
var fileCache = _fileDbManager.GetFileCacheByHash(item.Hash);
|
||||
if (fileCache != null)
|
||||
{
|
||||
moddedDictionary[gamePath] = fileCache.ResolvedFilepath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Missing file: " + item.Hash);
|
||||
missingFiles.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in _cachedData.FileReplacements.SelectMany(k => k.Value.Where(v => !string.IsNullOrEmpty(v.FileSwapPath))).ToList())
|
||||
{
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
Logger.Verbose("Adding file swap for " + gamePath + ":" + item.FileSwapPath);
|
||||
moddedDictionary[gamePath] = item.FileSwapPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.Message + Environment.NewLine + ex.StackTrace);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cachedData = new();
|
||||
var tempPlayerName = PlayerName;
|
||||
PlayerName = string.Empty;
|
||||
PlayerCharacter = IntPtr.Zero;
|
||||
IsVisible = false;
|
||||
Logger.Debug("Disposing " + tempPlayerName + " complete");
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializePlayer(IntPtr character, string name, CharacterCacheDto? cache, OptionalPluginWarning displayedChatWarning)
|
||||
{
|
||||
if (!_isDisposed) return;
|
||||
IsVisible = true;
|
||||
PlayerName = name;
|
||||
PlayerCharacter = character;
|
||||
Logger.Debug("Initializing Player " + this + " has cache: " + (cache != null));
|
||||
|
||||
_dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent;
|
||||
_originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
_currentCharacterEquipment = new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero,
|
||||
() => _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName)?.Address ?? IntPtr.Zero);
|
||||
_isDisposed = false;
|
||||
if (cache != null)
|
||||
{
|
||||
ApplyCharacterData(cache, displayedChatWarning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnDelayedFrameworkUpdate()
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
|
||||
|
||||
var curPlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!)?.Address ?? IntPtr.Zero;
|
||||
if (PlayerCharacter == IntPtr.Zero || PlayerCharacter != curPlayerCharacter)
|
||||
{
|
||||
DisposePlayer();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentCharacterEquipment?.CheckAndUpdateObject();
|
||||
if (_currentCharacterEquipment?.HasUnprocessedUpdate ?? false)
|
||||
{
|
||||
OnPlayerChanged();
|
||||
}
|
||||
|
||||
IsVisible = true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return PlayerNameHash + ":" + PlayerName + ":HasChar " + (PlayerCharacter != IntPtr.Zero);
|
||||
}
|
||||
|
||||
private Task? _penumbraRedrawEventTask;
|
||||
|
||||
private void IpcManagerOnPenumbraRedrawEvent(IntPtr address, int idx)
|
||||
{
|
||||
var player = _dalamudUtil.GetCharacterFromObjectTableByIndex(idx);
|
||||
if (player == null || !string.Equals(player.Name.ToString(), PlayerName, StringComparison.OrdinalIgnoreCase)) return;
|
||||
if (!_penumbraRedrawEventTask?.IsCompleted ?? false) return;
|
||||
|
||||
_penumbraRedrawEventTask = Task.Run(() =>
|
||||
{
|
||||
PlayerCharacter = address;
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
_dalamudUtil.WaitWhileCharacterIsDrawing(PlayerName!, PlayerCharacter, 10000, cts.Token);
|
||||
cts.Dispose();
|
||||
cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
if (RequestedPenumbraRedraw == false)
|
||||
{
|
||||
Logger.Debug("Unauthorized character change detected");
|
||||
ApplyCustomizationData(ObjectKind.Player, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
RequestedPenumbraRedraw = false;
|
||||
Logger.Debug(
|
||||
$"Penumbra Redraw done for {PlayerName}");
|
||||
}
|
||||
cts.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPlayerChanged()
|
||||
{
|
||||
Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}");
|
||||
_currentCharacterEquipment!.HasUnprocessedUpdate = false;
|
||||
if (!RequestedPenumbraRedraw && PlayerCharacter != IntPtr.Zero)
|
||||
{
|
||||
Logger.Debug($"Saving new Glamourer data");
|
||||
_lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter);
|
||||
PluginLog.Error(ex, "Something went wrong during calculation replacements");
|
||||
}
|
||||
Logger.Debug("ModdedPaths calculated, missing files: " + missingFiles.Count);
|
||||
return missingFiles;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using MareSynchronos.Utils;
|
||||
using Action = System.Action;
|
||||
@@ -9,14 +7,11 @@ using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.Delegates;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public delegate void PenumbraRedrawEvent(IntPtr address, int objTblIdx);
|
||||
public delegate void HeelsOffsetChange(float change);
|
||||
public delegate void PenumbraResourceLoadEvent(IntPtr drawObject, string gamePath, string filePath);
|
||||
public delegate void CustomizePlusScaleChange(string? scale);
|
||||
|
||||
public class IpcManager : IDisposable
|
||||
{
|
||||
private readonly ICallGateSubscriber<int> _glamourerApiVersion;
|
||||
@@ -57,10 +52,10 @@ public class IpcManager : IDisposable
|
||||
private readonly ICallGateSubscriber<string?, object> _customizePlusOnScaleUpdate;
|
||||
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private bool inGposeQueueMode = false;
|
||||
private ConcurrentQueue<Action> actionQueue => inGposeQueueMode ? gposeActionQueue : normalQueue;
|
||||
private readonly ConcurrentQueue<Action> normalQueue = new();
|
||||
private readonly ConcurrentQueue<Action> gposeActionQueue = new();
|
||||
private bool _inGposeQueueMode = false;
|
||||
private ConcurrentQueue<Action> ActionQueue => _inGposeQueueMode ? _gposeActionQueue : _normalQueue;
|
||||
private readonly ConcurrentQueue<Action> _normalQueue = new();
|
||||
private readonly ConcurrentQueue<Action> _gposeActionQueue = new();
|
||||
|
||||
public IpcManager(DalamudPluginInterface pi, DalamudUtil dalamudUtil)
|
||||
{
|
||||
@@ -121,7 +116,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void HandleGposeActionQueue()
|
||||
{
|
||||
if (gposeActionQueue.TryDequeue(out var action))
|
||||
if (_gposeActionQueue.TryDequeue(out var action))
|
||||
{
|
||||
if (action == null) return;
|
||||
Logger.Debug("Execution action in gpose queue: " + action.Method);
|
||||
@@ -131,7 +126,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
public void ToggleGposeQueueMode(bool on)
|
||||
{
|
||||
inGposeQueueMode = on;
|
||||
_inGposeQueueMode = on;
|
||||
}
|
||||
|
||||
private void PenumbraModSettingChangedHandler()
|
||||
@@ -141,15 +136,15 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void ClearActionQueue()
|
||||
{
|
||||
actionQueue.Clear();
|
||||
gposeActionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
_gposeActionQueue.Clear();
|
||||
}
|
||||
|
||||
private void ResourceLoaded(IntPtr ptr, string arg1, string arg2)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, true, System.Globalization.CultureInfo.InvariantCulture) != 0)
|
||||
if (ptr != IntPtr.Zero && string.Compare(arg1, arg2, ignoreCase: true, System.Globalization.CultureInfo.InvariantCulture) != 0)
|
||||
{
|
||||
PenumbraResourceLoadEvent?.Invoke(ptr, arg1, arg2);
|
||||
}
|
||||
@@ -158,7 +153,7 @@ public class IpcManager : IDisposable
|
||||
|
||||
private void HandleActionQueue()
|
||||
{
|
||||
if (actionQueue.TryDequeue(out var action))
|
||||
if (ActionQueue.TryDequeue(out var action))
|
||||
{
|
||||
if (action == null) return;
|
||||
Logger.Debug("Execution action in queue: " + action.Method);
|
||||
@@ -169,10 +164,10 @@ public class IpcManager : IDisposable
|
||||
public event VoidDelegate? PenumbraModSettingChanged;
|
||||
public event VoidDelegate? PenumbraInitialized;
|
||||
public event VoidDelegate? PenumbraDisposed;
|
||||
public event PenumbraRedrawEvent? PenumbraRedrawEvent;
|
||||
public event HeelsOffsetChange? HeelsOffsetChangeEvent;
|
||||
public event PenumbraResourceLoadEvent? PenumbraResourceLoadEvent;
|
||||
public event CustomizePlusScaleChange? CustomizePlusScaleChange;
|
||||
public event DrawObjectDelegate? PenumbraRedrawEvent;
|
||||
public event FloatDelegate? HeelsOffsetChangeEvent;
|
||||
public event PenumbraFileResourceDelegate? PenumbraResourceLoadEvent;
|
||||
public event StringDelegate? CustomizePlusScaleChange;
|
||||
|
||||
public bool Initialized => CheckPenumbraApi();
|
||||
public bool CheckGlamourerApi()
|
||||
@@ -228,11 +223,11 @@ public class IpcManager : IDisposable
|
||||
Logger.Verbose("Disposing " + nameof(IpcManager));
|
||||
|
||||
int totalSleepTime = 0;
|
||||
while (actionQueue.Count > 0 && totalSleepTime < 2000)
|
||||
while (!ActionQueue.IsEmpty && totalSleepTime < 2000)
|
||||
{
|
||||
Logger.Verbose("Waiting for actionqueue to clear...");
|
||||
HandleActionQueue();
|
||||
System.Threading.Thread.Sleep(16);
|
||||
Thread.Sleep(16);
|
||||
totalSleepTime += 16;
|
||||
}
|
||||
|
||||
@@ -244,7 +239,7 @@ public class IpcManager : IDisposable
|
||||
_dalamudUtil.FrameworkUpdate -= HandleActionQueue;
|
||||
_dalamudUtil.ZoneSwitchEnd -= ClearActionQueue;
|
||||
_dalamudUtil.GposeFrameworkUpdate -= HandleGposeActionQueue;
|
||||
actionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
|
||||
_penumbraGameObjectResourcePathResolved.Dispose();
|
||||
_penumbraDispose.Dispose();
|
||||
@@ -263,7 +258,7 @@ public class IpcManager : IDisposable
|
||||
public void HeelsSetOffsetForPlayer(float offset, IntPtr character)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
@@ -277,7 +272,7 @@ public class IpcManager : IDisposable
|
||||
public void HeelsRestoreOffsetForPlayer(IntPtr character)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
@@ -299,7 +294,7 @@ public class IpcManager : IDisposable
|
||||
public void CustomizePlusSetBodyScale(IntPtr character, string scale)
|
||||
{
|
||||
if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -314,7 +309,7 @@ public class IpcManager : IDisposable
|
||||
public void CustomizePlusRevert(IntPtr character)
|
||||
{
|
||||
if (!CheckCustomizePlusApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -328,7 +323,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyAll(string? customization, IntPtr obj)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(obj);
|
||||
if (gameObj is Character c)
|
||||
@@ -342,7 +337,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyOnlyEquipment(string customization, IntPtr character)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -356,7 +351,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerApplyOnlyCustomization(string customization, IntPtr character)
|
||||
{
|
||||
if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
@@ -393,7 +388,7 @@ public class IpcManager : IDisposable
|
||||
public void GlamourerRevertCharacterCustomization(GameObject character)
|
||||
{
|
||||
if (!CheckGlamourerApi()) return;
|
||||
actionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character));
|
||||
ActionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character));
|
||||
}
|
||||
|
||||
public string PenumbraGetMetaManipulations()
|
||||
@@ -411,7 +406,7 @@ public class IpcManager : IDisposable
|
||||
public void PenumbraRedraw(IntPtr obj)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(obj);
|
||||
if (gameObj != null)
|
||||
@@ -425,13 +420,13 @@ public class IpcManager : IDisposable
|
||||
public void PenumbraRedraw(string actorName)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() => _penumbraRedraw!.Invoke(actorName, RedrawType.Redraw));
|
||||
ActionQueue.Enqueue(() => _penumbraRedraw!.Invoke(actorName, RedrawType.Redraw));
|
||||
}
|
||||
|
||||
public void PenumbraRemoveTemporaryCollection(string characterName)
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var collName = "Mare_" + characterName;
|
||||
Logger.Verbose("Removing temp collection for " + collName);
|
||||
@@ -464,7 +459,7 @@ public class IpcManager : IDisposable
|
||||
{
|
||||
if (!CheckPenumbraApi()) return;
|
||||
|
||||
actionQueue.Enqueue(() =>
|
||||
ActionQueue.Enqueue(() =>
|
||||
{
|
||||
var idx = _dalamudUtil.GetIndexFromObjectTableByName(characterName);
|
||||
if (idx == null)
|
||||
@@ -474,7 +469,7 @@ public class IpcManager : IDisposable
|
||||
var collName = "Mare_" + characterName;
|
||||
var ret = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
||||
Logger.Verbose("Creating Temp Collection " + collName + ", Success: " + ret);
|
||||
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx.Value, true);
|
||||
var retAssign = _penumbraAssignTemporaryCollection.Invoke(collName, idx.Value, c: true);
|
||||
Logger.Verbose("Assigning Temp Collection " + collName + " to index " + idx.Value);
|
||||
foreach (var mod in modPaths)
|
||||
{
|
||||
@@ -511,6 +506,6 @@ public class IpcManager : IDisposable
|
||||
private void PenumbraDispose()
|
||||
{
|
||||
PenumbraDisposed?.Invoke();
|
||||
actionQueue.Clear();
|
||||
ActionQueue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
@@ -17,39 +10,24 @@ public class OnlinePlayerManager : IDisposable
|
||||
{
|
||||
private readonly ApiController _apiController;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly PlayerManager _playerManager;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ConcurrentDictionary<string, CachedPlayer> _onlineCachedPlayers = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, CharacterCacheDto> _temporaryStoredCharacterCache = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<CachedPlayer, CancellationTokenSource> _playerTokenDisposal = new();
|
||||
private readonly ConcurrentDictionary<string, OptionalPluginWarning> _shownWarnings = new(StringComparer.Ordinal);
|
||||
private readonly PairManager _pairManager;
|
||||
|
||||
private List<string> OnlineVisiblePlayerHashes => _onlineCachedPlayers.Select(p => p.Value).Where(p => p.PlayerCharacter != IntPtr.Zero)
|
||||
.Select(p => p.PlayerNameHash).ToList();
|
||||
|
||||
public OnlinePlayerManager(ApiController apiController, DalamudUtil dalamudUtil, IpcManager ipcManager, PlayerManager playerManager, FileCacheManager fileDbManager, Configuration configuration)
|
||||
public OnlinePlayerManager(ApiController apiController, DalamudUtil dalamudUtil, PlayerManager playerManager, FileCacheManager fileDbManager, PairManager pairManager)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(OnlinePlayerManager));
|
||||
|
||||
_apiController = apiController;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_ipcManager = ipcManager;
|
||||
_playerManager = playerManager;
|
||||
_fileDbManager = fileDbManager;
|
||||
_configuration = configuration;
|
||||
_apiController.PairedClientOnline += ApiControllerOnPairedClientOnline;
|
||||
_apiController.PairedClientOffline += ApiControllerOnPairedClientOffline;
|
||||
_apiController.Connected += ApiControllerOnConnected;
|
||||
_apiController.Disconnected += ApiControllerOnDisconnected;
|
||||
_apiController.CharacterReceived += ApiControllerOnCharacterReceived;
|
||||
_pairManager = pairManager;
|
||||
|
||||
_ipcManager.PenumbraDisposed += IpcManagerOnPenumbraDisposed;
|
||||
_playerManager.PlayerHasChanged += PlayerManagerOnPlayerHasChanged;
|
||||
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
_dalamudUtil.ZoneSwitchStart += DalamudUtilOnZoneSwitched;
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
@@ -57,49 +35,9 @@ public class OnlinePlayerManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnZoneSwitched()
|
||||
private void PlayerManagerOnPlayerHasChanged(CharacterData characterCache)
|
||||
{
|
||||
DisposePlayers();
|
||||
}
|
||||
|
||||
private void ApiControllerOnCharacterReceived(object? sender, CharacterReceivedEventArgs e)
|
||||
{
|
||||
if (!_shownWarnings.ContainsKey(e.CharacterNameHash)) _shownWarnings[e.CharacterNameHash] = new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
};
|
||||
if (_onlineCachedPlayers.TryGetValue(e.CharacterNameHash, out var visiblePlayer) && visiblePlayer.IsVisible)
|
||||
{
|
||||
Logger.Debug("Received data and applying to " + e.CharacterNameHash);
|
||||
visiblePlayer.ApplyCharacterData(e.CharacterData, _shownWarnings[e.CharacterNameHash]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("Received data but no fitting character visible for " + e.CharacterNameHash);
|
||||
_temporaryStoredCharacterCache[e.CharacterNameHash] = e.CharacterData;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayerManagerOnPlayerHasChanged(CharacterCacheDto characterCache)
|
||||
{
|
||||
PushCharacterData(OnlineVisiblePlayerHashes);
|
||||
}
|
||||
|
||||
private void ApiControllerOnConnected()
|
||||
{
|
||||
var apiTask = _apiController.UserGetOnlineCharacters();
|
||||
|
||||
Task.WaitAll(apiTask);
|
||||
|
||||
AddInitialPairs(apiTask.Result);
|
||||
|
||||
_playerManager.PlayerHasChanged += PlayerManagerOnPlayerHasChanged;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
PushCharacterData(_pairManager.VisibleUsers);
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
@@ -107,146 +45,53 @@ public class OnlinePlayerManager : IDisposable
|
||||
_dalamudUtil.DelayedFrameworkUpdate += FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
private void IpcManagerOnPenumbraDisposed()
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
DisposePlayers();
|
||||
}
|
||||
|
||||
private void DisposePlayers()
|
||||
{
|
||||
foreach (var kvp in _onlineCachedPlayers)
|
||||
{
|
||||
kvp.Value.DisposePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnDisconnected()
|
||||
{
|
||||
RestoreAllCharacters();
|
||||
_playerManager.PlayerHasChanged -= PlayerManagerOnPlayerHasChanged;
|
||||
}
|
||||
|
||||
public void AddInitialPairs(List<string> apiTaskResult)
|
||||
{
|
||||
_onlineCachedPlayers.Clear();
|
||||
foreach (var hash in apiTaskResult)
|
||||
{
|
||||
_onlineCachedPlayers.TryAdd(hash, CreateCachedPlayer(hash));
|
||||
}
|
||||
Logger.Verbose("Online and paired users: " + string.Join(Environment.NewLine, _onlineCachedPlayers.Select(k => k.Key)));
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(OnlinePlayerManager));
|
||||
|
||||
RestoreAllCharacters();
|
||||
|
||||
_apiController.PairedClientOnline -= ApiControllerOnPairedClientOnline;
|
||||
_apiController.PairedClientOffline -= ApiControllerOnPairedClientOffline;
|
||||
_apiController.Disconnected -= ApiControllerOnDisconnected;
|
||||
_apiController.Connected -= ApiControllerOnConnected;
|
||||
|
||||
_ipcManager.PenumbraDisposed -= ApiControllerOnDisconnected;
|
||||
|
||||
_playerManager.PlayerHasChanged -= PlayerManagerOnPlayerHasChanged;
|
||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
_dalamudUtil.ZoneSwitchStart -= DalamudUtilOnZoneSwitched;
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= FrameworkOnUpdate;
|
||||
}
|
||||
|
||||
private void RestoreAllCharacters()
|
||||
{
|
||||
DisposePlayers();
|
||||
_onlineCachedPlayers.Clear();
|
||||
}
|
||||
|
||||
private void ApiControllerOnPairedClientOffline(string charHash)
|
||||
{
|
||||
Logger.Debug("Player offline: " + charHash);
|
||||
RemovePlayer(charHash);
|
||||
}
|
||||
|
||||
private void ApiControllerOnPairedClientOnline(string charHash)
|
||||
{
|
||||
Logger.Debug("Player online: " + charHash);
|
||||
AddPlayer(charHash);
|
||||
return;
|
||||
}
|
||||
|
||||
private void AddPlayer(string characterNameHash)
|
||||
{
|
||||
if (_onlineCachedPlayers.TryGetValue(characterNameHash, out var cachedPlayer))
|
||||
{
|
||||
PushCharacterData(new List<string>() { characterNameHash });
|
||||
_playerTokenDisposal.TryGetValue(cachedPlayer, out var cancellationTokenSource);
|
||||
cancellationTokenSource?.Cancel();
|
||||
return;
|
||||
}
|
||||
_onlineCachedPlayers.TryAdd(characterNameHash, CreateCachedPlayer(characterNameHash));
|
||||
}
|
||||
|
||||
private void RemovePlayer(string characterHash)
|
||||
{
|
||||
if (!_onlineCachedPlayers.TryGetValue(characterHash, out var cachedPlayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cachedPlayer.DisposePlayer();
|
||||
_onlineCachedPlayers.TryRemove(characterHash, out _);
|
||||
}
|
||||
|
||||
private void FrameworkOnUpdate()
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return;
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_apiController.IsConnected) return;
|
||||
|
||||
var playerCharacters = _dalamudUtil.GetPlayerCharacters();
|
||||
var onlinePairs = _pairManager.OnlineUserPairs;
|
||||
foreach (var pChar in playerCharacters)
|
||||
{
|
||||
var hashedName = Crypto.GetHash256(pChar);
|
||||
if (_onlineCachedPlayers.TryGetValue(hashedName, out var existingPlayer) && !string.IsNullOrEmpty(existingPlayer.PlayerName))
|
||||
{
|
||||
existingPlayer.IsVisible = true;
|
||||
continue;
|
||||
}
|
||||
var pair = _pairManager.FindPair(pChar);
|
||||
if (pair == null) continue;
|
||||
|
||||
if (existingPlayer != null)
|
||||
{
|
||||
_temporaryStoredCharacterCache.TryRemove(hashedName, out var cache);
|
||||
if (!_shownWarnings.ContainsKey(hashedName)) _shownWarnings[hashedName] = new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configuration.DisableOptionalPluginWarnings,
|
||||
};
|
||||
existingPlayer.InitializePlayer(pChar.Address, pChar.Name.ToString(), cache, _shownWarnings[hashedName]);
|
||||
}
|
||||
pair.InitializePair(pChar.Address, pChar.Name.ToString());
|
||||
}
|
||||
|
||||
var newlyVisiblePlayers = _onlineCachedPlayers.Select(v => v.Value)
|
||||
.Where(p => p.PlayerCharacter != IntPtr.Zero && p.IsVisible && !p.WasVisible).Select(p => p.PlayerNameHash)
|
||||
var newlyVisiblePlayers = onlinePairs.Select(v => v.CachedPlayer)
|
||||
.Where(p => p != null && p.PlayerCharacter != IntPtr.Zero && p.IsVisible && !p.WasVisible).Select(p => (UserDto)p!.OnlineUser)
|
||||
.ToList();
|
||||
if (newlyVisiblePlayers.Any())
|
||||
{
|
||||
Logger.Verbose("Has new visible players, pushing character data");
|
||||
PushCharacterData(newlyVisiblePlayers);
|
||||
PushCharacterData(newlyVisiblePlayers.Select(c => c.User).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
private void PushCharacterData(List<string> visiblePlayers)
|
||||
private void PushCharacterData(List<UserData> visiblePlayers)
|
||||
{
|
||||
if (visiblePlayers.Any() && _playerManager.LastCreatedCharacterData != null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData,
|
||||
visiblePlayers).ConfigureAwait(false);
|
||||
await _apiController.PushCharacterData(_playerManager.LastCreatedCharacterData, visiblePlayers).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private CachedPlayer CreateCachedPlayer(string hashedName)
|
||||
{
|
||||
return new CachedPlayer(hashedName, _ipcManager, _apiController, _dalamudUtil, _fileDbManager);
|
||||
}
|
||||
}
|
||||
329
MareSynchronos/Managers/PairManager.cs
Normal file
329
MareSynchronos/Managers/PairManager.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Comparer;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class PairManager : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<UserData, Pair> _allClientPairs = new(UserDataComparer.Instance);
|
||||
private readonly ConcurrentDictionary<GroupData, GroupFullInfoDto> _allGroups = new(GroupDataComparer.Instance);
|
||||
private readonly CachedPlayerFactory _cachedPlayerFactory;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly PairFactory _pairFactory;
|
||||
private readonly UiBuilder _uiBuilder;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
|
||||
public PairManager(CachedPlayerFactory cachedPlayerFactory, DalamudUtil dalamudUtil, PairFactory pairFactory, UiBuilder uiBuilder, ConfigurationService configurationService)
|
||||
{
|
||||
_cachedPlayerFactory = cachedPlayerFactory;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_pairFactory = pairFactory;
|
||||
_uiBuilder = uiBuilder;
|
||||
_configurationService = configurationService;
|
||||
_dalamudUtil.ZoneSwitchStart += DalamudUtilOnZoneSwitched;
|
||||
_dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_directPairsInternal = DirectPairsLazy();
|
||||
_groupPairsInternal = GroupPairsLazy();
|
||||
}
|
||||
|
||||
private void RecreateLazy()
|
||||
{
|
||||
_directPairsInternal = DirectPairsLazy();
|
||||
_groupPairsInternal = GroupPairsLazy();
|
||||
}
|
||||
|
||||
private Lazy<List<Pair>> _directPairsInternal;
|
||||
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> _groupPairsInternal;
|
||||
public Dictionary<GroupFullInfoDto, List<Pair>> GroupPairs => _groupPairsInternal.Value;
|
||||
public List<Pair> DirectPairs => _directPairsInternal.Value;
|
||||
|
||||
private Lazy<List<Pair>> DirectPairsLazy() => new(() => _allClientPairs.Select(k => k.Value).Where(k => k.UserPair != null).ToList());
|
||||
private Lazy<Dictionary<GroupFullInfoDto, List<Pair>>> GroupPairsLazy()
|
||||
{
|
||||
return new Lazy<Dictionary<GroupFullInfoDto, List<Pair>>>(() =>
|
||||
{
|
||||
Dictionary<GroupFullInfoDto, List<Pair>> outDict = new();
|
||||
foreach (var group in _allGroups)
|
||||
{
|
||||
outDict[group.Value] = _allClientPairs.Select(p => p.Value).Where(p => p.GroupPair.Any(g => GroupDataComparer.Instance.Equals(group.Key, g.Key.Group))).ToList();
|
||||
}
|
||||
return outDict;
|
||||
});
|
||||
}
|
||||
|
||||
public List<Pair> OnlineUserPairs => _allClientPairs.Where(p => !string.IsNullOrEmpty(p.Value.PlayerNameHash)).Select(p => p.Value).ToList();
|
||||
public List<UserData> VisibleUsers => _allClientPairs.Where(p => p.Value.CachedPlayer != null && p.Value.CachedPlayer.IsVisible).Select(p => p.Key).ToList();
|
||||
|
||||
public void AddGroup(GroupFullInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group] = dto;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void RemoveGroup(GroupData data)
|
||||
{
|
||||
_allGroups.TryRemove(data, out _);
|
||||
foreach (var item in _allClientPairs.ToList())
|
||||
{
|
||||
foreach (var grpPair in item.Value.GroupPair.Select(k => k.Key).ToList())
|
||||
{
|
||||
if (GroupDataComparer.Instance.Equals(grpPair.Group, data))
|
||||
{
|
||||
_allClientPairs[item.Key].GroupPair.Remove(grpPair);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_allClientPairs[item.Key].HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(item.Key, out _);
|
||||
}
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void AddGroupPair(GroupPairFullInfoDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) _allClientPairs[dto.User] = _pairFactory.Create();
|
||||
|
||||
var group = _allGroups[dto.Group];
|
||||
_allClientPairs[dto.User].GroupPair[group] = dto;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void AddUserPair(UserPairDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) _allClientPairs[dto.User] = _pairFactory.Create();
|
||||
|
||||
_allClientPairs[dto.User].UserPair = dto;
|
||||
_allClientPairs[dto.User].ApplyLastReceivedData();
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void ClearPairs()
|
||||
{
|
||||
Logger.Debug("Clearing all Pairs");
|
||||
DisposePairs();
|
||||
_allClientPairs.Clear();
|
||||
_allGroups.Clear();
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate;
|
||||
_dalamudUtil.ZoneSwitchStart -= DalamudUtilOnZoneSwitched;
|
||||
DisposePairs();
|
||||
}
|
||||
|
||||
public void DisposePairs()
|
||||
{
|
||||
Logger.Debug("Disposing all Pairs");
|
||||
foreach (var item in _allClientPairs)
|
||||
{
|
||||
item.Value.CachedPlayer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Pair? FindPair(PlayerCharacter? pChar)
|
||||
{
|
||||
if (pChar == null) return null;
|
||||
var hash = pChar.GetHash256();
|
||||
return OnlineUserPairs.Find(p => string.Equals(p.PlayerNameHash, hash, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public void MarkPairOffline(UserData user)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(user, out var pair))
|
||||
{
|
||||
pair.CachedPlayer?.Dispose();
|
||||
pair.CachedPlayer = null;
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkPairOnline(OnlineUserIdentDto dto, ApiController controller)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto);
|
||||
|
||||
if (_allClientPairs[dto.User].CachedPlayer != null) return;
|
||||
|
||||
if (_configurationService.Current.ShowOnlineNotifications)
|
||||
{
|
||||
var pair = _allClientPairs[dto.User];
|
||||
if (_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs && pair.UserPair != null || !_configurationService.Current.ShowOnlineNotificationsOnlyForIndividualPairs)
|
||||
{
|
||||
_uiBuilder.AddNotification(string.Empty, "[Mare Synchronos] " + (pair.GetNote() ?? pair.UserData.AliasOrUID) + " is now online", Dalamud.Interface.Internal.Notifications.NotificationType.Info, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
_allClientPairs[dto.User].CachedPlayer?.Dispose();
|
||||
_allClientPairs[dto.User].CachedPlayer = _cachedPlayerFactory.Create(dto, controller);
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
public void ReceiveCharaData(OnlineUserCharaDataDto dto)
|
||||
{
|
||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto.User);
|
||||
|
||||
var pair = _allClientPairs[dto.User];
|
||||
if (!pair.PlayerName.IsNullOrEmpty())
|
||||
{
|
||||
pair.ApplyData(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
_allClientPairs[dto.User].LastReceivedCharacterData = dto.CharaData;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveGroupPair(GroupPairDto dto)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
pair.GroupPair.Remove(group);
|
||||
|
||||
if (!pair.HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(dto.User, out _);
|
||||
}
|
||||
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUserPair(UserDto dto)
|
||||
{
|
||||
if (_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
pair.UserPair = null;
|
||||
if (!pair.HasAnyConnection())
|
||||
{
|
||||
_allClientPairs.TryRemove(dto.User, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
pair.ApplyLastReceivedData();
|
||||
}
|
||||
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
if (!_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
throw new InvalidOperationException("No such pair for " + dto);
|
||||
}
|
||||
|
||||
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
|
||||
|
||||
pair.UserPair.OtherPermissions = dto.Permissions;
|
||||
if (!pair.UserPair.OtherPermissions.IsPaired())
|
||||
{
|
||||
pair.ApplyLastReceivedData();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSelfPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
if (!_allClientPairs.TryGetValue(dto.User, out var pair))
|
||||
{
|
||||
throw new InvalidOperationException("No such pair for " + dto);
|
||||
}
|
||||
|
||||
if (pair.UserPair == null) throw new InvalidOperationException("No direct pair for " + dto);
|
||||
|
||||
pair.UserPair.OwnPermissions = dto.Permissions;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnDelayedFrameworkUpdate()
|
||||
{
|
||||
foreach (var player in _allClientPairs.Select(p => p.Value).Where(p => p.CachedPlayer != null && p.CachedPlayer.IsVisible).ToList())
|
||||
{
|
||||
if (!player.CachedPlayer!.CheckExistence())
|
||||
{
|
||||
player.CachedPlayer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnZoneSwitched()
|
||||
{
|
||||
DisposePairs();
|
||||
}
|
||||
|
||||
public void SetGroupInfo(GroupInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group].Group = dto.Group;
|
||||
_allGroups[dto.Group].Owner = dto.Owner;
|
||||
_allGroups[dto.Group].GroupPermissions = dto.GroupPermissions;
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupPermissions(GroupPermissionDto dto)
|
||||
{
|
||||
var prevPermissions = _allGroups[dto.Group].GroupPermissions;
|
||||
_allGroups[dto.Group].GroupPermissions = dto.Permissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.Permissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.Permissions.IsDisableSounds())
|
||||
{
|
||||
RecreateLazy();
|
||||
var group = _allGroups[dto.Group];
|
||||
GroupPairs[group].ForEach(p => p.ApplyLastReceivedData());
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupPairUserPermissions(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
var prevPermissions = _allClientPairs[dto.User].GroupPair[group].GroupUserPermissions;
|
||||
_allClientPairs[dto.User].GroupPair[group].GroupUserPermissions = dto.GroupPairPermissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.GroupPairPermissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.GroupPairPermissions.IsDisableSounds())
|
||||
{
|
||||
_allClientPairs[dto.User].ApplyLastReceivedData();
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupUserPermissions(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
var prevPermissions = _allGroups[dto.Group].GroupUserPermissions;
|
||||
_allGroups[dto.Group].GroupUserPermissions = dto.GroupPairPermissions;
|
||||
if (prevPermissions.IsDisableAnimations() != dto.GroupPairPermissions.IsDisableAnimations()
|
||||
|| prevPermissions.IsDisableSounds() != dto.GroupPairPermissions.IsDisableSounds())
|
||||
{
|
||||
RecreateLazy();
|
||||
var group = _allGroups[dto.Group];
|
||||
GroupPairs[group].ForEach(p => p.ApplyLastReceivedData());
|
||||
}
|
||||
RecreateLazy();
|
||||
}
|
||||
|
||||
internal void SetGroupStatusInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
_allGroups[dto.Group].GroupUserInfo = dto.GroupUserInfo;
|
||||
}
|
||||
|
||||
internal void SetGroupPairStatusInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
var group = _allGroups[dto.Group];
|
||||
_allClientPairs[dto.User].GroupPair[group].GroupPairStatusInfo = dto.GroupUserInfo;
|
||||
RecreateLazy();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,17 @@
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.UI;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.Delegates;
|
||||
#if DEBUG
|
||||
using Newtonsoft.Json;
|
||||
#endif
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public delegate void PlayerHasChanged(CharacterCacheDto characterCache);
|
||||
|
||||
public class PlayerManager : IDisposable
|
||||
{
|
||||
@@ -28,15 +22,15 @@ public class PlayerManager : IDisposable
|
||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
||||
private readonly SettingsUi _settingsUi;
|
||||
private readonly IpcManager _ipcManager;
|
||||
public event PlayerHasChanged? PlayerHasChanged;
|
||||
public CharacterCacheDto? LastCreatedCharacterData { get; private set; }
|
||||
public CharacterData PermanentDataCache { get; private set; } = new();
|
||||
private readonly Dictionary<ObjectKind, Func<bool>> objectKindsToUpdate = new();
|
||||
public event CharacterDataDelegate? PlayerHasChanged;
|
||||
public API.Data.CharacterData? LastCreatedCharacterData { get; private set; }
|
||||
public Models.CharacterData PermanentDataCache { get; private set; } = new();
|
||||
private readonly Dictionary<ObjectKind, Func<bool>> _objectKindsToUpdate = new();
|
||||
|
||||
private CancellationTokenSource? _playerChangedCts = new();
|
||||
private CancellationTokenSource _transientUpdateCts = new();
|
||||
|
||||
private List<PlayerRelatedObject> playerRelatedObjects = new();
|
||||
private readonly List<PlayerRelatedObject> _playerRelatedObjects = new();
|
||||
|
||||
public unsafe PlayerManager(ApiController apiController, IpcManager ipcManager,
|
||||
CharacterDataFactory characterDataFactory, DalamudUtil dalamudUtil, TransientResourceManager transientResourceManager,
|
||||
@@ -66,7 +60,7 @@ public class PlayerManager : IDisposable
|
||||
ApiControllerOnConnected();
|
||||
}
|
||||
|
||||
playerRelatedObjects = new List<PlayerRelatedObject>()
|
||||
_playerRelatedObjects = new List<PlayerRelatedObject>()
|
||||
{
|
||||
new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero, () => _dalamudUtil.PlayerPointer),
|
||||
new PlayerRelatedObject(ObjectKind.MinionOrMount, IntPtr.Zero, IntPtr.Zero, () => (IntPtr)((Character*)_dalamudUtil.PlayerPointer)->CompanionObject),
|
||||
@@ -77,12 +71,12 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void DalamudUtilOnFrameworkUpdate()
|
||||
{
|
||||
_transientResourceManager.PlayerRelatedPointers = playerRelatedObjects.Select(f => f.CurrentAddress).ToArray();
|
||||
_transientResourceManager.PlayerRelatedPointers = _playerRelatedObjects.Select(f => f.CurrentAddress).ToArray();
|
||||
}
|
||||
|
||||
public void HandleTransientResourceLoad(IntPtr gameObj)
|
||||
public void HandleTransientResourceLoad(IntPtr gameObj, int idx)
|
||||
{
|
||||
foreach (var obj in playerRelatedObjects)
|
||||
foreach (var obj in _playerRelatedObjects)
|
||||
{
|
||||
if (obj.Address == gameObj && !obj.HasUnprocessedUpdate)
|
||||
{
|
||||
@@ -105,7 +99,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void HeelsOffsetChanged(float change)
|
||||
{
|
||||
var player = playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
var player = _playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && LastCreatedCharacterData.HeelsOffset != change && !player.IsProcessing)
|
||||
{
|
||||
Logger.Debug("Heels offset changed to " + change);
|
||||
@@ -116,8 +110,8 @@ public class PlayerManager : IDisposable
|
||||
private void CustomizePlusChanged(string? change)
|
||||
{
|
||||
change ??= string.Empty;
|
||||
var player = playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && LastCreatedCharacterData.CustomizePlusData != change && !player.IsProcessing)
|
||||
var player = _playerRelatedObjects.First(f => f.ObjectKind == ObjectKind.Player);
|
||||
if (LastCreatedCharacterData != null && !string.Equals(LastCreatedCharacterData.CustomizePlusData, change, StringComparison.Ordinal) && !player.IsProcessing)
|
||||
{
|
||||
Logger.Debug("CustomizePlus data changed to " + change);
|
||||
player.HasTransientsUpdate = true;
|
||||
@@ -146,8 +140,8 @@ public class PlayerManager : IDisposable
|
||||
{
|
||||
if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return;
|
||||
|
||||
playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject());
|
||||
if (playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && !c.IsProcessing))
|
||||
_playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject());
|
||||
if (_playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && !c.IsProcessing))
|
||||
{
|
||||
OnPlayerOrAttachedObjectsChanged();
|
||||
}
|
||||
@@ -167,9 +161,9 @@ public class PlayerManager : IDisposable
|
||||
_ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
|
||||
}
|
||||
|
||||
private async Task<CharacterCacheDto?> CreateFullCharacterCacheDto(CancellationToken token)
|
||||
private async Task<API.Data.CharacterData?> CreateFullCharacterCacheDto(CancellationToken token)
|
||||
{
|
||||
foreach (var unprocessedObject in playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList())
|
||||
foreach (var unprocessedObject in _playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList())
|
||||
{
|
||||
Logger.Verbose("Building Cache for " + unprocessedObject.ObjectKind);
|
||||
PermanentDataCache = _characterDataFactory.BuildCharacterData(PermanentDataCache, unprocessedObject, token);
|
||||
@@ -194,7 +188,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
Logger.Verbose("Cache creation complete");
|
||||
|
||||
var cache = PermanentDataCache.ToCharacterCacheDto();
|
||||
var cache = PermanentDataCache.ToAPI();
|
||||
//Logger.Verbose(JsonConvert.SerializeObject(cache, Formatting.Indented));
|
||||
return cache;
|
||||
}
|
||||
@@ -203,7 +197,7 @@ public class PlayerManager : IDisposable
|
||||
{
|
||||
Logger.Verbose("RedrawEvent for addr " + address);
|
||||
|
||||
foreach (var item in playerRelatedObjects)
|
||||
foreach (var item in _playerRelatedObjects)
|
||||
{
|
||||
if (address == item.Address)
|
||||
{
|
||||
@@ -212,7 +206,7 @@ public class PlayerManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
if (playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && (!c.IsProcessing || (c.IsProcessing && c.DoNotSendUpdate))))
|
||||
if (_playerRelatedObjects.Any(c => (c.HasUnprocessedUpdate || c.HasTransientsUpdate) && (!c.IsProcessing || (c.IsProcessing && c.DoNotSendUpdate))))
|
||||
{
|
||||
OnPlayerOrAttachedObjectsChanged();
|
||||
}
|
||||
@@ -220,7 +214,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
private void OnPlayerOrAttachedObjectsChanged()
|
||||
{
|
||||
var unprocessedObjects = playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList();
|
||||
var unprocessedObjects = _playerRelatedObjects.Where(c => c.HasUnprocessedUpdate || c.HasTransientsUpdate).ToList();
|
||||
foreach (var unprocessedObject in unprocessedObjects)
|
||||
{
|
||||
unprocessedObject.IsProcessing = true;
|
||||
@@ -253,7 +247,7 @@ public class PlayerManager : IDisposable
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
CharacterCacheDto? cacheDto = null;
|
||||
API.Data.CharacterData? cacheDto = null;
|
||||
try
|
||||
{
|
||||
_periodicFileScanner.HaltScan("Character creation");
|
||||
@@ -278,15 +272,13 @@ public class PlayerManager : IDisposable
|
||||
//Logger.Verbose(json);
|
||||
#endif
|
||||
|
||||
if ((LastCreatedCharacterData?.GetHashCode() ?? 0) == cacheDto.GetHashCode())
|
||||
if (string.Equals(LastCreatedCharacterData?.DataHash.Value ?? string.Empty, cacheDto.DataHash.Value, StringComparison.Ordinal))
|
||||
{
|
||||
Logger.Debug("Not sending data, already sent");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastCreatedCharacterData = cacheDto;
|
||||
}
|
||||
|
||||
LastCreatedCharacterData = cacheDto;
|
||||
|
||||
if (_apiController.IsConnected && !token.IsCancellationRequested && !doNotSendUpdate)
|
||||
{
|
||||
|
||||
168
MareSynchronos/Managers/ServerConfigurationManager.cs
Normal file
168
MareSynchronos/Managers/ServerConfigurationManager.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public class ServerConfigurationManager
|
||||
{
|
||||
private readonly Dictionary<JwtCache, string> _tokenDictionary = new();
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
|
||||
public string CurrentApiUrl => string.IsNullOrEmpty(_configService.Current.CurrentServer) ? ApiController.MainServiceUri : _configService.Current.CurrentServer;
|
||||
public ServerStorage? CurrentServer => (_configService.Current.ServerStorage.ContainsKey(CurrentApiUrl) ? _configService.Current.ServerStorage[CurrentApiUrl] : null);
|
||||
|
||||
public ServerConfigurationManager(ConfigurationService configService, DalamudUtil dalamudUtil)
|
||||
{
|
||||
_configService = configService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
}
|
||||
|
||||
public bool HasValidConfig()
|
||||
{
|
||||
return CurrentServer != null && (CurrentServer?.Authentications.Any() ?? false);
|
||||
}
|
||||
|
||||
public string[] GetServerApiUrls()
|
||||
{
|
||||
return _configService.Current.ServerStorage.Keys.ToArray();
|
||||
}
|
||||
|
||||
public string[] GetServerNames()
|
||||
{
|
||||
return _configService.Current.ServerStorage.Values.Select(v => v.ServerName).ToArray();
|
||||
}
|
||||
|
||||
public ServerStorage GetServerByIndex(int idx)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _configService.Current.ServerStorage.ElementAt(idx).Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_configService.Current.CurrentServer = ApiController.MainServiceUri;
|
||||
if (!_configService.Current.ServerStorage.ContainsKey(ApiController.MainServer))
|
||||
{
|
||||
_configService.Current.ServerStorage.Add(_configService.Current.CurrentServer, new ServerStorage() { ServerUri = ApiController.MainServiceUri, ServerName = ApiController.MainServer });
|
||||
}
|
||||
_configService.Save();
|
||||
return CurrentServer!;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCurrentServerIndex()
|
||||
{
|
||||
return Array.IndexOf(_configService.Current.ServerStorage.Keys.ToArray(), CurrentApiUrl);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
public void SelectServer(int idx)
|
||||
{
|
||||
_configService.Current.CurrentServer = GetServerByIndex(idx).ServerUri;
|
||||
CurrentServer!.FullPause = false;
|
||||
Save();
|
||||
}
|
||||
|
||||
public string? GetSecretKey(int serverIdx = -1)
|
||||
{
|
||||
ServerStorage? currentServer;
|
||||
currentServer = serverIdx == -1 ? CurrentServer : GetServerByIndex(serverIdx);
|
||||
Save();
|
||||
if (currentServer == null)
|
||||
{
|
||||
currentServer = new();
|
||||
Save();
|
||||
}
|
||||
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
if (!currentServer.Authentications.Any() && currentServer.SecretKeys.Any())
|
||||
{
|
||||
currentServer.Authentications.Add(new Authentication()
|
||||
{
|
||||
CharacterName = charaName,
|
||||
WorldId = worldId,
|
||||
SecretKeyIdx = currentServer.SecretKeys.Last().Key,
|
||||
});
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
var auth = currentServer.Authentications.Find(f => string.Equals(f.CharacterName, charaName, StringComparison.Ordinal) && f.WorldId == worldId);
|
||||
if (auth == null) return null;
|
||||
|
||||
if (currentServer.SecretKeys.TryGetValue(auth.SecretKeyIdx, out var secretKey))
|
||||
{
|
||||
return secretKey.Key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string? GetToken()
|
||||
{
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
var secretKey = GetSecretKey();
|
||||
if (secretKey == null) return null;
|
||||
if (_tokenDictionary.TryGetValue(new JwtCache(CurrentApiUrl, charaName, worldId, secretKey), out var token))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SaveToken(string token)
|
||||
{
|
||||
var charaName = _dalamudUtil.PlayerName;
|
||||
var worldId = _dalamudUtil.WorldId;
|
||||
var secretKey = GetSecretKey();
|
||||
if (string.IsNullOrEmpty(secretKey)) throw new InvalidOperationException("No secret key set");
|
||||
_tokenDictionary[new JwtCache(CurrentApiUrl, charaName, worldId, secretKey)] = token;
|
||||
}
|
||||
|
||||
internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1, bool addLastSecretKey = false)
|
||||
{
|
||||
if (serverSelectionIndex == -1) serverSelectionIndex = GetCurrentServerIndex();
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Add(new Authentication()
|
||||
{
|
||||
CharacterName = _dalamudUtil.PlayerName,
|
||||
WorldId = _dalamudUtil.WorldId,
|
||||
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1,
|
||||
});
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void AddEmptyCharacterToServer(int serverSelectionIndex)
|
||||
{
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Add(new Authentication());
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void RemoveCharacterFromServer(int serverSelectionIndex, Authentication item)
|
||||
{
|
||||
var server = GetServerByIndex(serverSelectionIndex);
|
||||
server.Authentications.Remove(item);
|
||||
}
|
||||
|
||||
internal void AddServer(ServerStorage serverStorage)
|
||||
{
|
||||
_configService.Current.ServerStorage[serverStorage.ServerUri] = serverStorage;
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
internal void DeleteServer(ServerStorage selectedServer)
|
||||
{
|
||||
_configService.Current.ServerStorage.Remove(selectedServer.ServerUri);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,70 @@
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.Delegates;
|
||||
using MareSynchronos.Factories;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MareSynchronos.Managers;
|
||||
|
||||
public delegate void TransientResourceLoadedEvent(IntPtr drawObject);
|
||||
|
||||
public class TransientResourceManager : IDisposable
|
||||
{
|
||||
private readonly IpcManager manager;
|
||||
private readonly DalamudUtil dalamudUtil;
|
||||
private readonly string configurationDirectory;
|
||||
|
||||
public event TransientResourceLoadedEvent? TransientResourceLoaded;
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
public event DrawObjectDelegate? TransientResourceLoaded;
|
||||
public IntPtr[] PlayerRelatedPointers = Array.Empty<IntPtr>();
|
||||
private readonly string[] FileTypesToHandle = new[] { "tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk" };
|
||||
private string PersistentDataCache => Path.Combine(configurationDirectory, "PersistentTransientData.lst");
|
||||
private readonly string[] _fileTypesToHandle = new[] { "tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp", "shpk" };
|
||||
[Obsolete]
|
||||
private string PersistentDataCache => Path.Combine(_configurationService.ConfigurationDirectory, "PersistentTransientData.lst");
|
||||
private string PlayerPersistentDataKey => _dalamudUtil.PlayerName + "_" + _dalamudUtil.WorldId;
|
||||
|
||||
private ConcurrentDictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
|
||||
private ConcurrentDictionary<ObjectKind, HashSet<FileReplacement>> SemiTransientResources { get; } = new();
|
||||
public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil, FileReplacementFactory fileReplacementFactory, string configurationDirectory)
|
||||
public TransientResourceManager(IpcManager manager, ConfigurationService configurationService, DalamudUtil dalamudUtil, FileReplacementFactory fileReplacementFactory)
|
||||
{
|
||||
manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent;
|
||||
manager.PenumbraModSettingChanged += Manager_PenumbraModSettingChanged;
|
||||
this.manager = manager;
|
||||
this.dalamudUtil = dalamudUtil;
|
||||
this.configurationDirectory = configurationDirectory;
|
||||
_ipcManager = manager;
|
||||
_configurationService = configurationService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
dalamudUtil.FrameworkUpdate += DalamudUtil_FrameworkUpdate;
|
||||
dalamudUtil.ClassJobChanged += DalamudUtil_ClassJobChanged;
|
||||
// migrate obsolete data to new format
|
||||
if (File.Exists(PersistentDataCache))
|
||||
{
|
||||
var persistentEntities = File.ReadAllLines(PersistentDataCache);
|
||||
SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet<FileReplacement>());
|
||||
var persistentEntities = File.ReadAllLines(PersistentDataCache).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey] = persistentEntities;
|
||||
_configurationService.Save();
|
||||
File.Delete(PersistentDataCache);
|
||||
}
|
||||
|
||||
SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet<FileReplacement>());
|
||||
if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var linesInConfig))
|
||||
{
|
||||
int restored = 0;
|
||||
foreach (var line in persistentEntities)
|
||||
foreach (var file in linesInConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileReplacement = fileReplacementFactory.Create();
|
||||
fileReplacement.ResolvePath(line);
|
||||
fileReplacement.ResolvePath(file);
|
||||
if (fileReplacement.HasFileReplacement)
|
||||
{
|
||||
Logger.Debug("Loaded persistent transient resource " + line);
|
||||
Logger.Debug("Loaded persistent transient resource " + file);
|
||||
SemiTransientResources[ObjectKind.Player].Add(fileReplacement);
|
||||
restored++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Error during loading persistent transient resource " + line, ex);
|
||||
Logger.Warn("Error during loading persistent transient resource " + file, ex);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Debug($"Restored {restored}/{persistentEntities.Count()} semi persistent resources");
|
||||
}
|
||||
Logger.Debug($"Restored {restored}/{linesInConfig.Count()} semi persistent resources");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +83,7 @@ public class TransientResourceManager : IDisposable
|
||||
return !verified;
|
||||
});
|
||||
if (!successfulValidation)
|
||||
TransientResourceLoaded?.Invoke(dalamudUtil.PlayerPointer);
|
||||
TransientResourceLoaded?.Invoke(_dalamudUtil.PlayerPointer, -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -95,7 +100,7 @@ public class TransientResourceManager : IDisposable
|
||||
{
|
||||
foreach (var item in TransientResources.ToList())
|
||||
{
|
||||
if (!dalamudUtil.IsGameObjectPresent(item.Key))
|
||||
if (!_dalamudUtil.IsGameObjectPresent(item.Key))
|
||||
{
|
||||
Logger.Debug("Object not present anymore: " + item.Key.ToString("X"));
|
||||
TransientResources.TryRemove(item.Key, out _);
|
||||
@@ -133,7 +138,7 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath)
|
||||
{
|
||||
if (!FileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!_fileTypesToHandle.Any(type => gamePath.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -171,7 +176,7 @@ public class TransientResourceManager : IDisposable
|
||||
{
|
||||
TransientResources[gameObject].Add(replacedGamePath);
|
||||
Logger.Debug($"Adding {replacedGamePath} for {gameObject} ({filePath})");
|
||||
TransientResourceLoaded?.Invoke(gameObject);
|
||||
TransientResourceLoaded?.Invoke(gameObject, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,9 +215,9 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
var fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), true);
|
||||
var fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), arg2: true);
|
||||
if (!fileReplacement.HasFileReplacement)
|
||||
fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), false);
|
||||
fileReplacement = createFileReplacement(gamePath.ToLowerInvariant(), arg2: false);
|
||||
if (fileReplacement.HasFileReplacement)
|
||||
{
|
||||
Logger.Debug("Persisting " + gamePath.ToLowerInvariant());
|
||||
@@ -234,22 +239,26 @@ public class TransientResourceManager : IDisposable
|
||||
|
||||
if (objectKind == ObjectKind.Player && SemiTransientResources.TryGetValue(ObjectKind.Player, out var fileReplacements))
|
||||
{
|
||||
File.WriteAllLines(PersistentDataCache, fileReplacements.SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey]
|
||||
= fileReplacements.SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Save();
|
||||
}
|
||||
TransientResources[gameObject].Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
|
||||
manager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
|
||||
dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged;
|
||||
manager.PenumbraModSettingChanged -= Manager_PenumbraModSettingChanged;
|
||||
_dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate;
|
||||
_ipcManager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent;
|
||||
_dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged;
|
||||
_ipcManager.PenumbraModSettingChanged -= Manager_PenumbraModSettingChanged;
|
||||
TransientResources.Clear();
|
||||
SemiTransientResources.Clear();
|
||||
if (SemiTransientResources.ContainsKey(ObjectKind.Player))
|
||||
{
|
||||
File.WriteAllLines(PersistentDataCache, SemiTransientResources[ObjectKind.Player].SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
_configurationService.Current.PlayerPersistentTransientCache[PlayerPersistentDataKey]
|
||||
= SemiTransientResources[ObjectKind.Player].SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
_configurationService.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
MareSynchronos/MareConfiguration/Authentication.cs
Normal file
9
MareSynchronos/MareConfiguration/Authentication.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
[Serializable]
|
||||
public class Authentication
|
||||
{
|
||||
public string CharacterName { get; set; } = string.Empty;
|
||||
public uint WorldId { get; set; } = 0;
|
||||
public int SecretKeyIdx { get; set; } = -1;
|
||||
}
|
||||
149
MareSynchronos/MareConfiguration/Configuration.cs
Normal file
149
MareSynchronos/MareConfiguration/Configuration.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Plugin;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
[Serializable]
|
||||
[Obsolete("Migrated to MareConfig")]
|
||||
public class Configuration : IPluginConfiguration
|
||||
{
|
||||
public int Version { get; set; } = 6;
|
||||
[NonSerialized]
|
||||
private DalamudPluginInterface? _pluginInterface;
|
||||
public Dictionary<string, ServerStorage> ServerStorage { get; set; } = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ ApiController.MainServiceUri, new ServerStorage() { ServerName = ApiController.MainServer, ServerUri = ApiController.MainServiceUri } },
|
||||
};
|
||||
public bool AcceptedAgreement { get; set; } = false;
|
||||
public string CacheFolder { get; set; } = string.Empty;
|
||||
public double MaxLocalCacheInGiB { get; set; } = 20;
|
||||
public bool ReverseUserSort { get; set; } = false;
|
||||
public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
|
||||
public bool FileScanPaused { get; set; } = false;
|
||||
public bool InitialScanComplete { get; set; } = false;
|
||||
public bool FullPause { get; set; } = false;
|
||||
public bool HideInfoMessages { get; set; } = false;
|
||||
public bool DisableOptionalPluginWarnings { get; set; } = false;
|
||||
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
||||
public bool ShowTransferWindow { get; set; } = true;
|
||||
public bool OpenPopupOnAdd { get; set; } = true;
|
||||
public string CurrentServer { get; set; } = string.Empty;
|
||||
|
||||
private string _apiUri = string.Empty;
|
||||
public string ApiUri
|
||||
{
|
||||
get => string.IsNullOrEmpty(_apiUri) ? ApiController.MainServiceUri : _apiUri;
|
||||
set => _apiUri = value;
|
||||
}
|
||||
public Dictionary<string, string> ClientSecret { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, string> CustomServerList { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, Dictionary<string, string>> UidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, Dictionary<string, string>> GidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||
/// <summary>
|
||||
/// Each paired user can have multiple tags. Each tag will create a category, and the user will
|
||||
/// be displayed into that category.
|
||||
/// The dictionary first maps a server URL to a dictionary, and that
|
||||
/// dictionary maps the OtherUID of the <see cref="ClientPairDto"/> to a list of tags.
|
||||
/// </summary>
|
||||
public Dictionary<string, Dictionary<string, List<string>>> UidServerPairedUserTags = new(StringComparer.Ordinal);
|
||||
/// <summary>
|
||||
/// A dictionary that maps a server URL to the tags the user has added for that server.
|
||||
/// </summary>
|
||||
public Dictionary<string, HashSet<string>> ServerAvailablePairTags = new(StringComparer.Ordinal);
|
||||
public HashSet<string> OpenPairTags = new(StringComparer.Ordinal);
|
||||
|
||||
|
||||
// the below exist just to make saving less cumbersome
|
||||
public void Initialize(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
|
||||
if (!Directory.Exists(CacheFolder))
|
||||
{
|
||||
InitialScanComplete = false;
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_pluginInterface!.SavePluginConfig(this);
|
||||
}
|
||||
|
||||
public MareConfig ToMareConfig()
|
||||
{
|
||||
MareConfig newConfig = new();
|
||||
Logger.Info("Migrating Config to MareConfig");
|
||||
|
||||
newConfig.AcceptedAgreement = AcceptedAgreement;
|
||||
newConfig.CacheFolder = CacheFolder;
|
||||
newConfig.MaxLocalCacheInGiB = MaxLocalCacheInGiB;
|
||||
newConfig.ReverseUserSort = ReverseUserSort;
|
||||
newConfig.TimeSpanBetweenScansInSeconds = TimeSpanBetweenScansInSeconds;
|
||||
newConfig.FileScanPaused = FileScanPaused;
|
||||
newConfig.InitialScanComplete = InitialScanComplete;
|
||||
newConfig.HideInfoMessages = HideInfoMessages;
|
||||
newConfig.DisableOptionalPluginWarnings = DisableOptionalPluginWarnings;
|
||||
newConfig.OpenGposeImportOnGposeStart = OpenGposeImportOnGposeStart;
|
||||
newConfig.ShowTransferWindow = ShowTransferWindow;
|
||||
newConfig.OpenPopupOnAdd = OpenPopupOnAdd;
|
||||
newConfig.CurrentServer = ApiUri;
|
||||
|
||||
// create all server storage based on current clientsecret
|
||||
foreach (var secret in ClientSecret)
|
||||
{
|
||||
Logger.Debug("Migrating " + secret.Key);
|
||||
var apiuri = secret.Key;
|
||||
var secretkey = secret.Value;
|
||||
ServerStorage toAdd = new();
|
||||
if (string.Equals(apiuri, ApiController.MainServiceUri, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
toAdd.ServerUri = ApiController.MainServiceUri;
|
||||
toAdd.ServerName = ApiController.MainServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
toAdd.ServerUri = apiuri;
|
||||
if (!CustomServerList.TryGetValue(apiuri, out var serverName)) serverName = apiuri;
|
||||
toAdd.ServerName = serverName;
|
||||
}
|
||||
|
||||
toAdd.SecretKeys[0] = new SecretKey()
|
||||
{
|
||||
FriendlyName = "Auto Migrated Secret Key (" + DateTime.Now.ToString("yyyy-MM-dd") + ")",
|
||||
Key = secretkey,
|
||||
};
|
||||
|
||||
if (GidServerComments.TryGetValue(apiuri, out var gids))
|
||||
{
|
||||
toAdd.GidServerComments = gids;
|
||||
}
|
||||
if (UidServerComments.TryGetValue(apiuri, out var uids))
|
||||
{
|
||||
toAdd.UidServerComments = uids;
|
||||
}
|
||||
if (UidServerPairedUserTags.TryGetValue(apiuri, out var uidtag))
|
||||
{
|
||||
toAdd.UidServerPairedUserTags = uidtag;
|
||||
}
|
||||
if (ServerAvailablePairTags.TryGetValue(apiuri, out var servertag))
|
||||
{
|
||||
toAdd.ServerAvailablePairTags = servertag;
|
||||
}
|
||||
toAdd.OpenPairTags = OpenPairTags;
|
||||
toAdd.FullPause = FullPause;
|
||||
|
||||
newConfig.ServerStorage[apiuri] = toAdd;
|
||||
}
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
MareSynchronos/MareConfiguration/ConfigurationExtensions.cs
Normal file
11
MareSynchronos/MareConfiguration/ConfigurationExtensions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
public static class ConfigurationExtensions
|
||||
{
|
||||
public static bool HasValidSetup(this MareConfig configuration)
|
||||
{
|
||||
return configuration.AcceptedAgreement && configuration.InitialScanComplete
|
||||
&& !string.IsNullOrEmpty(configuration.CacheFolder)
|
||||
&& Directory.Exists(configuration.CacheFolder);
|
||||
}
|
||||
}
|
||||
81
MareSynchronos/MareConfiguration/ConfigurationService.cs
Normal file
81
MareSynchronos/MareConfiguration/ConfigurationService.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Dalamud.Plugin;
|
||||
using MareSynchronos.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
public class ConfigurationService : IDisposable
|
||||
{
|
||||
private const string _configurationName = "Config.json";
|
||||
private string ConfigurationPath => Path.Combine(_pluginInterface.ConfigDirectory.FullName, _configurationName);
|
||||
public string ConfigurationDirectory => _pluginInterface.ConfigDirectory.FullName;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly CancellationTokenSource _periodicCheckCts = new();
|
||||
private DateTime _configLastWriteTime;
|
||||
|
||||
public MareConfig Current { get; private set; }
|
||||
|
||||
public ConfigurationService(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
|
||||
if (pluginInterface.GetPluginConfig() is Configuration oldConfig)
|
||||
{
|
||||
Current = oldConfig.ToMareConfig();
|
||||
File.Move(pluginInterface.ConfigFile.FullName, pluginInterface.ConfigFile.FullName + ".old", overwrite: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = LoadConfig();
|
||||
}
|
||||
|
||||
Save();
|
||||
|
||||
Task.Run(CheckForConfigUpdatesInternal, _periodicCheckCts.Token);
|
||||
}
|
||||
|
||||
private async Task CheckForConfigUpdatesInternal()
|
||||
{
|
||||
while (!_periodicCheckCts.IsCancellationRequested)
|
||||
{
|
||||
var lastWriteTime = GetConfigLastWriteTime();
|
||||
if (lastWriteTime != _configLastWriteTime)
|
||||
{
|
||||
Logger.Debug("Config changed, reloading config");
|
||||
Current = LoadConfig();
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), _periodicCheckCts.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private MareConfig LoadConfig()
|
||||
{
|
||||
MareConfig config;
|
||||
if (!File.Exists(ConfigurationPath))
|
||||
{
|
||||
config = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
config = JsonConvert.DeserializeObject<MareConfig>(File.ReadAllText(ConfigurationPath)) ?? new MareConfig();
|
||||
}
|
||||
|
||||
_configLastWriteTime = GetConfigLastWriteTime();
|
||||
return config;
|
||||
}
|
||||
|
||||
private DateTime GetConfigLastWriteTime() => new FileInfo(ConfigurationPath).LastWriteTimeUtc;
|
||||
|
||||
public void Save()
|
||||
{
|
||||
File.WriteAllText(ConfigurationPath, JsonConvert.SerializeObject(Current, Formatting.Indented));
|
||||
_configLastWriteTime = new FileInfo(ConfigurationPath).LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Save();
|
||||
_periodicCheckCts.Cancel();
|
||||
}
|
||||
}
|
||||
30
MareSynchronos/MareConfiguration/MareConfig.cs
Normal file
30
MareSynchronos/MareConfiguration/MareConfig.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Dalamud.Configuration;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
[Serializable]
|
||||
public class MareConfig : IPluginConfiguration
|
||||
{
|
||||
public int Version { get; set; } = 0;
|
||||
public Dictionary<string, ServerStorage> ServerStorage { get; set; } = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ ApiController.MainServiceUri, new ServerStorage() { ServerName = ApiController.MainServer, ServerUri = ApiController.MainServiceUri } },
|
||||
};
|
||||
public Dictionary<string, HashSet<string>> PlayerPersistentTransientCache { get; set; } = new(StringComparer.Ordinal);
|
||||
public bool AcceptedAgreement { get; set; } = false;
|
||||
public string CacheFolder { get; set; } = string.Empty;
|
||||
public double MaxLocalCacheInGiB { get; set; } = 20;
|
||||
public bool ReverseUserSort { get; set; } = false;
|
||||
public int TimeSpanBetweenScansInSeconds { get; set; } = 30;
|
||||
public bool FileScanPaused { get; set; } = false;
|
||||
public bool InitialScanComplete { get; set; } = false;
|
||||
public bool HideInfoMessages { get; set; } = false;
|
||||
public bool DisableOptionalPluginWarnings { get; set; } = false;
|
||||
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
||||
public bool ShowTransferWindow { get; set; } = true;
|
||||
public bool OpenPopupOnAdd { get; set; } = true;
|
||||
public string CurrentServer { get; set; } = string.Empty;
|
||||
public bool ShowOnlineNotifications { get; set; } = false;
|
||||
public bool ShowOnlineNotificationsOnlyForIndividualPairs { get; set; } = true;
|
||||
}
|
||||
8
MareSynchronos/MareConfiguration/SecretKey.cs
Normal file
8
MareSynchronos/MareConfiguration/SecretKey.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
[Serializable]
|
||||
public class SecretKey
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string FriendlyName { get; set; } = string.Empty;
|
||||
}
|
||||
16
MareSynchronos/MareConfiguration/ServerStorage.cs
Normal file
16
MareSynchronos/MareConfiguration/ServerStorage.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace MareSynchronos.MareConfiguration;
|
||||
|
||||
[Serializable]
|
||||
public class ServerStorage
|
||||
{
|
||||
public string ServerUri { get; set; } = string.Empty;
|
||||
public string ServerName { get; set; } = string.Empty;
|
||||
public List<Authentication> Authentications { get; set; } = new();
|
||||
public Dictionary<string, string> UidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, string> GidServerComments { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<string, List<string>> UidServerPairedUserTags = new(StringComparer.Ordinal);
|
||||
public HashSet<string> ServerAvailablePairTags { get; set; } = new(StringComparer.Ordinal);
|
||||
public HashSet<string> OpenPairTags { get; set; } = new(StringComparer.Ordinal);
|
||||
public Dictionary<int, SecretKey> SecretKeys { get; set; } = new();
|
||||
public bool FullPause { get; set; } = false;
|
||||
}
|
||||
@@ -28,17 +28,18 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.10" />
|
||||
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.4">
|
||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.13">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.1" />
|
||||
<PackageReference Include="Penumbra.Api" Version="1.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.2" />
|
||||
<PackageReference Include="Penumbra.Api" Version="1.0.6" />
|
||||
<PackageReference Include="Penumbra.String" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss:fffZ"))</SourceRevisionId>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -81,9 +82,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="FileCache.db">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="images\icon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Data;
|
||||
|
||||
namespace MareSynchronos.Models;
|
||||
|
||||
@@ -34,10 +32,10 @@ public class CharacterData
|
||||
|
||||
if (!FileReplacements.ContainsKey(objectKind)) FileReplacements.Add(objectKind, new List<FileReplacement>());
|
||||
|
||||
var existingReplacement = FileReplacements[objectKind].SingleOrDefault(f => string.Equals(f.ResolvedPath, fileReplacement.ResolvedPath, System.StringComparison.OrdinalIgnoreCase));
|
||||
var existingReplacement = FileReplacements[objectKind].SingleOrDefault(f => string.Equals(f.ResolvedPath, fileReplacement.ResolvedPath, StringComparison.OrdinalIgnoreCase));
|
||||
if (existingReplacement != null)
|
||||
{
|
||||
existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e, System.StringComparer.OrdinalIgnoreCase)));
|
||||
existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e, StringComparer.OrdinalIgnoreCase)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -45,13 +43,13 @@ public class CharacterData
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterCacheDto ToCharacterCacheDto()
|
||||
public API.Data.CharacterData ToAPI()
|
||||
{
|
||||
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 =>
|
||||
var fileReplacements = FileReplacements.ToDictionary(k => k.Key, k => k.Value.Where(f => f.HasFileReplacement && !f.IsFileSwap).GroupBy(f => f.Hash, StringComparer.OrdinalIgnoreCase).Select(g =>
|
||||
{
|
||||
return new FileReplacementDto()
|
||||
return new FileReplacementData()
|
||||
{
|
||||
GamePaths = g.SelectMany(f => f.GamePaths).Distinct(System.StringComparer.OrdinalIgnoreCase).ToArray(),
|
||||
GamePaths = g.SelectMany(f => f.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(),
|
||||
Hash = g.First().Hash,
|
||||
};
|
||||
}).ToList());
|
||||
@@ -69,20 +67,20 @@ public class CharacterData
|
||||
fileReplacements[item.Key].AddRange(fileSwapsToAdd);
|
||||
}
|
||||
|
||||
return new CharacterCacheDto()
|
||||
return new API.Data.CharacterData()
|
||||
{
|
||||
FileReplacements = fileReplacements,
|
||||
GlamourerData = GlamourerString.ToDictionary(d => d.Key, d => d.Value),
|
||||
ManipulationData = ManipulationString,
|
||||
HeelsOffset = HeelsOffset,
|
||||
CustomizePlusData = CustomizePlusScale
|
||||
CustomizePlusData = CustomizePlusScale,
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
foreach (var fileReplacement in FileReplacements.SelectMany(k => k.Value).OrderBy(a => a.GamePaths[0]))
|
||||
foreach (var fileReplacement in FileReplacements.SelectMany(k => k.Value).OrderBy(a => a.GamePaths[0], StringComparer.Ordinal))
|
||||
{
|
||||
stringBuilder.AppendLine(fileReplacement.ToString());
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using MareSynchronos.API.Data;
|
||||
|
||||
namespace MareSynchronos.Models;
|
||||
|
||||
public class FileReplacement
|
||||
{
|
||||
private readonly FileCacheManager fileDbManager;
|
||||
private readonly IpcManager ipcManager;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly IpcManager _ipcManager;
|
||||
|
||||
public FileReplacement(FileCacheManager fileDbManager, IpcManager ipcManager)
|
||||
{
|
||||
this.fileDbManager = fileDbManager;
|
||||
this.ipcManager = ipcManager;
|
||||
_fileDbManager = fileDbManager;
|
||||
_ipcManager = ipcManager;
|
||||
}
|
||||
|
||||
public bool Computed => IsFileSwap || !HasFileReplacement || !string.IsNullOrEmpty(Hash);
|
||||
|
||||
public List<string> GamePaths { get; set; } = new();
|
||||
|
||||
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => !string.Equals(p, ResolvedPath, System.StringComparison.Ordinal));
|
||||
public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => !string.Equals(p, ResolvedPath, StringComparison.Ordinal));
|
||||
|
||||
public bool IsFileSwap => !Regex.IsMatch(ResolvedPath, @"^[a-zA-Z]:(/|\\)", RegexOptions.ECMAScript) && !string.Equals(GamePaths.First(), ResolvedPath, System.StringComparison.Ordinal);
|
||||
public bool IsFileSwap => !Regex.IsMatch(ResolvedPath, @"^[a-zA-Z]:(/|\\)", RegexOptions.ECMAScript) && !string.Equals(GamePaths[0], ResolvedPath, StringComparison.Ordinal);
|
||||
|
||||
public string Hash { get; private set; } = string.Empty;
|
||||
|
||||
@@ -43,13 +39,13 @@ public class FileReplacement
|
||||
{
|
||||
try
|
||||
{
|
||||
var cache = fileDbManager.GetFileCacheByPath(ResolvedPath)!;
|
||||
var cache = _fileDbManager.GetFileCacheByPath(ResolvedPath)!;
|
||||
Hash = cache.Hash;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Could not set Hash for " + ResolvedPath + ", resetting to original");
|
||||
ResolvedPath = GamePaths.First();
|
||||
Logger.Warn("Could not set Hash for " + ResolvedPath + ", resetting to original", ex);
|
||||
ResolvedPath = GamePaths[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -58,34 +54,34 @@ public class FileReplacement
|
||||
{
|
||||
if (!IsFileSwap)
|
||||
{
|
||||
var cache = fileDbManager.GetFileCacheByPath(ResolvedPath);
|
||||
var cache = _fileDbManager.GetFileCacheByPath(ResolvedPath);
|
||||
if (cache == null)
|
||||
{
|
||||
Logger.Warn("Replacement Failed verification: " + GamePaths.First());
|
||||
Logger.Warn("Replacement Failed verification: " + GamePaths[0]);
|
||||
return false;
|
||||
}
|
||||
Hash = cache.Hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
ResolvePath(GamePaths.First());
|
||||
ResolvePath(GamePaths[0]);
|
||||
|
||||
var success = IsFileSwap;
|
||||
if (!success)
|
||||
{
|
||||
Logger.Warn("FileSwap Failed verification: " + GamePaths.First());
|
||||
Logger.Warn("FileSwap Failed verification: " + GamePaths[0]);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public FileReplacementDto ToFileReplacementDto()
|
||||
public FileReplacementData ToFileReplacementDto()
|
||||
{
|
||||
return new FileReplacementDto
|
||||
return new FileReplacementData
|
||||
{
|
||||
GamePaths = GamePaths.ToArray(),
|
||||
Hash = Hash,
|
||||
FileSwapPath = IsFileSwap ? ResolvedPath : string.Empty
|
||||
FileSwapPath = IsFileSwap ? ResolvedPath : string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,13 +94,13 @@ public class FileReplacement
|
||||
|
||||
internal void ReverseResolvePath(string path)
|
||||
{
|
||||
GamePaths = ipcManager.PenumbraReverseResolvePlayer(path).ToList();
|
||||
GamePaths = _ipcManager.PenumbraReverseResolvePlayer(path).ToList();
|
||||
SetResolvedPath(path);
|
||||
}
|
||||
|
||||
internal void ResolvePath(string path)
|
||||
{
|
||||
GamePaths = new List<string> { path };
|
||||
SetResolvedPath(ipcManager.PenumbraResolvePath(path));
|
||||
SetResolvedPath(_ipcManager.PenumbraResolvePath(path));
|
||||
}
|
||||
}
|
||||
|
||||
3
MareSynchronos/Models/JwtCache.cs
Normal file
3
MareSynchronos/Models/JwtCache.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MareSynchronos.Models;
|
||||
|
||||
public record JwtCache(string ApiUrl, string PlayerName, uint WorldId, string SecretKey);
|
||||
135
MareSynchronos/Models/Pair.cs
Normal file
135
MareSynchronos/Models/Pair.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Comparer;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
|
||||
namespace MareSynchronos.Models;
|
||||
|
||||
public class Pair
|
||||
{
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private OptionalPluginWarning? _pluginWarnings;
|
||||
|
||||
public Pair(ConfigurationService configService, ServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_configService = configService;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
public UserPairDto? UserPair { get; set; }
|
||||
public CachedPlayer? CachedPlayer { get; set; }
|
||||
public API.Data.CharacterData? LastReceivedCharacterData { get; set; }
|
||||
public Dictionary<GroupFullInfoDto, GroupPairFullInfoDto> GroupPair { get; set; } = new(GroupDtoComparer.Instance);
|
||||
public string PlayerNameHash => CachedPlayer?.PlayerNameHash ?? string.Empty;
|
||||
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
|
||||
public UserData UserData => UserPair?.User ?? GroupPair.First().Value.User;
|
||||
public bool IsOnline => CachedPlayer != null;
|
||||
public bool IsVisible => CachedPlayer != null && CachedPlayer.IsVisible;
|
||||
public bool IsPaused => UserPair != null && UserPair.OtherPermissions.IsPaired() ? (UserPair.OtherPermissions.IsPaused() || UserPair.OwnPermissions.IsPaused())
|
||||
: GroupPair.All(p => p.Key.GroupUserPermissions.IsPaused() || p.Value.GroupUserPermissions.IsPaused());
|
||||
|
||||
public string? GetNote()
|
||||
{
|
||||
if (_serverConfigurationManager.CurrentServer!.UidServerComments.TryGetValue(UserData.UID, out string? note))
|
||||
{
|
||||
return string.IsNullOrEmpty(note) ? null : note;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetNote(string note)
|
||||
{
|
||||
_serverConfigurationManager.CurrentServer!.UidServerComments[UserData.UID] = note;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
public bool HasAnyConnection()
|
||||
{
|
||||
return UserPair != null || GroupPair.Any();
|
||||
}
|
||||
|
||||
public void InitializePair(nint address, string name)
|
||||
{
|
||||
if (!PlayerName.IsNullOrEmpty()) return;
|
||||
|
||||
if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized");
|
||||
_pluginWarnings ??= new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configService.Current.DisableOptionalPluginWarnings,
|
||||
};
|
||||
|
||||
CachedPlayer.Initialize(address, name);
|
||||
|
||||
ApplyLastReceivedData();
|
||||
}
|
||||
|
||||
public void ApplyData(OnlineUserCharaDataDto data)
|
||||
{
|
||||
if (CachedPlayer == null) throw new InvalidOperationException("CachedPlayer not initialized");
|
||||
|
||||
if (string.Equals(LastReceivedCharacterData?.DataHash.Value, data.CharaData.DataHash.Value, StringComparison.Ordinal)) return;
|
||||
|
||||
LastReceivedCharacterData = data.CharaData;
|
||||
|
||||
ApplyLastReceivedData();
|
||||
}
|
||||
|
||||
public void ApplyLastReceivedData()
|
||||
{
|
||||
if (CachedPlayer == null) return;
|
||||
if (LastReceivedCharacterData == null) return;
|
||||
|
||||
_pluginWarnings ??= new()
|
||||
{
|
||||
ShownCustomizePlusWarning = _configService.Current.DisableOptionalPluginWarnings,
|
||||
ShownHeelsWarning = _configService.Current.DisableOptionalPluginWarnings,
|
||||
};
|
||||
|
||||
CachedPlayer.ApplyCharacterData(RemoveNotSyncedFiles(LastReceivedCharacterData.DeepClone())!, _pluginWarnings);
|
||||
}
|
||||
|
||||
private API.Data.CharacterData? RemoveNotSyncedFiles(API.Data.CharacterData? data)
|
||||
{
|
||||
Logger.Verbose("Removing not synced files");
|
||||
if (data == null || (UserPair != null && UserPair.OtherPermissions.IsPaired()))
|
||||
{
|
||||
Logger.Verbose("Nothing to remove or user is paired directly");
|
||||
return data;
|
||||
}
|
||||
|
||||
bool disableAnimations = GroupPair.All(pair =>
|
||||
{
|
||||
return pair.Value.GroupUserPermissions.IsDisableAnimations() || pair.Key.GroupPermissions.IsDisableAnimations() || pair.Key.GroupUserPermissions.IsDisableAnimations();
|
||||
});
|
||||
bool disableSounds = GroupPair.All(pair =>
|
||||
{
|
||||
return pair.Value.GroupUserPermissions.IsDisableSounds() || pair.Key.GroupPermissions.IsDisableSounds() || pair.Key.GroupUserPermissions.IsDisableSounds();
|
||||
});
|
||||
|
||||
if (disableAnimations || disableSounds)
|
||||
{
|
||||
Logger.Verbose($"Data cleaned up: Animations disabled: {disableAnimations}, Sounds disabled: {disableSounds}");
|
||||
foreach (var kvp in data.FileReplacements)
|
||||
{
|
||||
if (disableSounds)
|
||||
data.FileReplacements[kvp.Key] = data.FileReplacements[kvp.Key]
|
||||
.Where(f => !f.GamePaths.Any(p => p.EndsWith("scd", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
if (disableAnimations)
|
||||
data.FileReplacements[kvp.Key] = data.FileReplacements[kvp.Key]
|
||||
.Where(f => !f.GamePaths.Any(p => p.EndsWith("tmb", StringComparison.OrdinalIgnoreCase) || p.EndsWith("pap", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using MareSynchronos.API;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using System.Runtime.InteropServices;
|
||||
using MareSynchronos.Utils;
|
||||
using Penumbra.String;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
|
||||
namespace MareSynchronos.Models;
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Plugin;
|
||||
using MareSynchronos.Factories;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState;
|
||||
using System;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.WebAPI;
|
||||
@@ -16,16 +14,16 @@ using Dalamud.Game.ClientState.Conditions;
|
||||
using MareSynchronos.FileCache;
|
||||
using Dalamud.Game.Gui;
|
||||
using MareSynchronos.Export;
|
||||
using Dalamud.Data;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
|
||||
namespace MareSynchronos;
|
||||
|
||||
public sealed class Plugin : IDalamudPlugin
|
||||
{
|
||||
private const string CommandName = "/mare";
|
||||
private const string _commandName = "/mare";
|
||||
private readonly ApiController _apiController;
|
||||
private readonly CommandManager _commandManager;
|
||||
private readonly ChatGui _chatGui;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
||||
private readonly IntroUi _introUi;
|
||||
private readonly IpcManager _ipcManager;
|
||||
@@ -39,49 +37,52 @@ public sealed class Plugin : IDalamudPlugin
|
||||
private readonly DownloadUi _downloadUi;
|
||||
private readonly FileDialogManager _fileDialogManager;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly CompactUi _compactUi;
|
||||
private readonly UiShared _uiSharedComponent;
|
||||
private readonly Dalamud.Localization _localization;
|
||||
private readonly FileReplacementFactory _fileReplacementFactory;
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly GposeUi _gposeUi;
|
||||
private readonly ConfigurationService _configurationService;
|
||||
|
||||
|
||||
public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager,
|
||||
Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition,
|
||||
ChatGui chatGui)
|
||||
public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, DataManager gameData,
|
||||
Framework framework, ObjectTable objectTable, ClientState clientState, Condition condition, ChatGui chatGui)
|
||||
{
|
||||
Logger.Debug("Launching " + Name);
|
||||
_pluginInterface = pluginInterface;
|
||||
_pluginInterface.UiBuilder.DisableGposeUiHide = true;
|
||||
_commandManager = commandManager;
|
||||
_configuration = _pluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
_configuration.Initialize(_pluginInterface);
|
||||
_configuration.Migrate();
|
||||
_configurationService = new(_pluginInterface);
|
||||
|
||||
_localization = new Dalamud.Localization("MareSynchronos.Localization.", "", true);
|
||||
_localization = new Dalamud.Localization("MareSynchronos.Localization.", "", useEmbedded: true);
|
||||
_localization.SetupWithLangCode("en");
|
||||
|
||||
_windowSystem = new WindowSystem("MareSynchronos");
|
||||
|
||||
// those can be initialized outside of game login
|
||||
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition, chatGui);
|
||||
_dalamudUtil = new DalamudUtil(clientState, objectTable, framework, condition, chatGui, gameData);
|
||||
|
||||
_ipcManager = new IpcManager(_pluginInterface, _dalamudUtil);
|
||||
_fileDialogManager = new FileDialogManager();
|
||||
_fileCacheManager = new FileCacheManager(_ipcManager, _configuration, _pluginInterface.ConfigDirectory.FullName);
|
||||
_apiController = new ApiController(_configuration, _dalamudUtil, _fileCacheManager);
|
||||
_periodicFileScanner = new PeriodicFileScanner(_ipcManager, _configuration, _fileCacheManager, _apiController, _dalamudUtil);
|
||||
_fileCacheManager = new FileCacheManager(_ipcManager, _configurationService);
|
||||
_serverConfigurationManager = new ServerConfigurationManager(_configurationService, _dalamudUtil);
|
||||
_pairManager = new PairManager(new CachedPlayerFactory(_ipcManager, _dalamudUtil, _fileCacheManager), _dalamudUtil,
|
||||
new PairFactory(_configurationService, _serverConfigurationManager), _pluginInterface.UiBuilder, _configurationService);
|
||||
_apiController = new ApiController(_configurationService, _dalamudUtil, _fileCacheManager, _pairManager, _serverConfigurationManager);
|
||||
_periodicFileScanner = new PeriodicFileScanner(_ipcManager, _configurationService, _fileCacheManager, _apiController, _dalamudUtil);
|
||||
_fileReplacementFactory = new FileReplacementFactory(_fileCacheManager, _ipcManager);
|
||||
_mareCharaFileManager = new(_fileCacheManager, _ipcManager, _configuration, _dalamudUtil);
|
||||
_mareCharaFileManager = new(_fileCacheManager, _ipcManager, _configurationService, _dalamudUtil);
|
||||
|
||||
_uiSharedComponent =
|
||||
new UiShared(_ipcManager, _apiController, _periodicFileScanner, _fileDialogManager, _configuration, _dalamudUtil, _pluginInterface, _localization);
|
||||
_settingsUi = new SettingsUi(_windowSystem, _uiSharedComponent, _configuration, _apiController, _mareCharaFileManager);
|
||||
_compactUi = new CompactUi(_windowSystem, _uiSharedComponent, _configuration, _apiController);
|
||||
_gposeUi = new GposeUi(_windowSystem, _mareCharaFileManager, _dalamudUtil, _fileDialogManager, _configuration);
|
||||
new UiShared(_ipcManager, _apiController, _periodicFileScanner, _fileDialogManager, _configurationService, _dalamudUtil, _pluginInterface, _localization, _serverConfigurationManager);
|
||||
_settingsUi = new SettingsUi(_windowSystem, _uiSharedComponent, _configurationService, _mareCharaFileManager, _pairManager, _serverConfigurationManager);
|
||||
_compactUi = new CompactUi(_windowSystem, _uiSharedComponent, _configurationService, _apiController, _pairManager, _serverConfigurationManager);
|
||||
_gposeUi = new GposeUi(_windowSystem, _mareCharaFileManager, _dalamudUtil, _fileDialogManager, _configurationService);
|
||||
|
||||
_introUi = new IntroUi(_windowSystem, _uiSharedComponent, _configuration, _periodicFileScanner);
|
||||
_introUi = new IntroUi(_windowSystem, _uiSharedComponent, _configurationService, _periodicFileScanner, _serverConfigurationManager);
|
||||
_settingsUi.SwitchToIntroUi += () =>
|
||||
{
|
||||
_introUi.IsOpen = true;
|
||||
@@ -99,8 +100,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
{
|
||||
_settingsUi.Toggle();
|
||||
};
|
||||
_downloadUi = new DownloadUi(_windowSystem, _configuration, _apiController, _uiSharedComponent);
|
||||
|
||||
_downloadUi = new DownloadUi(_windowSystem, _configurationService, _apiController, _uiSharedComponent);
|
||||
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
@@ -112,12 +112,13 @@ public sealed class Plugin : IDalamudPlugin
|
||||
}
|
||||
|
||||
public string Name => "Mare Synchronos";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + Name);
|
||||
_apiController?.Dispose();
|
||||
|
||||
_commandManager.RemoveHandler(CommandName);
|
||||
_commandManager.RemoveHandler(_commandName);
|
||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
|
||||
@@ -128,6 +129,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
_compactUi?.Dispose();
|
||||
_gposeUi?.Dispose();
|
||||
|
||||
_pairManager.Dispose();
|
||||
_periodicFileScanner?.Dispose();
|
||||
_fileCacheManager?.Dispose();
|
||||
_playerManager?.Dispose();
|
||||
@@ -135,6 +137,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
_ipcManager?.Dispose();
|
||||
_transientResourceManager?.Dispose();
|
||||
_dalamudUtil.Dispose();
|
||||
_configurationService?.Dispose();
|
||||
Logger.Debug("Shut down");
|
||||
}
|
||||
|
||||
@@ -145,16 +148,15 @@ public sealed class Plugin : IDalamudPlugin
|
||||
|
||||
_pluginInterface.UiBuilder.Draw += Draw;
|
||||
_pluginInterface.UiBuilder.OpenConfigUi += OpenUi;
|
||||
_commandManager.AddHandler(CommandName, new CommandInfo(OnCommand)
|
||||
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
|
||||
{
|
||||
HelpMessage = "Opens the Mare Synchronos UI"
|
||||
HelpMessage = "Opens the Mare Synchronos UI",
|
||||
});
|
||||
|
||||
if (!_configuration.HasValidSetup())
|
||||
if (!_configurationService.Current.HasValidSetup() || !_serverConfigurationManager.HasValidConfig())
|
||||
{
|
||||
_introUi.IsOpen = true;
|
||||
_configuration.FullPause = false;
|
||||
_configuration.Save();
|
||||
_compactUi.IsOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,7 +172,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
_transientResourceManager?.Dispose();
|
||||
_pluginInterface.UiBuilder.Draw -= Draw;
|
||||
_pluginInterface.UiBuilder.OpenConfigUi -= OpenUi;
|
||||
_commandManager.RemoveHandler(CommandName);
|
||||
_commandManager.RemoveHandler(_commandName);
|
||||
}
|
||||
|
||||
public void ReLaunchCharacterManager()
|
||||
@@ -191,17 +193,18 @@ public sealed class Plugin : IDalamudPlugin
|
||||
|
||||
try
|
||||
{
|
||||
_transientResourceManager = new TransientResourceManager(_ipcManager, _dalamudUtil, _fileReplacementFactory, _pluginInterface.ConfigDirectory.FullName);
|
||||
Logger.Debug("Launching Managers");
|
||||
_transientResourceManager = new TransientResourceManager(_ipcManager, _configurationService, _dalamudUtil, _fileReplacementFactory);
|
||||
var characterCacheFactory =
|
||||
new CharacterDataFactory(_dalamudUtil, _ipcManager, _transientResourceManager, _fileReplacementFactory);
|
||||
_playerManager = new PlayerManager(_apiController, _ipcManager,
|
||||
characterCacheFactory, _dalamudUtil, _transientResourceManager, _periodicFileScanner, _settingsUi);
|
||||
_characterCacheManager = new OnlinePlayerManager(_apiController,
|
||||
_dalamudUtil, _ipcManager, _playerManager, _fileCacheManager, _configuration);
|
||||
_dalamudUtil, _playerManager, _fileCacheManager, _pairManager);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Debug(ex.Message);
|
||||
Logger.Warn(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,23 +225,24 @@ public sealed class Plugin : IDalamudPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
if (splitArgs[0] == "toggle")
|
||||
if (string.Equals(splitArgs[0], "toggle", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (_serverConfigurationManager.CurrentServer == null) return;
|
||||
var fullPause = splitArgs.Length > 1 ? splitArgs[1] switch
|
||||
{
|
||||
"on" => false,
|
||||
"off" => true,
|
||||
_ => !_configuration.FullPause,
|
||||
} : !_configuration.FullPause;
|
||||
_ => !_serverConfigurationManager.CurrentServer.FullPause,
|
||||
} : !_serverConfigurationManager.CurrentServer.FullPause;
|
||||
|
||||
if (fullPause != _configuration.FullPause)
|
||||
if (fullPause != _serverConfigurationManager.CurrentServer.FullPause)
|
||||
{
|
||||
_configuration.FullPause = fullPause;
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.CurrentServer.FullPause = fullPause;
|
||||
_serverConfigurationManager.Save();
|
||||
_ = _apiController.CreateConnections();
|
||||
}
|
||||
}
|
||||
else if (splitArgs[0] == "gpose")
|
||||
else if (string.Equals(splitArgs[0], "gpose", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_gposeUi.Toggle();
|
||||
}
|
||||
@@ -246,7 +250,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
|
||||
private void OpenUi()
|
||||
{
|
||||
if (_configuration.HasValidSetup())
|
||||
if (_configurationService.Current.HasValidSetup())
|
||||
_compactUi.Toggle();
|
||||
else
|
||||
_introUi.Toggle();
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using Dalamud.Interface;
|
||||
@@ -11,7 +8,12 @@ using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Delegates;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.UI.Components;
|
||||
using MareSynchronos.UI.Handlers;
|
||||
using MareSynchronos.Utils;
|
||||
@@ -22,7 +24,9 @@ namespace MareSynchronos.UI;
|
||||
public class CompactUi : Window, IDisposable
|
||||
{
|
||||
private readonly ApiController _apiController;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverManager;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly TagHandler _tagHandler;
|
||||
public readonly Dictionary<string, bool> ShowUidForEntry = new(StringComparer.Ordinal);
|
||||
private readonly UiShared _uiShared;
|
||||
@@ -37,14 +41,14 @@ public class CompactUi : Window, IDisposable
|
||||
private readonly Stopwatch _timeout = new();
|
||||
private bool _buttonState;
|
||||
|
||||
public float TransferPartHeight = 0;
|
||||
public float _windowContentWidth = 0;
|
||||
private bool _showModalForUserAddition = false;
|
||||
private bool _wasOpen = false;
|
||||
public float TransferPartHeight;
|
||||
public float WindowContentWidth;
|
||||
private bool _showModalForUserAddition;
|
||||
private bool _wasOpen;
|
||||
|
||||
private bool showSyncShells = false;
|
||||
private GroupPanel groupPanel;
|
||||
private ClientPairDto? _lastAddedUser;
|
||||
private bool _showSyncShells;
|
||||
private readonly GroupPanel _groupPanel;
|
||||
private UserPairDto? _lastAddedUser;
|
||||
private string _lastAddedUserComment = string.Empty;
|
||||
|
||||
private readonly SelectGroupForPairUi _selectGroupForPairUi;
|
||||
@@ -52,7 +56,8 @@ public class CompactUi : Window, IDisposable
|
||||
private readonly PairGroupsUi _pairGroupsUi;
|
||||
|
||||
public CompactUi(WindowSystem windowSystem,
|
||||
UiShared uiShared, Configuration configuration, ApiController apiController) : base("###MareSynchronosMainUI")
|
||||
UiShared uiShared, ConfigurationService configService, ApiController apiController, PairManager pairManager,
|
||||
ServerConfigurationManager serverManager) : base("###MareSynchronosMainUI")
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
@@ -77,13 +82,15 @@ public class CompactUi : Window, IDisposable
|
||||
|
||||
_windowSystem = windowSystem;
|
||||
_uiShared = uiShared;
|
||||
_configuration = configuration;
|
||||
_configService = configService;
|
||||
_apiController = apiController;
|
||||
_tagHandler = new(_configuration);
|
||||
_pairManager = pairManager;
|
||||
_serverManager = serverManager;
|
||||
_tagHandler = new(_serverManager);
|
||||
|
||||
groupPanel = new(this, uiShared, configuration, apiController);
|
||||
_selectGroupForPairUi = new(_tagHandler, configuration);
|
||||
_selectPairsForGroupUi = new(_tagHandler, configuration);
|
||||
_groupPanel = new(this, uiShared, _pairManager, _serverManager);
|
||||
_selectGroupForPairUi = new(_tagHandler);
|
||||
_selectPairsForGroupUi = new(_tagHandler);
|
||||
_pairGroupsUi = new(_tagHandler, DrawPairedClient, apiController, _selectPairsForGroupUi);
|
||||
|
||||
_uiShared.GposeStart += UiShared_GposeStart;
|
||||
@@ -109,7 +116,7 @@ public class CompactUi : Window, IDisposable
|
||||
IsOpen = false;
|
||||
}
|
||||
|
||||
public event SwitchUi? OpenSettingsUi;
|
||||
public event VoidDelegate? OpenSettingsUi;
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(CompactUi));
|
||||
@@ -120,14 +127,14 @@ public class CompactUi : Window, IDisposable
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
_windowContentWidth = UiShared.GetWindowContentRegionWidth();
|
||||
WindowContentWidth = UiShared.GetWindowContentRegionWidth();
|
||||
UiShared.DrawWithID("header", DrawUIDHeader);
|
||||
ImGui.Separator();
|
||||
UiShared.DrawWithID("serverstatus", DrawServerStatus);
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
var hasShownSyncShells = showSyncShells;
|
||||
var hasShownSyncShells = _showSyncShells;
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (!hasShownSyncShells)
|
||||
@@ -136,7 +143,7 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), new Vector2((UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
|
||||
{
|
||||
showSyncShells = false;
|
||||
_showSyncShells = false;
|
||||
}
|
||||
if (!hasShownSyncShells)
|
||||
{
|
||||
@@ -154,7 +161,7 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
if (ImGui.Button(FontAwesomeIcon.UserFriends.ToIconString(), new Vector2((UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X) / 2, 30 * ImGuiHelpers.GlobalScale)))
|
||||
{
|
||||
showSyncShells = true;
|
||||
_showSyncShells = true;
|
||||
}
|
||||
if (hasShownSyncShells)
|
||||
{
|
||||
@@ -171,17 +178,17 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
UiShared.DrawWithID("syncshells", groupPanel.DrawSyncshells);
|
||||
UiShared.DrawWithID("syncshells", _groupPanel.DrawSyncshells);
|
||||
|
||||
}
|
||||
ImGui.Separator();
|
||||
UiShared.DrawWithID("transfers", DrawTransfers);
|
||||
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
||||
UiShared.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_apiController.PairedClients, ShowUidForEntry));
|
||||
UiShared.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs, ShowUidForEntry));
|
||||
UiShared.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw(ShowUidForEntry));
|
||||
}
|
||||
|
||||
if (_configuration.OpenPopupOnAdd && _apiController.LastAddedUser != null)
|
||||
if (_configService.Current.OpenPopupOnAdd && _apiController.LastAddedUser != null)
|
||||
{
|
||||
_lastAddedUser = _apiController.LastAddedUser;
|
||||
_apiController.LastAddedUser = null;
|
||||
@@ -198,15 +205,14 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
var uid = string.IsNullOrEmpty(_lastAddedUser!.VanityUID) ? _lastAddedUser.OtherUID : _lastAddedUser.VanityUID;
|
||||
UiShared.TextWrapped($"You have successfully added {uid}. Set a local note for the user in the field below:");
|
||||
ImGui.InputTextWithHint("##noteforuser", $"Note for {uid}", ref _lastAddedUserComment, 100);
|
||||
UiShared.TextWrapped($"You have successfully added {_lastAddedUser.User.AliasOrUID}. Set a local note for the user in the field below:");
|
||||
ImGui.InputTextWithHint("##noteforuser", $"Note for {_lastAddedUser.User.AliasOrUID}", ref _lastAddedUserComment, 100);
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Save, "Save Note"))
|
||||
{
|
||||
_configuration.SetCurrentServerUidComment(_lastAddedUser.OtherUID, _lastAddedUserComment);
|
||||
_serverManager.CurrentServer!.UidServerComments[_lastAddedUser.User.UID] = _lastAddedUserComment;
|
||||
_serverManager.Save();
|
||||
_lastAddedUser = null;
|
||||
_lastAddedUserComment = string.Empty;
|
||||
_configuration.Save();
|
||||
_showModalForUserAddition = false;
|
||||
}
|
||||
}
|
||||
@@ -221,21 +227,27 @@ public class CompactUi : Window, IDisposable
|
||||
EditUserComment = string.Empty;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
private void DrawAddPair()
|
||||
{
|
||||
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonSize.X);
|
||||
ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20);
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - buttonSize.X);
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||
var canAdd = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal));
|
||||
if (!canAdd)
|
||||
{
|
||||
if (_apiController.PairedClients.All(w => !string.Equals(w.OtherUID, _pairToAdd, StringComparison.Ordinal)))
|
||||
ImGuiComponents.DisabledButton(FontAwesomeIcon.Plus);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||
{
|
||||
_ = _apiController.UserAddPair(_pairToAdd);
|
||||
_ = _apiController.UserAddPair(new(new(_pairToAdd)));
|
||||
_pairToAdd = string.Empty;
|
||||
}
|
||||
UiShared.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd));
|
||||
}
|
||||
UiShared.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd));
|
||||
|
||||
ImGuiHelpers.ScaledDummy(2);
|
||||
}
|
||||
@@ -244,12 +256,12 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
var buttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.ArrowUp);
|
||||
var playButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Play);
|
||||
if (!_configuration.ReverseUserSort)
|
||||
if (!_configService.Current.ReverseUserSort)
|
||||
{
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.ArrowDown))
|
||||
{
|
||||
_configuration.ReverseUserSort = true;
|
||||
_configuration.Save();
|
||||
_configService.Current.ReverseUserSort = true;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.AttachToolTip("Sort by name descending");
|
||||
}
|
||||
@@ -257,28 +269,30 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.ArrowUp))
|
||||
{
|
||||
_configuration.ReverseUserSort = false;
|
||||
_configuration.Save();
|
||||
_configService.Current.ReverseUserSort = false;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.AttachToolTip("Sort by name ascending");
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
||||
var users = GetFilteredUsers().ToList();
|
||||
var users = GetFilteredUsers();
|
||||
var userCount = users.Count;
|
||||
|
||||
var spacing = userCount > 0
|
||||
? playButtonSize.X + ImGui.GetStyle().ItemSpacing.X * 2
|
||||
: ImGui.GetStyle().ItemSpacing.X;
|
||||
|
||||
ImGui.SetNextItemWidth(_windowContentWidth - buttonSize.X - spacing);
|
||||
ImGui.SetNextItemWidth(WindowContentWidth - buttonSize.X - spacing);
|
||||
ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref _characterOrCommentFilter, 255);
|
||||
|
||||
if (userCount == 0) return;
|
||||
ImGui.SameLine();
|
||||
|
||||
var pausedUsers = users.Where(u => u.IsPaused).ToList();
|
||||
var resumedUsers = users.Where(u => !u.IsPaused).ToList();
|
||||
var pausedUsers = users.Where(u => u.UserPair!.OwnPermissions.IsPaused() && u.UserPair.OtherPermissions.IsPaired()).ToList();
|
||||
var resumedUsers = users.Where(u => !u.UserPair!.OwnPermissions.IsPaused() && u.UserPair.OtherPermissions.IsPaired()).ToList();
|
||||
|
||||
if (!pausedUsers.Any() && !resumedUsers.Any()) return;
|
||||
ImGui.SameLine();
|
||||
|
||||
switch (_buttonState)
|
||||
{
|
||||
@@ -306,10 +320,11 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
Logger.Debug(users.Count.ToString());
|
||||
foreach (var entry in users)
|
||||
{
|
||||
_ = _apiController.UserChangePairPauseStatus(entry.OtherUID, !entry.IsPaused);
|
||||
var perm = entry.UserPair!.OwnPermissions;
|
||||
perm.SetPaused(!perm.IsPaused());
|
||||
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, perm));
|
||||
}
|
||||
|
||||
_timeout.Start();
|
||||
@@ -326,49 +341,72 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPairedClient(ClientPairDto entry)
|
||||
private void DrawPairedClient(Pair entry)
|
||||
{
|
||||
var pauseIcon = entry.IsPaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||
if (entry.UserPair == null) return;
|
||||
|
||||
var pauseIcon = entry.UserPair!.OwnPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||
var pauseIconSize = UiShared.GetIconButtonSize(pauseIcon);
|
||||
var barButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||
var entryUID = string.IsNullOrEmpty(entry.VanityUID) ? entry.OtherUID : entry.VanityUID;
|
||||
var entryUID = entry.UserData.AliasOrUID;
|
||||
var textSize = ImGui.CalcTextSize(entryUID);
|
||||
var originalY = ImGui.GetCursorPosY();
|
||||
var buttonSizes = pauseIconSize.Y + barButtonSize.Y;
|
||||
var spacingX = ImGui.GetStyle().ItemSpacing.X;
|
||||
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth();
|
||||
|
||||
|
||||
var textPos = originalY + pauseIconSize.Y / 2 - textSize.Y / 2;
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
if (!entry.IsSynced)
|
||||
FontAwesomeIcon presenceIcon;
|
||||
FontAwesomeIcon connectionIcon;
|
||||
string connectionText = string.Empty;
|
||||
string presenceText = string.Empty;
|
||||
Vector4 presenceColor;
|
||||
Vector4 connectionColor;
|
||||
if (!(entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired()))
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(FontAwesomeIcon.ArrowUp.ToIconString(), ImGuiColors.DalamudRed);
|
||||
ImGui.PopFont();
|
||||
|
||||
UiShared.AttachToolTip(entryUID + " has not added you back");
|
||||
connectionIcon = FontAwesomeIcon.ArrowUp;
|
||||
connectionText = entryUID + " has not added you back";
|
||||
connectionColor = ImGuiColors.DalamudRed;
|
||||
presenceIcon = FontAwesomeIcon.Question;
|
||||
presenceColor = ImGuiColors.DalamudGrey;
|
||||
presenceText = entryUID + " online status is unknown (not paired)";
|
||||
}
|
||||
else if (entry.IsPaused || entry.IsPausedFromOthers)
|
||||
else if (entry.UserPair!.OwnPermissions.IsPaused() || entry.UserPair!.OtherPermissions.IsPaused())
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow);
|
||||
ImGui.PopFont();
|
||||
|
||||
UiShared.AttachToolTip("Pairing status with " + entryUID + " is paused");
|
||||
connectionIcon = FontAwesomeIcon.PauseCircle;
|
||||
connectionText = "Pairing status with " + entryUID + " is paused";
|
||||
connectionColor = ImGuiColors.DalamudYellow;
|
||||
presenceIcon = FontAwesomeIcon.Question;
|
||||
presenceColor = ImGuiColors.DalamudGrey;
|
||||
presenceText = entryUID + " online status is unknown (paused)";
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(FontAwesomeIcon.Check.ToIconString(), ImGuiColors.ParsedGreen);
|
||||
ImGui.PopFont();
|
||||
|
||||
UiShared.AttachToolTip("You are paired with " + entryUID);
|
||||
connectionIcon = FontAwesomeIcon.Check;
|
||||
connectionText = "You are paired with " + entryUID;
|
||||
connectionColor = ImGuiColors.ParsedGreen;
|
||||
presenceIcon = entry.IsVisible ? FontAwesomeIcon.Eye : (entry.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink);
|
||||
presenceColor = (entry.IsOnline || entry.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
presenceText = entryUID + " is offline";
|
||||
if (entry.IsOnline && !entry.IsVisible) presenceText = entryUID + " is online";
|
||||
else if (entry.IsOnline && entry.IsVisible) presenceText = entryUID + " is visible: " + entry.PlayerName;
|
||||
}
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(connectionIcon.ToIconString(), connectionColor);
|
||||
ImGui.PopFont();
|
||||
UiShared.AttachToolTip(connectionText);
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(presenceIcon.ToIconString(), presenceColor);
|
||||
ImGui.PopFont();
|
||||
UiShared.AttachToolTip(presenceText);
|
||||
|
||||
var textIsUid = true;
|
||||
ShowUidForEntry.TryGetValue(entry.OtherUID, out var showUidInsteadOfName);
|
||||
if (!showUidInsteadOfName && _configuration.GetCurrentServerUidComments().TryGetValue(entry.OtherUID, out var playerText))
|
||||
ShowUidForEntry.TryGetValue(entry.UserPair!.User.UID, out var showUidInsteadOfName);
|
||||
if (!showUidInsteadOfName && _serverManager.CurrentServer!.UidServerComments.TryGetValue(entry.UserPair!.User.UID, out var playerText))
|
||||
{
|
||||
if (string.IsNullOrEmpty(playerText))
|
||||
{
|
||||
@@ -385,7 +423,7 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (!string.Equals(EditNickEntry, entry.OtherUID, StringComparison.Ordinal))
|
||||
if (!string.Equals(EditNickEntry, entry.UserData.UID, StringComparison.Ordinal))
|
||||
{
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||
@@ -396,22 +434,20 @@ public class CompactUi : Window, IDisposable
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
var prevState = textIsUid;
|
||||
if (ShowUidForEntry.ContainsKey(entry.OtherUID))
|
||||
if (ShowUidForEntry.ContainsKey(entry.UserPair!.User.UID))
|
||||
{
|
||||
prevState = ShowUidForEntry[entry.OtherUID];
|
||||
prevState = ShowUidForEntry[entry.UserPair!.User.UID];
|
||||
}
|
||||
|
||||
ShowUidForEntry[entry.OtherUID] = !prevState;
|
||||
ShowUidForEntry[entry.UserPair!.User.UID] = !prevState;
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
_configuration.SetCurrentServerUidComment(EditNickEntry, EditUserComment);
|
||||
_configuration.Save();
|
||||
EditUserComment = _configuration.GetCurrentServerUidComments().ContainsKey(entry.OtherUID)
|
||||
? _configuration.GetCurrentServerUidComments()[entry.OtherUID]
|
||||
: string.Empty;
|
||||
EditNickEntry = entry.OtherUID;
|
||||
entry.SetNote(EditUserComment);
|
||||
_configService.Save();
|
||||
EditUserComment = entry.GetNote() ?? string.Empty;
|
||||
EditNickEntry = entry.UserPair!.User.UID;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -421,8 +457,8 @@ public class CompactUi : Window, IDisposable
|
||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
||||
if (ImGui.InputTextWithHint("", "Nick/Notes", ref EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
_configuration.SetCurrentServerUidComment(entry.OtherUID, EditUserComment);
|
||||
_configuration.Save();
|
||||
_serverManager.CurrentServer!.UidServerComments[entry.UserPair!.User.UID] = EditUserComment;
|
||||
_serverManager.Save();
|
||||
EditNickEntry = string.Empty;
|
||||
}
|
||||
|
||||
@@ -434,15 +470,17 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
|
||||
// Pause Button
|
||||
if (entry.IsSynced)
|
||||
if (entry.UserPair!.OwnPermissions.IsPaired() && entry.UserPair!.OtherPermissions.IsPaired())
|
||||
{
|
||||
ImGui.SameLine(windowEndX - barButtonSize.X - spacingX - pauseIconSize.X);
|
||||
ImGui.SetCursorPosY(originalY);
|
||||
if (ImGuiComponents.IconButton(pauseIcon))
|
||||
{
|
||||
_ = _apiController.UserChangePairPauseStatus(entry.OtherUID, !entry.IsPaused);
|
||||
var perm = entry.UserPair!.OwnPermissions;
|
||||
perm.SetPaused(!perm.IsPaused());
|
||||
_ = _apiController.UserSetPairPermissions(new(entry.UserData, perm));
|
||||
}
|
||||
UiShared.AttachToolTip(!entry.IsPaused
|
||||
UiShared.AttachToolTip(!entry.UserPair!.OwnPermissions.IsPaused()
|
||||
? "Pause pairing with " + entryUID
|
||||
: "Resume pairing with " + entryUID);
|
||||
}
|
||||
@@ -457,14 +495,14 @@ public class CompactUi : Window, IDisposable
|
||||
}
|
||||
if (ImGui.BeginPopup("User Flyout Menu"))
|
||||
{
|
||||
UiShared.DrawWithID($"buttons-{entry.OtherUID}", () => DrawPairedClientMenu(entry));
|
||||
UiShared.DrawWithID($"buttons-{entry.UserPair!.User.UID}", () => DrawPairedClientMenu(entry));
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPairedClientMenu(ClientPairDto entry)
|
||||
private void DrawPairedClientMenu(Pair entry)
|
||||
{
|
||||
var entryUID = string.IsNullOrEmpty(entry.VanityUID) ? entry.OtherUID : entry.VanityUID;
|
||||
var entryUID = entry.UserData.AliasOrUID;
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups"))
|
||||
{
|
||||
_selectGroupForPairUi.Open(entry);
|
||||
@@ -475,7 +513,7 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
_ = _apiController.UserRemovePair(entry.OtherUID);
|
||||
_ = _apiController.UserRemovePair(new(entry.UserData));
|
||||
}
|
||||
}
|
||||
UiShared.AttachToolTip("Hold CTRL and click to unpair permanently from " + entryUID);
|
||||
@@ -496,32 +534,65 @@ public class CompactUi : Window, IDisposable
|
||||
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - TransferPartHeight - ImGui.GetCursorPosY();
|
||||
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();
|
||||
ImGui.BeginChild("list", new Vector2(WindowContentWidth, ySize), border: false);
|
||||
var visibleUsers = users.Where(u => u.IsVisible && u.UserPair!.OtherPermissions.IsPaired()).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
var onlineUsers = users.Where(u => u.IsOnline && !u.IsVisible && u.UserPair!.OtherPermissions.IsPaired()).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
var offlineUsers = users.Where(u => !u.IsOnline && !u.IsVisible || !u.UserPair!.OtherPermissions.IsPaired()).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
ImGui.BeginChild("list", new Vector2(_windowContentWidth, ySize), false);
|
||||
var allAvailablePairs = users.ToList();
|
||||
var pairsWithoutTags = allAvailablePairs.Where(pair => !_tagHandler.HasAnyTag(pair));
|
||||
_pairGroupsUi.Draw(allAvailablePairs);
|
||||
foreach (var entry in pairsWithoutTags)
|
||||
if (_configService.Current.ReverseUserSort)
|
||||
{
|
||||
UiShared.DrawWithID(entry.OtherUID, () => DrawPairedClient(entry));
|
||||
visibleUsers.Reverse();
|
||||
onlineUsers.Reverse();
|
||||
offlineUsers.Reverse();
|
||||
}
|
||||
|
||||
_pairGroupsUi.Draw(visibleUsers, onlineUsers, offlineUsers);
|
||||
|
||||
visibleUsers = visibleUsers.Where(pair => !_tagHandler.HasAnyTag(pair.UserPair!)).ToList();
|
||||
onlineUsers = onlineUsers.Where(pair => !_tagHandler.HasAnyTag(pair.UserPair!)).ToList();
|
||||
offlineUsers = offlineUsers.Where(pair => !_tagHandler.HasAnyTag(pair.UserPair!)).ToList();
|
||||
|
||||
if (visibleUsers.Any())
|
||||
{
|
||||
ImGui.Text("Visible");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in visibleUsers)
|
||||
{
|
||||
UiShared.DrawWithID(entry.UserData.UID, () => DrawPairedClient(entry));
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineUsers.Any())
|
||||
{
|
||||
ImGui.Text("Online");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in onlineUsers)
|
||||
{
|
||||
UiShared.DrawWithID(entry.UserData.UID, () => DrawPairedClient(entry));
|
||||
}
|
||||
}
|
||||
|
||||
if (offlineUsers.Any())
|
||||
{
|
||||
ImGui.Text("Offline/Unknown");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in offlineUsers)
|
||||
{
|
||||
UiShared.DrawWithID(entry.UserData.UID, () => DrawPairedClient(entry));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IEnumerable<ClientPairDto> GetFilteredUsers()
|
||||
private List<Pair> GetFilteredUsers()
|
||||
{
|
||||
return _apiController.PairedClients.Where(p =>
|
||||
return _pairManager.DirectPairs.Where(p =>
|
||||
{
|
||||
if (_characterOrCommentFilter.IsNullOrEmpty()) return true;
|
||||
_configuration.GetCurrentServerUidComments().TryGetValue(p.OtherUID, out var comment);
|
||||
var uid = p.VanityUID.IsNullOrEmpty() ? p.OtherUID : p.VanityUID;
|
||||
return uid.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ||
|
||||
(comment?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
});
|
||||
return p.UserData.AliasOrUID.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ||
|
||||
(p.GetNote()?.Contains(_characterOrCommentFilter, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private void DrawServerStatus()
|
||||
@@ -565,18 +636,18 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2);
|
||||
}
|
||||
var color = UiShared.GetBoolColor(!_configuration.FullPause);
|
||||
var connectedIcon = !_configuration.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink;
|
||||
var color = UiShared.GetBoolColor(!_serverManager.CurrentServer!.FullPause);
|
||||
var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink;
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
||||
if (ImGuiComponents.IconButton(connectedIcon))
|
||||
{
|
||||
_configuration.FullPause = !_configuration.FullPause;
|
||||
_configuration.Save();
|
||||
_serverManager.CurrentServer.FullPause = !_serverManager.CurrentServer.FullPause;
|
||||
_serverManager.Save();
|
||||
_ = _apiController.CreateConnections();
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
UiShared.AttachToolTip(!_configuration.FullPause ? "Disconnect from " + _apiController.ServerDictionary[_configuration.ApiUri] : "Connect to " + _apiController.ServerDictionary[_configuration.ApiUri]);
|
||||
UiShared.AttachToolTip(!_serverManager.CurrentServer.FullPause ? "Disconnect from " + _serverManager.CurrentServer.ServerName : "Connect to " + _serverManager.CurrentServer.ServerName);
|
||||
}
|
||||
|
||||
private void DrawTransfers()
|
||||
@@ -598,7 +669,7 @@ public class CompactUi : Window, IDisposable
|
||||
ImGui.Text($"{doneUploads}/{totalUploads}");
|
||||
var uploadText = $"({UiShared.ByteToString(totalUploaded)}/{UiShared.ByteToString(totalToUpload)})";
|
||||
var textSize = ImGui.CalcTextSize(uploadText);
|
||||
ImGui.SameLine(_windowContentWidth - textSize.X);
|
||||
ImGui.SameLine(WindowContentWidth - textSize.X);
|
||||
ImGui.Text(uploadText);
|
||||
}
|
||||
else
|
||||
@@ -623,7 +694,7 @@ public class CompactUi : Window, IDisposable
|
||||
var downloadText =
|
||||
$"({UiShared.ByteToString(totalDownloaded)}/{UiShared.ByteToString(totalToDownload)})";
|
||||
var textSize = ImGui.CalcTextSize(downloadText);
|
||||
ImGui.SameLine(_windowContentWidth - textSize.X);
|
||||
ImGui.SameLine(WindowContentWidth - textSize.X);
|
||||
ImGui.Text(downloadText);
|
||||
}
|
||||
else
|
||||
@@ -688,6 +759,8 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
{
|
||||
ServerState.Connecting => "Attempting to connect to the server.",
|
||||
ServerState.Reconnecting => "Connection to server interrupted, attempting to reconnect to the server.",
|
||||
ServerState.Disconnected => "You are currently disconnected from the Mare Synchronos server.",
|
||||
ServerState.Unauthorized => "Server Response: " + _apiController.AuthFailureMessage,
|
||||
ServerState.Offline => "Your selected Mare Synchronos server is currently offline.",
|
||||
@@ -695,6 +768,7 @@ public class CompactUi : Window, IDisposable
|
||||
"Your plugin or the server you are connecting to is out of date. Please update your plugin now. If you already did so, contact the server provider to update their server to the latest version.",
|
||||
ServerState.RateLimited => "You are rate limited for (re)connecting too often. Disconnect, wait 10 minutes and try again.",
|
||||
ServerState.Connected => string.Empty,
|
||||
ServerState.NoSecretKey => "You have no secret key set for this current character. Open the settings and set a secret key. You can reuse one secret key for different characters.",
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
@@ -703,12 +777,15 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
{
|
||||
ServerState.Connecting => ImGuiColors.DalamudYellow,
|
||||
ServerState.Reconnecting => ImGuiColors.DalamudRed,
|
||||
ServerState.Connected => ImGuiColors.ParsedGreen,
|
||||
ServerState.Disconnected => ImGuiColors.DalamudYellow,
|
||||
ServerState.Unauthorized => ImGuiColors.DalamudRed,
|
||||
ServerState.VersionMisMatch => ImGuiColors.DalamudRed,
|
||||
ServerState.Offline => ImGuiColors.DalamudRed,
|
||||
ServerState.RateLimited => ImGuiColors.DalamudYellow,
|
||||
ServerState.NoSecretKey => ImGuiColors.DalamudYellow,
|
||||
_ => ImGuiColors.DalamudRed
|
||||
};
|
||||
}
|
||||
@@ -717,11 +794,14 @@ public class CompactUi : Window, IDisposable
|
||||
{
|
||||
return _apiController.ServerState switch
|
||||
{
|
||||
ServerState.Reconnecting => "Reconnecting",
|
||||
ServerState.Connecting => "Connecting",
|
||||
ServerState.Disconnected => "Disconnected",
|
||||
ServerState.Unauthorized => "Unauthorized",
|
||||
ServerState.VersionMisMatch => "Version mismatch",
|
||||
ServerState.Offline => "Unavailable",
|
||||
ServerState.RateLimited => "Rate Limited",
|
||||
ServerState.NoSecretKey => "No Secret Key",
|
||||
ServerState.Connected => _apiController.UID,
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Components;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.UI.Handlers;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
@@ -12,12 +10,12 @@ namespace MareSynchronos.UI.Components
|
||||
{
|
||||
public class PairGroupsUi
|
||||
{
|
||||
private readonly Action<ClientPairDto> _clientRenderFn;
|
||||
private readonly Action<Pair> _clientRenderFn;
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly SelectPairForGroupUi _selectGroupForPairUi;
|
||||
|
||||
public PairGroupsUi(TagHandler tagHandler, Action<ClientPairDto> clientRenderFn, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi)
|
||||
public PairGroupsUi(TagHandler tagHandler, Action<Pair> clientRenderFn, ApiController apiController, SelectPairForGroupUi selectGroupForPairUi)
|
||||
{
|
||||
_clientRenderFn = clientRenderFn;
|
||||
_tagHandler = tagHandler;
|
||||
@@ -25,30 +23,57 @@ namespace MareSynchronos.UI.Components
|
||||
_selectGroupForPairUi = selectGroupForPairUi;
|
||||
}
|
||||
|
||||
public void Draw(List<ClientPairDto> availablePairs)
|
||||
public void Draw(List<Pair> visibleUsers, List<Pair> onlineUsers, List<Pair> offlineUsers)
|
||||
{
|
||||
// Only render those tags that actually have pairs in them, otherwise
|
||||
// we can end up with a bunch of useless pair groups
|
||||
var tagsWithPairsInThem = _tagHandler.GetAllTagsSorted();
|
||||
foreach (var tag in tagsWithPairsInThem)
|
||||
{
|
||||
UiShared.DrawWithID($"group-{tag}", () => DrawCategory(tag, availablePairs));
|
||||
UiShared.DrawWithID($"group-{tag}", () => DrawCategory(tag, visibleUsers, onlineUsers, offlineUsers));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawCategory(string tag, List<ClientPairDto> availablePairs)
|
||||
public void DrawCategory(string tag, List<Pair> visibleUsers, List<Pair> onlineUsers, List<Pair> offlineUsers)
|
||||
{
|
||||
var otherUidsTaggedWithTag = _tagHandler.GetOtherUidsForTag(tag);
|
||||
var availablePairsInThisTag = availablePairs
|
||||
.Where(pair => otherUidsTaggedWithTag.Contains(pair.OtherUID))
|
||||
var visiblePairsInThisTag = visibleUsers
|
||||
.Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID))
|
||||
.ToList();
|
||||
if (availablePairsInThisTag.Any())
|
||||
var onlinePairsInThisTag = onlineUsers
|
||||
.Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID))
|
||||
.ToList();
|
||||
var offlinePairsInThisTag = offlineUsers
|
||||
.Where(pair => otherUidsTaggedWithTag.Contains(pair.UserData.UID))
|
||||
.ToList();
|
||||
if (visiblePairsInThisTag.Any() || onlinePairsInThisTag.Any() || offlinePairsInThisTag.Any())
|
||||
{
|
||||
DrawName(tag);
|
||||
UiShared.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, availablePairsInThisTag));
|
||||
UiShared.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, visiblePairsInThisTag));
|
||||
if (_tagHandler.IsTagOpen(tag))
|
||||
{
|
||||
DrawPairs(tag, availablePairsInThisTag);
|
||||
ImGui.Indent(20);
|
||||
if (visiblePairsInThisTag.Any())
|
||||
{
|
||||
ImGui.Text("Visible");
|
||||
ImGui.Separator();
|
||||
DrawPairs(tag, visiblePairsInThisTag);
|
||||
}
|
||||
|
||||
if (onlinePairsInThisTag.Any())
|
||||
{
|
||||
ImGui.Text("Online");
|
||||
ImGui.Separator();
|
||||
DrawPairs(tag, onlinePairsInThisTag);
|
||||
}
|
||||
|
||||
if (offlinePairsInThisTag.Any())
|
||||
{
|
||||
ImGui.Text("Offline/Unknown");
|
||||
ImGui.Separator();
|
||||
DrawPairs(tag, offlinePairsInThisTag);
|
||||
}
|
||||
ImGui.Unindent(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,9 +97,9 @@ namespace MareSynchronos.UI.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawButtons(string tag, List<ClientPairDto> availablePairsInThisTag)
|
||||
private void DrawButtons(string tag, List<Pair> availablePairsInThisTag)
|
||||
{
|
||||
var allArePaused = availablePairsInThisTag.All(pair => pair.IsPaused);
|
||||
var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused());
|
||||
var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||
var flyoutMenuX = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars).X;
|
||||
var pauseButtonX = UiShared.GetIconButtonSize(pauseButton).X;
|
||||
@@ -142,43 +167,37 @@ namespace MareSynchronos.UI.Components
|
||||
UiShared.AttachToolTip($"Delete Group {tag} (Will not delete the pairs)" + Environment.NewLine + "Hold CTRL to delete");
|
||||
}
|
||||
|
||||
private void DrawPairs(string tag, List<ClientPairDto> availablePairsInThisCategory)
|
||||
private void DrawPairs(string tag, List<Pair> availablePairsInThisCategory)
|
||||
{
|
||||
ImGui.Separator();
|
||||
// These are all the OtherUIDs that are tagged with this tag
|
||||
availablePairsInThisCategory
|
||||
.ForEach(pair => UiShared.DrawWithID($"tag-{tag}-pair-${pair.OtherUID}", () => DrawPair(pair)));
|
||||
.ForEach(pair => UiShared.DrawWithID($"tag-{tag}-pair-${pair.UserData.UID}", () => _clientRenderFn(pair)));
|
||||
ImGui.Separator();
|
||||
}
|
||||
|
||||
private void DrawPair(ClientPairDto pair)
|
||||
{
|
||||
// This is probably just dumb. Somehow, just setting the cursor position to the icon lenght
|
||||
// does not really push the child rendering further. So we'll just add two whitespaces and call it a day?
|
||||
UiShared.FontText(" ", UiBuilder.DefaultFont);
|
||||
ImGui.SameLine();
|
||||
_clientRenderFn(pair);
|
||||
}
|
||||
|
||||
private void ToggleTagOpen(string tag)
|
||||
{
|
||||
bool open = !_tagHandler.IsTagOpen(tag);
|
||||
_tagHandler.SetTagOpen(tag, open);
|
||||
}
|
||||
|
||||
private void PauseRemainingPairs(List<ClientPairDto> availablePairs)
|
||||
private void PauseRemainingPairs(List<Pair> availablePairs)
|
||||
{
|
||||
foreach (var pairToPause in availablePairs.Where(pair => !pair.IsPaused))
|
||||
foreach (var pairToPause in availablePairs.Where(pair => !pair.UserPair!.OwnPermissions.IsPaused()))
|
||||
{
|
||||
_ = _apiController.UserChangePairPauseStatus(pairToPause.OtherUID, paused: true);
|
||||
var perm = pairToPause.UserPair!.OwnPermissions;
|
||||
perm.SetPaused(paused: true);
|
||||
_ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm));
|
||||
}
|
||||
}
|
||||
|
||||
private void ResumeAllPairs(List<ClientPairDto> availablePairs)
|
||||
private void ResumeAllPairs(List<Pair> availablePairs)
|
||||
{
|
||||
foreach (var pairToPause in availablePairs)
|
||||
{
|
||||
_ = _apiController.UserChangePairPauseStatus(pairToPause.OtherUID, paused: false);
|
||||
var perm = pairToPause.UserPair!.OwnPermissions;
|
||||
perm.SetPaused(paused: false);
|
||||
_ = _apiController.UserSetPairPermissions(new(pairToPause.UserData, perm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.UI.Handlers;
|
||||
|
||||
namespace MareSynchronos.UI.Components;
|
||||
@@ -20,7 +19,7 @@ public class SelectGroupForPairUi
|
||||
/// The group UI is always open for a specific pair. This defines which pair the UI is open for.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private ClientPairDto? _pair;
|
||||
private Pair? _pair;
|
||||
|
||||
/// <summary>
|
||||
/// For the add category option, this stores the currently typed in tag name
|
||||
@@ -28,17 +27,15 @@ public class SelectGroupForPairUi
|
||||
private string _tagNameToAdd = "";
|
||||
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly Configuration _configuration;
|
||||
|
||||
public SelectGroupForPairUi(TagHandler tagHandler, Configuration configuration)
|
||||
public SelectGroupForPairUi(TagHandler tagHandler)
|
||||
{
|
||||
_show = false;
|
||||
_pair = null;
|
||||
_tagHandler = tagHandler;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public void Open(ClientPairDto pair)
|
||||
public void Open(Pair pair)
|
||||
{
|
||||
_pair = pair;
|
||||
// Using "_show" here to de-couple the opening of the popup
|
||||
@@ -56,7 +53,7 @@ public class SelectGroupForPairUi
|
||||
return;
|
||||
}
|
||||
|
||||
var name = PairName(showUidForEntry, _pair.OtherUID, _pair.VanityUID);
|
||||
var name = PairName(showUidForEntry, _pair);
|
||||
var popupName = $"Choose Groups for {name}";
|
||||
// Is the popup supposed to show but did not open yet? Open it
|
||||
if (_show)
|
||||
@@ -76,7 +73,7 @@ public class SelectGroupForPairUi
|
||||
{
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
UiShared.DrawWithID($"groups-pair-{_pair.OtherUID}-{tag}", () => DrawGroupName(_pair, tag));
|
||||
UiShared.DrawWithID($"groups-pair-{_pair.UserData.UID}-{tag}", () => DrawGroupName(_pair, tag));
|
||||
}
|
||||
ImGui.EndChild();
|
||||
}
|
||||
@@ -99,19 +96,19 @@ public class SelectGroupForPairUi
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroupName(ClientPairDto pair, string name)
|
||||
private void DrawGroupName(Pair pair, string name)
|
||||
{
|
||||
var hasTagBefore = _tagHandler.HasTag(pair, name);
|
||||
var hasTagBefore = _tagHandler.HasTag(pair.UserPair!, name);
|
||||
var hasTag = hasTagBefore;
|
||||
if (ImGui.Checkbox(name, ref hasTag))
|
||||
{
|
||||
if (hasTag)
|
||||
{
|
||||
_tagHandler.AddTagToPairedUid(pair, name);
|
||||
_tagHandler.AddTagToPairedUid(pair.UserPair!, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tagHandler.RemoveTagFromPairedUid(pair, name);
|
||||
_tagHandler.RemoveTagFromPairedUid(pair.UserPair!, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,19 +120,19 @@ public class SelectGroupForPairUi
|
||||
_tagHandler.AddTag(_tagNameToAdd);
|
||||
if (_pair != null)
|
||||
{
|
||||
_tagHandler.AddTagToPairedUid(_pair, _tagNameToAdd);
|
||||
_tagHandler.AddTagToPairedUid(_pair.UserPair!, _tagNameToAdd);
|
||||
}
|
||||
_tagNameToAdd = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string PairName(Dictionary<string, bool> showUidForEntry, string otherUid, string vanityUid)
|
||||
private string PairName(Dictionary<string, bool> showUidForEntry, Pair pair)
|
||||
{
|
||||
showUidForEntry.TryGetValue(otherUid, out var showUidInsteadOfName);
|
||||
_configuration.GetCurrentServerUidComments().TryGetValue(otherUid, out var playerText);
|
||||
showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
|
||||
var playerText = pair.GetNote();
|
||||
if (showUidInsteadOfName || string.IsNullOrEmpty(playerText))
|
||||
{
|
||||
playerText = string.IsNullOrEmpty(vanityUid) ? otherUid : vanityUid;
|
||||
playerText = pair.UserData.AliasOrUID;
|
||||
}
|
||||
return playerText;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.UI.Handlers;
|
||||
|
||||
namespace MareSynchronos.UI.Components;
|
||||
@@ -12,16 +10,14 @@ public class SelectPairForGroupUi
|
||||
{
|
||||
private bool _show = false;
|
||||
private bool _opened = false;
|
||||
private HashSet<string> _peopleInGroup = new(System.StringComparer.Ordinal);
|
||||
private HashSet<string> _peopleInGroup = new(StringComparer.Ordinal);
|
||||
private string _tag = string.Empty;
|
||||
private readonly TagHandler _tagHandler;
|
||||
private readonly Configuration _configuration;
|
||||
private string _filter = string.Empty;
|
||||
|
||||
public SelectPairForGroupUi(TagHandler tagHandler, Configuration configuration)
|
||||
public SelectPairForGroupUi(TagHandler tagHandler)
|
||||
{
|
||||
_tagHandler = tagHandler;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public void Open(string tag)
|
||||
@@ -31,7 +27,7 @@ public class SelectPairForGroupUi
|
||||
_show = true;
|
||||
}
|
||||
|
||||
public void Draw(List<ClientPairDto> pairs, Dictionary<string, bool> showUidForEntry)
|
||||
public void Draw(List<Pair> pairs, Dictionary<string, bool> showUidForEntry)
|
||||
{
|
||||
var workHeight = ImGui.GetMainViewport().WorkSize.Y / ImGuiHelpers.GlobalScale;
|
||||
var minSize = new Vector2(300, workHeight < 400 ? workHeight : 400) * ImGuiHelpers.GlobalScale;
|
||||
@@ -57,21 +53,21 @@ public class SelectPairForGroupUi
|
||||
{
|
||||
UiShared.FontText($"Select users for group {_tag}", UiBuilder.DefaultFont);
|
||||
ImGui.InputTextWithHint("##filter", "Filter", ref _filter, 255, ImGuiInputTextFlags.None);
|
||||
foreach (var item in pairs.OrderBy(p => PairName(showUidForEntry, p.OtherUID, p.VanityUID), System.StringComparer.OrdinalIgnoreCase)
|
||||
.Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p.OtherUID, p.VanityUID).Contains(_filter, System.StringComparison.OrdinalIgnoreCase)).ToList())
|
||||
foreach (var item in pairs.OrderBy(p => PairName(showUidForEntry, p), StringComparer.OrdinalIgnoreCase)
|
||||
.Where(p => string.IsNullOrEmpty(_filter) || PairName(showUidForEntry, p).Contains(_filter, StringComparison.OrdinalIgnoreCase)).ToList())
|
||||
{
|
||||
var isInGroup = _peopleInGroup.Contains(item.OtherUID);
|
||||
if (ImGui.Checkbox(PairName(showUidForEntry, item.OtherUID, item.VanityUID), ref isInGroup))
|
||||
var isInGroup = _peopleInGroup.Contains(item.UserData.UID);
|
||||
if (ImGui.Checkbox(PairName(showUidForEntry, item), ref isInGroup))
|
||||
{
|
||||
if (isInGroup)
|
||||
{
|
||||
_tagHandler.AddTagToPairedUid(item, _tag);
|
||||
_peopleInGroup.Add(item.OtherUID);
|
||||
_tagHandler.AddTagToPairedUid(item.UserPair!, _tag);
|
||||
_peopleInGroup.Add(item.UserData.UID);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tagHandler.RemoveTagFromPairedUid(item, _tag);
|
||||
_peopleInGroup.Remove(item.OtherUID);
|
||||
_tagHandler.RemoveTagFromPairedUid(item.UserPair!, _tag);
|
||||
_peopleInGroup.Remove(item.UserData.UID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,13 +80,13 @@ public class SelectPairForGroupUi
|
||||
}
|
||||
}
|
||||
|
||||
private string PairName(Dictionary<string, bool> showUidForEntry, string otherUid, string vanityUid)
|
||||
private string PairName(Dictionary<string, bool> showUidForEntry, Pair pair)
|
||||
{
|
||||
showUidForEntry.TryGetValue(otherUid, out var showUidInsteadOfName);
|
||||
_configuration.GetCurrentServerUidComments().TryGetValue(otherUid, out var playerText);
|
||||
showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
|
||||
var playerText = pair.GetNote();
|
||||
if (showUidInsteadOfName || string.IsNullOrEmpty(playerText))
|
||||
{
|
||||
playerText = string.IsNullOrEmpty(vanityUid) ? otherUid : vanityUid;
|
||||
playerText = pair.UserData.AliasOrUID;
|
||||
}
|
||||
return playerText;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
@@ -11,7 +10,7 @@ namespace MareSynchronos.UI;
|
||||
public class DownloadUi : Window, IDisposable
|
||||
{
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly UiShared _uiShared;
|
||||
private bool _wasOpen = false;
|
||||
@@ -22,18 +21,18 @@ public class DownloadUi : Window, IDisposable
|
||||
_windowSystem.RemoveWindow(this);
|
||||
}
|
||||
|
||||
public DownloadUi(WindowSystem windowSystem, Configuration pluginConfiguration, ApiController apiController, UiShared uiShared) : base("Mare Synchronos Downloads")
|
||||
public DownloadUi(WindowSystem windowSystem, ConfigurationService configService, ApiController apiController, UiShared uiShared) : base("Mare Synchronos Downloads")
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(DownloadUi));
|
||||
_windowSystem = windowSystem;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_configService = configService;
|
||||
_apiController = apiController;
|
||||
_uiShared = uiShared;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MaximumSize = new Vector2(300, 90),
|
||||
MinimumSize = new Vector2(300, 90)
|
||||
MinimumSize = new Vector2(300, 90),
|
||||
};
|
||||
|
||||
Flags |= ImGuiWindowFlags.NoMove;
|
||||
@@ -79,7 +78,7 @@ public class DownloadUi : Window, IDisposable
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
if (!_pluginConfiguration.ShowTransferWindow) return;
|
||||
if (!_configService.Current.ShowTransferWindow) return;
|
||||
if (!_apiController.IsDownloading && !_apiController.IsUploading) return;
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
@@ -4,9 +4,8 @@ using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.Export;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
@@ -16,15 +15,16 @@ public class GposeUi : Window, IDisposable
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileDialogManager _fileDialogManager;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ConfigurationService _configService;
|
||||
|
||||
public GposeUi(WindowSystem windowSystem, MareCharaFileManager mareCharaFileManager, DalamudUtil dalamudUtil, FileDialogManager fileDialogManager, Configuration configuration) : base("Mare Synchronos Gpose Import UI###MareSynchronosGposeUI")
|
||||
public GposeUi(WindowSystem windowSystem, MareCharaFileManager mareCharaFileManager,
|
||||
DalamudUtil dalamudUtil, FileDialogManager fileDialogManager, ConfigurationService configService) : base("Mare Synchronos Gpose Import UI###MareSynchronosGposeUI")
|
||||
{
|
||||
_windowSystem = windowSystem;
|
||||
_mareCharaFileManager = mareCharaFileManager;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileDialogManager = fileDialogManager;
|
||||
_configuration = configuration;
|
||||
_configService = configService;
|
||||
_dalamudUtil.GposeStart += StartGpose;
|
||||
_dalamudUtil.GposeEnd += EndGpose;
|
||||
IsOpen = _dalamudUtil.IsInGpose;
|
||||
@@ -40,7 +40,7 @@ public class GposeUi : Window, IDisposable
|
||||
|
||||
private void StartGpose()
|
||||
{
|
||||
IsOpen = _configuration.OpenGposeImportOnGposeStart;
|
||||
IsOpen = _configService.Current.OpenGposeImportOnGposeStart;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -3,24 +3,27 @@ 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;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Data.Extensions;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.API.Data.Comparer;
|
||||
|
||||
namespace MareSynchronos.UI
|
||||
{
|
||||
internal class GroupPanel
|
||||
{
|
||||
private readonly CompactUi _mainUi;
|
||||
private UiShared _uiShared;
|
||||
private Configuration _configuration;
|
||||
private ApiController _apiController;
|
||||
|
||||
private readonly UiShared _uiShared;
|
||||
private ApiController ApiController => _uiShared.ApiController;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly Dictionary<string, bool> _showGidForEntry = new(StringComparer.Ordinal);
|
||||
private string _editGroupEntry = string.Empty;
|
||||
private string _editGroupComment = string.Empty;
|
||||
@@ -38,8 +41,8 @@ namespace MareSynchronos.UI
|
||||
private bool _isPasswordValid;
|
||||
private bool _errorGroupJoin;
|
||||
private bool _errorGroupCreate = false;
|
||||
private GroupCreatedDto? _lastCreatedGroup = null;
|
||||
private readonly Dictionary<string, bool> ExpandedGroupState = new(StringComparer.Ordinal);
|
||||
private GroupPasswordDto? _lastCreatedGroup = null;
|
||||
private readonly Dictionary<string, bool> _expandedGroupState = new(StringComparer.Ordinal);
|
||||
private List<BannedGroupUserDto> _bannedUsers = new();
|
||||
private List<string> _bulkOneTimeInvites = new();
|
||||
private bool _modalBanListOpened;
|
||||
@@ -48,12 +51,12 @@ namespace MareSynchronos.UI
|
||||
private bool _modalChangePwOpened;
|
||||
private int _bulkInviteCount = 10;
|
||||
|
||||
public GroupPanel(CompactUi mainUi, UiShared uiShared, Configuration configuration, ApiController apiController)
|
||||
public GroupPanel(CompactUi mainUi, UiShared uiShared, PairManager pairManager, ServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_mainUi = mainUi;
|
||||
_uiShared = uiShared;
|
||||
_configuration = configuration;
|
||||
_apiController = apiController;
|
||||
_pairManager = pairManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
public void DrawSyncshells()
|
||||
@@ -70,12 +73,13 @@ namespace MareSynchronos.UI
|
||||
ImGui.InputTextWithHint("##syncshellid", "Syncshell GID/Alias (leave empty to create)", 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;
|
||||
bool userCanJoinMoreGroups = _pairManager.GroupPairs.Count < ApiController.ServerInfo.MaxGroupsJoinedByUser;
|
||||
bool userCanCreateMoreGroups = _pairManager.GroupPairs.Count(u => string.Equals(u.Key.Owner.UID, 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 (_pairManager.GroupPairs.All(w => !string.Equals(w.Key.Group.GID, _syncShellToJoin, StringComparison.Ordinal) && !string.Equals(w.Key.Group.Alias, _syncShellToJoin, StringComparison.Ordinal))
|
||||
&& !string.IsNullOrEmpty(_syncShellToJoin))
|
||||
{
|
||||
if (userCanJoinMoreGroups)
|
||||
{
|
||||
@@ -96,8 +100,8 @@ namespace MareSynchronos.UI
|
||||
}
|
||||
}
|
||||
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"));
|
||||
? (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, UiShared.PopupWindowFlags))
|
||||
{
|
||||
@@ -108,15 +112,15 @@ namespace MareSynchronos.UI
|
||||
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.",
|
||||
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.GroupJoin(shell, pw).Result;
|
||||
_errorGroupJoin = !ApiController.GroupJoin(new(new GroupData(shell), pw)).Result;
|
||||
if (!_errorGroupJoin)
|
||||
{
|
||||
_syncShellToJoin = string.Empty;
|
||||
@@ -136,7 +140,7 @@ namespace MareSynchronos.UI
|
||||
{
|
||||
try
|
||||
{
|
||||
_lastCreatedGroup = _apiController.GroupCreate().Result;
|
||||
_lastCreatedGroup = ApiController.GroupCreate().Result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -149,7 +153,7 @@ namespace MareSynchronos.UI
|
||||
{
|
||||
ImGui.Separator();
|
||||
_errorGroupCreate = false;
|
||||
ImGui.TextUnformatted("Syncshell ID: " + _lastCreatedGroup.GID);
|
||||
ImGui.TextUnformatted("Syncshell ID: " + _lastCreatedGroup.Group.GID);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted("Syncshell Password: " + _lastCreatedGroup.Password);
|
||||
ImGui.SameLine();
|
||||
@@ -178,22 +182,21 @@ namespace MareSynchronos.UI
|
||||
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())
|
||||
ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false);
|
||||
foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList())
|
||||
{
|
||||
UiShared.DrawWithID(entry.GID, () => DrawSyncshell(entry));
|
||||
UiShared.DrawWithID(entry.Key.Group.GID, () => DrawSyncshell(entry.Key, entry.Value));
|
||||
}
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
private void DrawSyncshell(GroupDto group)
|
||||
private void DrawSyncshell(GroupFullInfoDto groupDto, List<Pair> pairsInGroup)
|
||||
{
|
||||
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))
|
||||
var name = groupDto.Group.Alias ?? groupDto.GID;
|
||||
if (!_expandedGroupState.TryGetValue(groupDto.GID, out bool isExpanded))
|
||||
{
|
||||
isExpanded = false;
|
||||
ExpandedGroupState.Add(group.GID, isExpanded);
|
||||
_expandedGroupState.Add(groupDto.GID, isExpanded);
|
||||
}
|
||||
var icon = isExpanded ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight;
|
||||
var collapseButton = UiShared.GetIconButtonSize(icon);
|
||||
@@ -201,22 +204,23 @@ namespace MareSynchronos.UI
|
||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(0, 0, 0, 0));
|
||||
if (ImGuiComponents.IconButton(icon))
|
||||
{
|
||||
ExpandedGroupState[group.GID] = !ExpandedGroupState[group.GID];
|
||||
_expandedGroupState[groupDto.GID] = !_expandedGroupState[groupDto.GID];
|
||||
}
|
||||
ImGui.PopStyleColor(2);
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + collapseButton.X);
|
||||
var pauseIcon = (group.IsPaused ?? false) ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||
var pauseIcon = groupDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
|
||||
if (ImGuiComponents.IconButton(pauseIcon))
|
||||
{
|
||||
_ = _apiController.GroupChangePauseState(group.GID, !group.IsPaused ?? false);
|
||||
var userPerm = groupDto.GroupUserPermissions ^ GroupUserPermissions.Paused;
|
||||
_ = ApiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(groupDto.Group, new UserData(ApiController.UID), userPerm));
|
||||
}
|
||||
UiShared.AttachToolTip(((group.IsPaused ?? false) ? "Resume" : "Pause") + " pairing with all users in this Syncshell");
|
||||
UiShared.AttachToolTip((groupDto.GroupUserPermissions.IsPaused() ? "Resume" : "Pause") + " pairing with all users in this Syncshell");
|
||||
ImGui.SameLine();
|
||||
|
||||
var groupName = string.IsNullOrEmpty(group.Alias) ? group.GID : group.Alias;
|
||||
var textIsGid = true;
|
||||
string groupName = groupDto.GroupAliasOrGID;
|
||||
|
||||
if (string.Equals(group.OwnedBy, _apiController.UID, StringComparison.Ordinal))
|
||||
if (string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal))
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.Text(FontAwesomeIcon.Crown.ToIconString());
|
||||
@@ -224,7 +228,7 @@ namespace MareSynchronos.UI
|
||||
UiShared.AttachToolTip("You are the owner of Syncshell " + groupName);
|
||||
ImGui.SameLine();
|
||||
}
|
||||
else if (group.IsModerator ?? false)
|
||||
else if (groupDto.GroupUserInfo.IsModerator())
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
ImGui.Text(FontAwesomeIcon.UserShield.ToIconString());
|
||||
@@ -233,8 +237,8 @@ namespace MareSynchronos.UI
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
_showGidForEntry.TryGetValue(group.GID, out var showGidInsteadOfName);
|
||||
if (!showGidInsteadOfName && _configuration.GetCurrentServerGidComments().TryGetValue(group.GID, out var groupComment))
|
||||
_showGidForEntry.TryGetValue(groupDto.GID, out var showGidInsteadOfName);
|
||||
if (!showGidInsteadOfName && _serverConfigurationManager.CurrentServer!.GidServerComments.TryGetValue(groupDto.GID, out var groupComment))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(groupComment))
|
||||
{
|
||||
@@ -243,33 +247,31 @@ namespace MareSynchronos.UI
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.Equals(_editGroupEntry, group.GID, StringComparison.Ordinal))
|
||||
if (!string.Equals(_editGroupEntry, groupDto.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);
|
||||
+ "Users: " + (pairsInGroup.Count + 1) + ", Owner: " + groupDto.OwnerAliasOrUID);
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
var prevState = textIsGid;
|
||||
if (_showGidForEntry.ContainsKey(group.GID))
|
||||
if (_showGidForEntry.ContainsKey(groupDto.GID))
|
||||
{
|
||||
prevState = _showGidForEntry[group.GID];
|
||||
prevState = _showGidForEntry[groupDto.GID];
|
||||
}
|
||||
|
||||
_showGidForEntry[group.GID] = !prevState;
|
||||
_showGidForEntry[groupDto.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;
|
||||
_serverConfigurationManager.CurrentServer!.GidServerComments[_editGroupEntry] = _editGroupComment;
|
||||
_serverConfigurationManager.Save();
|
||||
_editGroupComment = _serverConfigurationManager.CurrentServer!.GidServerComments.TryGetValue(groupDto.GID, out string? value) ? value : string.Empty;
|
||||
_editGroupEntry = groupDto.GID;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -278,8 +280,8 @@ namespace MareSynchronos.UI
|
||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
||||
if (ImGui.InputTextWithHint("", "Comment/Notes", ref _editGroupComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
_configuration.SetCurrentServerGidComment(group.GID, _editGroupComment);
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.CurrentServer!.GidServerComments[groupDto.GID] = _editGroupComment;
|
||||
_serverConfigurationManager.Save();
|
||||
_editGroupEntry = string.Empty;
|
||||
}
|
||||
|
||||
@@ -290,26 +292,27 @@ namespace MareSynchronos.UI
|
||||
UiShared.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
||||
}
|
||||
|
||||
UiShared.DrawWithID(group.GID + "settings", () => DrawSyncShellButtons(group, name));
|
||||
UiShared.DrawWithID(groupDto.GID + "settings", () => DrawSyncShellButtons(groupDto, pairsInGroup));
|
||||
|
||||
if (_showModalBanList && !_modalBanListOpened)
|
||||
{
|
||||
_modalBanListOpened = true;
|
||||
ImGui.OpenPopup("Manage Banlist for " + group.GID);
|
||||
ImGui.OpenPopup("Manage Banlist for " + groupDto.GID);
|
||||
}
|
||||
|
||||
if (!_showModalBanList) _modalBanListOpened = false;
|
||||
|
||||
if (ImGui.BeginPopupModal("Manage Banlist for " + group.GID, ref _showModalBanList, UiShared.PopupWindowFlags))
|
||||
if (ImGui.BeginPopupModal("Manage Banlist for " + groupDto.GID, ref _showModalBanList, UiShared.PopupWindowFlags))
|
||||
{
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
||||
{
|
||||
_bannedUsers = _apiController.GroupGetBannedUsers(group.GID).Result;
|
||||
_bannedUsers = ApiController.GroupGetBannedUsers(groupDto).Result;
|
||||
}
|
||||
|
||||
if (ImGui.BeginTable("bannedusertable" + group.GID, 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY))
|
||||
if (ImGui.BeginTable("bannedusertable" + groupDto.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY))
|
||||
{
|
||||
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1);
|
||||
ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1);
|
||||
ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1);
|
||||
ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2);
|
||||
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3);
|
||||
@@ -322,6 +325,8 @@ namespace MareSynchronos.UI
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(bannedUser.UID);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(bannedUser.BannedBy);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture));
|
||||
@@ -330,7 +335,7 @@ namespace MareSynchronos.UI
|
||||
ImGui.TableNextColumn();
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Check, "Unban"))
|
||||
{
|
||||
_ = _apiController.GroupUnbanUser(group.GID, bannedUser.UID);
|
||||
_ = ApiController.GroupUnbanUser(bannedUser);
|
||||
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
@@ -349,7 +354,6 @@ namespace MareSynchronos.UI
|
||||
|
||||
if (!_showModalChangePassword) _modalChangePwOpened = false;
|
||||
|
||||
|
||||
if (ImGui.BeginPopupModal("Change Syncshell Password", ref _showModalChangePassword, UiShared.PopupWindowFlags))
|
||||
{
|
||||
UiShared.TextWrapped("Enter the new Syncshell password for Syncshell " + name + " here.");
|
||||
@@ -359,7 +363,7 @@ namespace MareSynchronos.UI
|
||||
if (ImGui.Button("Change password"))
|
||||
{
|
||||
var pw = _newSyncShellPassword;
|
||||
_isPasswordValid = _apiController.GroupChangePassword(group.GID, pw).Result;
|
||||
_isPasswordValid = ApiController.GroupChangePassword(new(groupDto.Group, pw)).Result;
|
||||
_newSyncShellPassword = string.Empty;
|
||||
if (_isPasswordValid) _showModalChangePassword = false;
|
||||
}
|
||||
@@ -392,7 +396,7 @@ namespace MareSynchronos.UI
|
||||
ImGui.SliderInt("Amount##bulkinvites", ref _bulkInviteCount, 1, 100);
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.MailBulk, "Create invites"))
|
||||
{
|
||||
_bulkOneTimeInvites = _apiController.GroupCreateTempInvite(group.GID, _bulkInviteCount).Result;
|
||||
_bulkOneTimeInvites = ApiController.GroupCreateTempInvite(groupDto, _bulkInviteCount).Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -409,22 +413,55 @@ namespace MareSynchronos.UI
|
||||
}
|
||||
|
||||
ImGui.Indent(collapseButton.X);
|
||||
if (ExpandedGroupState[group.GID])
|
||||
if (_expandedGroupState[groupDto.GID])
|
||||
{
|
||||
pairsInGroup = pairsInGroup.OrderBy(p => string.Equals(p.UserUID, group.OwnedBy, StringComparison.Ordinal) ? 0 : 1)
|
||||
.ThenBy(p => p.IsModerator ?? false ? 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,
|
||||
group.OwnedBy!,
|
||||
string.Equals(group.OwnedBy, _apiController.UID, StringComparison.Ordinal),
|
||||
group.IsModerator ?? false,
|
||||
group?.IsPaused ?? false));
|
||||
var visibleUsers = pairsInGroup.Where(u => u.IsVisible).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
var onlineUsers = pairsInGroup.Where(u => u.IsOnline && !u.IsVisible).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
var offlineUsers = pairsInGroup.Where(u => !u.IsOnline && !u.IsVisible).OrderBy(u => u.GetNote() ?? u.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
if (visibleUsers.Any())
|
||||
{
|
||||
ImGui.Text("Visible");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in visibleUsers)
|
||||
{
|
||||
UiShared.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
|
||||
entry,
|
||||
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
|
||||
groupDto.OwnerUID,
|
||||
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
|
||||
groupDto.GroupUserInfo.IsModerator()));
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineUsers.Any())
|
||||
{
|
||||
ImGui.Text("Online");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in onlineUsers)
|
||||
{
|
||||
UiShared.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
|
||||
entry,
|
||||
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
|
||||
groupDto.OwnerUID,
|
||||
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
|
||||
groupDto.GroupUserInfo.IsModerator()));
|
||||
}
|
||||
}
|
||||
|
||||
if (offlineUsers.Any())
|
||||
{
|
||||
ImGui.Text("Offline/Unknown");
|
||||
ImGui.Separator();
|
||||
foreach (var entry in offlineUsers)
|
||||
{
|
||||
UiShared.DrawWithID(groupDto.GID + entry.UserData.UID, () => DrawSyncshellPairedClient(
|
||||
entry,
|
||||
entry.GroupPair.Single(g => GroupDataComparer.Instance.Equals(g.Key.Group, groupDto.Group)).Value,
|
||||
groupDto.OwnerUID,
|
||||
string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal),
|
||||
groupDto.GroupUserInfo.IsModerator()));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
@@ -433,21 +470,97 @@ namespace MareSynchronos.UI
|
||||
ImGui.Unindent(collapseButton.X);
|
||||
}
|
||||
|
||||
private void DrawSyncShellButtons(GroupDto entry, string name)
|
||||
private void DrawSyncShellButtons(GroupFullInfoDto groupDto, List<Pair> groupPairs)
|
||||
{
|
||||
bool invitesEnabled = entry.InvitesEnabled ?? true;
|
||||
var lockedIcon = invitesEnabled ? FontAwesomeIcon.LockOpen : FontAwesomeIcon.Lock;
|
||||
var iconSize = UiShared.GetIconSize(lockedIcon);
|
||||
var diffLockUnlockIcons = invitesEnabled ? 0 : (UiShared.GetIconSize(FontAwesomeIcon.LockOpen).X - iconSize.X) / 2;
|
||||
var barbuttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||
var isOwner = string.Equals(entry.OwnedBy, _apiController.UID, StringComparison.Ordinal);
|
||||
var infoIcon = FontAwesomeIcon.InfoCircle;
|
||||
|
||||
bool invitesEnabled = !groupDto.GroupPermissions.IsDisableInvites();
|
||||
var soundsDisabled = groupDto.GroupPermissions.IsDisableSounds();
|
||||
var animDisabled = groupDto.GroupPermissions.IsDisableAnimations();
|
||||
|
||||
var userSoundsDisabled = groupDto.GroupUserPermissions.IsDisableSounds();
|
||||
var userAnimDisabled = groupDto.GroupUserPermissions.IsDisableAnimations();
|
||||
|
||||
bool showInfoIcon = !invitesEnabled || soundsDisabled || animDisabled || userSoundsDisabled || userAnimDisabled;
|
||||
|
||||
var lockedIcon = invitesEnabled ? FontAwesomeIcon.LockOpen : FontAwesomeIcon.Lock;
|
||||
var animIcon = animDisabled ? FontAwesomeIcon.Stop : FontAwesomeIcon.Running;
|
||||
var soundsIcon = soundsDisabled ? FontAwesomeIcon.VolumeOff : FontAwesomeIcon.VolumeUp;
|
||||
var userAnimIcon = userAnimDisabled ? FontAwesomeIcon.Stop : FontAwesomeIcon.Running;
|
||||
var userSoundsIcon = userSoundsDisabled ? FontAwesomeIcon.VolumeOff : FontAwesomeIcon.VolumeUp;
|
||||
|
||||
var iconSize = UiShared.GetIconSize(infoIcon);
|
||||
var diffLockUnlockIcons = showInfoIcon ? (UiShared.GetIconSize(infoIcon).X - iconSize.X) / 2 : 0;
|
||||
var barbuttonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||
var isOwner = string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal);
|
||||
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - barbuttonSize.X - (showInfoIcon ? iconSize.X : 0) - diffLockUnlockIcons - (showInfoIcon ? ImGui.GetStyle().ItemSpacing.X : 0));
|
||||
if (showInfoIcon)
|
||||
{
|
||||
UiShared.FontText(infoIcon.ToIconString(), UiBuilder.IconFont);
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
if (!invitesEnabled || soundsDisabled || animDisabled)
|
||||
{
|
||||
ImGui.Text("Syncshell permissions");
|
||||
|
||||
if (!invitesEnabled)
|
||||
{
|
||||
var lockedText = "Syncshell is closed for joining";
|
||||
UiShared.FontText(lockedIcon.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(lockedText);
|
||||
}
|
||||
|
||||
if (soundsDisabled)
|
||||
{
|
||||
var soundsText = "Sound sync disabled through owner";
|
||||
UiShared.FontText(soundsIcon.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(soundsText);
|
||||
}
|
||||
|
||||
if (animDisabled)
|
||||
{
|
||||
var animText = "Animation sync disabled through owner";
|
||||
UiShared.FontText(animIcon.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(animText);
|
||||
}
|
||||
}
|
||||
|
||||
if (userSoundsDisabled || userAnimDisabled)
|
||||
{
|
||||
if (!invitesEnabled || soundsDisabled || animDisabled)
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.Text("Your permissions");
|
||||
|
||||
if (userSoundsDisabled)
|
||||
{
|
||||
var userSoundsText = "Sound sync disabled through you";
|
||||
UiShared.FontText(userSoundsIcon.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(userSoundsText);
|
||||
}
|
||||
|
||||
if (userAnimDisabled)
|
||||
{
|
||||
var userAnimText = "Animation sync disabled through you";
|
||||
UiShared.FontText(userAnimIcon.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(userAnimText);
|
||||
}
|
||||
|
||||
if (!invitesEnabled || soundsDisabled || animDisabled)
|
||||
UiShared.TextWrapped("Note that syncshell permissions for disabling take precedence over your own set permissions");
|
||||
}
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - barbuttonSize.X - iconSize.X - diffLockUnlockIcons - 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();
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + diffLockUnlockIcons);
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
|
||||
{
|
||||
@@ -460,35 +573,65 @@ namespace MareSynchronos.UI
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
_ = _apiController.GroupLeave(entry.GID);
|
||||
_ = ApiController.GroupLeave(groupDto);
|
||||
}
|
||||
}
|
||||
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."));
|
||||
UiShared.AttachToolTip("Hold CTRL and click to leave this Syncshell" + (!string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal) ? string.Empty : Environment.NewLine
|
||||
+ "WARNING: This action is irreversible" + 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.CloseCurrentPopup();
|
||||
ImGui.SetClipboardText(string.IsNullOrEmpty(entry.Alias) ? entry.GID : entry.Alias);
|
||||
ImGui.SetClipboardText(groupDto.GroupAliasOrGID);
|
||||
}
|
||||
UiShared.AttachToolTip("Copy Syncshell ID to Clipboard");
|
||||
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes"))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
ImGui.SetClipboardText(_uiShared.GetNotes(entry.GID));
|
||||
ImGui.SetClipboardText(UiShared.GetNotes(groupPairs));
|
||||
}
|
||||
UiShared.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> Privacy -> Import Notes from Clipboard");
|
||||
|
||||
if (isOwner || (entry.IsModerator ?? false))
|
||||
|
||||
var soundsText = userSoundsDisabled ? "Enable sound sync" : "Disable sound sync";
|
||||
if (UiShared.IconTextButton(userSoundsIcon, soundsText))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
var perm = groupDto.GroupUserPermissions;
|
||||
perm.SetDisableSounds(!perm.IsDisableSounds());
|
||||
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
||||
}
|
||||
UiShared.AttachToolTip("Sets your allowance for sound synchronization for users of this syncshell."
|
||||
+ Environment.NewLine + "Disabling the synchronization will stop applying sound modifications for users of this syncshell."
|
||||
+ Environment.NewLine + "Note: this setting can be forcefully overridden to 'disabled' through the syncshell owner."
|
||||
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
||||
|
||||
var animText = userAnimDisabled ? "Enable animations sync" : "Disable animations sync";
|
||||
if (UiShared.IconTextButton(userAnimIcon, animText))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
var perm = groupDto.GroupUserPermissions;
|
||||
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
||||
_ = ApiController.GroupChangeIndividualPermissionState(new(groupDto.Group, new UserData(ApiController.UID), perm));
|
||||
}
|
||||
UiShared.AttachToolTip("Sets your allowance for animations synchronization for users of this syncshell."
|
||||
+ Environment.NewLine + "Disabling the synchronization will stop applying animations modifications for users of this syncshell."
|
||||
+ Environment.NewLine + "Note: this setting might also affect sound synchronization"
|
||||
+ Environment.NewLine + "Note: this setting can be forcefully overridden to 'disabled' through the syncshell owner."
|
||||
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
||||
|
||||
if (isOwner || groupDto.GroupUserInfo.IsModerator())
|
||||
{
|
||||
ImGui.Separator();
|
||||
|
||||
var changedToIcon = !invitesEnabled ? FontAwesomeIcon.LockOpen : FontAwesomeIcon.Lock;
|
||||
var changedToIcon = invitesEnabled ? FontAwesomeIcon.LockOpen : FontAwesomeIcon.Lock;
|
||||
if (UiShared.IconTextButton(changedToIcon, invitesEnabled ? "Lock Syncshell" : "Unlock Syncshell"))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupChangeInviteState(entry.GID, !entry.InvitesEnabled ?? true);
|
||||
var groupPerm = groupDto.GroupPermissions;
|
||||
groupPerm.SetDisableInvites(invitesEnabled);
|
||||
_ = ApiController.GroupChangeGroupPermissionState(new GroupPermissionDto(groupDto.Group, groupPerm));
|
||||
}
|
||||
UiShared.AttachToolTip("Change Syncshell joining permissions" + Environment.NewLine + "Syncshell is currently " + (invitesEnabled ? "open" : "closed") + " for people to join");
|
||||
|
||||
@@ -508,16 +651,40 @@ namespace MareSynchronos.UI
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupClear(entry.GID);
|
||||
_ = ApiController.GroupClear(groupDto);
|
||||
}
|
||||
}
|
||||
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.");
|
||||
|
||||
var groupSoundsText = soundsDisabled ? "Enable syncshell sound sync" : "Disable syncshell sound sync";
|
||||
if (UiShared.IconTextButton(soundsIcon, groupSoundsText))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
var perm = groupDto.GroupPermissions;
|
||||
perm.SetDisableSounds(!perm.IsDisableSounds());
|
||||
_ = ApiController.GroupChangeGroupPermissionState(new(groupDto.Group, perm));
|
||||
}
|
||||
UiShared.AttachToolTip("Sets syncshell-wide allowance for sound synchronization for all users." + Environment.NewLine
|
||||
+ "Note: users that are individually paired with others in the syncshell will ignore this setting." + Environment.NewLine
|
||||
+ "Note: if the synchronization is enabled, users can individually override this setting to disabled.");
|
||||
|
||||
var groupAnimText = animDisabled ? "Enable syncshell animations sync" : "Disable syncshell animations sync";
|
||||
if (UiShared.IconTextButton(animIcon, groupAnimText))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
var perm = groupDto.GroupPermissions;
|
||||
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
||||
_ = ApiController.GroupChangeGroupPermissionState(new(groupDto.Group, perm));
|
||||
}
|
||||
UiShared.AttachToolTip("Sets syncshell-wide allowance for animations synchronization for all users." + Environment.NewLine
|
||||
+ "Note: users that are individually paired with others in the syncshell will ignore this setting." + Environment.NewLine
|
||||
+ "Note: if the synchronization is enabled, users can individually override this setting to disabled.");
|
||||
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite"))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(entry.GID, 1).Result.FirstOrDefault() ?? string.Empty);
|
||||
ImGui.SetClipboardText(ApiController.GroupCreateTempInvite(groupDto, 1).Result.FirstOrDefault() ?? string.Empty);
|
||||
}
|
||||
UiShared.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard.");
|
||||
|
||||
@@ -533,7 +700,7 @@ namespace MareSynchronos.UI
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_showModalBanList = true;
|
||||
_bannedUsers = _apiController.GroupGetBannedUsers(entry.GID).Result;
|
||||
_bannedUsers = ApiController.GroupGetBannedUsers(groupDto).Result;
|
||||
}
|
||||
|
||||
if (isOwner)
|
||||
@@ -543,7 +710,7 @@ namespace MareSynchronos.UI
|
||||
if (UiShared.CtrlPressed() && UiShared.ShiftPressed())
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupDelete(entry.GID);
|
||||
_ = ApiController.GroupDelete(groupDto);
|
||||
}
|
||||
}
|
||||
UiShared.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
|
||||
@@ -554,20 +721,32 @@ namespace MareSynchronos.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSyncshellPairedClient(GroupPairDto entry, string ownerUid, bool isOwner, bool isModerator, bool isPausedByYou)
|
||||
private void DrawSyncshellPairedClient(Pair pair, GroupPairFullInfoDto entry, string ownerUid, bool isOwner, bool isModerator)
|
||||
{
|
||||
var plusButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Plus);
|
||||
var barButtonSize = UiShared.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||
var entryUID = string.IsNullOrEmpty(entry.UserAlias) ? entry.UserUID : entry.UserAlias;
|
||||
var entryUID = entry.UserAliasOrUID;
|
||||
var textSize = ImGui.CalcTextSize(entryUID);
|
||||
var originalY = ImGui.GetCursorPosY();
|
||||
var userIsMod = entry.IsModerator ?? false;
|
||||
var userIsMod = entry.GroupPairStatusInfo.IsModerator();
|
||||
var userIsOwner = string.Equals(entryUID, ownerUid, StringComparison.Ordinal);
|
||||
var isPinned = entry.GroupPairStatusInfo.IsPinned();
|
||||
var isPaused = pair.IsPaused;
|
||||
var presenceIcon = pair.IsVisible ? FontAwesomeIcon.Eye : (pair.IsOnline ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink);
|
||||
var presenceColor = (pair.IsOnline || pair.IsVisible) ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
var presenceText = entryUID + " is offline";
|
||||
|
||||
var soundsDisabled = entry.GroupUserPermissions.IsDisableSounds();
|
||||
var animDisabled = entry.GroupUserPermissions.IsDisableAnimations();
|
||||
|
||||
var textPos = originalY + barButtonSize.Y / 2 - textSize.Y / 2;
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
if (isPausedByYou || (entry.IsPaused ?? false))
|
||||
if (pair.IsPaused)
|
||||
{
|
||||
presenceIcon = FontAwesomeIcon.Question;
|
||||
presenceColor = ImGuiColors.DalamudGrey;
|
||||
presenceText = entryUID + " online status is unknown (paused)";
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(FontAwesomeIcon.PauseCircle.ToIconString(), ImGuiColors.DalamudYellow);
|
||||
ImGui.PopFont();
|
||||
@@ -583,6 +762,16 @@ namespace MareSynchronos.UI
|
||||
UiShared.AttachToolTip("You are paired with " + entryUID);
|
||||
}
|
||||
|
||||
if (pair.IsOnline && !pair.IsVisible) presenceText = entryUID + " is online";
|
||||
else if (pair.IsOnline && pair.IsVisible) presenceText = entryUID + " is visible: " + pair.PlayerName;
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
UiShared.ColorText(presenceIcon.ToIconString(), presenceColor);
|
||||
ImGui.PopFont();
|
||||
UiShared.AttachToolTip(presenceText);
|
||||
|
||||
if (userIsOwner)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
@@ -601,7 +790,7 @@ namespace MareSynchronos.UI
|
||||
ImGui.PopFont();
|
||||
UiShared.AttachToolTip("User is moderator of this Syncshell");
|
||||
}
|
||||
else if (entry.IsPinned ?? false)
|
||||
else if (isPinned)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
@@ -612,8 +801,8 @@ namespace MareSynchronos.UI
|
||||
}
|
||||
|
||||
var textIsUid = true;
|
||||
_mainUi.ShowUidForEntry.TryGetValue(entry.UserUID, out var showUidInsteadOfName);
|
||||
if (!showUidInsteadOfName && _configuration.GetCurrentServerUidComments().TryGetValue(entry.UserUID, out var playerText))
|
||||
_mainUi.ShowUidForEntry.TryGetValue(entry.UID, out var showUidInsteadOfName);
|
||||
if (!showUidInsteadOfName && _serverConfigurationManager.CurrentServer!.UidServerComments.TryGetValue(entry.UID, out var playerText))
|
||||
{
|
||||
if (string.IsNullOrEmpty(playerText))
|
||||
{
|
||||
@@ -629,10 +818,10 @@ namespace MareSynchronos.UI
|
||||
playerText = entryUID;
|
||||
}
|
||||
|
||||
bool plusButtonShown = !_apiController.PairedClients.Any(p => string.Equals(p.OtherUID, entry.UserUID, StringComparison.Ordinal));
|
||||
bool plusButtonShown = !_pairManager.DirectPairs.Any(p => string.Equals(p.UserData.UID, entry.UID, StringComparison.Ordinal));
|
||||
|
||||
ImGui.SameLine();
|
||||
if (!string.Equals(_mainUi.EditNickEntry, entry.UserUID, StringComparison.Ordinal))
|
||||
if (!string.Equals(_mainUi.EditNickEntry, entry.UID, StringComparison.Ordinal))
|
||||
{
|
||||
ImGui.SetCursorPosY(textPos);
|
||||
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
||||
@@ -643,22 +832,20 @@ namespace MareSynchronos.UI
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
var prevState = textIsUid;
|
||||
if (_mainUi.ShowUidForEntry.ContainsKey(entry.UserUID))
|
||||
if (_mainUi.ShowUidForEntry.ContainsKey(entry.UID))
|
||||
{
|
||||
prevState = _mainUi.ShowUidForEntry[entry.UserUID];
|
||||
prevState = _mainUi.ShowUidForEntry[entry.UID];
|
||||
}
|
||||
|
||||
_mainUi.ShowUidForEntry[entry.UserUID] = !prevState;
|
||||
_mainUi.ShowUidForEntry[entry.UID] = !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;
|
||||
_serverConfigurationManager.CurrentServer!.UidServerComments[_mainUi.EditNickEntry] = _mainUi.EditUserComment;
|
||||
_serverConfigurationManager.Save();
|
||||
_mainUi.EditUserComment = _serverConfigurationManager.CurrentServer.UidServerComments.TryGetValue(entry.UID, out string? value) ? value : string.Empty;
|
||||
_mainUi.EditNickEntry = entry.UID;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -670,8 +857,8 @@ namespace MareSynchronos.UI
|
||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * buttons);
|
||||
if (ImGui.InputTextWithHint("", "Nick/Notes", ref _mainUi.EditUserComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
_configuration.SetCurrentServerUidComment(entry.UserUID, _mainUi.EditUserComment);
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.CurrentServer!.UidServerComments[entry.UID] = _mainUi.EditUserComment;
|
||||
_serverConfigurationManager.Save();
|
||||
_mainUi.EditNickEntry = string.Empty;
|
||||
}
|
||||
|
||||
@@ -692,7 +879,7 @@ namespace MareSynchronos.UI
|
||||
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
||||
{
|
||||
_ = _apiController.UserAddPair(entry.UserUID);
|
||||
_ = ApiController.UserAddPair(new UserDto(entry.User));
|
||||
}
|
||||
UiShared.AttachToolTip("Pair with " + entryUID + " individually");
|
||||
}
|
||||
@@ -705,18 +892,64 @@ namespace MareSynchronos.UI
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Bars))
|
||||
{
|
||||
ImGui.OpenPopup("Popup");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ((animDisabled || soundsDisabled) && pair.UserPair == null)
|
||||
{
|
||||
var infoIconPosDist = (plusButtonShown ? plusButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0)
|
||||
+ ((isOwner || (isModerator && !userIsMod && !userIsOwner)) ? barButtonSize.X + ImGui.GetStyle().ItemSpacing.X : 0);
|
||||
var icon = FontAwesomeIcon.InfoCircle;
|
||||
var iconwidth = UiShared.GetIconSize(icon);
|
||||
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMin().X + UiShared.GetWindowContentRegionWidth() - infoIconPosDist - iconwidth.X);
|
||||
ImGui.SetCursorPosY(originalY);
|
||||
|
||||
UiShared.FontText(icon.ToIconString(), UiBuilder.IconFont);
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
|
||||
|
||||
ImGui.Text("User permissions");
|
||||
|
||||
if (soundsDisabled)
|
||||
{
|
||||
var userSoundsText = "Sound sync disabled by " + pair.UserData.AliasOrUID;
|
||||
UiShared.FontText(FontAwesomeIcon.VolumeOff.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(userSoundsText);
|
||||
}
|
||||
|
||||
if (animDisabled)
|
||||
{
|
||||
var userAnimText = "Animation sync disabled by " + pair.UserData.AliasOrUID;
|
||||
UiShared.FontText(FontAwesomeIcon.Stop.ToIconString(), UiBuilder.IconFont);
|
||||
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.Text(userAnimText);
|
||||
}
|
||||
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
if (!plusButtonShown && !(isOwner || (isModerator && !userIsMod && !userIsOwner)))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.Dummy(barButtonSize with { X = 0 });
|
||||
}
|
||||
|
||||
if (ImGui.BeginPopup("Popup"))
|
||||
{
|
||||
if ((!entry.IsModerator ?? false) && !(userIsMod || userIsOwner))
|
||||
if ((!isModerator) && !(userIsMod || userIsOwner))
|
||||
{
|
||||
var pinText = (entry?.IsPinned ?? false) ? "Unpin user" : "Pin user";
|
||||
var pinText = isPinned ? "Unpin user" : "Pin user";
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Thumbtack, pinText))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupChangePinned(entry.GroupGID, entry.UserUID, !entry.IsPinned ?? false);
|
||||
var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsPinned;
|
||||
_ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo));
|
||||
}
|
||||
UiShared.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean");
|
||||
|
||||
@@ -725,11 +958,11 @@ namespace MareSynchronos.UI
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupRemoveUser(entry.GroupGID, entry.UserUID);
|
||||
_ = ApiController.GroupRemoveUser(entry);
|
||||
}
|
||||
}
|
||||
|
||||
UiShared.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAlias ?? entry.UserUID) + " from Syncshell");
|
||||
UiShared.AttachToolTip("Hold CTRL and click to remove user " + (entry.UserAliasOrUID) + " from Syncshell");
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
|
||||
{
|
||||
_showModalBanUser = true;
|
||||
@@ -740,36 +973,31 @@ namespace MareSynchronos.UI
|
||||
|
||||
if (isOwner)
|
||||
{
|
||||
string modText = (entry.IsModerator ?? false) ? "Demod user" : "Mod user";
|
||||
string modText = userIsMod ? "Demod user" : "Mod user";
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.UserShield, modText))
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupSetModerator(entry.GroupGID, entry.UserUID, !entry.IsModerator ?? false);
|
||||
var userInfo = entry.GroupPairStatusInfo ^ GroupUserInfo.IsModerator;
|
||||
_ = ApiController.GroupSetUserInfo(new GroupPairUserInfoDto(entry.Group, entry.User, userInfo));
|
||||
}
|
||||
}
|
||||
UiShared.AttachToolTip("Hold CTRL to change the moderator status for " + (entry.UserAlias ?? entry.UserUID) + Environment.NewLine +
|
||||
UiShared.AttachToolTip("Hold CTRL to change the moderator status for " + (entry.UserAliasOrUID) + Environment.NewLine +
|
||||
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership"))
|
||||
{
|
||||
if (UiShared.CtrlPressed() && UiShared.ShiftPressed())
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
_ = _apiController.GroupChangeOwnership(entry.GroupGID, entry.UserUID);
|
||||
_ = ApiController.GroupChangeOwnership(entry);
|
||||
}
|
||||
}
|
||||
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.");
|
||||
UiShared.AttachToolTip("Hold CTRL and SHIFT and click to transfer ownership of this Syncshell to " + (entry.UserAliasOrUID) + Environment.NewLine + "WARNING: This action is irreversible.");
|
||||
}
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
if (!plusButtonShown && !(isOwner || (isModerator && !userIsMod && !userIsOwner)))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.Dummy(barButtonSize with { X = 0 });
|
||||
}
|
||||
|
||||
if (_showModalBanUser && !_banUserPopupOpen)
|
||||
{
|
||||
ImGui.OpenPopup("Ban User");
|
||||
@@ -780,13 +1008,13 @@ namespace MareSynchronos.UI
|
||||
|
||||
if (ImGui.BeginPopupModal("Ban User", ref _showModalBanUser, UiShared.PopupWindowFlags))
|
||||
{
|
||||
UiShared.TextWrapped("User " + (entry.UserAlias ?? entry.UserUID) + " will be banned and removed from this Syncshell.");
|
||||
UiShared.TextWrapped("User " + (entry.UserAliasOrUID) + " will be banned and removed from this Syncshell.");
|
||||
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
|
||||
if (ImGui.Button("Ban User"))
|
||||
{
|
||||
ImGui.CloseCurrentPopup();
|
||||
var reason = _banReason;
|
||||
_ = _apiController.GroupBanUser(entry.GroupGID, entry.UserUID, reason);
|
||||
_ = ApiController.GroupBanUser(entry, reason);
|
||||
_banReason = string.Empty;
|
||||
}
|
||||
UiShared.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.WebAPI;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Managers;
|
||||
|
||||
namespace MareSynchronos.UI.Handlers
|
||||
{
|
||||
public class TagHandler
|
||||
{
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
public TagHandler(Configuration configuration)
|
||||
public TagHandler(ServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
public void AddTag(string tag)
|
||||
{
|
||||
GetAvailableTagsForCurrentServer().Add(tag);
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
public void RemoveTag(string tag)
|
||||
@@ -30,20 +26,20 @@ namespace MareSynchronos.UI.Handlers
|
||||
GetUidTagDictionaryForCurrentServer().Keys
|
||||
.ToList()
|
||||
.ForEach(otherUid => RemoveTagFromPairedUid(otherUid, tag));
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
public void SetTagOpen(string tag, bool open)
|
||||
{
|
||||
if (open)
|
||||
{
|
||||
_configuration.OpenPairTags.Add(tag);
|
||||
_serverConfigurationManager.CurrentServer!.OpenPairTags.Add(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
_configuration.OpenPairTags.Remove(tag);
|
||||
_serverConfigurationManager.CurrentServer!.OpenPairTags.Remove(tag);
|
||||
}
|
||||
_configuration.Save();
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,7 +49,7 @@ namespace MareSynchronos.UI.Handlers
|
||||
/// <returns>open true/false</returns>
|
||||
public bool IsTagOpen(string tag)
|
||||
{
|
||||
return _configuration.OpenPairTags.Contains(tag);
|
||||
return _serverConfigurationManager.CurrentServer!.OpenPairTags.Contains(tag);
|
||||
}
|
||||
|
||||
public List<string> GetAllTagsSorted()
|
||||
@@ -71,30 +67,30 @@ namespace MareSynchronos.UI.Handlers
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public void AddTagToPairedUid(ClientPairDto pair, string tagName)
|
||||
public void AddTagToPairedUid(UserPairDto pair, string tagName)
|
||||
{
|
||||
var tagDictionary = GetUidTagDictionaryForCurrentServer();
|
||||
var tagsForPair = tagDictionary.GetValueOrDefault(pair.OtherUID, new List<string>());
|
||||
var tagsForPair = tagDictionary.GetValueOrDefault(pair.User.UID, new List<string>());
|
||||
tagsForPair.Add(tagName);
|
||||
tagDictionary[pair.OtherUID] = tagsForPair;
|
||||
_configuration.Save();
|
||||
tagDictionary[pair.User.UID] = tagsForPair;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
public void RemoveTagFromPairedUid(ClientPairDto pair, string tagName)
|
||||
public void RemoveTagFromPairedUid(UserPairDto pair, string tagName)
|
||||
{
|
||||
RemoveTagFromPairedUid(pair.OtherUID, tagName);
|
||||
_configuration.Save();
|
||||
RemoveTagFromPairedUid(pair.User.UID, tagName);
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
public bool HasTag(ClientPairDto pair, string tagName)
|
||||
public bool HasTag(UserPairDto pair, string tagName)
|
||||
{
|
||||
var tagsForPair = GetUidTagDictionaryForCurrentServer().GetValueOrDefault(pair.OtherUID, new List<string>());
|
||||
var tagsForPair = GetUidTagDictionaryForCurrentServer().GetValueOrDefault(pair.User.UID, new List<string>());
|
||||
return tagsForPair.Contains(tagName, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public bool HasAnyTag(ClientPairDto pair)
|
||||
public bool HasAnyTag(UserPairDto pair)
|
||||
{
|
||||
return GetUidTagDictionaryForCurrentServer().ContainsKey(pair.OtherUID);
|
||||
return GetUidTagDictionaryForCurrentServer().ContainsKey(pair.User.UID);
|
||||
}
|
||||
|
||||
private void RemoveTagFromPairedUid(string otherUid, string tagName)
|
||||
@@ -114,22 +110,12 @@ namespace MareSynchronos.UI.Handlers
|
||||
|
||||
private Dictionary<string, List<string>> GetUidTagDictionaryForCurrentServer()
|
||||
{
|
||||
if (!_configuration.UidServerPairedUserTags.ContainsKey(_configuration.ApiUri))
|
||||
{
|
||||
_configuration.UidServerPairedUserTags.Add(_configuration.ApiUri, new(StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
return _configuration.UidServerPairedUserTags[_configuration.ApiUri];
|
||||
return _serverConfigurationManager.CurrentServer!.UidServerPairedUserTags;
|
||||
}
|
||||
|
||||
private HashSet<string> GetAvailableTagsForCurrentServer()
|
||||
{
|
||||
if (!_configuration.ServerAvailablePairTags.ContainsKey(_configuration.ApiUri))
|
||||
{
|
||||
_configuration.ServerAvailablePairTags.Add(_configuration.ApiUri, new(StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
return _configuration.ServerAvailablePairTags[_configuration.ApiUri];
|
||||
return _serverConfigurationManager.CurrentServer!.ServerAvailablePairTags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using ImGuiNET;
|
||||
@@ -11,25 +6,30 @@ using MareSynchronos.Utils;
|
||||
using MareSynchronos.Localization;
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.FileCache;
|
||||
using Dalamud.Interface;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Delegates;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
internal class IntroUi : Window, IDisposable
|
||||
{
|
||||
private readonly UiShared _uiShared;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly PeriodicFileScanner _fileCacheManager;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private bool _readFirstPage;
|
||||
|
||||
public event SwitchUi? SwitchToMainUi;
|
||||
public event VoidDelegate? SwitchToMainUi;
|
||||
|
||||
private string[] TosParagraphs;
|
||||
private string[]? _tosParagraphs;
|
||||
|
||||
private Task _timeoutTask;
|
||||
private string _timeoutLabel;
|
||||
private Task? _timeoutTask;
|
||||
private string _timeoutLabel = string.Empty;
|
||||
|
||||
private Dictionary<string, string> _languages = new(StringComparer.Ordinal) { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
||||
private readonly Dictionary<string, string> _languages = new(StringComparer.Ordinal) { { "English", "en" }, { "Deutsch", "de" }, { "Français", "fr" } };
|
||||
private int _currentLanguage;
|
||||
|
||||
public void Dispose()
|
||||
@@ -39,20 +39,22 @@ internal class IntroUi : Window, IDisposable
|
||||
_windowSystem.RemoveWindow(this);
|
||||
}
|
||||
|
||||
public IntroUi(WindowSystem windowSystem, UiShared uiShared, Configuration pluginConfiguration,
|
||||
PeriodicFileScanner fileCacheManager) : base("Mare Synchronos Setup")
|
||||
public IntroUi(WindowSystem windowSystem, UiShared uiShared, ConfigurationService configService,
|
||||
PeriodicFileScanner fileCacheManager, ServerConfigurationManager serverConfigurationManager) : base("Mare Synchronos Setup")
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(IntroUi));
|
||||
|
||||
_uiShared = uiShared;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_configService = configService;
|
||||
_fileCacheManager = fileCacheManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_windowSystem = windowSystem;
|
||||
IsOpen = false;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MinimumSize = new Vector2(600, 400),
|
||||
MaximumSize = new Vector2(600, 2000)
|
||||
MaximumSize = new Vector2(600, 2000),
|
||||
};
|
||||
|
||||
GetToSLocalization();
|
||||
@@ -64,7 +66,7 @@ internal class IntroUi : Window, IDisposable
|
||||
{
|
||||
if (_uiShared.IsInGpose) return;
|
||||
|
||||
if (!_pluginConfiguration.AcceptedAgreement && !_readFirstPage)
|
||||
if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
|
||||
{
|
||||
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
||||
ImGui.TextUnformatted("Welcome to Mare Synchronos");
|
||||
@@ -87,13 +89,12 @@ internal class IntroUi : Window, IDisposable
|
||||
for (int i = 60; i > 0; i--)
|
||||
{
|
||||
_timeoutLabel = $"{Strings.ToS.ButtonWillBeAvailableIn} {i}s";
|
||||
Logger.Debug(_timeoutLabel);
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (!_pluginConfiguration.AcceptedAgreement && _readFirstPage)
|
||||
else if (!_configService.Current.AcceptedAgreement && _readFirstPage)
|
||||
{
|
||||
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
||||
var textSize = ImGui.CalcTextSize(Strings.ToS.LanguageLabel);
|
||||
@@ -124,20 +125,20 @@ internal class IntroUi : Window, IDisposable
|
||||
ImGui.Separator();
|
||||
|
||||
|
||||
UiShared.TextWrapped(TosParagraphs[0]);
|
||||
UiShared.TextWrapped(TosParagraphs[1]);
|
||||
UiShared.TextWrapped(TosParagraphs[2]);
|
||||
UiShared.TextWrapped(TosParagraphs[3]);
|
||||
UiShared.TextWrapped(TosParagraphs[4]);
|
||||
UiShared.TextWrapped(TosParagraphs[5]);
|
||||
UiShared.TextWrapped(_tosParagraphs![0]);
|
||||
UiShared.TextWrapped(_tosParagraphs![1]);
|
||||
UiShared.TextWrapped(_tosParagraphs![2]);
|
||||
UiShared.TextWrapped(_tosParagraphs![3]);
|
||||
UiShared.TextWrapped(_tosParagraphs![4]);
|
||||
UiShared.TextWrapped(_tosParagraphs![5]);
|
||||
|
||||
ImGui.Separator();
|
||||
if (_timeoutTask?.IsCompleted ?? true)
|
||||
{
|
||||
if (ImGui.Button(Strings.ToS.AgreeLabel + "##toSetup"))
|
||||
{
|
||||
_pluginConfiguration.AcceptedAgreement = true;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.AcceptedAgreement = true;
|
||||
_configService.Save();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -145,10 +146,10 @@ internal class IntroUi : Window, IDisposable
|
||||
UiShared.TextWrapped(_timeoutLabel);
|
||||
}
|
||||
}
|
||||
else if (_pluginConfiguration.AcceptedAgreement
|
||||
&& (string.IsNullOrEmpty(_pluginConfiguration.CacheFolder)
|
||||
|| _pluginConfiguration.InitialScanComplete == false
|
||||
|| !Directory.Exists(_pluginConfiguration.CacheFolder)))
|
||||
else if (_configService.Current.AcceptedAgreement
|
||||
&& (string.IsNullOrEmpty(_configService.Current.CacheFolder)
|
||||
|| _configService.Current.InitialScanComplete == false
|
||||
|| !Directory.Exists(_configService.Current.CacheFolder)))
|
||||
{
|
||||
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
||||
ImGui.TextUnformatted("File Storage Setup");
|
||||
@@ -171,11 +172,11 @@ internal class IntroUi : Window, IDisposable
|
||||
_uiShared.DrawCacheDirectorySetting();
|
||||
}
|
||||
|
||||
if (!_fileCacheManager.IsScanRunning && !string.IsNullOrEmpty(_pluginConfiguration.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_pluginConfiguration.CacheFolder))
|
||||
if (!_fileCacheManager.IsScanRunning && !string.IsNullOrEmpty(_configService.Current.CacheFolder) && _uiShared.HasValidPenumbraModPath && Directory.Exists(_configService.Current.CacheFolder))
|
||||
{
|
||||
if (ImGui.Button("Start Scan##startScan"))
|
||||
{
|
||||
_fileCacheManager.InvokeScan(true);
|
||||
_fileCacheManager.InvokeScan(forced: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -204,7 +205,37 @@ internal class IntroUi : Window, IDisposable
|
||||
|
||||
UiShared.TextWrapped("Once you have received a secret key you can connect to the service using the tools provided below.");
|
||||
|
||||
_uiShared.DrawServiceSelection(() => { });
|
||||
var idx = _uiShared.DrawServiceSelection(selectOnChange: true);
|
||||
|
||||
var text = "Enter Secret Key";
|
||||
var buttonText = "Save";
|
||||
var buttonWidth = _secretKey.Length != 64 ? 0 : ImGuiHelpers.GetButtonSize(buttonText).X + ImGui.GetStyle().ItemSpacing.X;
|
||||
var textSize = ImGui.CalcTextSize(text);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text(text);
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(UiShared.GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonWidth - textSize.X);
|
||||
ImGui.InputText("", ref _secretKey, 64);
|
||||
if (_secretKey.Length > 0 && _secretKey.Length != 64)
|
||||
{
|
||||
UiShared.ColorTextWrapped("Your secret key must be exactly 64 characters long. Don't enter your Lodestone auth here.", ImGuiColors.DalamudRed);
|
||||
}
|
||||
else if (_secretKey.Length == 64)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button(buttonText))
|
||||
{
|
||||
if (_serverConfigurationManager.CurrentServer == null) _serverConfigurationManager.SelectServer(0);
|
||||
_serverConfigurationManager.CurrentServer!.SecretKeys.Add(_serverConfigurationManager.CurrentServer.SecretKeys.Select(k => k.Key).LastOrDefault() + 1, new SecretKey()
|
||||
{
|
||||
FriendlyName = $"Secret Key added on Setup ({DateTime.Now:yyyy-MM-dd})",
|
||||
Key = _secretKey,
|
||||
});
|
||||
_serverConfigurationManager.AddCurrentCharacterToServer(addLastSecretKey: true);
|
||||
_secretKey = string.Empty;
|
||||
Task.Run(() => _uiShared.ApiController.CreateConnections(forceGetToken: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -222,6 +253,6 @@ internal class IntroUi : Window, IDisposable
|
||||
_uiShared.LoadLocalization(_languages.ElementAt(changeLanguageTo).Value);
|
||||
}
|
||||
|
||||
TosParagraphs = new[] { Strings.ToS.Paragraph1, Strings.ToS.Paragraph2, Strings.ToS.Paragraph3, Strings.ToS.Paragraph4, Strings.ToS.Paragraph5, Strings.ToS.Paragraph6 };
|
||||
_tosParagraphs = new[] { Strings.ToS.Paragraph1, Strings.ToS.Paragraph2, Strings.ToS.Paragraph3, Strings.ToS.Paragraph4, Strings.ToS.Paragraph5, Strings.ToS.Paragraph6 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,42 +3,40 @@ using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Dalamud.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using MareSynchronos.Export;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.API.Data.Comparer;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Delegates;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
public delegate void SwitchUi();
|
||||
public class SettingsUi : Window, IDisposable
|
||||
{
|
||||
private readonly Configuration _configuration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly WindowSystem _windowSystem;
|
||||
private readonly ApiController _apiController;
|
||||
private ApiController ApiController => _uiShared.ApiController;
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly UiShared _uiShared;
|
||||
public CharacterCacheDto LastCreatedCharacterData { private get; set; }
|
||||
public CharacterData? LastCreatedCharacterData { private get; set; }
|
||||
|
||||
public event SwitchUi? SwitchToIntroUi;
|
||||
public event VoidDelegate? SwitchToIntroUi;
|
||||
private bool _overwriteExistingLabels = false;
|
||||
private bool? _notesSuccessfullyApplied = null;
|
||||
private string _lastTab = string.Empty;
|
||||
private bool _openPopupOnAddition;
|
||||
private bool _hideInfoMessages;
|
||||
private bool _disableOptionalPluginsWarnings;
|
||||
private bool _wasOpen = false;
|
||||
|
||||
public SettingsUi(WindowSystem windowSystem,
|
||||
UiShared uiShared, Configuration configuration, ApiController apiController,
|
||||
MareCharaFileManager mareCharaFileManager) : base("Mare Synchronos Settings")
|
||||
UiShared uiShared, ConfigurationService configService,
|
||||
MareCharaFileManager mareCharaFileManager, PairManager pairManager, ServerConfigurationManager serverConfigurationManager) : base("Mare Synchronos Settings")
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(SettingsUi));
|
||||
|
||||
@@ -48,27 +46,26 @@ public class SettingsUi : Window, IDisposable
|
||||
MaximumSize = new Vector2(800, 2000),
|
||||
};
|
||||
|
||||
_configuration = configuration;
|
||||
_configService = configService;
|
||||
_windowSystem = windowSystem;
|
||||
_apiController = apiController;
|
||||
_mareCharaFileManager = mareCharaFileManager;
|
||||
_pairManager = pairManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_uiShared = uiShared;
|
||||
_openPopupOnAddition = _configuration.OpenPopupOnAdd;
|
||||
_hideInfoMessages = _configuration.HideInfoMessages;
|
||||
_disableOptionalPluginsWarnings = _configuration.DisableOptionalPluginWarnings;
|
||||
|
||||
_uiShared.GposeStart += _uiShared_GposeStart;
|
||||
_uiShared.GposeEnd += _uiShared_GposeEnd;
|
||||
|
||||
_uiShared.GposeStart += UiShared_GposeStart;
|
||||
_uiShared.GposeEnd += UiShared_GposeEnd;
|
||||
|
||||
windowSystem.AddWindow(this);
|
||||
}
|
||||
|
||||
private void _uiShared_GposeEnd()
|
||||
private void UiShared_GposeEnd()
|
||||
{
|
||||
IsOpen = _wasOpen;
|
||||
}
|
||||
|
||||
private void _uiShared_GposeStart()
|
||||
private void UiShared_GposeStart()
|
||||
{
|
||||
_wasOpen = IsOpen;
|
||||
IsOpen = false;
|
||||
@@ -78,15 +75,15 @@ public class SettingsUi : Window, IDisposable
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(SettingsUi));
|
||||
|
||||
_uiShared.GposeStart -= _uiShared_GposeStart;
|
||||
_uiShared.GposeEnd -= _uiShared_GposeEnd;
|
||||
_uiShared.GposeStart -= UiShared_GposeStart;
|
||||
_uiShared.GposeEnd -= UiShared_GposeEnd;
|
||||
|
||||
_windowSystem.RemoveWindow(this);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var pluginState = _uiShared.DrawOtherPluginState();
|
||||
_ = _uiShared.DrawOtherPluginState();
|
||||
|
||||
DrawSettingsContent();
|
||||
}
|
||||
@@ -116,7 +113,7 @@ public class SettingsUi : Window, IDisposable
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
if (ApiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
if (ImGui.BeginTabItem("Transfers"))
|
||||
{
|
||||
@@ -131,344 +128,29 @@ public class SettingsUi : Window, IDisposable
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("User Administration"))
|
||||
if (ImGui.BeginTabItem("Service Settings"))
|
||||
{
|
||||
DrawUserAdministration(_apiController.IsConnected);
|
||||
DrawServerConfiguration();
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (_apiController.IsConnected && _apiController.IsModerator)
|
||||
if (ImGui.BeginTabItem("Debug"))
|
||||
{
|
||||
if (ImGui.BeginTabItem("Administration"))
|
||||
{
|
||||
DrawAdministration();
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
DrawDebug();
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
private string _forbiddenFileHashEntry = string.Empty;
|
||||
private string _forbiddenFileHashForbiddenBy = string.Empty;
|
||||
private string _bannedUserHashEntry = string.Empty;
|
||||
private string _bannedUserReasonEntry = string.Empty;
|
||||
|
||||
private void DrawGeneral()
|
||||
private void DrawServerConfiguration()
|
||||
{
|
||||
if (!string.Equals(_lastTab, "General", StringComparison.OrdinalIgnoreCase))
|
||||
_lastTab = "Service Settings";
|
||||
if (ApiController.ServerAlive)
|
||||
{
|
||||
_notesSuccessfullyApplied = null;
|
||||
}
|
||||
UiShared.FontText("Service Actions", _uiShared.UidFont);
|
||||
|
||||
_lastTab = "General";
|
||||
UiShared.FontText("Notes", _uiShared.UidFont);
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
||||
{
|
||||
ImGui.SetClipboardText(_uiShared.GetNotes());
|
||||
}
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard"))
|
||||
{
|
||||
_notesSuccessfullyApplied = null;
|
||||
var notes = ImGui.GetClipboardText();
|
||||
_notesSuccessfullyApplied = _uiShared.ApplyNotesFromClipboard(notes, _overwriteExistingLabels);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Overwrite existing notes", ref _overwriteExistingLabels);
|
||||
UiShared.DrawHelpText("If this option is selected all already existing notes for UIDs will be overwritten by the imported notes.");
|
||||
if (_notesSuccessfullyApplied.HasValue && _notesSuccessfullyApplied.Value)
|
||||
{
|
||||
UiShared.ColorTextWrapped("User Notes successfully imported", ImGuiColors.HealerGreen);
|
||||
}
|
||||
else if (_notesSuccessfullyApplied.HasValue && !_notesSuccessfullyApplied.Value)
|
||||
{
|
||||
UiShared.ColorTextWrapped("Attempt to import notes from clipboard failed. Check formatting and try again", ImGuiColors.DalamudRed);
|
||||
}
|
||||
if (ImGui.Checkbox("Open Notes Popup on user addition", ref _openPopupOnAddition))
|
||||
{
|
||||
_apiController.LastAddedUser = null;
|
||||
_configuration.OpenPopupOnAdd = _openPopupOnAddition;
|
||||
_configuration.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("This will open a popup that allows you to set the notes for a user after successfully adding them to your individual pairs.");
|
||||
|
||||
ImGui.Separator();
|
||||
UiShared.FontText("Server Messages", _uiShared.UidFont);
|
||||
if (ImGui.Checkbox("Hide Server Info Messages", ref _hideInfoMessages))
|
||||
{
|
||||
_configuration.HideInfoMessages = _hideInfoMessages;
|
||||
_configuration.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will not print any \"Info\" labeled messages into the game chat.");
|
||||
if (ImGui.Checkbox("Disable optional plugin warnings", ref _disableOptionalPluginsWarnings))
|
||||
{
|
||||
_configuration.DisableOptionalPluginWarnings = _disableOptionalPluginsWarnings;
|
||||
_configuration.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will not print any \"Warning\" labeled messages for missing optional plugins Heels or Customize+ in the game chat.");
|
||||
}
|
||||
|
||||
private void DrawAdministration()
|
||||
{
|
||||
_lastTab = "Administration";
|
||||
if (ImGui.TreeNode("Forbidden Files Changes"))
|
||||
{
|
||||
if (ImGui.BeginTable("ForbiddenFilesTable", 3, ImGuiTableFlags.RowBg))
|
||||
{
|
||||
ImGui.TableSetupColumn("File Hash", ImGuiTableColumnFlags.None, 290);
|
||||
ImGui.TableSetupColumn("Forbidden By", ImGuiTableColumnFlags.None, 290);
|
||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var forbiddenFile in _apiController.AdminForbiddenFiles)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImGui.Text(forbiddenFile.Hash);
|
||||
ImGui.TableNextColumn();
|
||||
string by = forbiddenFile.ForbiddenBy;
|
||||
if (ImGui.InputText("##forbiddenBy" + forbiddenFile.Hash, ref by, 255))
|
||||
{
|
||||
forbiddenFile.ForbiddenBy = by;
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (_apiController.IsAdmin)
|
||||
{
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(
|
||||
FontAwesomeIcon.Upload.ToIconString() + "##updateFile" + forbiddenFile.Hash))
|
||||
{
|
||||
_ = _apiController.AdminUpdateOrAddForbiddenFile(forbiddenFile);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteFile" +
|
||||
forbiddenFile.Hash))
|
||||
{
|
||||
_ = _apiController.AdminDeleteForbiddenFile(forbiddenFile);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_apiController.IsAdmin)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.InputText("##addFileHash", ref _forbiddenFileHashEntry, 255);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.InputText("##addForbiddenBy", ref _forbiddenFileHashForbiddenBy, 255);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString() + "##addForbiddenFile"))
|
||||
{
|
||||
_ = _apiController.AdminUpdateOrAddForbiddenFile(new ForbiddenFileDto()
|
||||
{
|
||||
ForbiddenBy = _forbiddenFileHashForbiddenBy,
|
||||
Hash = _forbiddenFileHashEntry
|
||||
});
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
ImGui.NextColumn();
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (ImGui.TreeNode("Banned Users"))
|
||||
{
|
||||
if (ImGui.BeginTable("BannedUsersTable", 3, ImGuiTableFlags.RowBg))
|
||||
{
|
||||
ImGui.TableSetupColumn("Character Hash", ImGuiTableColumnFlags.None, 290);
|
||||
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 290);
|
||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var bannedUser in _apiController.AdminBannedUsers)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(bannedUser.CharacterHash);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
string reason = bannedUser.Reason;
|
||||
ImGuiInputTextFlags moderatorFlags = _apiController.IsModerator
|
||||
? ImGuiInputTextFlags.ReadOnly
|
||||
: ImGuiInputTextFlags.None;
|
||||
if (ImGui.InputText("##bannedReason" + bannedUser.CharacterHash, ref reason, 255,
|
||||
moderatorFlags))
|
||||
{
|
||||
bannedUser.Reason = reason;
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (_apiController.IsAdmin)
|
||||
{
|
||||
if (ImGui.Button(FontAwesomeIcon.Upload.ToIconString() + "##updateUser" +
|
||||
bannedUser.CharacterHash))
|
||||
{
|
||||
_ = _apiController.AdminUpdateOrAddBannedUser(bannedUser);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteUser" +
|
||||
bannedUser.CharacterHash))
|
||||
{
|
||||
_ = _apiController.AdminDeleteBannedUser(bannedUser);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.InputText("##addUserHash", ref _bannedUserHashEntry, 255);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (_apiController.IsAdmin)
|
||||
{
|
||||
ImGui.InputText("##addUserReason", ref _bannedUserReasonEntry, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bannedUserReasonEntry = "Banned by " + _uiShared.PlayerName;
|
||||
ImGui.InputText("##addUserReason", ref _bannedUserReasonEntry, 255,
|
||||
ImGuiInputTextFlags.ReadOnly);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString() + "##addForbiddenFile"))
|
||||
{
|
||||
_ = _apiController.AdminUpdateOrAddBannedUser(new BannedUserDto()
|
||||
{
|
||||
CharacterHash = _forbiddenFileHashForbiddenBy,
|
||||
Reason = _forbiddenFileHashEntry
|
||||
});
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (ImGui.TreeNode("Online Users"))
|
||||
{
|
||||
if (ImGui.Button("Refresh Online Users"))
|
||||
{
|
||||
_ = _apiController.RefreshOnlineUsers();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTable("OnlineUsersTable", 3, ImGuiTableFlags.RowBg))
|
||||
{
|
||||
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 100);
|
||||
ImGui.TableSetupColumn("Character Hash", ImGuiTableColumnFlags.None, 300);
|
||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var onlineUser in _apiController.AdminOnlineUsers)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
string icon = onlineUser.IsModerator
|
||||
? FontAwesomeIcon.ChessKing.ToIconString()
|
||||
: onlineUser.IsAdmin
|
||||
? FontAwesomeIcon.Crown.ToIconString()
|
||||
: FontAwesomeIcon.User.ToIconString();
|
||||
ImGui.Text(icon);
|
||||
ImGui.PopFont();
|
||||
ImGui.SameLine();
|
||||
|
||||
ImGui.Text(onlineUser.UID);
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.Copy.ToIconString() + "##onlineUserCopyUID" +
|
||||
onlineUser.CharacterNameHash))
|
||||
{
|
||||
ImGui.SetClipboardText(onlineUser.UID);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
string charNameHash = onlineUser.CharacterNameHash;
|
||||
ImGui.InputText("##onlineUserHash" + onlineUser.CharacterNameHash, ref charNameHash, 255,
|
||||
ImGuiInputTextFlags.ReadOnly);
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.Copy.ToIconString() + "##onlineUserCopyHash" +
|
||||
onlineUser.CharacterNameHash))
|
||||
{
|
||||
ImGui.SetClipboardText(onlineUser.UID);
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.SkullCrossbones.ToIconString() + "##onlineUserBan" +
|
||||
onlineUser.CharacterNameHash))
|
||||
{
|
||||
_ = _apiController.AdminUpdateOrAddBannedUser(new BannedUserDto
|
||||
{
|
||||
CharacterHash = onlineUser.CharacterNameHash,
|
||||
Reason = "Banned by " + _uiShared.PlayerName
|
||||
});
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (!string.Equals(onlineUser.UID, _apiController.UID, StringComparison.Ordinal) && _apiController.IsAdmin)
|
||||
{
|
||||
if (!onlineUser.IsModerator)
|
||||
{
|
||||
if (ImGui.Button(FontAwesomeIcon.ChessKing.ToIconString() +
|
||||
"##onlineUserModerator" +
|
||||
onlineUser.CharacterNameHash))
|
||||
{
|
||||
_apiController.AdminChangeModeratorStatus(onlineUser.UID, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui.Button(FontAwesomeIcon.User.ToIconString() +
|
||||
"##onlineUserNonModerator" +
|
||||
onlineUser.CharacterNameHash))
|
||||
{
|
||||
_apiController.AdminChangeModeratorStatus(onlineUser.UID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
ImGui.EndTable();
|
||||
}
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _deleteFilesPopupModalShown = false;
|
||||
private bool _deleteAccountPopupModalShown = false;
|
||||
|
||||
private void DrawUserAdministration(bool serverAlive)
|
||||
{
|
||||
_lastTab = "UserAdministration";
|
||||
if (serverAlive)
|
||||
{
|
||||
if (ImGui.Button("Delete all my files"))
|
||||
{
|
||||
_deleteFilesPopupModalShown = true;
|
||||
@@ -490,7 +172,7 @@ public class SettingsUi : Window, IDisposable
|
||||
|
||||
if (ImGui.Button("Delete everything", new Vector2(buttonSize, 0)))
|
||||
{
|
||||
Task.Run(() => _apiController.FilesDeleteAll());
|
||||
Task.Run(() => ApiController.FilesDeleteAll());
|
||||
_deleteFilesPopupModalShown = false;
|
||||
}
|
||||
|
||||
@@ -504,7 +186,7 @@ public class SettingsUi : Window, IDisposable
|
||||
UiShared.SetScaledWindowSize(325);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Delete account"))
|
||||
{
|
||||
_deleteAccountPopupModalShown = true;
|
||||
@@ -527,7 +209,7 @@ public class SettingsUi : Window, IDisposable
|
||||
|
||||
if (ImGui.Button("Delete account", new Vector2(buttonSize, 0)))
|
||||
{
|
||||
Task.Run(() => _apiController.UserDelete());
|
||||
Task.Run(() => ApiController.UserDelete());
|
||||
_deleteAccountPopupModalShown = false;
|
||||
SwitchToIntroUi?.Invoke();
|
||||
}
|
||||
@@ -542,37 +224,277 @@ public class SettingsUi : Window, IDisposable
|
||||
UiShared.SetScaledWindowSize(325);
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
ImGui.Separator();
|
||||
|
||||
}
|
||||
|
||||
if (!_configuration.FullPause)
|
||||
UiShared.FontText("Service & Character Settings", _uiShared.UidFont);
|
||||
|
||||
var idx = _uiShared.DrawServiceSelection();
|
||||
|
||||
ImGui.Dummy(new Vector2(10, 10));
|
||||
|
||||
var selectedServer = _serverConfigurationManager.GetServerByIndex(idx);
|
||||
if (selectedServer == _serverConfigurationManager.CurrentServer)
|
||||
{
|
||||
UiShared.ColorTextWrapped("Note: to change servers or your secret key you need to disconnect from your current Mare Synchronos server.", ImGuiColors.DalamudYellow);
|
||||
UiShared.ColorTextWrapped("For any changes to be applied to the current service you need to reconnect to the service.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
|
||||
var marePaused = _configuration.FullPause;
|
||||
|
||||
if (_configuration.HasValidSetup())
|
||||
if (ImGui.BeginTabBar("serverTabBar"))
|
||||
{
|
||||
if (ImGui.Checkbox("Disconnect Mare Synchronos", ref marePaused))
|
||||
if (ImGui.BeginTabItem("Character Management"))
|
||||
{
|
||||
_configuration.FullPause = marePaused;
|
||||
_configuration.Save();
|
||||
Task.Run(() => _apiController.CreateConnections(false));
|
||||
UiShared.ColorTextWrapped("Characters listed here will automatically connect to the selected Mare service with the settings as provided below." +
|
||||
" Make sure to enter the character names correctly or use the 'Add current character' button at the bottom.", ImGuiColors.DalamudYellow);
|
||||
int i = 0;
|
||||
foreach (var item in selectedServer.Authentications.ToList())
|
||||
{
|
||||
UiShared.DrawWithID("selectedChara" + i, () =>
|
||||
{
|
||||
var charaName = item.CharacterName;
|
||||
if (ImGui.InputText("Character Name", ref charaName, 64))
|
||||
{
|
||||
item.CharacterName = charaName;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
var worldIdx = (ushort)item.WorldId;
|
||||
var data = _uiShared.WorldData;
|
||||
if (!data.TryGetValue(worldIdx, out string? worldPreview))
|
||||
{
|
||||
worldPreview = data.First().Value;
|
||||
}
|
||||
if (ImGui.BeginCombo("World", worldPreview))
|
||||
{
|
||||
foreach (var world in data)
|
||||
{
|
||||
bool isSelected = worldIdx == world.Key;
|
||||
if (ImGui.Selectable(world.Value, isSelected))
|
||||
{
|
||||
item.WorldId = world.Key;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
var secretKeyIdx = item.SecretKeyIdx;
|
||||
var keys = selectedServer.SecretKeys;
|
||||
if (!keys.TryGetValue(secretKeyIdx, out var secretKey))
|
||||
{
|
||||
secretKey = new();
|
||||
}
|
||||
var friendlyName = secretKey.FriendlyName;
|
||||
if (ImGui.BeginCombo("Secret Key", friendlyName))
|
||||
{
|
||||
foreach (var kvp in keys)
|
||||
{
|
||||
bool isSelected = kvp.Key == secretKeyIdx;
|
||||
if (ImGui.Selectable(kvp.Value.FriendlyName, isSelected))
|
||||
{
|
||||
item.SecretKeyIdx = kvp.Key;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Character"))
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
_serverConfigurationManager.RemoveCharacterFromServer(idx, item);
|
||||
}
|
||||
UiShared.AttachToolTip("Hold CTRL to delete this entry.");
|
||||
|
||||
if (item != selectedServer.Authentications.LastOrDefault())
|
||||
ImGui.Separator();
|
||||
});
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
if (!selectedServer.Authentications.Any(c => string.Equals(c.CharacterName, _uiShared.PlayerName, StringComparison.Ordinal)
|
||||
&& c.WorldId == _uiShared.WorldId))
|
||||
{
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.User, "Add current character"))
|
||||
{
|
||||
_serverConfigurationManager.AddCurrentCharacterToServer(idx);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Plus, "Add new character"))
|
||||
{
|
||||
_serverConfigurationManager.AddEmptyCharacterToServer(idx);
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
UiShared.DrawHelpText("Completely pauses the sync and clears your current data (not uploaded files) on the service.");
|
||||
if (ImGui.BeginTabItem("Secret Key Management"))
|
||||
{
|
||||
foreach (var item in selectedServer.SecretKeys.ToList())
|
||||
{
|
||||
UiShared.DrawWithID("key" + item.Key, () =>
|
||||
{
|
||||
var friendlyName = item.Value.FriendlyName;
|
||||
if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255))
|
||||
{
|
||||
item.Value.FriendlyName = friendlyName;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
var key = item.Value.Key;
|
||||
if (ImGui.InputText("Secret Key", ref key, 64))
|
||||
{
|
||||
item.Value.Key = key;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key"))
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
selectedServer.SecretKeys.Remove(item.Key);
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
}
|
||||
UiShared.AttachToolTip("Hold CTRL to delete this secret key entry");
|
||||
});
|
||||
|
||||
if (item.Key != selectedServer.SecretKeys.Keys.LastOrDefault())
|
||||
ImGui.Separator();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key"))
|
||||
{
|
||||
selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Last().Key + 1, new SecretKey()
|
||||
{
|
||||
FriendlyName = "New Secret Key",
|
||||
});
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Service Settings"))
|
||||
{
|
||||
var serverUri = selectedServer.ServerUri;
|
||||
ImGui.InputText("Service URI", ref serverUri, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
UiShared.DrawHelpText("You cannot edit the service URI. Add a new service if you need to edit the URI.");
|
||||
var serverName = selectedServer.ServerName;
|
||||
var isMain = string.Equals(serverName, ApiController.MainServer, StringComparison.OrdinalIgnoreCase);
|
||||
var flags = isMain ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
|
||||
if (ImGui.InputText("Service Name", ref serverName, 255, flags))
|
||||
{
|
||||
selectedServer.ServerName = serverName;
|
||||
_serverConfigurationManager.Save();
|
||||
}
|
||||
if (isMain)
|
||||
{
|
||||
UiShared.DrawHelpText("You cannot edit the name of the main service.");
|
||||
}
|
||||
if (!isMain)
|
||||
{
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Service"))
|
||||
{
|
||||
if (UiShared.CtrlPressed())
|
||||
{
|
||||
_serverConfigurationManager.DeleteServer(selectedServer);
|
||||
}
|
||||
}
|
||||
UiShared.DrawHelpText("Hold CTRL to delete this service");
|
||||
}
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
private void DrawGeneral()
|
||||
{
|
||||
if (!string.Equals(_lastTab, "General", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
UiShared.ColorText("You cannot reconnect without a valid account on the service.", ImGuiColors.DalamudYellow);
|
||||
_notesSuccessfullyApplied = null;
|
||||
}
|
||||
|
||||
if (marePaused)
|
||||
_lastTab = "General";
|
||||
UiShared.FontText("Notes", _uiShared.UidFont);
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
||||
{
|
||||
_uiShared.DrawServiceSelection(() => { });
|
||||
ImGui.SetClipboardText(UiShared.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList()));
|
||||
}
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard"))
|
||||
{
|
||||
_notesSuccessfullyApplied = null;
|
||||
var notes = ImGui.GetClipboardText();
|
||||
_notesSuccessfullyApplied = _uiShared.ApplyNotesFromClipboard(notes, _overwriteExistingLabels);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Overwrite existing notes", ref _overwriteExistingLabels);
|
||||
UiShared.DrawHelpText("If this option is selected all already existing notes for UIDs will be overwritten by the imported notes.");
|
||||
if (_notesSuccessfullyApplied.HasValue && _notesSuccessfullyApplied.Value)
|
||||
{
|
||||
UiShared.ColorTextWrapped("User Notes successfully imported", ImGuiColors.HealerGreen);
|
||||
}
|
||||
else if (_notesSuccessfullyApplied.HasValue && !_notesSuccessfullyApplied.Value)
|
||||
{
|
||||
UiShared.ColorTextWrapped("Attempt to import notes from clipboard failed. Check formatting and try again", ImGuiColors.DalamudRed);
|
||||
}
|
||||
|
||||
var openPopupOnAddition = _configService.Current.OpenPopupOnAdd;
|
||||
var hideInfoMessages = _configService.Current.HideInfoMessages;
|
||||
var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
|
||||
var onlineNotifs = _configService.Current.ShowOnlineNotifications;
|
||||
var onlineNotifsPairsOnly = _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs;
|
||||
|
||||
if (ImGui.Checkbox("Open Notes Popup on user addition", ref openPopupOnAddition))
|
||||
{
|
||||
ApiController.LastAddedUser = null;
|
||||
_configService.Current.OpenPopupOnAdd = openPopupOnAddition;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("This will open a popup that allows you to set the notes for a user after successfully adding them to your individual pairs.");
|
||||
|
||||
ImGui.Separator();
|
||||
UiShared.FontText("Server Messages", _uiShared.UidFont);
|
||||
if (ImGui.Checkbox("Hide Server Info Messages", ref hideInfoMessages))
|
||||
{
|
||||
_configService.Current.HideInfoMessages = hideInfoMessages;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will not print any \"Info\" labeled messages into the game chat.");
|
||||
if (ImGui.Checkbox("Disable optional plugin warnings", ref disableOptionalPluginWarnings))
|
||||
{
|
||||
_configService.Current.DisableOptionalPluginWarnings = disableOptionalPluginWarnings;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will not print any \"Warning\" labeled messages for missing optional plugins Heels or Customize+ in the game chat.");
|
||||
if (ImGui.Checkbox("Enable online notifications", ref onlineNotifs))
|
||||
{
|
||||
_configService.Current.ShowOnlineNotifications = onlineNotifs;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will show a small notification in the bottom right corner when pairs go online.");
|
||||
|
||||
if (!onlineNotifs) ImGui.BeginDisabled();
|
||||
if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
|
||||
{
|
||||
_configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("Enabling this will only show online notifications for individual pairs.");
|
||||
if (!onlineNotifs) ImGui.EndDisabled();
|
||||
}
|
||||
|
||||
private bool _deleteFilesPopupModalShown = false;
|
||||
private bool _deleteAccountPopupModalShown = false;
|
||||
|
||||
private void DrawDebug()
|
||||
{
|
||||
_lastTab = "Debug";
|
||||
|
||||
UiShared.FontText("Debug", _uiShared.UidFont);
|
||||
|
||||
@@ -590,9 +512,6 @@ public class SettingsUi : Window, IDisposable
|
||||
UiShared.AttachToolTip("Use this when reporting mods being rejected from the server.");
|
||||
}
|
||||
|
||||
private string _charaFileSavePath = string.Empty;
|
||||
private string _charaFileLoadPath = string.Empty;
|
||||
|
||||
private void DrawBlockedTransfers()
|
||||
{
|
||||
_lastTab = "BlockedTransfers";
|
||||
@@ -609,7 +528,7 @@ public class SettingsUi : Window, IDisposable
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var item in _apiController.ForbiddenTransfers)
|
||||
foreach (var item in ApiController.ForbiddenTransfers)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
if (item is UploadFileTransfer transfer)
|
||||
@@ -630,14 +549,14 @@ public class SettingsUi : Window, IDisposable
|
||||
private void DrawCurrentTransfers()
|
||||
{
|
||||
_lastTab = "Transfers";
|
||||
bool showTransferWindow = _configuration.ShowTransferWindow;
|
||||
bool showTransferWindow = _configService.Current.ShowTransferWindow;
|
||||
if (ImGui.Checkbox("Show separate Transfer window while transfers are active", ref showTransferWindow))
|
||||
{
|
||||
_configuration.ShowTransferWindow = showTransferWindow;
|
||||
_configuration.Save();
|
||||
_configService.Current.ShowTransferWindow = showTransferWindow;
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
if (_configuration.ShowTransferWindow)
|
||||
if (_configService.Current.ShowTransferWindow)
|
||||
{
|
||||
ImGui.Indent();
|
||||
bool editTransferWindowPosition = _uiShared.EditTrackerPosition;
|
||||
@@ -651,8 +570,8 @@ public class SettingsUi : Window, IDisposable
|
||||
if (ImGui.BeginTable("TransfersTable", 2))
|
||||
{
|
||||
ImGui.TableSetupColumn(
|
||||
$"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Total))})");
|
||||
ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Total))})");
|
||||
$"Uploads ({UiShared.ByteToString(ApiController.CurrentUploads.Sum(a => a.Transferred))} / {UiShared.ByteToString(ApiController.CurrentUploads.Sum(a => a.Total))})");
|
||||
ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(ApiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Transferred))} / {UiShared.ByteToString(ApiController.CurrentDownloads.SelectMany(k => k.Value).ToList().Sum(a => a.Total))})");
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
@@ -663,7 +582,7 @@ public class SettingsUi : Window, IDisposable
|
||||
ImGui.TableSetupColumn("Uploaded");
|
||||
ImGui.TableSetupColumn("Size");
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var transfer in _apiController.CurrentUploads.ToArray())
|
||||
foreach (var transfer in ApiController.CurrentUploads.ToArray())
|
||||
{
|
||||
var color = UiShared.UploadColor((transfer.Transferred, transfer.Total));
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
||||
@@ -687,7 +606,7 @@ public class SettingsUi : Window, IDisposable
|
||||
ImGui.TableSetupColumn("Downloaded");
|
||||
ImGui.TableSetupColumn("Size");
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var transfer in _apiController.CurrentDownloads.SelectMany(k => k.Value).ToArray())
|
||||
foreach (var transfer in ApiController.CurrentDownloads.SelectMany(k => k.Value).ToArray())
|
||||
{
|
||||
var color = UiShared.UploadColor((transfer.Transferred, transfer.Total));
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
||||
@@ -761,11 +680,11 @@ public class SettingsUi : Window, IDisposable
|
||||
|
||||
ImGui.Unindent();
|
||||
}
|
||||
bool openInGpose = _configuration.OpenGposeImportOnGposeStart;
|
||||
bool openInGpose = _configService.Current.OpenGposeImportOnGposeStart;
|
||||
if (ImGui.Checkbox("Open MCDF import window when GPose loads", ref openInGpose))
|
||||
{
|
||||
_configuration.OpenGposeImportOnGposeStart = openInGpose;
|
||||
_configuration.Save();
|
||||
_configService.Current.OpenGposeImportOnGposeStart = openInGpose;
|
||||
_configService.Save();
|
||||
}
|
||||
UiShared.DrawHelpText("This will automatically open the import menu when loading into Gpose. If unchecked you can open the menu manually with /mare gpose");
|
||||
|
||||
@@ -788,7 +707,7 @@ public class SettingsUi : Window, IDisposable
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(_configuration.CacheFolder))
|
||||
foreach (var file in Directory.GetFiles(_configService.Current.CacheFolder))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
@@ -1,40 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.Delegates;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Localization;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Models;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
public class UiShared : IDisposable
|
||||
public partial class UiShared : IDisposable
|
||||
{
|
||||
[DllImport("user32")]
|
||||
public static extern short GetKeyState(int nVirtKey);
|
||||
[LibraryImport("user32")]
|
||||
internal static partial short GetKeyState(int nVirtKey);
|
||||
|
||||
private readonly IpcManager _ipcManager;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly PeriodicFileScanner _cacheScanner;
|
||||
public readonly FileDialogManager FileDialogManager;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly Dalamud.Localization _localization;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
public long FileCacheSize => _cacheScanner.FileCacheSize;
|
||||
public string PlayerName => _dalamudUtil.PlayerName;
|
||||
public uint WorldId => _dalamudUtil.WorldId;
|
||||
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
|
||||
public bool HasValidPenumbraModPath => !(_ipcManager.PenumbraModDirectory() ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.PenumbraModDirectory());
|
||||
public bool EditTrackerPosition { get; set; }
|
||||
public ImFontPtr UidFont { get; private set; }
|
||||
@@ -45,38 +49,40 @@ public class UiShared : IDisposable
|
||||
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 static ImGuiWindowFlags PopupWindowFlags = ImGuiWindowFlags.NoResize |
|
||||
public static readonly ImGuiWindowFlags PopupWindowFlags = ImGuiWindowFlags.NoResize |
|
||||
ImGuiWindowFlags.NoScrollbar |
|
||||
ImGuiWindowFlags.NoScrollWithMouse;
|
||||
|
||||
public ApiController ApiController => _apiController;
|
||||
|
||||
public UiShared(IpcManager ipcManager, ApiController apiController, PeriodicFileScanner cacheScanner, FileDialogManager fileDialogManager,
|
||||
Configuration pluginConfiguration, DalamudUtil dalamudUtil, DalamudPluginInterface pluginInterface, Dalamud.Localization localization)
|
||||
ConfigurationService configService, DalamudUtil dalamudUtil, DalamudPluginInterface pluginInterface, Dalamud.Localization localization,
|
||||
ServerConfigurationManager serverManager)
|
||||
{
|
||||
_ipcManager = ipcManager;
|
||||
_apiController = apiController;
|
||||
_cacheScanner = cacheScanner;
|
||||
FileDialogManager = fileDialogManager;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_configService = configService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_pluginInterface = pluginInterface;
|
||||
_localization = localization;
|
||||
_isDirectoryWritable = IsDirectoryWritable(_pluginConfiguration.CacheFolder);
|
||||
_serverConfigurationManager = serverManager;
|
||||
_isDirectoryWritable = IsDirectoryWritable(_configService.Current.CacheFolder);
|
||||
|
||||
_pluginInterface.UiBuilder.BuildFonts += BuildFont;
|
||||
_pluginInterface.UiBuilder.RebuildFonts();
|
||||
|
||||
_dalamudUtil.GposeStart += _dalamudUtil_GposeStart;
|
||||
_dalamudUtil.GposeEnd += _dalamudUtil_GposeEnd;
|
||||
_dalamudUtil.GposeStart += DalamudUtil_GposeStart;
|
||||
_dalamudUtil.GposeEnd += DalamudUtil_GposeEnd;
|
||||
}
|
||||
|
||||
private void _dalamudUtil_GposeEnd()
|
||||
private void DalamudUtil_GposeEnd()
|
||||
{
|
||||
GposeEnd?.Invoke();
|
||||
}
|
||||
|
||||
private void _dalamudUtil_GposeStart()
|
||||
private void DalamudUtil_GposeStart()
|
||||
{
|
||||
GposeStart?.Invoke();
|
||||
}
|
||||
@@ -219,13 +225,13 @@ public class UiShared : IDisposable
|
||||
? "Collecting files"
|
||||
: $"Processing {_cacheScanner.CurrentFileProgress} / {_cacheScanner.TotalFiles} files");
|
||||
}
|
||||
else if (_pluginConfiguration.FileScanPaused)
|
||||
else if (_configService.Current.FileScanPaused)
|
||||
{
|
||||
ImGui.Text("File scanner is paused");
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Force Rescan##forcedrescan"))
|
||||
{
|
||||
_cacheScanner.InvokeScan(true);
|
||||
_cacheScanner.InvokeScan(forced: true);
|
||||
}
|
||||
}
|
||||
else if (_cacheScanner.haltScanLocks.Any(f => f.Value > 0))
|
||||
@@ -245,18 +251,15 @@ public class UiShared : IDisposable
|
||||
|
||||
public void PrintServerState()
|
||||
{
|
||||
var serverName = _apiController.ServerDictionary.ContainsKey(_pluginConfiguration.ApiUri)
|
||||
? _apiController.ServerDictionary[_pluginConfiguration.ApiUri]
|
||||
: _pluginConfiguration.ApiUri;
|
||||
if (_apiController.ServerState is ServerState.Connected)
|
||||
{
|
||||
ImGui.TextUnformatted("Service " + serverName + ":");
|
||||
ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":");
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, "Available");
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("(");
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString());
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.SameLine();
|
||||
ImGui.Text("Users Online");
|
||||
ImGui.SameLine();
|
||||
@@ -345,17 +348,23 @@ public class UiShared : IDisposable
|
||||
return $"{dblSByte:0.00} {suffix[i]}";
|
||||
}
|
||||
|
||||
private int _serverSelectionIndex = 0;
|
||||
private int _serverSelectionIndex = -1;
|
||||
private string _customServerName = "";
|
||||
private string _customServerUri = "";
|
||||
private bool _enterSecretKey = false;
|
||||
private bool _cacheDirectoryHasOtherFilesThanCache = false;
|
||||
private bool _cacheDirectoryIsValidPath = true;
|
||||
|
||||
public void DrawServiceSelection(Action? callBackOnExit = null)
|
||||
public int DrawServiceSelection(bool selectOnChange = false)
|
||||
{
|
||||
string[] comboEntries = _apiController.ServerDictionary.Values.ToArray();
|
||||
_serverSelectionIndex = Array.IndexOf(_apiController.ServerDictionary.Keys.ToArray(), _pluginConfiguration.ApiUri);
|
||||
string[] comboEntries = _serverConfigurationManager.GetServerNames();
|
||||
|
||||
if (_serverSelectionIndex == -1)
|
||||
_serverSelectionIndex = Array.IndexOf(_serverConfigurationManager.GetServerApiUrls(), _serverConfigurationManager.CurrentApiUrl);
|
||||
for (int i = 0; i < comboEntries.Length; i++)
|
||||
{
|
||||
if (string.Equals(_serverConfigurationManager.CurrentServer?.ServerName, comboEntries[i], StringComparison.OrdinalIgnoreCase))
|
||||
comboEntries[i] += " [Current]";
|
||||
}
|
||||
if (ImGui.BeginCombo("Select Service", comboEntries[_serverSelectionIndex]))
|
||||
{
|
||||
for (int i = 0; i < comboEntries.Length; i++)
|
||||
@@ -363,9 +372,11 @@ public class UiShared : IDisposable
|
||||
bool isSelected = _serverSelectionIndex == i;
|
||||
if (ImGui.Selectable(comboEntries[i], isSelected))
|
||||
{
|
||||
_pluginConfiguration.ApiUri = _apiController.ServerDictionary.Single(k => string.Equals(k.Value, comboEntries[i], StringComparison.Ordinal)).Key;
|
||||
_pluginConfiguration.Save();
|
||||
_ = _apiController.CreateConnections();
|
||||
_serverSelectionIndex = i;
|
||||
if (selectOnChange)
|
||||
{
|
||||
_serverConfigurationManager.SelectServer(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSelected)
|
||||
@@ -377,113 +388,61 @@ public class UiShared : IDisposable
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (_serverSelectionIndex != 0)
|
||||
if (_serverConfigurationManager.GetSecretKey(_serverSelectionIndex) != null)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteService"))
|
||||
var text = "Connect";
|
||||
if (_serverSelectionIndex == _serverConfigurationManager.GetCurrentServerIndex()) text = "Reconnect";
|
||||
if (IconTextButton(FontAwesomeIcon.Link, text))
|
||||
{
|
||||
_pluginConfiguration.CustomServerList.Remove(_pluginConfiguration.ApiUri);
|
||||
_pluginConfiguration.ApiUri = _apiController.ServerDictionary.First().Key;
|
||||
_pluginConfiguration.Save();
|
||||
_serverConfigurationManager.SelectServer(_serverSelectionIndex);
|
||||
_ = _apiController.CreateConnections();
|
||||
}
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
if (ImGui.TreeNode("Add Custom Service"))
|
||||
{
|
||||
ImGui.SetNextItemWidth(250);
|
||||
ImGui.InputText("Custom Service Name", ref _customServerName, 255);
|
||||
ImGui.InputText("Custom Service URI", ref _customServerUri, 255);
|
||||
ImGui.SetNextItemWidth(250);
|
||||
ImGui.InputText("Custom Service Address", ref _customServerUri, 255);
|
||||
if (ImGui.Button("Add Custom Service"))
|
||||
ImGui.InputText("Custom Service Name", ref _customServerName, 255);
|
||||
if (UiShared.IconTextButton(FontAwesomeIcon.Plus, "Add Custom Service"))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_customServerUri)
|
||||
&& !string.IsNullOrEmpty(_customServerName)
|
||||
&& !_pluginConfiguration.CustomServerList.ContainsValue(_customServerName)
|
||||
&& !_pluginConfiguration.CustomServerList.ContainsKey(_customServerUri))
|
||||
&& !string.IsNullOrEmpty(_customServerName))
|
||||
{
|
||||
_pluginConfiguration.CustomServerList[_customServerUri] = _customServerName;
|
||||
_customServerUri = string.Empty;
|
||||
_serverConfigurationManager.AddServer(new ServerStorage()
|
||||
{
|
||||
ServerName = _customServerName,
|
||||
ServerUri = _customServerUri,
|
||||
});
|
||||
_customServerName = string.Empty;
|
||||
_pluginConfiguration.Save();
|
||||
_customServerUri = string.Empty;
|
||||
_configService.Save();
|
||||
}
|
||||
}
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
PrintServerState();
|
||||
|
||||
if (!_apiController.ServerAlive && (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri) && !_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri].IsNullOrEmpty()))
|
||||
{
|
||||
ColorTextWrapped("You already have an account on this server.", ImGuiColors.DalamudYellow);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Connect##connectToService"))
|
||||
{
|
||||
_pluginConfiguration.FullPause = false;
|
||||
_pluginConfiguration.Save();
|
||||
Task.Run(() => _apiController.CreateConnections(true));
|
||||
}
|
||||
}
|
||||
|
||||
string checkboxText = _pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri)
|
||||
? "I want to switch accounts"
|
||||
: "I have an account";
|
||||
ImGui.Checkbox(checkboxText, ref _enterSecretKey);
|
||||
|
||||
if (_enterSecretKey)
|
||||
{
|
||||
if (_pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri))
|
||||
{
|
||||
ColorTextWrapped("A secret key was previously set for this service. Entering a new secret key will overwrite the one set prior.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
|
||||
var text = "Enter Secret Key";
|
||||
var buttonText = "Save";
|
||||
var buttonWidth = _secretKey.Length != 64 ? 0 : ImGuiHelpers.GetButtonSize(buttonText).X + ImGui.GetStyle().ItemSpacing.X;
|
||||
var textSize = ImGui.CalcTextSize(text);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text(text);
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(GetWindowContentRegionWidth() - ImGui.GetWindowContentRegionMin().X - buttonWidth - textSize.X);
|
||||
ImGui.InputText("", ref _secretKey, 64);
|
||||
if (_secretKey.Length > 0 && _secretKey.Length != 64)
|
||||
{
|
||||
ColorTextWrapped("Your secret key must be exactly 64 characters long. Don't enter your Lodestone auth here.", ImGuiColors.DalamudRed);
|
||||
}
|
||||
else if (_secretKey.Length == 64)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button(buttonText))
|
||||
{
|
||||
_pluginConfiguration.ClientSecret[_pluginConfiguration.ApiUri] = _secretKey;
|
||||
_pluginConfiguration.Save();
|
||||
_secretKey = string.Empty;
|
||||
Task.Run(() => _apiController.CreateConnections(true));
|
||||
_enterSecretKey = false;
|
||||
callBackOnExit?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _serverSelectionIndex;
|
||||
}
|
||||
|
||||
private string _secretKey = "";
|
||||
|
||||
public static void OutlineTextWrapped(string text, Vector4 textcolor, Vector4 outlineColor, float dist = 3)
|
||||
{
|
||||
var cursorPos = ImGui.GetCursorPos();
|
||||
UiShared.ColorTextWrapped(text, outlineColor);
|
||||
ColorTextWrapped(text, outlineColor);
|
||||
ImGui.SetCursorPos(new(cursorPos.X, cursorPos.Y + dist));
|
||||
UiShared.ColorTextWrapped(text, outlineColor);
|
||||
ColorTextWrapped(text, outlineColor);
|
||||
ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y));
|
||||
UiShared.ColorTextWrapped(text, outlineColor);
|
||||
ColorTextWrapped(text, outlineColor);
|
||||
ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y + dist));
|
||||
UiShared.ColorTextWrapped(text, outlineColor);
|
||||
ColorTextWrapped(text, outlineColor);
|
||||
|
||||
ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2));
|
||||
UiShared.ColorTextWrapped(text, textcolor);
|
||||
ColorTextWrapped(text, textcolor);
|
||||
ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2));
|
||||
UiShared.ColorTextWrapped(text, textcolor);
|
||||
ColorTextWrapped(text, textcolor);
|
||||
}
|
||||
|
||||
public static void DrawHelpText(string helpText)
|
||||
@@ -507,7 +466,7 @@ public class UiShared : IDisposable
|
||||
public void DrawCacheDirectorySetting()
|
||||
{
|
||||
ColorTextWrapped("Note: The storage folder should be somewhere close to root (i.e. C:\\MareStorage) in a new empty folder. DO NOT point this to your game folder. DO NOT point this to your Penumbra folder.", ImGuiColors.DalamudYellow);
|
||||
var cacheDirectory = _pluginConfiguration.CacheFolder;
|
||||
var cacheDirectory = _configService.Current.CacheFolder;
|
||||
ImGui.InputText("Storage Folder##cache", ref cacheDirectory, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
|
||||
ImGui.SameLine();
|
||||
@@ -531,8 +490,8 @@ public class UiShared : IDisposable
|
||||
&& !_cacheDirectoryHasOtherFilesThanCache
|
||||
&& _cacheDirectoryIsValidPath)
|
||||
{
|
||||
_pluginConfiguration.CacheFolder = path;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.CacheFolder = path;
|
||||
_configService.Save();
|
||||
_cacheScanner.StartScan();
|
||||
}
|
||||
});
|
||||
@@ -557,11 +516,11 @@ public class UiShared : IDisposable
|
||||
"Restrict yourself to latin letters (A-Z), underscores (_), dashes (-) and arabic numbers (0-9).", ImGuiColors.DalamudRed);
|
||||
}
|
||||
|
||||
float maxCacheSize = (float)_pluginConfiguration.MaxLocalCacheInGiB;
|
||||
float maxCacheSize = (float)_configService.Current.MaxLocalCacheInGiB;
|
||||
if (ImGui.SliderFloat("Maximum Storage Size in GiB", ref maxCacheSize, 1f, 200f, "%.2f GiB"))
|
||||
{
|
||||
_pluginConfiguration.MaxLocalCacheInGiB = maxCacheSize;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.MaxLocalCacheInGiB = maxCacheSize;
|
||||
_configService.Save();
|
||||
}
|
||||
DrawHelpText("The storage is automatically governed by Mare. It will clear itself automatically once it reaches the set capacity by removing the oldest unused files. You typically do not need to clear it yourself.");
|
||||
}
|
||||
@@ -569,19 +528,17 @@ public class UiShared : IDisposable
|
||||
private bool _isDirectoryWritable = false;
|
||||
private bool _isPenumbraDirectory = false;
|
||||
|
||||
public bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
|
||||
public static bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = File.Create(
|
||||
using FileStream fs = File.Create(
|
||||
Path.Combine(
|
||||
dirPath,
|
||||
Path.GetRandomFileName()
|
||||
),
|
||||
1,
|
||||
FileOptions.DeleteOnClose)
|
||||
)
|
||||
{ }
|
||||
FileOptions.DeleteOnClose);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@@ -595,23 +552,23 @@ public class UiShared : IDisposable
|
||||
|
||||
public void RecalculateFileCacheSize()
|
||||
{
|
||||
_cacheScanner.InvokeScan(true);
|
||||
_cacheScanner.InvokeScan(forced: true);
|
||||
}
|
||||
|
||||
public void DrawTimeSpanBetweenScansSetting()
|
||||
{
|
||||
var timeSpan = _pluginConfiguration.TimeSpanBetweenScansInSeconds;
|
||||
var timeSpan = _configService.Current.TimeSpanBetweenScansInSeconds;
|
||||
if (ImGui.SliderInt("Seconds between scans##timespan", ref timeSpan, 20, 60))
|
||||
{
|
||||
_pluginConfiguration.TimeSpanBetweenScansInSeconds = timeSpan;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.TimeSpanBetweenScansInSeconds = timeSpan;
|
||||
_configService.Save();
|
||||
}
|
||||
DrawHelpText("This is the time in seconds between file scans. Increase it to reduce system load. A too high setting can cause issues when manually fumbling about in the cache or Penumbra mods folders.");
|
||||
var isPaused = _pluginConfiguration.FileScanPaused;
|
||||
var isPaused = _configService.Current.FileScanPaused;
|
||||
if (ImGui.Checkbox("Pause periodic file scan##filescanpause", ref isPaused))
|
||||
{
|
||||
_pluginConfiguration.FileScanPaused = isPaused;
|
||||
_pluginConfiguration.Save();
|
||||
_configService.Current.FileScanPaused = isPaused;
|
||||
_configService.Save();
|
||||
}
|
||||
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.");
|
||||
}
|
||||
@@ -653,24 +610,21 @@ public class UiShared : IDisposable
|
||||
return buttonClicked;
|
||||
}
|
||||
|
||||
private const string NotesStart = "##MARE_SYNCHRONOS_USER_NOTES_START##";
|
||||
private const string NotesEnd = "##MARE_SYNCHRONOS_USER_NOTES_END##";
|
||||
private const string _notesStart = "##MARE_SYNCHRONOS_USER_NOTES_START##";
|
||||
private const string _notesEnd = "##MARE_SYNCHRONOS_USER_NOTES_END##";
|
||||
|
||||
public string GetNotes(string? gid = null)
|
||||
public static string GetNotes(List<Pair> pairs)
|
||||
{
|
||||
var comments = _pluginConfiguration.GetCurrentServerUidComments();
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(NotesStart);
|
||||
foreach (var userEntry in comments.Where(c => !string.IsNullOrEmpty(c.Key)))
|
||||
sb.AppendLine(_notesStart);
|
||||
foreach (var entry in pairs)
|
||||
{
|
||||
if (gid != null)
|
||||
{
|
||||
if (!ApiController.GroupPairedClients.Any(p => string.Equals(p.GroupGID, gid, StringComparison.Ordinal) && string.Equals(p.UserUID, userEntry.Key, StringComparison.Ordinal))) continue;
|
||||
}
|
||||
var note = entry.GetNote();
|
||||
if (note.IsNullOrEmpty()) continue;
|
||||
|
||||
sb.AppendLine(userEntry.Key + ":\"" + userEntry.Value + "\"");
|
||||
sb.AppendLine(entry.UserData.UID + ":\"" + entry.GetNote() + "\"");
|
||||
}
|
||||
sb.AppendLine(NotesEnd);
|
||||
sb.AppendLine(_notesEnd);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
@@ -680,14 +634,14 @@ public class UiShared : IDisposable
|
||||
var splitNotes = notes.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
var splitNotesStart = splitNotes.FirstOrDefault();
|
||||
var splitNotesEnd = splitNotes.LastOrDefault();
|
||||
if (!string.Equals(splitNotesStart, NotesStart) || !string.Equals(splitNotesEnd, NotesEnd))
|
||||
if (!string.Equals(splitNotesStart, _notesStart) || !string.Equals(splitNotesEnd, _notesEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
splitNotes.RemoveAll(n => string.Equals(n, NotesStart) || string.Equals(n, NotesEnd));
|
||||
splitNotes.RemoveAll(n => string.Equals(n, _notesStart) || string.Equals(n, _notesEnd));
|
||||
|
||||
var comments = _pluginConfiguration.GetCurrentServerUidComments();
|
||||
var comments = _serverConfigurationManager.CurrentServer!.UidServerComments;
|
||||
|
||||
foreach (var note in splitNotes)
|
||||
{
|
||||
@@ -697,7 +651,7 @@ public class UiShared : IDisposable
|
||||
var uid = splittedEntry[0];
|
||||
var comment = splittedEntry[1].Trim('"');
|
||||
if (comments.ContainsKey(uid) && !overwrite) continue;
|
||||
_pluginConfiguration.SetCurrentServerUidComment(uid, comment);
|
||||
_serverConfigurationManager.CurrentServer.UidServerComments[uid] = comment;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -705,7 +659,7 @@ public class UiShared : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
_pluginConfiguration.Save();
|
||||
_serverConfigurationManager.Save();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -713,7 +667,7 @@ public class UiShared : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
_pluginInterface.UiBuilder.BuildFonts -= BuildFont;
|
||||
_dalamudUtil.GposeStart -= _dalamudUtil_GposeStart;
|
||||
_dalamudUtil.GposeEnd -= _dalamudUtil_GposeEnd;
|
||||
_dalamudUtil.GposeStart -= DalamudUtil_GposeStart;
|
||||
_dalamudUtil.GposeEnd -= DalamudUtil_GposeEnd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
public class Crypto
|
||||
public static class Crypto
|
||||
{
|
||||
public static string GetFileHash(string filePath)
|
||||
{
|
||||
using SHA1CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(File.ReadAllBytes(filePath))).Replace("-", "", StringComparison.Ordinal);
|
||||
return BitConverter.ToString(SHA1.HashData(File.ReadAllBytes(filePath))).Replace("-", "", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string GetHash(string stringToHash)
|
||||
public static string GetHash(this string stringToHash)
|
||||
{
|
||||
using SHA1CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||
return BitConverter.ToString(SHA1.HashData(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string GetHash256(string stringToHash)
|
||||
public static string GetHash256(this string stringToHash)
|
||||
{
|
||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||
return BitConverter.ToString(SHA256.HashData(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static string GetHash256(PlayerCharacter character)
|
||||
public static string GetHash256(this PlayerCharacter character)
|
||||
{
|
||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(character.Name + character.HomeWorld.Id.ToString()))).Replace("-", "", StringComparison.Ordinal);
|
||||
return BitConverter.ToString(SHA256.HashData(Encoding.UTF8.GetBytes(character.Name + character.HomeWorld.Id.ToString()))).Replace("-", "", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using MareSynchronos.Delegates;
|
||||
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
public delegate void PlayerChange(Dalamud.Game.ClientState.Objects.Types.Character actor);
|
||||
|
||||
public delegate void VoidDelegate();
|
||||
|
||||
public class DalamudUtil : IDisposable
|
||||
{
|
||||
private readonly ClientState _clientState;
|
||||
private readonly ObjectTable _objectTable;
|
||||
private readonly Framework _framework;
|
||||
private readonly Condition _condition;
|
||||
private readonly Dalamud.Game.ClientState.Conditions.Condition _condition;
|
||||
private readonly ChatGui _chatGui;
|
||||
private readonly Dalamud.Data.DataManager _gameData;
|
||||
|
||||
public event VoidDelegate? LogIn;
|
||||
public event VoidDelegate? LogOut;
|
||||
public event VoidDelegate? FrameworkUpdate;
|
||||
public event VoidDelegate? ClassJobChanged;
|
||||
private uint? classJobId = 0;
|
||||
private uint? _classJobId = 0;
|
||||
public event VoidDelegate? DelayedFrameworkUpdate;
|
||||
public event VoidDelegate? ZoneSwitchStart;
|
||||
public event VoidDelegate? ZoneSwitchEnd;
|
||||
@@ -58,21 +52,31 @@ public class DalamudUtil : IDisposable
|
||||
return false;
|
||||
}
|
||||
|
||||
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Condition condition, ChatGui chatGui)
|
||||
public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework, Dalamud.Game.ClientState.Conditions.Condition condition, ChatGui chatGui,
|
||||
Dalamud.Data.DataManager gameData)
|
||||
{
|
||||
_clientState = clientState;
|
||||
_objectTable = objectTable;
|
||||
_framework = framework;
|
||||
_condition = condition;
|
||||
_chatGui = chatGui;
|
||||
_gameData = gameData;
|
||||
_framework.Update += FrameworkOnUpdate;
|
||||
if (IsLoggedIn)
|
||||
{
|
||||
classJobId = _clientState.LocalPlayer!.ClassJob.Id;
|
||||
ClientStateOnLogin(null, EventArgs.Empty);
|
||||
_classJobId = _clientState.LocalPlayer!.ClassJob.Id;
|
||||
ClientStateOnLogin(sender: null, EventArgs.Empty);
|
||||
}
|
||||
WorldData = new(() =>
|
||||
{
|
||||
return gameData.GetExcelSheet<World>(Dalamud.ClientLanguage.English)!
|
||||
.Where(w => w.IsPublic && !w.Name.RawData.IsEmpty)
|
||||
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
public Lazy<Dictionary<ushort, string>> WorldData { get; private set; }
|
||||
|
||||
public void PrintInfoChat(string message)
|
||||
{
|
||||
SeStringBuilder se = new SeStringBuilder().AddText("[Mare Synchronos] Info: ").AddItalics(message);
|
||||
@@ -119,7 +123,8 @@ public class DalamudUtil : IDisposable
|
||||
|
||||
return;
|
||||
}
|
||||
else if (_sentBetweenAreas)
|
||||
|
||||
if (_sentBetweenAreas)
|
||||
{
|
||||
Logger.Debug("Zone switch/Gpose end");
|
||||
_sentBetweenAreas = false;
|
||||
@@ -160,9 +165,9 @@ public class DalamudUtil : IDisposable
|
||||
{
|
||||
var newclassJobId = _clientState.LocalPlayer.ClassJob.Id;
|
||||
|
||||
if (classJobId != newclassJobId)
|
||||
if (_classJobId != newclassJobId)
|
||||
{
|
||||
classJobId = newclassJobId;
|
||||
_classJobId = newclassJobId;
|
||||
ClassJobChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -205,7 +210,7 @@ public class DalamudUtil : IDisposable
|
||||
|
||||
public bool IsPlayerPresent => _clientState.LocalPlayer != null && _clientState.LocalPlayer.IsValid();
|
||||
|
||||
public bool IsObjectPresent(Dalamud.Game.ClientState.Objects.Types.GameObject? obj)
|
||||
public static bool IsObjectPresent(Dalamud.Game.ClientState.Objects.Types.GameObject? obj)
|
||||
{
|
||||
return obj != null && obj.IsValid();
|
||||
}
|
||||
@@ -218,18 +223,19 @@ public class DalamudUtil : IDisposable
|
||||
public unsafe IntPtr GetPet(IntPtr? playerPointer = null)
|
||||
{
|
||||
var mgr = CharacterManager.Instance();
|
||||
if (playerPointer == null) playerPointer = PlayerPointer;
|
||||
return (IntPtr)mgr->LookupPetByOwnerObject((FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara*)playerPointer);
|
||||
playerPointer ??= PlayerPointer;
|
||||
return (IntPtr)mgr->LookupPetByOwnerObject((BattleChara*)playerPointer);
|
||||
}
|
||||
|
||||
public unsafe IntPtr GetCompanion(IntPtr? playerPointer = null)
|
||||
{
|
||||
var mgr = CharacterManager.Instance();
|
||||
if (playerPointer == null) playerPointer = PlayerPointer;
|
||||
return (IntPtr)mgr->LookupBuddyByOwnerObject((FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara*)playerPointer);
|
||||
playerPointer ??= PlayerPointer;
|
||||
return (IntPtr)mgr->LookupBuddyByOwnerObject((BattleChara*)playerPointer);
|
||||
}
|
||||
|
||||
public string PlayerName => _clientState.LocalPlayer?.Name.ToString() ?? "--";
|
||||
public uint WorldId => _clientState.LocalPlayer!.HomeWorld.Id;
|
||||
|
||||
public IntPtr PlayerPointer => _clientState.LocalPlayer?.Address ?? IntPtr.Zero;
|
||||
|
||||
@@ -241,7 +247,7 @@ public class DalamudUtil : IDisposable
|
||||
{
|
||||
return _objectTable.Where(obj =>
|
||||
obj.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player &&
|
||||
!string.Equals(obj.Name.ToString(), PlayerName, StringComparison.Ordinal)).Select(p => (PlayerCharacter)p).ToList();
|
||||
!string.Equals(obj.Name.ToString(), PlayerName, StringComparison.Ordinal)).Cast<PlayerCharacter>().ToList();
|
||||
}
|
||||
|
||||
public Dalamud.Game.ClientState.Objects.Types.Character? GetCharacterFromObjectTableByIndex(int index)
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
internal class Logger : ILogger
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly string _name;
|
||||
|
||||
public static void Info(string info)
|
||||
public static void Info(string? info)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
PluginLog.Information($"[{caller}] {info}");
|
||||
}
|
||||
|
||||
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";
|
||||
if (debug.Contains(stringToHighlight, StringComparison.Ordinal) && !stringToHighlight.IsNullOrEmpty())
|
||||
if (debug != null && debug.Contains(stringToHighlight, StringComparison.Ordinal) && !stringToHighlight.IsNullOrEmpty())
|
||||
{
|
||||
PluginLog.Warning($"[{caller}] {debug}");
|
||||
}
|
||||
@@ -30,31 +28,31 @@ internal class Logger : ILogger
|
||||
}
|
||||
}
|
||||
|
||||
public static void Error(string msg, Exception ex)
|
||||
public static void Error(string? msg, Exception ex)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
PluginLog.Error($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
|
||||
}
|
||||
|
||||
public static void Warn(string msg, Exception ex)
|
||||
public static void Warn(string? msg, Exception ex)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
PluginLog.Warning($"[{caller}] {msg} {Environment.NewLine} Exception: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
|
||||
}
|
||||
|
||||
public static void Error(string msg)
|
||||
public static void Error(string? msg)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
PluginLog.Error($"[{caller}] {msg}");
|
||||
}
|
||||
|
||||
public static void Warn(string warn)
|
||||
public static void Warn(string? warn)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
PluginLog.Warning($"[{caller}] {warn}");
|
||||
}
|
||||
|
||||
public static void Verbose(string verbose)
|
||||
public static void Verbose(string? verbose)
|
||||
{
|
||||
var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown";
|
||||
#if DEBUG
|
||||
@@ -66,7 +64,7 @@ internal class Logger : ILogger
|
||||
|
||||
public Logger(string name)
|
||||
{
|
||||
this.name = name;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
@@ -76,22 +74,22 @@ internal class Logger : ILogger
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
PluginLog.Debug($"[{name}] [{eventId}] {formatter(state, exception)}");
|
||||
PluginLog.Debug($"[{_name}] [{eventId}] {formatter(state, exception)}");
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
case LogLevel.Critical:
|
||||
PluginLog.Error($"[{name}] [{eventId}] {formatter(state, exception)}");
|
||||
PluginLog.Error($"[{_name}] [{eventId}] {formatter(state, exception)}");
|
||||
break;
|
||||
case LogLevel.Information:
|
||||
PluginLog.Information($"[{name}] [{eventId}] {formatter(state, exception)}");
|
||||
PluginLog.Information($"[{_name}] [{eventId}] {formatter(state, exception)}");
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
PluginLog.Warning($"[{name}] [{eventId}] {formatter(state, exception)}");
|
||||
PluginLog.Warning($"[{_name}] [{eventId}] {formatter(state, exception)}");
|
||||
break;
|
||||
case LogLevel.Trace:
|
||||
default:
|
||||
#if DEBUG
|
||||
PluginLog.Verbose($"[{name}] [{eventId}] {formatter(state, exception)}");
|
||||
PluginLog.Verbose($"[{_name}] [{eventId}] {formatter(state, exception)}");
|
||||
#else
|
||||
PluginLog.Verbose($"[{name}] {eventId} {state} {formatter(state, exception)}");
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
@@ -28,4 +26,9 @@ public static class VariousExtensions
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public static T DeepClone<T>(this T obj)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj))!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Utility;
|
||||
using LZ4;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.Files;
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
@@ -188,7 +183,7 @@ public partial class ApiController
|
||||
|
||||
public int GetDownloadId() => _downloadId++;
|
||||
|
||||
public async Task DownloadFiles(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
public async Task DownloadFiles(int currentDownloadId, List<FileReplacementData> fileReplacementDto, CancellationToken ct)
|
||||
{
|
||||
DownloadStarted?.Invoke();
|
||||
try
|
||||
@@ -213,7 +208,7 @@ public partial class ApiController
|
||||
|
||||
private async Task<HttpResponseMessage> SendRequestInternalAsync(HttpRequestMessage requestMessage, CancellationToken? ct = null)
|
||||
{
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.Authorization);
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._serverManager.GetToken());
|
||||
|
||||
if (requestMessage.Content != null)
|
||||
{
|
||||
@@ -224,7 +219,7 @@ public partial class ApiController
|
||||
Logger.Debug("Sending " + requestMessage.Method + " to " + requestMessage.RequestUri);
|
||||
}
|
||||
|
||||
if(ct != null)
|
||||
if (ct != null)
|
||||
return await _httpClient.SendAsync(requestMessage, ct.Value).ConfigureAwait(false);
|
||||
return await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
}
|
||||
@@ -236,12 +231,12 @@ public partial class ApiController
|
||||
return await SendRequestInternalAsync(requestMessage, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task DownloadFilesInternal(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
private async Task DownloadFilesInternal(int currentDownloadId, List<FileReplacementData> fileReplacement, CancellationToken ct)
|
||||
{
|
||||
Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");
|
||||
|
||||
List<DownloadFileDto> downloadFileInfoFromService = new();
|
||||
downloadFileInfoFromService.AddRange(await FilesGetSizes(fileReplacementDto.Select(f => f.Hash).ToList()).ConfigureAwait(false));
|
||||
downloadFileInfoFromService.AddRange(await FilesGetSizes(fileReplacement.Select(f => f.Hash).ToList()).ConfigureAwait(false));
|
||||
|
||||
Logger.Debug("Files with size 0 or less: " + string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
|
||||
|
||||
@@ -261,7 +256,7 @@ public partial class ApiController
|
||||
await Parallel.ForEachAsync(downloadGroups, new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = downloadGroups.Count(),
|
||||
CancellationToken = ct
|
||||
CancellationToken = ct,
|
||||
},
|
||||
async (fileGroup, token) =>
|
||||
{
|
||||
@@ -277,7 +272,7 @@ public partial class ApiController
|
||||
file.Transferred += bytesDownloaded;
|
||||
});
|
||||
|
||||
var tempPath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + ".tmp");
|
||||
var tempPath = Path.Combine(_configService.Current.CacheFolder, file.Hash + ".tmp");
|
||||
try
|
||||
{
|
||||
await DownloadFileHttpClient(file, tempPath, progress, token).ConfigureAwait(false);
|
||||
@@ -298,7 +293,7 @@ public partial class ApiController
|
||||
var tempFileData = await File.ReadAllBytesAsync(tempPath, token).ConfigureAwait(false);
|
||||
var extratokenedFile = LZ4Codec.Unwrap(tempFileData);
|
||||
File.Delete(tempPath);
|
||||
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash);
|
||||
var filePath = Path.Combine(_configService.Current.CacheFolder, file.Hash);
|
||||
await File.WriteAllBytesAsync(filePath, extratokenedFile, token).ConfigureAwait(false);
|
||||
var fi = new FileInfo(filePath);
|
||||
Func<DateTime> RandomDayInThePast()
|
||||
@@ -329,10 +324,10 @@ public partial class ApiController
|
||||
CancelDownload(currentDownloadId);
|
||||
}
|
||||
|
||||
public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
||||
public async Task PushCharacterData(API.Data.CharacterData character, List<UserData> visibleCharacters)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", StringComparison.Ordinal)) return;
|
||||
Logger.Debug("Sending Character data to service " + ApiUri);
|
||||
if (!IsConnected) return;
|
||||
Logger.Debug("Sending Character data to service " + _serverManager.CurrentApiUrl);
|
||||
|
||||
CancelUpload();
|
||||
_uploadCancellationTokenSource = new CancellationTokenSource();
|
||||
@@ -367,7 +362,7 @@ public partial class ApiController
|
||||
{
|
||||
CurrentUploads.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
Total = new FileInfo(_fileDbManager.GetFileCacheByHash(file.Hash)!.ResolvedFilepath).Length
|
||||
Total = new FileInfo(_fileDbManager.GetFileCacheByHash(file.Hash)!.ResolvedFilepath).Length,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -383,7 +378,7 @@ public partial class ApiController
|
||||
{
|
||||
ForbiddenTransfers.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
LocalFile = _fileDbManager.GetFileCacheByHash(file.Hash)?.ResolvedFilepath ?? string.Empty
|
||||
LocalFile = _fileDbManager.GetFileCacheByHash(file.Hash)?.ResolvedFilepath ?? string.Empty,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -431,7 +426,7 @@ public partial class ApiController
|
||||
|
||||
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(", ", visibleCharacters.Select(c => c.AliasOrUID)));
|
||||
StringBuilder sb = new();
|
||||
foreach (var item in character.FileReplacements)
|
||||
{
|
||||
@@ -442,14 +437,14 @@ public partial class ApiController
|
||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||
}
|
||||
Logger.Debug("Chara data contained: " + Environment.NewLine + sb.ToString());
|
||||
await UserPushData(character, visibleCharacterIds).ConfigureAwait(false);
|
||||
await UserPushData(new(visibleCharacters, character)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("=== Upload operation was cancelled ===");
|
||||
}
|
||||
|
||||
Logger.Verbose("Upload complete for " + character.GetHashCode());
|
||||
Logger.Verbose("Upload complete for " + character.DataHash);
|
||||
_uploadCancellationTokenSource = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
@@ -11,18 +8,17 @@ public partial class ApiController
|
||||
{
|
||||
public async Task UserDelete()
|
||||
{
|
||||
_pluginConfiguration.ClientSecret.Remove(ApiUri);
|
||||
_pluginConfiguration.Save();
|
||||
CheckConnection();
|
||||
await FilesDeleteAll().ConfigureAwait(false);
|
||||
await _mareHub!.SendAsync(nameof(UserDelete)).ConfigureAwait(false);
|
||||
await CreateConnections().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserPushData(CharacterCacheDto characterCache, List<string> visibleCharacterIds)
|
||||
public async Task UserPushData(UserCharaDataMessageDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _mareHub!.InvokeAsync(nameof(UserPushData), characterCache, visibleCharacterIds).ConfigureAwait(false);
|
||||
await _mareHub!.InvokeAsync(nameof(UserPushData), dto).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -30,33 +26,31 @@ public partial class ApiController
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ClientPairDto>> UserGetPairedClients()
|
||||
public async Task<List<UserPairDto>> UserGetPairedClients()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<ClientPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false);
|
||||
return await _mareHub!.InvokeAsync<List<UserPairDto>>(nameof(UserGetPairedClients)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<string>> UserGetOnlineCharacters()
|
||||
public async Task<List<OnlineUserIdentDto>> UserGetOnlinePairs()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<string>>(nameof(UserGetOnlineCharacters)).ConfigureAwait(false);
|
||||
return await _mareHub!.InvokeAsync<List<OnlineUserIdentDto>>(nameof(UserGetOnlinePairs)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserAddPair(string uid)
|
||||
public async Task UserSetPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(UserAddPair), uid.Trim()).ConfigureAwait(false);
|
||||
Logger.Verbose("Sending UserSetPairPermissions: " + dto);
|
||||
await _mareHub!.SendAsync(nameof(UserSetPairPermissions), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserChangePairPauseStatus(string uid, bool paused)
|
||||
public async Task UserAddPair(UserDto dto)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(UserChangePairPauseStatus), uid, paused).ConfigureAwait(false);
|
||||
if (!IsConnected) return;
|
||||
await _mareHub!.SendAsync(nameof(UserAddPair), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task UserRemovePair(string uid)
|
||||
public async Task UserRemovePair(UserDto dto)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(UserRemovePair), uid).ConfigureAwait(false);
|
||||
if (!IsConnected) return;
|
||||
await _mareHub!.SendAsync(nameof(UserRemovePair), dto).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public async Task AdminUpdateOrAddForbiddenFile(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(AdminUpdateOrAddForbiddenFile), forbiddenFile).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task AdminDeleteForbiddenFile(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(AdminDeleteForbiddenFile), forbiddenFile).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task AdminUpdateOrAddBannedUser(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(AdminUpdateOrAddBannedUser), bannedUser).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task AdminDeleteBannedUser(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(AdminDeleteBannedUser), bannedUser).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task RefreshOnlineUsers()
|
||||
{
|
||||
AdminOnlineUsers = await AdminGetOnlineUsers().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<OnlineUserDto>> AdminGetOnlineUsers()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<OnlineUserDto>>(nameof(AdminGetOnlineUsers)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public List<OnlineUserDto> AdminOnlineUsers { get; set; } = new List<OnlineUserDto>();
|
||||
|
||||
public async Task AdminChangeModeratorStatus(string onlineUserUID, bool isModerator)
|
||||
{
|
||||
await _mareHub!.SendAsync(nameof(AdminChangeModeratorStatus), onlineUserUID, isModerator).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<ForbiddenFileDto>> AdminGetForbiddenFiles()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<ForbiddenFileDto>>(nameof(AdminGetForbiddenFiles)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<BannedUserDto>> AdminGetBannedUsers()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<BannedUserDto>>(nameof(AdminGetBannedUsers)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Dto;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public ClientPairDto? LastAddedUser { get; set; }
|
||||
|
||||
public void OnUserUpdateClientPairs(Action<ClientPairDto> act)
|
||||
public UserPairDto? LastAddedUser { get; set; }
|
||||
private void ExecuteSafely(Action act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserUpdateClientPairs), act);
|
||||
try
|
||||
{
|
||||
act();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Error on executing safely", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdateSystemInfo(Action<SystemInfoDto> act)
|
||||
@@ -24,60 +28,12 @@ public partial class ApiController
|
||||
_mareHub!.On(nameof(Client_UpdateSystemInfo), act);
|
||||
}
|
||||
|
||||
public void OnUserReceiveCharacterData(Action<CharacterCacheDto, string> act)
|
||||
public void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserReceiveCharacterData), act);
|
||||
}
|
||||
|
||||
public void OnUserChangePairedPlayer(Action<string, bool> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserChangePairedPlayer), act);
|
||||
}
|
||||
|
||||
public void OnGroupChange(Action<GroupDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupChange), act);
|
||||
}
|
||||
|
||||
public void OnGroupUserChange(Action<GroupPairDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupUserChange), act);
|
||||
}
|
||||
|
||||
public void OnAdminForcedReconnect(Action act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_AdminForcedReconnect), act);
|
||||
}
|
||||
|
||||
public void OnAdminDeleteBannedUser(Action<BannedUserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_AdminDeleteBannedUser), act);
|
||||
}
|
||||
|
||||
public void OnAdminDeleteForbiddenFile(Action<ForbiddenFileDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_AdminDeleteForbiddenFile), act);
|
||||
}
|
||||
|
||||
public void OnAdminUpdateOrAddBannedUser(Action<BannedUserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_AdminUpdateOrAddBannedUser), act);
|
||||
}
|
||||
|
||||
public void OnAdminUpdateOrAddForbiddenFile(Action<ForbiddenFileDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_AdminUpdateOrAddForbiddenFile), act);
|
||||
}
|
||||
|
||||
public void OnReceiveServerMessage(Action<MessageSeverity, string> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
@@ -90,25 +46,200 @@ public partial class ApiController
|
||||
_mareHub!.On(nameof(Client_DownloadReady), act);
|
||||
}
|
||||
|
||||
public Task Client_UserUpdateClientPairs(ClientPairDto dto)
|
||||
public void OnGroupSendFullInfo(Action<GroupFullInfoDto> act)
|
||||
{
|
||||
var entry = PairedClients.SingleOrDefault(e => string.Equals(e.OtherUID, dto.OtherUID, System.StringComparison.Ordinal));
|
||||
if (dto.IsRemoved)
|
||||
{
|
||||
PairedClients.RemoveAll(p => string.Equals(p.OtherUID, dto.OtherUID, System.StringComparison.Ordinal));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (entry == null)
|
||||
{
|
||||
LastAddedUser = dto;
|
||||
PairedClients.Add(dto);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupSendFullInfo), act);
|
||||
}
|
||||
|
||||
entry.IsPaused = dto.IsPaused;
|
||||
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
||||
entry.IsSynced = dto.IsSynced;
|
||||
public Task Client_GroupSendFullInfo(GroupFullInfoDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupSendFullInfo: " + dto);
|
||||
ExecuteSafely(() => _pairManager.AddGroup(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupSendInfo(Action<GroupInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupSendInfo), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupSendInfo(GroupInfoDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupSendInfo: " + dto);
|
||||
ExecuteSafely(() => _pairManager.SetGroupInfo(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupDelete(Action<GroupDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupDelete), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupDelete(GroupDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupDelete: " + dto);
|
||||
ExecuteSafely(() => _pairManager.RemoveGroup(dto.Group));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupPairJoined(Action<GroupPairFullInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairJoined), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupPairJoined(GroupPairFullInfoDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupPairJoined: " + dto);
|
||||
ExecuteSafely(() => _pairManager.AddGroupPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupPairLeft(Action<GroupPairDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairLeft), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupPairLeft(GroupPairDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupPairLeft: " + dto);
|
||||
ExecuteSafely(() => _pairManager.RemoveGroupPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupChangePermissions(Action<GroupPermissionDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupChangePermissions), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupChangePermissions(GroupPermissionDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupChangePermissions: " + dto);
|
||||
ExecuteSafely(() => _pairManager.SetGroupPermissions(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupPairChangePermissions(Action<GroupPairUserPermissionDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairChangePermissions), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupPairChangePermissions(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupPairChangePermissions: " + dto);
|
||||
ExecuteSafely(() =>
|
||||
{
|
||||
if (string.Equals(dto.UID, UID, StringComparison.Ordinal)) _pairManager.SetGroupUserPermissions(dto);
|
||||
else _pairManager.SetGroupPairUserPermissions(dto);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnGroupPairChangeUserInfo(Action<GroupPairUserInfoDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_GroupPairChangeUserInfo), act);
|
||||
}
|
||||
|
||||
public Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_GroupPairChangeUserInfo: " + dto);
|
||||
ExecuteSafely(() =>
|
||||
{
|
||||
if (string.Equals(dto.UID, UID, StringComparison.Ordinal)) _pairManager.SetGroupStatusInfo(dto);
|
||||
else _pairManager.SetGroupPairStatusInfo(dto);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dto)
|
||||
{
|
||||
Logger.Verbose("Client_UserReceiveCharacterData: " + dto.User);
|
||||
ExecuteSafely(() => _pairManager.ReceiveCharaData(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserAddClientPair(Action<UserPairDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserAddClientPair), act);
|
||||
}
|
||||
|
||||
public Task Client_UserAddClientPair(UserPairDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserAddClientPair: " + dto);
|
||||
ExecuteSafely(() => _pairManager.AddUserPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserRemoveClientPair(Action<UserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserRemoveClientPair), act);
|
||||
}
|
||||
|
||||
public Task Client_UserRemoveClientPair(UserDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserRemoveClientPair: " + dto);
|
||||
ExecuteSafely(() => _pairManager.RemoveUserPair(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserSendOffline(Action<UserDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserSendOffline), act);
|
||||
}
|
||||
|
||||
public Task Client_UserSendOffline(UserDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserSendOffline: {dto}");
|
||||
ExecuteSafely(() => _pairManager.MarkPairOffline(dto.User));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserSendOnline(Action<OnlineUserIdentDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserSendOnline), act);
|
||||
}
|
||||
|
||||
public Task Client_UserSendOnline(OnlineUserIdentDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserSendOnline: {dto}");
|
||||
ExecuteSafely(() => _pairManager.MarkPairOnline(dto, this));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserUpdateOtherPairPermissions(Action<UserPermissionsDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserUpdateOtherPairPermissions), act);
|
||||
}
|
||||
|
||||
public Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserUpdateOtherPairPermissions: {dto}");
|
||||
ExecuteSafely(() => _pairManager.UpdatePairPermissions(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act)
|
||||
{
|
||||
if (_initialized) return;
|
||||
_mareHub!.On(nameof(Client_UserUpdateSelfPairPermissions), act);
|
||||
}
|
||||
|
||||
public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
Logger.Debug($"Client_UserUpdateSelfPairPermissions: {dto}");
|
||||
ExecuteSafely(() => _pairManager.UpdateSelfPairPermissions(dto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -118,114 +249,6 @@ public partial class ApiController
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserReceiveCharacterData(CharacterCacheDto clientPairDto, string characterIdent)
|
||||
{
|
||||
Logger.Verbose("Received DTO for " + characterIdent);
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterIdent, clientPairDto));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_UserChangePairedPlayer(string characterIdent, bool isOnline)
|
||||
{
|
||||
if (isOnline) PairedClientOnline?.Invoke(characterIdent);
|
||||
else PairedClientOffline?.Invoke(characterIdent);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Client_GroupChange(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 GroupsGetUsersInGroup(dto.GID).ConfigureAwait(false));
|
||||
return;
|
||||
}
|
||||
|
||||
existingGroup.OwnedBy = dto.OwnedBy ?? existingGroup.OwnedBy;
|
||||
existingGroup.InvitesEnabled = dto.InvitesEnabled ?? existingGroup.InvitesEnabled;
|
||||
existingGroup.IsPaused = dto.IsPaused ?? existingGroup.IsPaused;
|
||||
existingGroup.IsModerator = dto.IsModerator ?? existingGroup.IsModerator;
|
||||
}
|
||||
|
||||
public Task Client_GroupUserChange(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 Task.CompletedTask;
|
||||
}
|
||||
|
||||
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 Task.CompletedTask;
|
||||
}
|
||||
|
||||
existingUser.IsPaused = dto.IsPaused ?? existingUser.IsPaused;
|
||||
existingUser.UserAlias = dto.UserAlias ?? existingUser.UserAlias;
|
||||
existingUser.IsPinned = dto.IsPinned ?? existingUser.IsPinned;
|
||||
existingUser.IsModerator = dto.IsModerator ?? existingUser.IsModerator;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_AdminForcedReconnect()
|
||||
{
|
||||
_ = CreateConnections();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_AdminDeleteBannedUser(BannedUserDto dto)
|
||||
{
|
||||
AdminBannedUsers.RemoveAll(a => string.Equals(a.CharacterHash, dto.CharacterHash, System.StringComparison.Ordinal));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_AdminDeleteForbiddenFile(ForbiddenFileDto dto)
|
||||
{
|
||||
AdminForbiddenFiles.RemoveAll(f => string.Equals(f.Hash, dto.Hash, System.StringComparison.Ordinal));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_AdminUpdateOrAddBannedUser(BannedUserDto dto)
|
||||
{
|
||||
var user = AdminBannedUsers.SingleOrDefault(b => string.Equals(b.CharacterHash, dto.CharacterHash, System.StringComparison.Ordinal));
|
||||
if (user == null)
|
||||
{
|
||||
AdminBannedUsers.Add(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Reason = dto.Reason;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_AdminUpdateOrAddForbiddenFile(ForbiddenFileDto dto)
|
||||
{
|
||||
var user = AdminForbiddenFiles.SingleOrDefault(b => string.Equals(b.Hash, dto.Hash, System.StringComparison.Ordinal));
|
||||
if (user == null)
|
||||
{
|
||||
AdminForbiddenFiles.Add(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.ForbiddenBy = dto.ForbiddenBy;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Client_ReceiveServerMessage(MessageSeverity severity, string message)
|
||||
{
|
||||
switch (severity)
|
||||
@@ -240,7 +263,7 @@ public partial class ApiController
|
||||
break;
|
||||
case MessageSeverity.Information:
|
||||
Logger.Info(message);
|
||||
if (!_pluginConfiguration.HideInfoMessages)
|
||||
if (_configService.Current.HideInfoMessages)
|
||||
{
|
||||
_dalamudUtil.PrintInfoChat(message);
|
||||
}
|
||||
|
||||
@@ -1,116 +1,115 @@
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.API.Dto.Group;
|
||||
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> GroupCreate()
|
||||
private void CheckConnection()
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new GroupCreatedDto();
|
||||
return await _mareHub!.InvokeAsync<GroupCreatedDto>(nameof(GroupCreate)).ConfigureAwait(false);
|
||||
if (ServerState is not (ServerState.Connected or ServerState.Connecting or ServerState.Reconnecting)) throw new System.Exception("Not connected");
|
||||
}
|
||||
|
||||
public async Task<bool> GroupChangePassword(string gid, string newpassword)
|
||||
public async Task<List<BannedGroupUserDto>> GroupGetBannedUsers(GroupDto group)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return false;
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupChangePassword), gid, newpassword).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<BannedGroupUserDto>>(nameof(GroupGetBannedUsers), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<GroupDto>> GroupsGetAll()
|
||||
public async Task GroupClear(GroupDto group)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new List<GroupDto>();
|
||||
return await _mareHub!.InvokeAsync<List<GroupDto>>(nameof(GroupsGetAll)).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupClear), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<GroupPairDto>> GroupsGetUsersInGroup(string gid)
|
||||
public async Task GroupChangeOwnership(GroupPairDto groupPair)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new List<GroupPairDto>();
|
||||
return await _mareHub!.InvokeAsync<List<GroupPairDto>>(nameof(GroupsGetUsersInGroup), gid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeOwnership), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> GroupJoin(string gid, string password)
|
||||
public async Task<bool> GroupChangePassword(GroupPasswordDto groupPassword)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return false;
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupJoin), gid.Trim(), password).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupChangePassword), groupPassword).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangeInviteState(string gid, bool opened)
|
||||
public async Task<GroupPasswordDto> GroupCreate()
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeInviteState), gid, opened).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<GroupPasswordDto>(nameof(GroupCreate)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupDelete(string gid)
|
||||
public async Task<List<GroupFullInfoDto>> GroupsGetAll()
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupDelete), gid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<GroupFullInfoDto>>(nameof(GroupsGetAll)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangePinned(string gid, string uid, bool isPinned)
|
||||
public async Task<List<GroupPairFullInfoDto>> GroupsGetUsersInGroup(GroupDto group)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupChangePinned), gid, uid, isPinned).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<GroupPairFullInfoDto>>(nameof(GroupsGetUsersInGroup), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupClear(string gid)
|
||||
public async Task GroupBanUser(GroupPairDto dto, string reason)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupClear), gid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupBanUser), dto, reason).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupLeave(string gid)
|
||||
public async Task GroupChangeGroupPermissionState(GroupPermissionDto dto)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupLeave), gid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeGroupPermissionState), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangePauseState(string gid, bool isPaused)
|
||||
public async Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupChangePauseState), gid, isPaused).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
Logger.Debug("Sending " + dto);
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeIndividualPermissionState), dto).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupRemoveUser(string gid, string uid)
|
||||
public async Task GroupDelete(GroupDto group)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupRemoveUser), gid, uid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupDelete), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupChangeOwnership(string gid, string uid)
|
||||
public async Task<bool> GroupJoin(GroupPasswordDto passwordedGroup)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupChangeOwnership), gid, uid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<bool>(nameof(GroupJoin), passwordedGroup).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupBanUser(string gid, string uid, string reason)
|
||||
public async Task GroupLeave(GroupDto group)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupBanUser), gid, uid, reason).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupLeave), group).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupUnbanUser(string gid, string uid)
|
||||
public async Task GroupRemoveUser(GroupPairDto groupPair)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupUnbanUser), gid, uid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupRemoveUser), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<BannedGroupUserDto>> GroupGetBannedUsers(string gid)
|
||||
public async Task GroupUnbanUser(GroupPairDto groupPair)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new();
|
||||
return await _mareHub!.InvokeAsync<List<BannedGroupUserDto>>(nameof(GroupGetBannedUsers), gid).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupUnbanUser), groupPair).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GroupSetModerator(string gid, string uid, bool isModerator)
|
||||
public async Task GroupSetUserInfo(GroupPairUserInfoDto userInfo)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return;
|
||||
await _mareHub!.SendAsync(nameof(GroupSetModerator), gid, uid, isModerator).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
await _mareHub!.SendAsync(nameof(GroupSetUserInfo), userInfo).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GroupCreateTempInvite(string gid, int amount)
|
||||
public async Task<List<string>> GroupCreateTempInvite(GroupDto group, int amount)
|
||||
{
|
||||
if (!IsConnected || string.Equals(SecretKey, "-", System.StringComparison.Ordinal)) return new();
|
||||
return await _mareHub!.InvokeAsync<List<string>>(nameof(GroupCreateTempInvite), gid, amount).ConfigureAwait(false);
|
||||
CheckConnection();
|
||||
return await _mareHub!.InvokeAsync<List<string>>(nameof(GroupCreateTempInvite), group, amount).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using System.Collections.Concurrent;
|
||||
using MareSynchronos.API.Routes;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MareSynchronos.API.Dto;
|
||||
using MareSynchronos.API.SignalR;
|
||||
using MareSynchronos.Managers;
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.Delegates;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public delegate void SimpleStringDelegate(string str);
|
||||
|
||||
public record JwtCache(string ApiUrl, string CharaIdent, string SecretKey);
|
||||
|
||||
public partial class ApiController : IDisposable, IMareHubClient
|
||||
{
|
||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||
@@ -28,20 +21,19 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
public readonly int[] SupportedServerVersions = { IMareHub.ApiVersion };
|
||||
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly ConfigurationService _configService;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverManager;
|
||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||
private Dictionary<JwtCache, string> _jwtToken = new();
|
||||
private string Authorization => _jwtToken.GetValueOrDefault(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), string.Empty);
|
||||
|
||||
private HubConnection? _mareHub;
|
||||
|
||||
private CancellationTokenSource? _uploadCancellationTokenSource = new();
|
||||
private CancellationTokenSource? _healthCheckTokenSource = new();
|
||||
|
||||
private ConnectionDto? _connectionDto;
|
||||
public ServerInfoDto ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfoDto();
|
||||
public ServerInfo ServerInfo => _connectionDto?.ServerInfo ?? new ServerInfo();
|
||||
public string AuthFailureMessage { get; private set; } = string.Empty;
|
||||
|
||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||
@@ -51,18 +43,21 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public ApiController(Configuration pluginConfiguration, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
public ApiController(ConfigurationService configService, DalamudUtil dalamudUtil, FileCacheManager fileDbManager, PairManager pairManager, ServerConfigurationManager serverManager)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(ApiController));
|
||||
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_configService = configService;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileDbManager = fileDbManager;
|
||||
_pairManager = pairManager;
|
||||
_serverManager = serverManager;
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
ServerState = ServerState.Offline;
|
||||
_verifiedUploadedHashes = new(StringComparer.Ordinal);
|
||||
_httpClient = new();
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
@@ -72,25 +67,17 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false));
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token, ServerState.Disconnected).ConfigureAwait(false));
|
||||
ServerState = ServerState.Offline;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
{
|
||||
Task.Run(() => CreateConnections(true));
|
||||
Task.Run(() => CreateConnections(forceGetToken: true));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -100,33 +87,14 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
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 IsDownloading => !CurrentDownloads.IsEmpty;
|
||||
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 string UID => _connectionDto?.User.UID ?? string.Empty;
|
||||
public string DisplayName => _connectionDto?.User.AliasOrUID ?? string.Empty;
|
||||
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
||||
|
||||
private ServerState _serverState;
|
||||
@@ -149,16 +117,24 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
_httpClient?.Dispose();
|
||||
_httpClient = new();
|
||||
|
||||
if (_pluginConfiguration.FullPause)
|
||||
if (_serverManager.CurrentServer?.FullPause ?? true)
|
||||
{
|
||||
Logger.Info("Not recreating Connection, paused");
|
||||
ServerState = ServerState.Disconnected;
|
||||
_connectionDto = null;
|
||||
await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false);
|
||||
await StopConnection(_connectionCancellationTokenSource.Token, ServerState.Disconnected).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false);
|
||||
var secretKey = _serverManager.GetSecretKey();
|
||||
if (secretKey.IsNullOrEmpty())
|
||||
{
|
||||
Logger.Warn("No secret key set for current character");
|
||||
_connectionDto = null;
|
||||
await StopConnection(_connectionCancellationTokenSource.Token, ServerState.NoSecretKey).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await StopConnection(_connectionCancellationTokenSource.Token, ServerState.Disconnected).ConfigureAwait(false);
|
||||
|
||||
Logger.Info("Recreating Connection");
|
||||
|
||||
@@ -168,37 +144,31 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
_verifiedUploadedHashes.Clear();
|
||||
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SecretKey))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
AuthFailureMessage = string.Empty;
|
||||
|
||||
await StopConnection(token).ConfigureAwait(false);
|
||||
await StopConnection(token, ServerState.Disconnected).ConfigureAwait(false);
|
||||
ServerState = ServerState.Connecting;
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Building connection");
|
||||
|
||||
if (!_jwtToken.TryGetValue(new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey), out var jwtToken) || forceGetToken)
|
||||
if (_serverManager.GetToken() == null || forceGetToken)
|
||||
{
|
||||
Logger.Debug("Requesting new JWT");
|
||||
using HttpClient httpClient = new();
|
||||
var postUri = MareAuth.AuthFullPath(new Uri(ApiUri
|
||||
var postUri = MareAuth.AuthFullPath(new Uri(_serverManager.CurrentApiUrl
|
||||
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
|
||||
using var sha256 = SHA256.Create();
|
||||
var auth = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(SecretKey))).Replace("-", "", StringComparison.OrdinalIgnoreCase);
|
||||
var auth = secretKey.GetHash256();
|
||||
var result = await httpClient.PostAsync(postUri, new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("auth", auth),
|
||||
new KeyValuePair<string, string>("charaIdent", _dalamudUtil.PlayerNameHashed)
|
||||
new KeyValuePair<string, string>("charaIdent", _dalamudUtil.PlayerNameHashed),
|
||||
})).ConfigureAwait(false);
|
||||
AuthFailureMessage = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
_jwtToken[new JwtCache(ApiUri, _dalamudUtil.PlayerNameHashed, SecretKey)] = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
_serverManager.SaveToken(await result.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
Logger.Debug("JWT Success");
|
||||
}
|
||||
|
||||
@@ -214,8 +184,7 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
await _mareHub.StartAsync(token).ConfigureAwait(false);
|
||||
|
||||
OnReceiveServerMessage((sev, msg) => Client_ReceiveServerMessage(sev, msg));
|
||||
OnUpdateSystemInfo((dto) => Client_UpdateSystemInfo(dto));
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
|
||||
@@ -223,15 +192,12 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
||||
{
|
||||
ServerState = ServerState.VersionMisMatch;
|
||||
await StopConnection(token).ConfigureAwait(false);
|
||||
await StopConnection(token, ServerState.VersionMisMatch).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;
|
||||
@@ -245,13 +211,12 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
|
||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
ServerState = ServerState.Unauthorized;
|
||||
await StopConnection(token).ConfigureAwait(false);
|
||||
await StopConnection(token, ServerState.Unauthorized).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerState = ServerState.Offline;
|
||||
ServerState = ServerState.Reconnecting;
|
||||
Logger.Info("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||
}
|
||||
@@ -267,12 +232,6 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
}
|
||||
}
|
||||
|
||||
private Task MareHubOnReconnected(string? arg)
|
||||
{
|
||||
_ = Task.Run(() => CreateConnections(false));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ClientHealthCheck(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested && _mareHub != null)
|
||||
@@ -289,36 +248,55 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeData(CancellationToken token)
|
||||
private async Task InitializeData()
|
||||
{
|
||||
if (_mareHub == null) return;
|
||||
|
||||
Logger.Debug("Initializing data");
|
||||
OnUserUpdateClientPairs((dto) => Client_UserUpdateClientPairs(dto));
|
||||
OnUserChangePairedPlayer((ident, online) => Client_UserChangePairedPlayer(ident, online));
|
||||
OnUserReceiveCharacterData((dto, ident) => Client_UserReceiveCharacterData(dto, ident));
|
||||
OnGroupChange(async (dto) => await Client_GroupChange(dto).ConfigureAwait(false));
|
||||
OnGroupUserChange((dto) => Client_GroupUserChange(dto));
|
||||
OnDownloadReady((guid) => Client_DownloadReady(guid));
|
||||
OnReceiveServerMessage((sev, msg) => Client_ReceiveServerMessage(sev, msg));
|
||||
OnUpdateSystemInfo((dto) => Client_UpdateSystemInfo(dto));
|
||||
|
||||
OnAdminForcedReconnect(() => Client_AdminForcedReconnect());
|
||||
OnUserSendOffline((dto) => Client_UserSendOffline(dto));
|
||||
OnUserAddClientPair((dto) => Client_UserAddClientPair(dto));
|
||||
OnUserReceiveCharacterData((dto) => Client_UserReceiveCharacterData(dto));
|
||||
OnUserRemoveClientPair(dto => Client_UserRemoveClientPair(dto));
|
||||
OnUserSendOnline(dto => Client_UserSendOnline(dto));
|
||||
OnUserUpdateOtherPairPermissions(dto => Client_UserUpdateOtherPairPermissions(dto));
|
||||
OnUserUpdateSelfPairPermissions(dto => Client_UserUpdateSelfPairPermissions(dto));
|
||||
|
||||
PairedClients = await UserGetPairedClients().ConfigureAwait(false);
|
||||
Groups = await GroupsGetAll().ConfigureAwait(false);
|
||||
GroupPairedClients.Clear();
|
||||
foreach (var group in Groups)
|
||||
OnGroupChangePermissions((dto) => Client_GroupChangePermissions(dto));
|
||||
OnGroupDelete((dto) => Client_GroupDelete(dto));
|
||||
OnGroupPairChangePermissions((dto) => Client_GroupPairChangePermissions(dto));
|
||||
OnGroupPairChangeUserInfo((dto) => Client_GroupPairChangeUserInfo(dto));
|
||||
OnGroupPairJoined((dto) => Client_GroupPairJoined(dto));
|
||||
OnGroupPairLeft((dto) => Client_GroupPairLeft(dto));
|
||||
OnGroupSendFullInfo((dto) => Client_GroupSendFullInfo(dto));
|
||||
OnGroupSendInfo((dto) => Client_GroupSendInfo(dto));
|
||||
|
||||
foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false))
|
||||
{
|
||||
GroupPairedClients.AddRange(await GroupsGetUsersInGroup(group.GID).ConfigureAwait(false));
|
||||
Logger.Debug($"Pair: {userPair}");
|
||||
_pairManager.AddUserPair(userPair);
|
||||
}
|
||||
foreach (var entry in await GroupsGetAll().ConfigureAwait(false))
|
||||
{
|
||||
Logger.Debug($"Group: {entry}");
|
||||
_pairManager.AddGroup(entry);
|
||||
}
|
||||
foreach (var group in _pairManager.GroupPairs.Keys)
|
||||
{
|
||||
var users = await GroupsGetUsersInGroup(group).ConfigureAwait(false);
|
||||
foreach (var user in users)
|
||||
{
|
||||
Logger.Debug($"GroupPair: {user}");
|
||||
_pairManager.AddGroupPair(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsModerator)
|
||||
foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false))
|
||||
{
|
||||
AdminForbiddenFiles = await AdminGetForbiddenFiles().ConfigureAwait(false);
|
||||
AdminBannedUsers = await AdminGetBannedUsers().ConfigureAwait(false);
|
||||
OnAdminUpdateOrAddBannedUser((dto) => Client_AdminUpdateOrAddBannedUser(dto));
|
||||
OnAdminDeleteBannedUser((dto) => Client_AdminDeleteBannedUser(dto));
|
||||
OnAdminUpdateOrAddForbiddenFile(dto => Client_AdminUpdateOrAddForbiddenFile(dto));
|
||||
OnAdminDeleteForbiddenFile(dto => Client_AdminDeleteForbiddenFile(dto));
|
||||
_pairManager.MarkPairOnline(entry, this);
|
||||
}
|
||||
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
@@ -338,7 +316,7 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
|
||||
ServerState = ServerState.Offline;
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token).ConfigureAwait(false));
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token, ServerState.Disconnected).ConfigureAwait(false));
|
||||
_connectionCancellationTokenSource?.Cancel();
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
@@ -347,9 +325,9 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
private HubConnection BuildHubConnection(string hubName)
|
||||
{
|
||||
return new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + hubName, options =>
|
||||
.WithUrl(_serverManager.CurrentApiUrl + hubName, options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", "Bearer " + Authorization);
|
||||
options.Headers.Add("Authorization", "Bearer " + _serverManager.GetToken());
|
||||
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
||||
})
|
||||
.WithAutomaticReconnect(new ForeverRetryPolicy())
|
||||
@@ -366,7 +344,9 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
Disconnected?.Invoke();
|
||||
_pairManager.ClearPairs();
|
||||
ServerState = ServerState.Offline;
|
||||
Logger.Info("Connection closed");
|
||||
return Task.CompletedTask;
|
||||
@@ -376,16 +356,24 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
{
|
||||
_connectionDto = null;
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
ServerState = ServerState.Disconnected;
|
||||
ServerState = ServerState.Reconnecting;
|
||||
Logger.Warn("Connection closed... Reconnecting");
|
||||
Logger.Warn(arg?.Message ?? string.Empty);
|
||||
Logger.Warn(arg?.StackTrace ?? string.Empty);
|
||||
Disconnected?.Invoke();
|
||||
ServerState = ServerState.Offline;
|
||||
_pairManager.ClearPairs();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StopConnection(CancellationToken token)
|
||||
private async Task MareHubOnReconnected(string? arg)
|
||||
{
|
||||
ServerState = ServerState.Connecting;
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
ServerState = ServerState.Connected;
|
||||
}
|
||||
|
||||
private async Task StopConnection(CancellationToken token, ServerState state)
|
||||
{
|
||||
if (_mareHub is not null)
|
||||
{
|
||||
@@ -401,15 +389,9 @@ public partial class ApiController : IDisposable, IMareHubClient
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
Disconnected?.Invoke();
|
||||
_pairManager.ClearPairs();
|
||||
_mareHub = null;
|
||||
}
|
||||
|
||||
if (ServerState != ServerState.Disconnected)
|
||||
{
|
||||
while (ServerState != ServerState.Offline)
|
||||
{
|
||||
await Task.Delay(16).ConfigureAwait(false);
|
||||
}
|
||||
ServerState = state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
public enum ServerState
|
||||
{
|
||||
Offline,
|
||||
Connecting,
|
||||
Reconnecting,
|
||||
Disconnected,
|
||||
Connected,
|
||||
Unauthorized,
|
||||
VersionMisMatch,
|
||||
RateLimited
|
||||
RateLimited,
|
||||
NoSecretKey,
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using MareSynchronos.API;
|
||||
|
||||
namespace MareSynchronos.WebAPI.Utils;
|
||||
|
||||
public class CharacterReceivedEventArgs : EventArgs
|
||||
{
|
||||
public CharacterReceivedEventArgs(string characterNameHash, CharacterCacheDto characterData)
|
||||
{
|
||||
CharacterData = characterData;
|
||||
CharacterNameHash = characterNameHash;
|
||||
}
|
||||
|
||||
public CharacterCacheDto CharacterData { get; set; }
|
||||
public string CharacterNameHash { get; set; }
|
||||
}
|
||||
17
MareSynchronos/WebAPI/Utils/DownloadFileTransfer.cs
Normal file
17
MareSynchronos/WebAPI/Utils/DownloadFileTransfer.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using MareSynchronos.API.Dto.Files;
|
||||
|
||||
namespace MareSynchronos.WebAPI.Utils;
|
||||
|
||||
public class DownloadFileTransfer : FileTransfer
|
||||
{
|
||||
private DownloadFileDto Dto => (DownloadFileDto)TransferDto;
|
||||
public DownloadFileTransfer(DownloadFileDto dto) : base(dto) { }
|
||||
public Uri DownloadUri => new(Dto.Url);
|
||||
public override long Total
|
||||
{
|
||||
set { }
|
||||
get => Dto.Size;
|
||||
}
|
||||
|
||||
public override bool CanBeTransferred => Dto.FileExists && !Dto.IsForbidden && Dto.Size > 0;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using MareSynchronos.API;
|
||||
using System;
|
||||
using MareSynchronos.API.Dto.Files;
|
||||
|
||||
namespace MareSynchronos.WebAPI.Utils;
|
||||
|
||||
@@ -26,24 +25,3 @@ public abstract class FileTransfer
|
||||
return Hash;
|
||||
}
|
||||
}
|
||||
|
||||
public class UploadFileTransfer : FileTransfer
|
||||
{
|
||||
public UploadFileTransfer(UploadFileDto dto) : base(dto) { }
|
||||
public override long Total { get; set; }
|
||||
public string LocalFile { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class DownloadFileTransfer : FileTransfer
|
||||
{
|
||||
private DownloadFileDto Dto => (DownloadFileDto)TransferDto;
|
||||
public DownloadFileTransfer(DownloadFileDto dto) : base(dto) { }
|
||||
public Uri DownloadUri => new(Dto.Url);
|
||||
public override long Total
|
||||
{
|
||||
set { }
|
||||
get => Dto.Size;
|
||||
}
|
||||
|
||||
public override bool CanBeTransferred => Dto.FileExists && !Dto.IsForbidden && Dto.Size > 0;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI.Utils;
|
||||
|
||||
|
||||
10
MareSynchronos/WebAPI/Utils/UploadFileTransfer.cs
Normal file
10
MareSynchronos/WebAPI/Utils/UploadFileTransfer.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using MareSynchronos.API.Dto.Files;
|
||||
|
||||
namespace MareSynchronos.WebAPI.Utils;
|
||||
|
||||
public class UploadFileTransfer : FileTransfer
|
||||
{
|
||||
public UploadFileTransfer(UploadFileDto dto) : base(dto) { }
|
||||
public override long Total { get; set; }
|
||||
public string LocalFile { get; set; } = string.Empty;
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user