style: apply editorconfig & analyzer suggestions
This commit is contained in:
parent
cb5bdfc0f1
commit
e70d32d532
21 changed files with 263 additions and 264 deletions
110
.editorconfig
110
.editorconfig
|
@ -90,8 +90,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
||||||
dotnet_style_predefined_type_for_member_access = true:warning
|
dotnet_style_predefined_type_for_member_access = true:warning
|
||||||
# Modifier preferences
|
# Modifier preferences
|
||||||
dotnet_style_require_accessibility_modifiers = always:warning
|
dotnet_style_require_accessibility_modifiers = always:warning
|
||||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
|
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:warning
|
||||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning
|
visual_basic_preferred_modifier_order = Partial, Default, Private, Protected, Public, Friend, NotOverridable, Overridable, MustOverride, Overloads, Overrides, MustInherit, NotInheritable, Static, Shared, Shadows, ReadOnly, WriteOnly, Dim, Const, WithEvents, Widening, Narrowing, Custom, Async:warning
|
||||||
dotnet_style_readonly_field = true:warning
|
dotnet_style_readonly_field = true:warning
|
||||||
# Parentheses preferences
|
# Parentheses preferences
|
||||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
|
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
|
||||||
|
@ -274,11 +274,11 @@ dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I
|
||||||
dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case
|
dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case
|
||||||
dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T
|
dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T
|
||||||
# disallowed_style - Anything that has this style applied is marked as disallowed
|
# disallowed_style - Anything that has this style applied is marked as disallowed
|
||||||
dotnet_naming_style.disallowed_style.capitalization = pascal_case
|
dotnet_naming_style.disallowed_style.capitalization = pascal_case
|
||||||
dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____
|
dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____
|
||||||
dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____
|
dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____
|
||||||
# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file
|
# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file
|
||||||
dotnet_naming_style.internal_error_style.capitalization = pascal_case
|
dotnet_naming_style.internal_error_style.capitalization = pascal_case
|
||||||
dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____
|
dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____
|
||||||
dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____
|
dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____
|
||||||
|
|
||||||
|
@ -291,28 +291,28 @@ dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR___
|
||||||
# All public/protected/protected_internal constant fields must be PascalCase
|
# All public/protected/protected_internal constant fields must be PascalCase
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
||||||
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
|
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
|
||||||
dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
|
dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
|
||||||
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
|
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
|
||||||
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
|
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
|
||||||
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning
|
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning
|
||||||
|
|
||||||
# All public/protected/protected_internal static readonly fields must be PascalCase
|
# All public/protected/protected_internal static readonly fields must be PascalCase
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
||||||
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal
|
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal
|
||||||
dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly
|
dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly
|
||||||
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group
|
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group
|
||||||
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
|
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
|
||||||
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning
|
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning
|
||||||
|
|
||||||
# No other public/protected/protected_internal fields are allowed
|
# No other public/protected/protected_internal fields are allowed
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
||||||
dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal
|
dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal
|
||||||
dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
|
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
|
||||||
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
|
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
|
||||||
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
|
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# StyleCop Field Naming Rules
|
# StyleCop Field Naming Rules
|
||||||
|
@ -324,52 +324,52 @@ dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity
|
||||||
# All constant fields must be PascalCase
|
# All constant fields must be PascalCase
|
||||||
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
|
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
|
||||||
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
|
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
|
||||||
dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
|
dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
|
||||||
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
|
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
|
||||||
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
|
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
|
||||||
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
|
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
|
||||||
|
|
||||||
# All static readonly fields must be PascalCase
|
# All static readonly fields must be PascalCase
|
||||||
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
|
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
|
||||||
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
|
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
|
||||||
dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
|
dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
|
||||||
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
|
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
|
||||||
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
|
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
|
||||||
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
|
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
|
||||||
|
|
||||||
# No non-private instance fields are allowed
|
# No non-private instance fields are allowed
|
||||||
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
|
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
|
||||||
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
|
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
|
||||||
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
|
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
|
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
|
||||||
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
|
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
|
||||||
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
|
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
|
||||||
|
|
||||||
# Private fields must be camelCase
|
# Private fields must be camelCase
|
||||||
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
|
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
|
||||||
dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private
|
dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private
|
||||||
dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field
|
dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group
|
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group
|
||||||
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style
|
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style
|
||||||
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning
|
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning
|
||||||
|
|
||||||
# Local variables must be camelCase
|
# Local variables must be camelCase
|
||||||
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
|
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
|
||||||
dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
|
dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
|
||||||
dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
|
dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
|
||||||
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
|
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
|
||||||
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
|
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
|
||||||
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent
|
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent
|
||||||
|
|
||||||
# This rule should never fire. However, it's included for at least two purposes:
|
# This rule should never fire. However, it's included for at least two purposes:
|
||||||
# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
|
# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
|
||||||
# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
|
# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
|
||||||
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = *
|
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = *
|
||||||
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field
|
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field
|
||||||
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group
|
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group
|
||||||
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
|
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
|
||||||
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
|
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
|
||||||
|
|
||||||
|
|
||||||
|
@ -389,30 +389,30 @@ dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
|
||||||
# - Constructors, Properties, Events, Methods
|
# - Constructors, Properties, Events, Methods
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
|
||||||
dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property
|
dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property
|
||||||
dotnet_naming_rule.element_rule.symbols = element_group
|
dotnet_naming_rule.element_rule.symbols = element_group
|
||||||
dotnet_naming_rule.element_rule.style = pascal_case_style
|
dotnet_naming_rule.element_rule.style = pascal_case_style
|
||||||
dotnet_naming_rule.element_rule.severity = warning
|
dotnet_naming_rule.element_rule.severity = warning
|
||||||
|
|
||||||
# Interfaces use PascalCase and are prefixed with uppercase 'I'
|
# Interfaces use PascalCase and are prefixed with uppercase 'I'
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
|
||||||
dotnet_naming_symbols.interface_group.applicable_kinds = interface
|
dotnet_naming_symbols.interface_group.applicable_kinds = interface
|
||||||
dotnet_naming_rule.interface_rule.symbols = interface_group
|
dotnet_naming_rule.interface_rule.symbols = interface_group
|
||||||
dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style
|
dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style
|
||||||
dotnet_naming_rule.interface_rule.severity = warning
|
dotnet_naming_rule.interface_rule.severity = warning
|
||||||
|
|
||||||
# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
|
# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
|
||||||
dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter
|
dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter
|
||||||
dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group
|
dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group
|
||||||
dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style
|
dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style
|
||||||
dotnet_naming_rule.type_parameter_rule.severity = warning
|
dotnet_naming_rule.type_parameter_rule.severity = warning
|
||||||
|
|
||||||
# Function parameters use camelCase
|
# Function parameters use camelCase
|
||||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
|
# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
|
||||||
dotnet_naming_symbols.parameters_group.applicable_kinds = parameter
|
dotnet_naming_symbols.parameters_group.applicable_kinds = parameter
|
||||||
dotnet_naming_rule.parameters_rule.symbols = parameters_group
|
dotnet_naming_rule.parameters_rule.symbols = parameters_group
|
||||||
dotnet_naming_rule.parameters_rule.style = camel_case_style
|
dotnet_naming_rule.parameters_rule.style = camel_case_style
|
||||||
dotnet_naming_rule.parameters_rule.severity = warning
|
dotnet_naming_rule.parameters_rule.severity = warning
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# License
|
# License
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Security.Cryptography;
|
namespace Benchmarks;
|
||||||
|
|
||||||
|
using System.Security.Cryptography;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
using BenchmarkDotNet.Order;
|
using BenchmarkDotNet.Order;
|
||||||
|
|
||||||
namespace Benchmarks;
|
|
||||||
|
|
||||||
[SimpleJob(RuntimeMoniker.NativeAot70)]
|
[SimpleJob(RuntimeMoniker.NativeAot70)]
|
||||||
[SimpleJob(RuntimeMoniker.Net70)]
|
[SimpleJob(RuntimeMoniker.Net70)]
|
||||||
[SimpleJob(RuntimeMoniker.Net60)]
|
[SimpleJob(RuntimeMoniker.Net60)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Reflection;
|
namespace Benchmarks;
|
||||||
using BenchmarkDotNet.Running;
|
|
||||||
|
|
||||||
namespace Benchmarks;
|
using System.Reflection;
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
using System.Reflection;
|
namespace Server;
|
||||||
using System.Text;
|
|
||||||
|
using System.Reflection;
|
||||||
using MassTransit.Mediator;
|
using MassTransit.Mediator;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NetCoreServer;
|
using NetCoreServer;
|
||||||
using Server.Packets;
|
using Packets;
|
||||||
|
|
||||||
namespace Server;
|
|
||||||
|
|
||||||
public class AuthSession : TcpSession
|
public class AuthSession : TcpSession
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
private readonly ILogger<AuthSession> logger;
|
||||||
private readonly ILogger<AuthSession> _logger;
|
private readonly IMediator mediator;
|
||||||
|
|
||||||
public AuthSession(TcpServer
|
public AuthSession(TcpServer
|
||||||
server, IMediator mediator, ILogger<AuthSession> logger) : base(server)
|
server, IMediator mediator, ILogger<AuthSession> logger) : base(server)
|
||||||
{
|
{
|
||||||
this._mediator = mediator;
|
this.mediator = mediator;
|
||||||
this._logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long Send(byte[] buffer)
|
public override long Send(byte[] buffer)
|
||||||
{
|
{
|
||||||
this._logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
|
this.logger.LogInformation("Data being sent is: {Data}", BitConverter.ToString(buffer));
|
||||||
return base.Send(buffer);
|
return base.Send(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(IPacket packet)
|
public void Send(IPacket packet)
|
||||||
{
|
{
|
||||||
var type = packet.GetType();
|
var type = packet.GetType();
|
||||||
this._logger.LogTrace("Packet of type {Type} is being serialized", type.Name);
|
this.logger.LogTrace("Packet of type {Type} is being serialized", type.Name);
|
||||||
var packetIdAttribute = type.GetCustomAttribute<PacketId>();
|
var packetIdAttribute = type.GetCustomAttribute<PacketIdAttribute>();
|
||||||
if (packetIdAttribute == null)
|
if (packetIdAttribute == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -56,38 +55,38 @@ public class AuthSession : TcpSession
|
||||||
buffer[2 + i] = bytesOfOpcode[i];
|
buffer[2 + i] = bytesOfOpcode[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logger.LogTrace("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
this.logger.LogTrace("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
||||||
this._logger.LogTrace("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
this.logger.LogTrace("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
||||||
|
|
||||||
this.Send(buffer);
|
this.Send(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnReceived(byte[] buffer, long offset, long size)
|
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||||
{
|
{
|
||||||
this._logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
|
this.logger.LogDebug("Length: {Size} & offset: {Offset}", size, offset);
|
||||||
Span<byte> decryptedBuffer = new byte[size];
|
Span<byte> decryptedBuffer = new byte[size];
|
||||||
|
|
||||||
// xor every value after the first 8 bytes
|
// xor every value after the first 8 bytes
|
||||||
var dataBuffer = this.Decrypt(new ArraySegment<byte>(buffer, 8, (int)size - 8).ToArray());
|
var dataBuffer = Decrypt(new ArraySegment<byte>(buffer, 8, (int)size - 8).ToArray());
|
||||||
|
|
||||||
this._logger.LogDebug("Length {length}", BitConverter.ToUInt16(buffer, 0));
|
this.logger.LogDebug("Length {length}", BitConverter.ToUInt16(buffer, 0));
|
||||||
var opCode = BitConverter.ToUInt16(buffer.ToArray(), 2);
|
var opCode = BitConverter.ToUInt16(buffer.ToArray(), 2);
|
||||||
this._logger.LogDebug("Packet Op Code: {OpCode}", opCode);
|
this.logger.LogDebug("Packet Op Code: {OpCode}", opCode);
|
||||||
this._logger.LogDebug("Some Value: {RandomValue}" + buffer[4]);
|
this.logger.LogDebug("Some Value: {RandomValue}", buffer[4]);
|
||||||
var clientAliveTime = BitConverter.ToUInt16(buffer.ToArray(), 5);
|
var clientAliveTime = BitConverter.ToUInt16(buffer.ToArray(), 5);
|
||||||
this._logger.LogDebug("Client Alive time: {ClientAliveTime}", clientAliveTime);
|
this.logger.LogDebug("Client Alive time: {ClientAliveTime}", clientAliveTime);
|
||||||
this._logger.LogDebug("Might be a flag: {Flag}", buffer[7]);
|
this.logger.LogDebug("Might be a flag: {Flag}", buffer[7]);
|
||||||
|
|
||||||
this._logger.LogDebug("Full buffer: {Buffer}", BitConverter.ToString(dataBuffer.ToArray()));
|
this.logger.LogDebug("Full buffer: {Buffer}", BitConverter.ToString(dataBuffer.ToArray()));
|
||||||
|
|
||||||
var rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[7],
|
var rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[7],
|
||||||
buffer[4], this.Id, this);
|
buffer[4], this.Id, this);
|
||||||
Task.Run(() => this._mediator.Send(rawPacket));
|
Task.Run(() => this.mediator.Send(rawPacket));
|
||||||
this._logger.LogInformation("Connection from: {@RemoteEndpoint}", this.Socket.RemoteEndPoint?.ToString());
|
this.logger.LogInformation("Connection from: {@RemoteEndpoint}", this.Socket.RemoteEndPoint?.ToString());
|
||||||
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
|
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] Decrypt(byte[] buffer)
|
private static byte[] Decrypt(byte[] buffer)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < buffer.Length; ++i)
|
for (var i = 0; i < buffer.Length; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
using System.Security.Cryptography;
|
namespace Server;
|
||||||
|
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MassTransit.Mediator;
|
using MassTransit.Mediator;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NetCoreServer;
|
using NetCoreServer;
|
||||||
|
|
||||||
namespace Server;
|
|
||||||
|
|
||||||
public class ChannelSession : TcpSession
|
public class ChannelSession : TcpSession
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
private readonly ILogger<ChannelSession> _logger;
|
|
||||||
|
|
||||||
private static readonly byte[] Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 }
|
private static readonly byte[] Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 }
|
||||||
.Reverse().ToArray();
|
.Reverse().ToArray();
|
||||||
|
|
||||||
private static readonly byte[] IV = new byte[]
|
private static readonly byte[] Iv = new byte[]
|
||||||
{
|
{
|
||||||
0xfe, 220, 0xba, 0x98, 0x76, 0x54, 50, 0x10, 15, 30, 0x2d, 60, 0x4b, 90, 0x69, 120
|
0xfe, 220, 0xba, 0x98, 0x76, 0x54, 50, 0x10, 15, 30, 0x2d, 60, 0x4b, 90, 0x69, 120
|
||||||
}.Reverse().ToArray();
|
}.Reverse().ToArray();
|
||||||
|
|
||||||
private readonly ICryptoTransform _encryptor;
|
private readonly ICryptoTransform decryptor;
|
||||||
private readonly ICryptoTransform _decryptor;
|
|
||||||
|
private readonly ICryptoTransform encryptor;
|
||||||
|
private readonly ILogger<ChannelSession> logger;
|
||||||
|
private readonly IMediator mediator;
|
||||||
|
|
||||||
public ChannelSession(TcpServer server, IMediator mediator, ILogger<ChannelSession> logger) : base(server)
|
public ChannelSession(TcpServer server, IMediator mediator, ILogger<ChannelSession> logger) : base(server)
|
||||||
{
|
{
|
||||||
this._mediator = mediator;
|
this.mediator = mediator;
|
||||||
this._logger = logger;
|
this.logger = logger;
|
||||||
var aes = Aes.Create();
|
var aes = Aes.Create();
|
||||||
aes.Key = Key;
|
aes.Key = Key;
|
||||||
aes.IV = IV;
|
aes.IV = Iv;
|
||||||
aes.Padding = PaddingMode.None;
|
aes.Padding = PaddingMode.None;
|
||||||
aes.Mode = CipherMode.ECB;
|
aes.Mode = CipherMode.ECB;
|
||||||
|
|
||||||
this._decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
this.decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
this._encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
this.encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnReceived(byte[] buffer, long offset, long size)
|
protected override void OnReceived(byte[] buffer, long offset, long size)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var ms = new MemoryStream(this.Decrypt(buffer)))
|
using (var ms = new MemoryStream(Decrypt(buffer)))
|
||||||
using (var cs = new CryptoStream(ms, this._decryptor, CryptoStreamMode.Read))
|
using (var cs = new CryptoStream(ms, this.decryptor, CryptoStreamMode.Read))
|
||||||
{
|
{
|
||||||
cs.Read(buffer);
|
cs.Read(buffer);
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ public class ChannelSession : TcpSession
|
||||||
}
|
}
|
||||||
catch (CryptographicException ex)
|
catch (CryptographicException ex)
|
||||||
{
|
{
|
||||||
this._logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message);
|
this.logger.LogError("An error has occured while decrypting: {ErrorMessage}", ex.Message);
|
||||||
this._logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer));
|
this.logger.LogError("Default buffer message: {Message}", Encoding.ASCII.GetString(buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] Decrypt(byte[] buffer)
|
private static byte[] Decrypt(byte[] buffer)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < buffer.Length; ++i)
|
for (var i = 0; i < buffer.Length; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
using MassTransit;
|
namespace Server.Consumers;
|
||||||
using Server.Packets;
|
|
||||||
using Server.Services;
|
|
||||||
|
|
||||||
namespace Server.Consumers;
|
using MassTransit;
|
||||||
|
using Packets;
|
||||||
|
using Services;
|
||||||
|
|
||||||
public class PacketConsumer : IConsumer<RawPacket>
|
public class PacketConsumer : IConsumer<RawPacket>
|
||||||
{
|
{
|
||||||
private readonly PacketDistributorService _distributorService;
|
private readonly PacketDistributorService distributorService;
|
||||||
|
|
||||||
public PacketConsumer(PacketDistributorService distributorService) => this._distributorService = distributorService;
|
public PacketConsumer(PacketDistributorService distributorService) => this.distributorService = distributorService;
|
||||||
|
|
||||||
public Task Consume(ConsumeContext<RawPacket> context)
|
public Task Consume(ConsumeContext<RawPacket> context)
|
||||||
{
|
{
|
||||||
this._distributorService.AddPacket(context.Message);
|
this.distributorService.AddPacket(context.Message);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using CouchDB.Driver.Types;
|
namespace Server.DB.Documents;
|
||||||
|
|
||||||
namespace Server.DB.Documents;
|
using CouchDB.Driver.Types;
|
||||||
|
|
||||||
public class Account : CouchDocument
|
public class Account : CouchDocument
|
||||||
{
|
{
|
||||||
|
@ -12,8 +12,8 @@ public class Account : CouchDocument
|
||||||
this.PermissionLevel = permissionLevel;
|
this.PermissionLevel = permissionLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Username { get; private set; }
|
public string Username { get; set; }
|
||||||
public string Password { get; private set; }
|
public string Password { get; set; }
|
||||||
public string Email { get; private set; }
|
public string Email { get; set; }
|
||||||
public byte PermissionLevel { get; private set; }
|
public byte PermissionLevel { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
using CouchDB.Driver;
|
namespace Server.DB;
|
||||||
using CouchDB.Driver.Options;
|
|
||||||
using Server.DB.Documents;
|
|
||||||
|
|
||||||
namespace Server.DB;
|
using CouchDB.Driver;
|
||||||
|
using CouchDB.Driver.Options;
|
||||||
|
using Documents;
|
||||||
|
|
||||||
public class WonderkingContext : CouchContext
|
public class WonderkingContext : CouchContext
|
||||||
{
|
{
|
||||||
public CouchDatabase<Account> Accounts { get; set; }
|
|
||||||
|
|
||||||
public WonderkingContext(CouchOptions<WonderkingContext> options) : base(options)
|
public WonderkingContext(CouchOptions<WonderkingContext> options) : base(options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CouchDatabase<Account> Accounts { get; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using NetCoreServer;
|
namespace Server.PacketHandlers;
|
||||||
using Server.Packets;
|
|
||||||
|
|
||||||
namespace Server.PacketHandlers;
|
using JetBrains.Annotations;
|
||||||
|
using NetCoreServer;
|
||||||
|
using Packets;
|
||||||
|
|
||||||
public interface IPacketHandler<in T> where T : IPacket
|
public interface IPacketHandler<in T> where T : IPacket
|
||||||
{
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
public Task HandleAsync(T packet, TcpSession session);
|
public Task HandleAsync(T packet, TcpSession session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
namespace Server.PacketHandlers;
|
||||||
|
|
||||||
|
using DB;
|
||||||
|
using DB.Documents;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NetCoreServer;
|
using NetCoreServer;
|
||||||
using Server.DB;
|
using Packets.Incoming;
|
||||||
using Server.DB.Documents;
|
using Packets.Outgoing;
|
||||||
using Server.Packets.Incoming;
|
|
||||||
using Server.Packets.Outgoing;
|
|
||||||
|
|
||||||
namespace Server.PacketHandlers;
|
|
||||||
|
|
||||||
public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
||||||
{
|
{
|
||||||
private readonly ILogger<LoginHandler> _logger;
|
private readonly IConfiguration configuration;
|
||||||
private readonly WonderkingContext _wonderkingContext;
|
private readonly ILogger<LoginHandler> logger;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly WonderkingContext wonderkingContext;
|
||||||
|
|
||||||
public LoginHandler(ILogger<LoginHandler> logger, WonderkingContext wonderkingContext, IConfiguration configuration)
|
public LoginHandler(ILogger<LoginHandler> logger, WonderkingContext wonderkingContext, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
this._logger = logger;
|
this.logger = logger;
|
||||||
this._wonderkingContext = wonderkingContext;
|
this.wonderkingContext = wonderkingContext;
|
||||||
this._configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
|
||||||
{
|
{
|
||||||
this._logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username,
|
this.logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username,
|
||||||
packet.Password);
|
packet.Password);
|
||||||
var account = this._wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
var account = this.wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
|
||||||
if (account == null)
|
if (account == null)
|
||||||
{
|
{
|
||||||
if (this._configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
if (this.configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
|
||||||
{
|
{
|
||||||
var result =
|
var result =
|
||||||
this._wonderkingContext.Accounts.AddAsync(new Account(packet.Username, packet.Password, "", 0));
|
this.wonderkingContext.Accounts.AddAsync(new Account(packet.Username, packet.Password, "", 0));
|
||||||
await result;
|
await result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Send Message that account does not exist
|
// TODO: Send Message that account does not exist
|
||||||
this._logger.LogInformation("Requested account for user: {Username} does not exist", packet.Username);
|
this.logger.LogInformation("Requested account for user: {Username} does not exist", packet.Username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var loginResponsePacket = new LoginResponsePacket
|
var loginResponsePacket = new LoginResponsePacket
|
||||||
{
|
{
|
||||||
LoginResponseReason = LoginResponseReason.OK,
|
ResponseReason = LoginResponseReason.Ok,
|
||||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 75, ServerId = 0 } },
|
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 75, ServerId = 0 } },
|
||||||
UnknownFlag = 1,
|
UnknownFlag = 1,
|
||||||
IsGameMaster = true
|
IsGameMaster = true
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using System.Text;
|
namespace Server.Packets.Incoming;
|
||||||
|
|
||||||
namespace Server.Packets.Incoming;
|
using System.Text;
|
||||||
|
|
||||||
[PacketId(OperationCode.LoginInfo)]
|
[PacketId(OperationCode.LoginInfo)]
|
||||||
public class LoginInfoPacket : IPacket
|
public class LoginInfoPacket : IPacket
|
||||||
{
|
{
|
||||||
public string Username;
|
public required string Username { get; set; }
|
||||||
|
|
||||||
public string Password;
|
public required string Password { get; set; }
|
||||||
|
|
||||||
public void Deserialize(byte[] data)
|
public void Deserialize(byte[] data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
[PacketId(OperationCode.LoginResponse)]
|
[PacketId(OperationCode.LoginResponse)]
|
||||||
public class LoginResponsePacket : IPacket
|
public class LoginResponsePacket : IPacket
|
||||||
{
|
{
|
||||||
public LoginResponseReason LoginResponseReason;
|
public required LoginResponseReason ResponseReason { get; set; }
|
||||||
public byte UnknownFlag = 1;
|
public required byte UnknownFlag { get; set; } = 1;
|
||||||
public bool IsGameMaster;
|
public required bool IsGameMaster { get; set; }
|
||||||
|
|
||||||
public ServerChannelData[] ChannelData;
|
public required ServerChannelData[] ChannelData { get; set; }
|
||||||
|
|
||||||
public void Deserialize(byte[] data)
|
public void Deserialize(byte[] data)
|
||||||
{
|
{
|
||||||
this.LoginResponseReason = (LoginResponseReason)data[0];
|
this.ResponseReason = (LoginResponseReason)data[0];
|
||||||
this.UnknownFlag = data[1];
|
this.UnknownFlag = data[1];
|
||||||
this.IsGameMaster = BitConverter.ToBoolean(data, 2);
|
this.IsGameMaster = BitConverter.ToBoolean(data, 2);
|
||||||
var channelAmount = BitConverter.ToUInt16(data, 3);
|
var channelAmount = BitConverter.ToUInt16(data, 3);
|
||||||
var sizeOfServerChannelData = 5;
|
const int sizeOfServerChannelData = 5;
|
||||||
this.ChannelData = Enumerable.Repeat(0, channelAmount).Select(i => new ServerChannelData
|
this.ChannelData = Enumerable.Repeat(0, channelAmount).Select(i => new ServerChannelData
|
||||||
{
|
{
|
||||||
ServerId = BitConverter.ToUInt16(data, 5 + 0 + (i * sizeOfServerChannelData)),
|
ServerId = BitConverter.ToUInt16(data, 5 + 0 + (i * sizeOfServerChannelData)),
|
||||||
|
@ -29,7 +29,7 @@ public class LoginResponsePacket : IPacket
|
||||||
var sizeOfServerChannelData = 5;
|
var sizeOfServerChannelData = 5;
|
||||||
Span<byte> dataSpan = stackalloc byte[5 + (this.ChannelData.Length * sizeOfServerChannelData)];
|
Span<byte> dataSpan = stackalloc byte[5 + (this.ChannelData.Length * sizeOfServerChannelData)];
|
||||||
dataSpan.Clear();
|
dataSpan.Clear();
|
||||||
dataSpan[0] = (byte)this.LoginResponseReason;
|
dataSpan[0] = (byte)this.ResponseReason;
|
||||||
dataSpan[1] = this.UnknownFlag;
|
dataSpan[1] = this.UnknownFlag;
|
||||||
dataSpan[2] = BitConverter.GetBytes(this.IsGameMaster)[0];
|
dataSpan[2] = BitConverter.GetBytes(this.IsGameMaster)[0];
|
||||||
var bytesOfChannelAmount = BitConverter.GetBytes((ushort)this.ChannelData.Length);
|
var bytesOfChannelAmount = BitConverter.GetBytes((ushort)this.ChannelData.Length);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
public enum LoginResponseReason : byte
|
public enum LoginResponseReason : byte
|
||||||
{
|
{
|
||||||
OK,
|
Ok,
|
||||||
AcountNotExist,
|
AcountNotExist,
|
||||||
WrongPassword,
|
WrongPassword,
|
||||||
Error,
|
Error,
|
||||||
|
@ -11,8 +11,8 @@ public enum LoginResponseReason : byte
|
||||||
PleaseSignUp,
|
PleaseSignUp,
|
||||||
Suspended2,
|
Suspended2,
|
||||||
EmailVerify,
|
EmailVerify,
|
||||||
NotCBTUser,
|
NotCbtUser,
|
||||||
ServerNotFoundOrDBProblem,
|
ServerNotFoundOrDbProblem,
|
||||||
LockedDuePayment,
|
LockedDuePayment,
|
||||||
UserDeletedBigpoint,
|
UserDeletedBigpoint,
|
||||||
NoMoreTries
|
NoMoreTries
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
public struct ServerChannelData
|
public struct ServerChannelData
|
||||||
{
|
{
|
||||||
public ushort ServerId;
|
public ushort ServerId { get; set; }
|
||||||
public ushort ChannelId;
|
public ushort ChannelId { get; set; }
|
||||||
public byte LoadPercentage;
|
public byte LoadPercentage { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Server.Packets;
|
|
||||||
|
|
||||||
public class PacketId : Attribute
|
|
||||||
{
|
|
||||||
public readonly OperationCode Code;
|
|
||||||
|
|
||||||
public PacketId(OperationCode code) => this.Code = code;
|
|
||||||
}
|
|
8
Server/Packets/PacketIdAttribute.cs
Normal file
8
Server/Packets/PacketIdAttribute.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Server.Packets;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class PacketIdAttribute : Attribute
|
||||||
|
{
|
||||||
|
public PacketIdAttribute(OperationCode code) => this.Code = code;
|
||||||
|
public OperationCode Code { get; }
|
||||||
|
}
|
|
@ -1,19 +1,10 @@
|
||||||
using MassTransit;
|
namespace Server.Packets;
|
||||||
|
|
||||||
namespace Server.Packets;
|
using MassTransit;
|
||||||
|
|
||||||
[MessageUrn("packets")]
|
[MessageUrn("packets")]
|
||||||
public class RawPacket
|
public class RawPacket
|
||||||
{
|
{
|
||||||
public readonly OperationCode OperationCode;
|
|
||||||
public byte[] MessageBody;
|
|
||||||
public readonly TimeSpan ClientAliveTime;
|
|
||||||
public readonly byte UnknownValue;
|
|
||||||
public readonly byte UnknownValue2;
|
|
||||||
public readonly Guid SessionId;
|
|
||||||
|
|
||||||
public readonly AuthSession Session;
|
|
||||||
|
|
||||||
public RawPacket(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2,
|
public RawPacket(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2,
|
||||||
byte unknownValue, Guid sessionId, AuthSession session)
|
byte unknownValue, Guid sessionId, AuthSession session)
|
||||||
{
|
{
|
||||||
|
@ -31,4 +22,13 @@ public class RawPacket
|
||||||
*/
|
*/
|
||||||
this.ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
this.ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OperationCode OperationCode { get; }
|
||||||
|
public byte[] MessageBody { get; }
|
||||||
|
public TimeSpan ClientAliveTime { get; }
|
||||||
|
public byte UnknownValue { get; }
|
||||||
|
public byte UnknownValue2 { get; }
|
||||||
|
public Guid SessionId { get; }
|
||||||
|
|
||||||
|
public AuthSession Session { get; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Net;
|
#pragma warning disable AV1500
|
||||||
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using CouchDB.Driver.DependencyInjection;
|
using CouchDB.Driver.DependencyInjection;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
|
@ -14,7 +15,7 @@ var configurationRoot = builder.Configuration.AddJsonFile("settings.json", false
|
||||||
.AddEnvironmentVariables().Build();
|
.AddEnvironmentVariables().Build();
|
||||||
builder.Services.AddLogging();
|
builder.Services.AddLogging();
|
||||||
builder.Logging.AddFile("Logs/Server-{Date}.log", LogLevel.Debug);
|
builder.Logging.AddFile("Logs/Server-{Date}.log", LogLevel.Debug);
|
||||||
builder.Logging.AddFile("Logs/Server-{Date}.json.log", isJson: true, minimumLevel: LogLevel.Debug);
|
builder.Logging.AddFile("Logs/Server-{Date}.json.log", LogLevel.Debug, isJson: true);
|
||||||
builder.Services.AddCouchContext<WonderkingContext>(cfg =>
|
builder.Services.AddCouchContext<WonderkingContext>(cfg =>
|
||||||
{
|
{
|
||||||
cfg.UseEndpoint(configurationRoot["DB:Endpoint"] ?? throw new InvalidOperationException())
|
cfg.UseEndpoint(configurationRoot["DB:Endpoint"] ?? throw new InvalidOperationException())
|
||||||
|
@ -27,8 +28,8 @@ builder.Services.AddHostedService<PacketDistributorService>(provider =>
|
||||||
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
|
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
|
||||||
builder.Services.AddMassTransit(x =>
|
builder.Services.AddMassTransit(x =>
|
||||||
{
|
{
|
||||||
x.UsingInMemory((context, configurator) => { configurator.ConfigureEndpoints(context); });
|
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
|
||||||
x.AddMediator(cfg => { cfg.AddConsumers(Assembly.GetExecutingAssembly()); });
|
x.AddMediator(cfg => cfg.AddConsumers(Assembly.GetExecutingAssembly()));
|
||||||
});
|
});
|
||||||
builder.Services.AddHostedService<WonderkingAuthServer>(provider => new WonderkingAuthServer(IPAddress.Any, 10001,
|
builder.Services.AddHostedService<WonderkingAuthServer>(provider => new WonderkingAuthServer(IPAddress.Any, 10001,
|
||||||
provider.GetService<ILogger<WonderkingAuthServer>>() ?? throw new InvalidOperationException(),
|
provider.GetService<ILogger<WonderkingAuthServer>>() ?? throw new InvalidOperationException(),
|
||||||
|
@ -36,3 +37,4 @@ builder.Services.AddHostedService<WonderkingAuthServer>(provider => new Wonderki
|
||||||
|
|
||||||
using var host = builder.Build();
|
using var host = builder.Build();
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
|
#pragma warning restore AV1500
|
||||||
|
|
|
@ -19,10 +19,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CouchDB.NET" Version="3.4.0"/>
|
<PackageReference Include="CouchDB.NET" Version="3.4.0"/>
|
||||||
<PackageReference Include="CouchDB.NET.DependencyInjection" Version="3.4.0"/>
|
<PackageReference Include="CouchDB.NET.DependencyInjection" Version="3.4.0"/>
|
||||||
<PackageReference Include="CSharpGuidelinesAnalyzer" Version="3.8.3">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
|
<PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Collections.Concurrent;
|
namespace Server.Services;
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using MassTransit.Internals;
|
using MassTransit.Internals;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
@ -6,49 +8,51 @@ using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.VisualBasic.CompilerServices;
|
using Microsoft.VisualBasic.CompilerServices;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Server.PacketHandlers;
|
using PacketHandlers;
|
||||||
using Server.Packets;
|
using Packets;
|
||||||
|
|
||||||
namespace Server.Services;
|
|
||||||
|
|
||||||
public class PacketDistributorService : IHostedService
|
public class PacketDistributorService : IHostedService
|
||||||
{
|
{
|
||||||
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
|
private readonly ConcurrentQueue<RawPacket> concurrentQueue;
|
||||||
private readonly ILogger<PacketDistributorService> _logger;
|
private readonly ILogger<PacketDistributorService> logger;
|
||||||
private readonly Dictionary<OperationCode, Type> _packetsTypes;
|
private readonly Dictionary<OperationCode, Type> packetHandlers;
|
||||||
private readonly Dictionary<OperationCode, Type> _packetHandlers;
|
private readonly Dictionary<OperationCode, Type> packetsTypes;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this._concurrentQueue = new ConcurrentQueue<RawPacket>();
|
this.concurrentQueue = new ConcurrentQueue<RawPacket>();
|
||||||
this._logger = logger;
|
this.logger = logger;
|
||||||
this._serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
this._packetHandlers = new Dictionary<OperationCode, Type>();
|
this.packetHandlers = new Dictionary<OperationCode, Type>();
|
||||||
this._packetsTypes = new Dictionary<OperationCode, Type>();
|
this.packetsTypes = new Dictionary<OperationCode, Type>();
|
||||||
|
|
||||||
var executingAssembly = Assembly.GetExecutingAssembly();
|
var executingAssembly = Assembly.GetExecutingAssembly();
|
||||||
this._packetsTypes = this.GetPacketsWithId(executingAssembly);
|
this.packetsTypes = this.GetPacketsWithId(executingAssembly);
|
||||||
this._packetHandlers = this.GetAllPacketHandlersWithId(executingAssembly);
|
this.packetHandlers = this.GetAllPacketHandlersWithId(executingAssembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
|
||||||
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
|
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
|
||||||
{
|
{
|
||||||
var packetsWithId = executingAssembly.GetTypes().AsParallel()
|
var packetsWithId = executingAssembly.GetTypes().AsParallel()
|
||||||
.Where(type => type.GetCustomAttribute<PacketId>() != null && type.HasInterface(typeof(IPacket)) &&
|
.Where(type => type.GetCustomAttribute<PacketIdAttribute>() != null && type.HasInterface(typeof(IPacket)) &&
|
||||||
!type.IsInterface)
|
!type.IsInterface)
|
||||||
.ToDictionary(packet => packet.GetCustomAttribute<PacketId>()!.Code);
|
.ToDictionary(packet => packet.GetCustomAttribute<PacketIdAttribute>()!.Code);
|
||||||
if (packetsWithId is not { Count: 0 })
|
if (packetsWithId is not { Count: 0 })
|
||||||
{
|
{
|
||||||
packetsWithId.AsParallel().ForAll(packet =>
|
packetsWithId.AsParallel().ForAll(packet =>
|
||||||
{
|
{
|
||||||
this._logger.LogTrace("Packet with ID: {PacketID} has been added as {PacketName}", packet.Key,
|
this.logger.LogTrace("Packet with ID: {PacketID} has been added as {PacketName}", packet.Key,
|
||||||
packet.Value.FullName);
|
packet.Value.FullName);
|
||||||
});
|
});
|
||||||
return packetsWithId;
|
return packetsWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logger.LogCritical("No Packets have been found");
|
this.logger.LogCritical("No Packets have been found");
|
||||||
throw new IncompleteInitialization();
|
throw new IncompleteInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,38 +63,36 @@ public class PacketDistributorService : IHostedService
|
||||||
.GetInterfaces().Any(i =>
|
.GetInterfaces().Any(i =>
|
||||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<>))).ToDictionary(type =>
|
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<>))).ToDictionary(type =>
|
||||||
type.GetInterfaces().First(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IPacketHandler<>))
|
type.GetInterfaces().First(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IPacketHandler<>))
|
||||||
.GetGenericArguments()[0].GetCustomAttribute<PacketId>().Code);
|
.GetGenericArguments()[0].GetCustomAttribute<PacketIdAttribute>().Code);
|
||||||
|
|
||||||
if (packetHandlersWithId is not { Count: 0 })
|
if (packetHandlersWithId is not { Count: 0 })
|
||||||
{
|
{
|
||||||
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
|
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
|
||||||
{
|
{
|
||||||
this._logger.LogTrace("PacketHandler with ID: {PacketID} has been added as {PacketName}",
|
this.logger.LogTrace("PacketHandler with ID: {PacketID} has been added as {PacketName}",
|
||||||
packetHandler.Key,
|
packetHandler.Key,
|
||||||
packetHandler.Value.FullName);
|
packetHandler.Value.FullName);
|
||||||
});
|
});
|
||||||
return packetHandlersWithId;
|
return packetHandlersWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logger.LogCritical("No PacketHandlers have been found");
|
this.logger.LogCritical("No PacketHandlers have been found");
|
||||||
throw new IncompleteInitialization();
|
throw new IncompleteInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public void AddPacket(RawPacket rawPacket)
|
public void AddPacket(RawPacket rawPacket)
|
||||||
{
|
{
|
||||||
this._concurrentQueue.Enqueue(rawPacket);
|
this.concurrentQueue.Enqueue(rawPacket);
|
||||||
Task.Run(() => this.DequeueRawPacketAsync());
|
Task.Run(() => this.DequeueRawPacketAsync());
|
||||||
this._logger.LogInformation("Packet with ID: {MessageOperationCode} has been received",
|
this.logger.LogInformation("Packet with ID: {MessageOperationCode} has been received",
|
||||||
rawPacket.OperationCode);
|
rawPacket.OperationCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DequeueRawPacketAsync()
|
private async Task DequeueRawPacketAsync()
|
||||||
{
|
{
|
||||||
if (this._concurrentQueue.TryDequeue(out var item))
|
if (this.concurrentQueue.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
Task.Run(() => { this.InvokePacketHandler(item); });
|
Task.Run(() => this.InvokePacketHandler(item));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -100,21 +102,19 @@ public class PacketDistributorService : IHostedService
|
||||||
|
|
||||||
private void InvokePacketHandler(RawPacket? item)
|
private void InvokePacketHandler(RawPacket? item)
|
||||||
{
|
{
|
||||||
this._logger.LogTrace("[{TempId}] Packet with ID: {MessageOperationCode} is being dequeued",
|
this.logger.LogTrace("[{TempId}] Packet with ID: {MessageOperationCode} is being dequeued",
|
||||||
item.Session.Id, item.OperationCode);
|
item.Session.Id, item.OperationCode);
|
||||||
var packetType = this._packetsTypes[item.OperationCode];
|
var packetType = this.packetsTypes[item.OperationCode];
|
||||||
var packet = (IPacket)Activator.CreateInstance(packetType)!;
|
var packet = (IPacket)Activator.CreateInstance(packetType)!;
|
||||||
packet.Deserialize(item.MessageBody);
|
packet.Deserialize(item.MessageBody);
|
||||||
var packetHandler =
|
var packetHandler =
|
||||||
ActivatorUtilities.GetServiceOrCreateInstance(this._serviceProvider,
|
ActivatorUtilities.GetServiceOrCreateInstance(this.serviceProvider,
|
||||||
this._packetHandlers[item.OperationCode]);
|
this.packetHandlers[item.OperationCode]);
|
||||||
packetHandler.GetType().GetMethod("HandleAsync")
|
packetHandler.GetType().GetMethod("HandleAsync")
|
||||||
?.Invoke(packetHandler, new object[] { packet, item.Session });
|
?.Invoke(packetHandler, new object[] { packet, item.Session });
|
||||||
this._logger.LogDebug("Packet data {PacketData}", JsonConvert.SerializeObject(packet));
|
this.logger.LogDebug("Packet data {PacketData}", JsonConvert.SerializeObject(packet));
|
||||||
this._logger.LogTrace("[{TempId}] Packet with ID: {MessageOperationCode} has finished",
|
this.logger.LogTrace("[{TempId}] Packet with ID: {MessageOperationCode} has finished",
|
||||||
item.Session.Id,
|
item.Session.Id,
|
||||||
item.OperationCode);
|
item.OperationCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,24 @@
|
||||||
using System.Net;
|
namespace Server.Services;
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NetCoreServer;
|
using NetCoreServer;
|
||||||
|
|
||||||
namespace Server.Services;
|
|
||||||
|
|
||||||
public class WonderkingAuthServer : TcpServer, IHostedService
|
public class WonderkingAuthServer : TcpServer, IHostedService
|
||||||
{
|
{
|
||||||
private readonly ILogger<WonderkingAuthServer> _logger;
|
private readonly ILogger<WonderkingAuthServer> logger;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public WonderkingAuthServer(IPAddress address, int port, ILogger<WonderkingAuthServer> logger,
|
public WonderkingAuthServer(IPAddress address, int port, ILogger<WonderkingAuthServer> logger,
|
||||||
IServiceProvider serviceProvider) : base(address, port)
|
IServiceProvider serviceProvider) : base(address, port)
|
||||||
{
|
{
|
||||||
this._logger = logger;
|
this.logger = logger;
|
||||||
this._serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TcpSession CreateSession() =>
|
|
||||||
ActivatorUtilities.CreateInstance<AuthSession>(this._serviceProvider, this);
|
|
||||||
|
|
||||||
protected override void OnStarting()
|
|
||||||
{
|
|
||||||
this._logger.LogInformation("Starting");
|
|
||||||
base.OnStarting();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStarted()
|
|
||||||
{
|
|
||||||
this._logger.LogInformation("Started");
|
|
||||||
base.OnStarted();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStopping()
|
|
||||||
{
|
|
||||||
this._logger.LogInformation("Stopping");
|
|
||||||
base.OnStopping();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStopped()
|
|
||||||
{
|
|
||||||
this._logger.LogInformation("Stopped");
|
|
||||||
base.OnStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnError(SocketError error) => this._logger.LogError("An error has occured {Error}", error);
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
this.Start();
|
this.Start();
|
||||||
|
@ -59,4 +30,33 @@ public class WonderkingAuthServer : TcpServer, IHostedService
|
||||||
this.Stop();
|
this.Stop();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override TcpSession CreateSession() =>
|
||||||
|
ActivatorUtilities.CreateInstance<AuthSession>(this.serviceProvider, this);
|
||||||
|
|
||||||
|
protected override void OnStarting()
|
||||||
|
{
|
||||||
|
this.logger.LogInformation("Starting");
|
||||||
|
base.OnStarting();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStarted()
|
||||||
|
{
|
||||||
|
this.logger.LogInformation("Started");
|
||||||
|
base.OnStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStopping()
|
||||||
|
{
|
||||||
|
this.logger.LogInformation("Stopping");
|
||||||
|
base.OnStopping();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStopped()
|
||||||
|
{
|
||||||
|
this.logger.LogInformation("Stopped");
|
||||||
|
base.OnStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnError(SocketError error) => this.logger.LogError("An error has occured {Error}", error);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue