~ruther/NosSmooth

24c8d2cc70193b8d82a830337060e0aba50f6867 — František Boháček 3 years ago 040e682
chore: add stylecop, ensure stylecop rules are met
33 files changed, 996 insertions(+), 140 deletions(-)

M Core/NosSmooth.Core/Client/BaseNostaleClient.cs
M Core/NosSmooth.Core/Client/INostaleClient.cs
M Core/NosSmooth.Core/Commands/CommandProcessor.cs
M Core/NosSmooth.Core/Commands/ICommand.cs
M Core/NosSmooth.Core/Commands/ICommandHandler.cs
M Core/NosSmooth.Core/Commands/WalkCommand.cs
M Core/NosSmooth.Core/Errors/CommandHandlerNotFound.cs
M Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs
A Core/NosSmooth.Core/IsExternalInit.cs
M Core/NosSmooth.Core/Packets/IPacketHandler.cs
M Core/NosSmooth.Core/Packets/IPacketResponder.cs
M Core/NosSmooth.Core/Packets/IPacketSerializer.cs
M Core/NosSmooth.Core/Packets/PacketHandler.cs
M Core/NosSmooth.Core/Packets/PacketSerializer.cs
M Core/NosSmooth.Core/Packets/PacketSerializerProvider.cs
D Core/NosSmooth.Core/RecordFix.cs
A Directory.build.props
M Local/NosSmooth.LocalClient/CommandHandlers/WalkCommandHandler.cs
M Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs
A Local/NosSmooth.LocalClient/IPacketInterceptor.cs
A Local/NosSmooth.LocalClient/LocalClientOptions.cs
M Local/NosSmooth.LocalClient/NostaleLocalClient.cs
M Local/NosSmooth.LocalClient/NostaleWindow.cs
A Local/NosSmooth.LocalClient/Utils/User32.cs
M Local/NosSmooth.LocalCore/NetworkUnmanaged.cpp
M NosSmooth.sln
D Remote/NosSmooth.Cryptography/EncryptionProvider.cs
M Samples/Test/DllMain.cs
A Samples/Test/Test.cs
M Samples/Test/Test.csproj
A libs/Directory.build.props
A stylecop.json
A stylecop.ruleset
M Core/NosSmooth.Core/Client/BaseNostaleClient.cs => Core/NosSmooth.Core/Client/BaseNostaleClient.cs +33 -8
@@ 1,49 1,74 @@
using System;
//
//  BaseNostaleClient.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading;
using System.Threading.Tasks;
using NosCore.Packets;
using NosCore.Packets.Interfaces;
using NosCore.Packets.ServerPackets.Event;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Packets;
using Remora.Results;

namespace NosSmooth.Core.Client;

/// <summary>
/// Represents base class of <see cref="INostaleClient"/>.
/// </summary>
/// <remarks>
/// This class serializes packets and processes commands.
/// </remarks>
public abstract class BaseNostaleClient : INostaleClient
{
    protected readonly CommandProcessor _commandProcessor;
    protected readonly IPacketSerializer _packetSerializer;
    private readonly CommandProcessor _commandProcessor;
    private readonly IPacketSerializer _packetSerializer;

    protected BaseNostaleClient(CommandProcessor commandProcessor, IPacketSerializer packetSerializer)
    /// <summary>
    /// Initializes a new instance of the <see cref="BaseNostaleClient"/> class.
    /// </summary>
    /// <param name="commandProcessor">The command processor.</param>
    /// <param name="packetSerializer">The packet serializer.</param>
    protected BaseNostaleClient
    (
        CommandProcessor commandProcessor,
        IPacketSerializer packetSerializer
    )
    {
        _commandProcessor = commandProcessor;
        _packetSerializer = packetSerializer;
    }

    /// <inheritdoc />
    public abstract Task<Result> RunAsync(CancellationToken stopRequested = default);

    /// <inheritdoc />
    public virtual Task<Result> SendPacketAsync(IPacket packet, CancellationToken ct = default)
    {
        var serialized = _packetSerializer.Serialize(packet);

        return serialized.IsSuccess
            ? SendPacketAsync(serialized.Entity, ct) 
            ? SendPacketAsync(serialized.Entity, ct)
            : Task.FromResult(Result.FromError(serialized));
    }

    /// <inheritdoc />
    public abstract Task<Result> SendPacketAsync(string packetString, CancellationToken ct = default);

    /// <inheritdoc />
    public abstract Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default);

    /// <inheritdoc />
    public virtual Task<Result> ReceivePacketAsync(IPacket packet, CancellationToken ct = default)
    {
        var serialized = _packetSerializer.Serialize(packet);

        return serialized.IsSuccess
            ? ReceivePacketAsync(serialized.Entity, ct) 
            ? ReceivePacketAsync(serialized.Entity, ct)
            : Task.FromResult(Result.FromError(serialized));
    }

    /// <inheritdoc />
    public Task<Result> SendCommandAsync(ICommand command, CancellationToken ct = default) =>
        _commandProcessor.ProcessCommand(command, ct);
}
\ No newline at end of file

M Core/NosSmooth.Core/Client/INostaleClient.cs => Core/NosSmooth.Core/Client/INostaleClient.cs +11 -5
@@ 1,4 1,10 @@
using System.Threading;
//
//  INostaleClient.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading;
using System.Threading.Tasks;
using NosCore.Packets.Interfaces;
using NosSmooth.Core.Commands;


@@ 17,7 23,7 @@ public interface INostaleClient
    /// <param name="stopRequested">A cancellation token for stopping the client.</param>
    /// <returns>The result that may or may not have succeeded.</returns>
    public Task<Result> RunAsync(CancellationToken stopRequested = default);
    

    /// <summary>
    /// Sends the given packet to the server.
    /// </summary>


@@ 25,7 31,7 @@ public interface INostaleClient
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Task<Result> SendPacketAsync(IPacket packet, CancellationToken ct = default);
    

    /// <summary>
    /// Sends the given raw packet string.
    /// </summary>


@@ 41,9 47,9 @@ public interface INostaleClient
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default);
    

    /// <summary>
    /// Receives the given packet. 
    /// Receives the given packet.
    /// </summary>
    /// <param name="packet">The packet to receive.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>

M Core/NosSmooth.Core/Commands/CommandProcessor.cs => Core/NosSmooth.Core/Commands/CommandProcessor.cs +22 -1
@@ 1,4 1,10 @@
using System;
//
//  CommandProcessor.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;


@@ 8,15 14,30 @@ using Remora.Results;

namespace NosSmooth.Core.Commands;

/// <summary>
/// Calls <see cref="ICommandHandler"/> for the executing command
/// by using <see cref="IServiceProvider"/> dependency injection.
/// </summary>
public class CommandProcessor
{
    private readonly IServiceProvider _provider;

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandProcessor"/> class.
    /// </summary>
    /// <param name="provider">The dependency injection provider.</param>
    public CommandProcessor(IServiceProvider provider)
    {
        _provider = provider;
    }

    /// <summary>
    /// Processes the given command, calling its handler or returning error.
    /// </summary>
    /// <param name="command">The command to process.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    /// <exception cref="InvalidOperationException">Thrown on critical error.</exception>
    public Task<Result> ProcessCommand(ICommand command, CancellationToken ct = default)
    {
        var processMethod = GetType().GetMethod

M Core/NosSmooth.Core/Commands/ICommand.cs => Core/NosSmooth.Core/Commands/ICommand.cs +16 -2
@@ 1,6 1,20 @@
namespace NosSmooth.Core.Commands;
//
//  ICommand.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Core.Client;

namespace NosSmooth.Core.Commands;

/// <summary>
/// Represents command for <see cref="INostaleClient"/>.
/// </summary>
/// <remarks>
/// Commands do complex operations that may take more time.
/// For handling commands, <see cref="ICommandHandler"/> is used.
/// </remarks>
public interface ICommand
{
    
}
\ No newline at end of file

M Core/NosSmooth.Core/Commands/ICommandHandler.cs => Core/NosSmooth.Core/Commands/ICommandHandler.cs +23 -2
@@ 1,13 1,34 @@
using System.Threading;
//
//  ICommandHandler.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading;
using System.Threading.Tasks;
using Remora.Results;

namespace NosSmooth.Core.Commands;

public interface ICommandHandler {}
/// <summary>
/// Represents interface for command handlers that process <see cref="ICommand"/>.
/// </summary>
public interface ICommandHandler
{
}

/// <summary>
/// Represents interface of class that handles <see cref="ICommand"/> of type <typeparamref name="TCommand"/>.
/// </summary>
/// <typeparam name="TCommand">The command type that this handler can execute.</typeparam>
public interface ICommandHandler<TCommand> : ICommandHandler
    where TCommand : ICommand
{
    /// <summary>
    /// Execute the given command.
    /// </summary>
    /// <param name="command">The command to execute.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Task<Result> HandleCommand(TCommand command, CancellationToken ct = default);
}
\ No newline at end of file

M Core/NosSmooth.Core/Commands/WalkCommand.cs => Core/NosSmooth.Core/Commands/WalkCommand.cs +7 -1
@@ 1,4 1,10 @@
namespace NosSmooth.Core.Commands;
//
//  WalkCommand.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.Core.Commands;

/// <summary>
/// Command that moves the player to the specified target position.

M Core/NosSmooth.Core/Errors/CommandHandlerNotFound.cs => Core/NosSmooth.Core/Errors/CommandHandlerNotFound.cs +7 -1
@@ 1,4 1,10 @@
using System;
//
//  CommandHandlerNotFound.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Remora.Results;

namespace NosSmooth.Core.Errors;

M Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Core/Extensions/ServiceCollectionExtensions.cs +36 -12
@@ 1,4 1,10 @@
using System;
//
//  ServiceCollectionExtensions.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;


@@ 13,6 19,9 @@ using NosSmooth.Core.Packets;

namespace NosSmooth.Core.Extensions;

/// <summary>
/// Contains extension methods for <see cref="IServiceCollection"/>.
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>


@@ 21,8 30,11 @@ public static class ServiceCollectionExtensions
    /// <param name="serviceCollection">The service collection to register the responder to.</param>
    /// <param name="additionalPacketTypes">Custom types of packets to serialize and deserialize.</param>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddNostaleCore(this IServiceCollection serviceCollection,
        params Type[] additionalPacketTypes)
    public static IServiceCollection AddNostaleCore
    (
        this IServiceCollection serviceCollection,
        params Type[] additionalPacketTypes
    )
    {
        serviceCollection
            .TryAddSingleton<IPacketHandler, PacketHandler>();


@@ 39,7 51,7 @@ public static class ServiceCollectionExtensions

        serviceCollection.AddSingleton(_ =>
            new PacketSerializerProvider(clientPacketTypes, serverPacketTypes));
        serviceCollection.AddSingleton(p => p.GetRequiredService<PacketSerializerProvider>().GetServerSerializer());
        serviceCollection.AddSingleton(p => p.GetRequiredService<PacketSerializerProvider>().ServerSerializer);

        serviceCollection.AddSingleton<CommandProcessor>();



@@ 53,8 65,10 @@ public static class ServiceCollectionExtensions
    /// <typeparam name="TPacketResponder">The type of the responder.</typeparam>
    /// <exception cref="ArgumentException">Thrown if the type of the responder is incorrect.</exception>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddPacketResponder<TPacketResponder>(
        this IServiceCollection serviceCollection)
    public static IServiceCollection AddPacketResponder<TPacketResponder>
    (
        this IServiceCollection serviceCollection
    )
        where TPacketResponder : class, IPacketResponder
    {
        return serviceCollection.AddPacketResponder(typeof(TPacketResponder));


@@ 67,13 81,17 @@ public static class ServiceCollectionExtensions
    /// <param name="responderType">The type of the responder.</param>
    /// <returns>The collection.</returns>
    /// <exception cref="ArgumentException">Thrown if the type of the responder is incorrect.</exception>
    public static IServiceCollection AddPacketResponder(this IServiceCollection serviceCollection, Type responderType)
    public static IServiceCollection AddPacketResponder
    (
        this IServiceCollection serviceCollection,
        Type responderType
    )
    {
        if (responderType.GetInterfaces().Any(i => i == typeof(IEveryPacketResponder)))
        {
            return serviceCollection.AddScoped(typeof(IEveryPacketResponder), responderType);
        }
        

        if (!responderType.GetInterfaces().Any(
                i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketResponder<>)
            ))


@@ 96,7 114,7 @@ public static class ServiceCollectionExtensions

        return serviceCollection;
    }
    

    /// <summary>
    /// Adds the specified command handler.
    /// </summary>


@@ 104,8 122,10 @@ public static class ServiceCollectionExtensions
    /// <typeparam name="TCommandHandler">The type of the command.</typeparam>
    /// <exception cref="ArgumentException">Thrown if the type of the responder is incorrect.</exception>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddCommandHandler<TCommandHandler>(
        this IServiceCollection serviceCollection)
    public static IServiceCollection AddCommandHandler<TCommandHandler>
    (
        this IServiceCollection serviceCollection
    )
        where TCommandHandler : class, ICommandHandler
    {
        return serviceCollection.AddCommandHandler(typeof(TCommandHandler));


@@ 118,7 138,11 @@ public static class ServiceCollectionExtensions
    /// <param name="commandHandlerType">The type of the command handler.</param>
    /// <returns>The collection.</returns>
    /// <exception cref="ArgumentException">Thrown if the type of the responder is incorrect.</exception>
    public static IServiceCollection AddCommandHandler(this IServiceCollection serviceCollection, Type commandHandlerType)
    public static IServiceCollection AddCommandHandler
    (
        this IServiceCollection serviceCollection,
        Type commandHandlerType
    )
    {
        if (!commandHandlerType.GetInterfaces().Any(
                i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICommandHandler<>)

A Core/NosSmooth.Core/IsExternalInit.cs => Core/NosSmooth.Core/IsExternalInit.cs +15 -0
@@ 0,0 1,15 @@
//
//  IsExternalInit.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace System.Runtime.CompilerServices
{
    /// <summary>
    /// Dummy.
    /// </summary>
    public class IsExternalInit
    {
    }
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/IPacketHandler.cs => Core/NosSmooth.Core/Packets/IPacketHandler.cs +7 -1
@@ 1,4 1,10 @@
using System.Threading;
//
//  IPacketHandler.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading;
using System.Threading.Tasks;
using NosCore.Packets.Interfaces;
using Remora.Results;

M Core/NosSmooth.Core/Packets/IPacketResponder.cs => Core/NosSmooth.Core/Packets/IPacketResponder.cs +26 -2
@@ 1,14 1,28 @@
using System.Threading;
//
//  IPacketResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading;
using System.Threading.Tasks;
using NosCore.Packets.Interfaces;
using Remora.Results;

namespace NosSmooth.Core.Packets;

/// <summary>
/// Represents interface for classes that respond to packets.
/// </summary>
public interface IPacketResponder
{
}

/// <summary>
/// Represents interface for classes that respond to packets.
/// Responds to <typeparamref name="TPacket"/>.
/// </summary>
/// <typeparam name="TPacket">The packet type this responder responds to.</typeparam>
public interface IPacketResponder<TPacket> : IPacketResponder
    where TPacket : IPacket
{


@@ 17,12 31,22 @@ public interface IPacketResponder<TPacket> : IPacketResponder
    /// </summary>
    /// <param name="packet">The packet to respond to.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns></returns>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Task<Result> Respond(TPacket packet, CancellationToken ct = default);
}

/// <summary>
/// Represents interface for classes that respond to every type of packets.
/// </summary>
public interface IEveryPacketResponder : IPacketResponder
{
    /// <summary>
    /// Respond to the given packet.
    /// </summary>
    /// <param name="packet">The packet to respond to.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <typeparam name="TPacket">The type of the packet.</typeparam>
    /// <returns>A result that may or may not have succeeded.</returns>
    public Task<Result> Respond<TPacket>(TPacket packet, CancellationToken ct = default)
        where TPacket : IPacket;
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/IPacketSerializer.cs => Core/NosSmooth.Core/Packets/IPacketSerializer.cs +19 -1
@@ 1,12 1,30 @@
using System.Threading.Tasks;
//
//  IPacketSerializer.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosCore.Packets.Interfaces;
using Remora.Results;

namespace NosSmooth.Core.Packets;

/// <summary>
/// Represents serialiazer and deserializer of <see cref="IPacket"/>.
/// </summary>
public interface IPacketSerializer
{
    /// <summary>
    /// Serializes the given <see cref="IPacket"/> into string.
    /// </summary>
    /// <param name="packet">The packet to serialize.</param>
    /// <returns>The serialized packet.</returns>
    public Result<string> Serialize(IPacket packet);

    /// <summary>
    /// Deserializes the given string into <see cref="IPacket"/>.
    /// </summary>
    /// <param name="packetString">The packet to deserialize.</param>
    /// <returns>The deserialized packet.</returns>
    public Result<IPacket> Deserialize(string packetString);
}
\ No newline at end of file

M Core/NosSmooth.Core/Packets/PacketHandler.cs => Core/NosSmooth.Core/Packets/PacketHandler.cs +13 -3
@@ 1,4 1,10 @@
using System;
//
//  PacketHandler.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;


@@ 15,6 21,10 @@ public class PacketHandler : IPacketHandler
{
    private readonly IServiceProvider _provider;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketHandler"/> class.
    /// </summary>
    /// <param name="provider">The dependency injection provider.</param>
    public PacketHandler(IServiceProvider provider)
    {
        _provider = provider;


@@ 52,9 62,9 @@ public class PacketHandler : IPacketHandler

        var tasks = packetResponders.Select(responder => responder.Respond(packet, ct)).ToList();
        tasks.AddRange(genericPacketResponders.Select(responder => responder.Respond(packet, ct)));
        

        var results = await Task.WhenAll(tasks);
        

        var errors = new List<Result>();
        foreach (var result in results)
        {

M Core/NosSmooth.Core/Packets/PacketSerializer.cs => Core/NosSmooth.Core/Packets/PacketSerializer.cs +15 -1
@@ 1,4 1,10 @@
using System;
//
//  PacketSerializer.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Threading.Tasks;
using NosCore.Packets;
using NosCore.Packets.Interfaces;


@@ 6,17 12,24 @@ using Remora.Results;

namespace NosSmooth.Core.Packets;

/// <inheritdoc />
public class PacketSerializer : IPacketSerializer
{
    private readonly Serializer _serializer;
    private readonly Deserializer _deserializer;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketSerializer"/> class.
    /// </summary>
    /// <param name="serializer">The NosCore serializer.</param>
    /// <param name="deserializer">The NosCore deserializer.</param>
    public PacketSerializer(Serializer serializer, Deserializer deserializer)
    {
        _serializer = serializer;
        _deserializer = deserializer;
    }

    /// <inheritdoc />
    public Result<string> Serialize(IPacket packet)
    {
        try


@@ 29,6 42,7 @@ public class PacketSerializer : IPacketSerializer
        }
    }

    /// <inheritdoc />
    public Result<IPacket> Deserialize(string packetString)
    {
        try

M Core/NosSmooth.Core/Packets/PacketSerializerProvider.cs => Core/NosSmooth.Core/Packets/PacketSerializerProvider.cs +39 -13
@@ 1,9 1,18 @@
using System;
//
//  PacketSerializerProvider.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using NosCore.Packets;

namespace NosSmooth.Core.Packets;

/// <summary>
/// Provides serializer for the server or client packets.
/// </summary>
public class PacketSerializerProvider
{
    private readonly List<Type> _clientPacketTypes;


@@ 12,31 21,48 @@ public class PacketSerializerProvider
    private IPacketSerializer? _serverSerializer;
    private IPacketSerializer? _clientSerializer;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketSerializerProvider"/> class.
    /// </summary>
    /// <param name="clientPacketTypes">The types of client packets.</param>
    /// <param name="serverPacketTypes">The types of server packets.</param>
    public PacketSerializerProvider(List<Type> clientPacketTypes, List<Type> serverPacketTypes)
    {
        _clientPacketTypes = clientPacketTypes;
        _serverPacketTypes = serverPacketTypes;
    }

    public IPacketSerializer GetServerSerializer()
    /// <summary>
    /// Gets the server serializer.
    /// </summary>
    public IPacketSerializer ServerSerializer
    {
        if (_serverSerializer is null)
        get
        {
            _serverSerializer =
                new PacketSerializer(new Serializer(_serverPacketTypes), new Deserializer(_serverPacketTypes));
        }
            if (_serverSerializer is null)
            {
                _serverSerializer =
                    new PacketSerializer(new Serializer(_serverPacketTypes), new Deserializer(_serverPacketTypes));
            }

        return _serverSerializer;
            return _serverSerializer;
        }
    }

    public IPacketSerializer GetClientSerializer()
    /// <summary>
    /// Gets the client serializer.
    /// </summary>
    public IPacketSerializer ClientSerializer
    {
        if (_clientSerializer is null)
        get
        {
            _clientSerializer =
                new PacketSerializer(new Serializer(_clientPacketTypes), new Deserializer(_clientPacketTypes));
        }
            if (_clientSerializer is null)
            {
                _clientSerializer =
                    new PacketSerializer(new Serializer(_clientPacketTypes), new Deserializer(_clientPacketTypes));
            }

        return _clientSerializer;
            return _clientSerializer;
        }
    }
}
\ No newline at end of file

D Core/NosSmooth.Core/RecordFix.cs => Core/NosSmooth.Core/RecordFix.cs +0 -4
@@ 1,4 0,0 @@
namespace System.Runtime.CompilerServices
{
    public class IsExternalInit { }
}
\ No newline at end of file

A Directory.build.props => Directory.build.props +22 -0
@@ 0,0 1,22 @@
<Project>
    <PropertyGroup>
        <StyleCopRuleset>$(MSBuildThisFileDirectory)stylecop.ruleset</StyleCopRuleset>
        <StyleCopConfiguration>$(MSBuildThisFileDirectory)stylecop.json</StyleCopConfiguration>

        <Nullable>enable</Nullable>
        <WarningsAsErrors>nullable</WarningsAsErrors>

        <GenerateDocumentationFile>true</GenerateDocumentationFile>

        <CodeAnalysisRuleSet>$(StyleCopRuleset)</CodeAnalysisRuleSet>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.261">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>

        <AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json"/>
    </ItemGroup>
</Project>

M Local/NosSmooth.LocalClient/CommandHandlers/WalkCommandHandler.cs => Local/NosSmooth.LocalClient/CommandHandlers/WalkCommandHandler.cs +25 -2
@@ 1,12 1,35 @@
using NosSmooth.Core.Commands;
//
//  WalkCommandHandler.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Core.Commands;
using NosSmoothCore;
using Remora.Results;

namespace NosSmooth.LocalClient.CommandHandlers;

/// <summary>
/// Handles <see cref="WalkCommand"/>.
/// </summary>
public class WalkCommandHandler : ICommandHandler<WalkCommand>
{
    private readonly NosClient _nosClient;

    /// <summary>
    /// Initializes a new instance of the <see cref="WalkCommandHandler"/> class.
    /// </summary>
    /// <param name="nosClient">The local client.</param>
    public WalkCommandHandler(NosClient nosClient)
    {
        _nosClient = nosClient;
    }

    /// <inheritdoc/>
    public Task<Result> HandleCommand(WalkCommand command, CancellationToken ct = default)
    {
        throw new NotImplementedException();
        _nosClient.GetCharacter().Walk(command.TargetX, command.TargetY);
        return Task.Delay(1000).ContinueWith(_ => Result.FromSuccess()); // TODO: Wait for the move to finish
    }
}
\ No newline at end of file

M Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +19 -3
@@ 1,19 1,35 @@
using Microsoft.Extensions.DependencyInjection;
//
//  ServiceCollectionExtensions.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalClient.CommandHandlers;
using NosSmoothCore;

namespace NosSmooth.LocalClient.Extensions;

/// <summary>
/// Contains extension methods for <see cref="IServiceCollection"/>.
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Adds <see cref="NostaleLocalClient"/> along with all core dependencies.
    /// </summary>
    /// <param name="serviceCollection">The service collection.</param>
    /// <returns>The collection.</returns>
    public static IServiceCollection AddLocalClient(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddNostaleCore();
        serviceCollection.AddCommandHandler<WalkCommandHandler>();
        
        serviceCollection.TryAddSingleton<INostaleClient, NostaleLocalClient>();
        serviceCollection.TryAddSingleton<NostaleLocalClient>();
        serviceCollection.TryAddSingleton<NosClient>();
        serviceCollection.TryAddSingleton<INostaleClient>(p => p.GetRequiredService<NostaleLocalClient>());

        return serviceCollection;
    }

A Local/NosSmooth.LocalClient/IPacketInterceptor.cs => Local/NosSmooth.LocalClient/IPacketInterceptor.cs +28 -0
@@ 0,0 1,28 @@
//
//  IPacketInterceptor.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.LocalClient;

/// <summary>
/// Used for intercepting packet communication,
/// changing the contents of the packets and/or cancelling them altogether.
/// </summary>
public interface IPacketInterceptor
{
    /// <summary>
    /// Intercept the given packet.
    /// </summary>
    /// <param name="packet">The packet itself, if it is changed, the modified packet will be sent.</param>
    /// <returns>Whether to send the packet to the server.</returns>
    public bool InterceptSend(ref string packet);

    /// <summary>
    /// Intercept the given packet.
    /// </summary>
    /// <param name="packet">The packet itself, if it is changed, the modified packet will be received.</param>
    /// <returns>Whether to receive the packet by the client.</returns>
    public bool InterceptReceive(ref string packet);
}
\ No newline at end of file

A Local/NosSmooth.LocalClient/LocalClientOptions.cs => Local/NosSmooth.LocalClient/LocalClientOptions.cs +18 -0
@@ 0,0 1,18 @@
//
//  LocalClientOptions.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.LocalClient;

/// <summary>
/// Options for <see cref="NostaleLocalClient"/>.
/// </summary>
public class LocalClientOptions
{
    /// <summary>
    /// Gets or sets whether the interception of packets should be allowed.
    /// </summary>
    public bool AllowIntercept { get; set; }
}
\ No newline at end of file

M Local/NosSmooth.LocalClient/NostaleLocalClient.cs => Local/NosSmooth.LocalClient/NostaleLocalClient.cs +113 -17
@@ 1,41 1,79 @@
using NosSmooth.Core.Client;
//
//  NostaleLocalClient.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Packets;
using NosSmoothCore;
using Remora.Results;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;

namespace NosSmooth.LocalClient;

/// <summary>
/// The local nostale client.
/// </summary>
/// <remarks>
/// Client used for living in the same process as NostaleClientX.exe.
/// It hooks the send and receive packet methods.
/// </remarks>
public class NostaleLocalClient : BaseNostaleClient
{
    private readonly IPacketSerializer _packetSerializer;
    private readonly IPacketHandler _packetHandler;
    private readonly ILogger _logger;
    private readonly NosClient _client;
    private readonly LocalClientOptions _options;
    private readonly IPacketInterceptor? _interceptor;

    public NostaleLocalClient(CommandProcessor commandProcessor, IPacketSerializer packetSerializer,
        ILogger<NostaleLocalClient> logger)
    /// <summary>
    /// Initializes a new instance of the <see cref="NostaleLocalClient"/> class.
    /// </summary>
    /// <param name="commandProcessor">The command processor.</param>
    /// <param name="packetSerializer">The packet serializer.</param>
    /// <param name="packetHandler">The packet handler.</param>
    /// <param name="logger">The logger.</param>
    /// <param name="options">The options for the client.</param>
    /// <param name="provider">The dependency injection provider.</param>
    /// <param name="client">The nostale managed client.</param>
    public NostaleLocalClient
    (
        CommandProcessor commandProcessor,
        IPacketSerializer packetSerializer,
        IPacketHandler packetHandler,
        ILogger<NostaleLocalClient> logger,
        IOptions<LocalClientOptions> options,
        IServiceProvider provider,
        NosClient client
    )
        : base(commandProcessor, packetSerializer)
    {
        _options = options.Value;
        _packetSerializer = packetSerializer;
        _packetHandler = packetHandler;
        _logger = logger;
        NosClient = new NosClient();
    }

    public NosClient NosClient { get; }
        _client = client;

    public override Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default)
    {
        NosClient.GetNetwork().ReceivePacket(packetString);
        return Task.FromResult(Result.FromSuccess());
        if (_options.AllowIntercept)
        {
            _interceptor = provider.GetRequiredService<IPacketInterceptor>();
        }
    }

    /// <inheritdoc />
    public override async Task<Result> RunAsync(CancellationToken stopRequested = default)
    {
        _logger.LogInformation("Starting local client");
        NetworkCallback receiveCallback = ReceiveCallback;
        NetworkCallback sendCallback = SendCallback;

        NosClient.GetNetwork().SetReceiveCallback(receiveCallback);
        NosClient.GetNetwork().SetSendCallback(sendCallback);
        _client.GetNetwork().SetReceiveCallback(receiveCallback);
        _client.GetNetwork().SetSendCallback(sendCallback);
        _logger.LogInformation("Packet methods hooked successfully");

        try


@@ 46,26 84,84 @@ public class NostaleLocalClient : BaseNostaleClient
        {
        }

        NosClient.ResetHooks();
        _client.ResetHooks();

        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public override Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default)
    {
        ReceivePacket(packetString);
        return Task.FromResult(Result.FromSuccess());
    }

    /// <inheritdoc />
    public override Task<Result> SendPacketAsync(string packetString, CancellationToken ct = default)
    {
        NosClient.GetNetwork().SendPacket(packetString);
        SendPacket(packetString);
        return Task.FromResult(Result.FromSuccess());
    }

    private bool ReceiveCallback(string packet)
    {
        if (_options.AllowIntercept)
        {
            if (_interceptor is null)
            {
                throw new InvalidOperationException("The interceptor cannot be null if interception is allowed.");
            }

            var process = _interceptor.InterceptReceive(ref packet);
            if (process)
            {
                ReceivePacket(packet);
            }
        }
        else
        {
            ReceivePacket(packet);
        }

        // TODO: handlers

        _logger.LogInformation($"Received packet {packet}");
        return true;
    }

    private bool SendCallback(string packet)
    {
        if (_options.AllowIntercept)
        {
            if (_interceptor is null)
            {
                throw new InvalidOperationException("The interceptor cannot be null if interception is allowed.");
            }

            var process = _interceptor.InterceptSend(ref packet);
            if (process)
            {
                SendPacket(packet);
            }
        }
        else
        {
            SendPacket(packet);
        }

        // TODO: handlers

        _logger.LogInformation($"Sent packet {packet}");
        return true;
    }

    private void SendPacket(string packetString)
    {
        _client.GetNetwork().SendPacket(packetString);
    }

    private void ReceivePacket(string packetString)
    {
        _client.GetNetwork().ReceivePacket(packetString);
    }
}
\ No newline at end of file

M Local/NosSmooth.LocalClient/NostaleWindow.cs => Local/NosSmooth.LocalClient/NostaleWindow.cs +54 -2
@@ 1,6 1,58 @@
namespace NosSmooth.LocalClient;
//
//  NostaleWindow.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.LocalClient.Utils;

namespace NosSmooth.LocalClient;

/// <summary>
/// Represents window of nostale client.
/// </summary>
public class NostaleWindow
{
    
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private const int WM_CHAR = 0x0102;

    /// <summary>
    /// Initializes a new instance of the <see cref="NostaleWindow"/> class.
    /// </summary>
    /// <param name="handle">The handle of the window.</param>
    public NostaleWindow(IntPtr handle) => Handle = handle;

    /// <summary>
    /// Gets the window handle.
    /// </summary>
    public IntPtr Handle { get; }

    /// <summary>
    /// Changes the title of the window.
    /// </summary>
    /// <param name="name">The new name of the window.</param>
    public void Rename(string name)
    {
        User32.SetWindowText(Handle, name);
    }

    /// <summary>
    /// Bring the window to front.
    /// </summary>
    public void BringToFront()
    {
        User32.SetForegroundWindow(Handle);
    }

    /// <summary>
    /// Send the given key to the window.
    /// </summary>
    /// <param name="key">The id of the key.</param>
    public void SendKey(uint key)
    {
        User32.PostMessage(Handle, WM_KEYDOWN, key, 0);
        User32.PostMessage(Handle, WM_CHAR, key, 0);
        User32.PostMessage(Handle, WM_KEYUP, key, 0);
    }
}
\ No newline at end of file

A Local/NosSmooth.LocalClient/Utils/User32.cs => Local/NosSmooth.LocalClient/Utils/User32.cs +87 -0
@@ 0,0 1,87 @@
//
//  User32.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;

namespace NosSmooth.LocalClient.Utils;

/// <summary>
/// Represents class with extern calls to user32.dll.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600", MessageId = "Elements should be documented", Justification = "user32.dll methods do not need documentation, it can be found on msdn.")]
public class User32
{
    public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern int PostMessage(IntPtr hWnd, int uMsg, uint wParam, uint lParam);

    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern bool SetWindowText(IntPtr hWnd, string text);

    [DllImport("user32.dll")]
    public static extern int EnumWindows(EnumWindowsProc callback, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

    /// <summary>
    /// Finds all windows with the given title matching.
    /// </summary>
    /// <param name="title">The title to match.</param>
    /// <returns>The matched windows.</returns>
    public static IEnumerable<IntPtr> FindWindowsWithTitle(string title)
    {
        var windows = new List<IntPtr>();
        EnumWindows(
            (hWnd, lParam) =>
            {
                string windowTitle = GetWindowTitle(hWnd);
                if (windowTitle.Equals(title))
                {
                    windows.Add(hWnd);
                }

                return true;
            },
            IntPtr.Zero
        );

        return windows;
    }

    /// <summary>
    /// Returns the title of a window.
    /// </summary>
    /// <param name="hWnd">The handle of the window.</param>
    /// <returns>The title of the window.</returns>
    public static string GetWindowTitle(IntPtr hWnd)
    {
        int size = GetWindowTextLength(hWnd);
        if (size == 0)
        {
            return string.Empty;
        }

        var sb = new StringBuilder(size + 1);
        GetWindowText(hWnd, sb, sb.Capacity);
        return sb.ToString();
    }
}
\ No newline at end of file

M Local/NosSmooth.LocalCore/NetworkUnmanaged.cpp => Local/NosSmooth.LocalCore/NetworkUnmanaged.cpp +3 -13
@@ 24,18 24,13 @@ void PacketSendDetour()
        mov packet, edx
    }

    bool isAccepted = NetworkUnmanaged::GetInstance()->ExecuteSendCallback(packet);
    NetworkUnmanaged::GetInstance()->ExecuteSendCallback(packet);

    __asm
    {
        popfd
        popad
    }

    if (isAccepted)
    {
        NetworkUnmanaged::GetInstance()->SendPacket(packet);
    }
}

void PacketReceiveDetour()


@@ 50,25 45,20 @@ void PacketReceiveDetour()
        mov packet, edx
    }

    bool isAccepted = NetworkUnmanaged::GetInstance()->ExecuteReceiveCallback(packet);
    NetworkUnmanaged::GetInstance()->ExecuteReceiveCallback(packet);

    __asm
    {
        popfd
        popad
    }

    if (isAccepted)
    {
        NetworkUnmanaged::GetInstance()->ReceivePacket(packet);
    }
}

void NetworkUnmanaged::Setup(ModuleHook moduleHook)
{
    auto sendFunction = moduleHook.FindPattern(SEND_PATTERN, SEND_MASK);
    auto receiveFunction = moduleHook.FindPattern(RECV_PATTERN, RECV_MASK);
    auto callerObject = *reinterpret_cast<unsigned int*>(moduleHook.FindPattern(PACKET_CALLER_PATTERN, PACKET_CALLER_MASK) + 1);
    auto callerObject = *reinterpret_cast<unsigned int*>((DWORD_PTR)moduleHook.FindPattern(PACKET_CALLER_PATTERN, PACKET_CALLER_MASK) + 1);

    if (sendFunction == 0)
    {

M NosSmooth.sln => NosSmooth.sln +5 -0
@@ 33,6 33,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosCore.Shared", "libs\NosC
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testapp", "Samples\testapp\testapp.csproj", "{E4E4969E-F79C-4074-A3A8-E1EF9C2A4718}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".metadata", ".metadata", "{FA63BCED-9D81-4FF7-BA75-A6F3BA31ECDE}"
	ProjectSection(SolutionItems) = preProject
		Directory.build.props = Directory.build.props
	EndProjectSection
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU

D Remote/NosSmooth.Cryptography/EncryptionProvider.cs => Remote/NosSmooth.Cryptography/EncryptionProvider.cs +0 -24
@@ 1,24 0,0 @@
namespace NosSmooth.Cryptography;

public class EncryptionProvider
{
    public IDecryptor GetDecryptor()
    {
        
    }

    public IEncryptor GetEncryptor()
    {
        
    }

    public void SwitchToWorld(string encryptionKey)
    {
        
    }

    public void SwitchToLogin()
    {
        
    }
}
\ No newline at end of file

M Samples/Test/DllMain.cs => Samples/Test/DllMain.cs +29 -19
@@ 1,4 1,10 @@
using System.Runtime.InteropServices;
//
//  DllMain.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;


@@ 9,48 15,52 @@ using NosSmooth.LocalClient.Extensions;

namespace Test
{
    /// <summary>
    /// Entry point of the dll.
    /// </summary>
    public class DllMain
    {
        /// <summary>
        /// Create console.
        /// </summary>
        public static void CreateConsole()
        {
            AllocConsole();
        }

        [DllImport("kernel32.dll",
        [DllImport(
            "kernel32.dll",
            EntryPoint = "GetStdHandle",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr GetStdHandle(int nStdHandle);
        [DllImport("kernel32.dll",

        [DllImport(
            "kernel32.dll",
            EntryPoint = "AllocConsole",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern int AllocConsole();

        /// <summary>
        /// The entrypoint method.
        /// </summary>
        /// <param name="moduleHandle">The handle of the dll.</param>
        /// <returns>The error code.</returns>
        [DllExport]
        public static int Main(IntPtr moduleHandle)
        {
            CreateConsole();
            
            new Thread(async () =>
            {
                var provider = new ServiceCollection()
                    .AddNostaleCore()
                    .AddLocalClient()
                    .AddLogging(b => b.AddSimpleConsole())
                    .BuildServiceProvider();
                Console.WriteLine("Test");
                var logger = provider.GetRequiredService<ILogger<DllMain>>();
                Console.WriteLine("Hell");
                logger.LogInformation("Built services");
                Thread.Sleep(1000); 

                var client = provider.GetRequiredService<INostaleClient>();
                await client.RunAsync();
            }).Start();
            Task.Run(Start);
            return 0;
        }

        private static Task Start()
        {
            return new Test().Start();
        }
    }
}
\ No newline at end of file

A Samples/Test/Test.cs => Samples/Test/Test.cs +38 -0
@@ 0,0 1,38 @@
//
//  Test.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalClient.Extensions;

namespace Test;

public class Test
{
    public async Task Start()
    {
        var provider = new ServiceCollection()
            .AddNostaleCore()
            .AddLocalClient()
            .AddLogging(b =>
            {
                b.ClearProviders();
                b.AddSimpleConsole();
                b.SetMinimumLevel(LogLevel.Debug);
            })
            .BuildServiceProvider();
        Console.WriteLine("Test");
        var logger = provider.GetRequiredService<ILogger<DllMain>>();
        Console.WriteLine("Hell");
        logger.LogInformation("Built services");
        Thread.Sleep(1000); 

        var client = provider.GetRequiredService<INostaleClient>();
        await client.RunAsync();
    }
}
\ No newline at end of file

M Samples/Test/Test.csproj => Samples/Test/Test.csproj +0 -3
@@ 45,9 45,6 @@
    <ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
    <ProjectReference Include="..\..\Local\NosSmooth.LocalCore\NosSmooth.LocalCore.vcxproj" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
  <ImportGroup Label=".NET DllExport">
    <Import Project="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
  </ImportGroup>

A libs/Directory.build.props => libs/Directory.build.props +3 -0
@@ 0,0 1,3 @@
<Project>
    <!-- Only here so that the default Directory.Build.props will not be used. -->
</Project>

A stylecop.json => stylecop.json +61 -0
@@ 0,0 1,61 @@
{
  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
  "settings": {
    "indentation": {
      "indentationSize": 4,
      "tabSize": 4,
      "useTabs": false
    },
    "spacingRules": {
    },
    "readabilityRules": {
    },
    "orderingRules": {
      "elementOrder": [
        "kind",
        "constant",
        "accessibility",
        "static",
        "readonly"
      ],
      "systemUsingDirectivesFirst": true,
      "usingDirectivesPlacement": "outsideNamespace",
      "blankLinesBetweenUsingGroups": "allow"
    },
    "namingRules": {
      "allowCommonHungarianPrefixes": true,
      "allowedHungarianPrefixes": [
        "gl",
        "f",
        "db"
      ]
    },
    "maintainabilityRules": {
      "topLevelTypes": [
        "class",
        "interface",
        "struct",
        "enum"
      ]
    },
    "layoutRules": {
      "allowConsecutiveUsings": false
    },
    "documentationRules": {
      "companyName": "František Boháček",
      "copyrightText": "\n {fileName}\n\n Copyright (c) {companyName}. All rights reserved.\n Licensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.",
      "variables": {
        "licenseName": "MIT",
        "licenseFile": "LICENSE"
      },
      "xmlHeader": false,
      "documentInterfaces": true,
      "documentExposedElements": true,
      "documentInternalElements": true,
      "documentPrivateElements": false,
      "documentPrivateFields": false,
      "documentationCulture": "en-US",
      "fileNamingConvention": "stylecop"
    }
  }
}

A stylecop.ruleset => stylecop.ruleset +202 -0
@@ 0,0 1,202 @@
<RuleSet Name="Christofel rules" ToolsVersion="14.0">
    <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">

        <!-- Special rules -->
        <Rule Id="SA0001" Action="Error"/> <!-- XML comment analysis disabled -->
        <Rule Id="SA0002" Action="Error"/> <!-- Invalid settings file -->

        <!-- Spacing rules -->
        <Rule Id="SA1000" Action="Error"/> <!-- Keywords must be spaced correctly -->
        <Rule Id="SA1001" Action="Error"/> <!-- Commas must be spaced correctly -->
        <Rule Id="SA1002" Action="Error"/> <!-- Semicolons must be spaced correctly -->
        <Rule Id="SA1003" Action="Error"/> <!-- Symbols must be spaced correctly -->
        <Rule Id="SA1004" Action="Error"/> <!-- Documentation lines must begin with single space -->
        <Rule Id="SA1005" Action="Error"/> <!-- Single line comments must begin with single space -->
        <Rule Id="SA1006" Action="Error"/> <!-- Preprocessor keywords must not be preceded by space -->
        <Rule Id="SA1007" Action="Error"/> <!-- Operator keyword must be followed by space -->
        <Rule Id="SA1008" Action="Error"/> <!-- Opening parenthesis must be spaced correctly -->
        <Rule Id="SA1009" Action="None"/> <!-- Closing parenthesis must be spaced correctly -->
        <Rule Id="SA1010" Action="Error"/> <!-- Opening square brackets must be spaced correctly -->
        <Rule Id="SA1011" Action="Error"/> <!-- Closing square brackets must be spaced correctly -->
        <Rule Id="SA1012" Action="Error"/> <!-- Opening braces must be spaced correctly -->
        <Rule Id="SA1013" Action="Error"/> <!-- Closing braces must be spaced correctly -->
        <Rule Id="SA1014" Action="Error"/> <!-- Opening generic brackets must be spaced correctly -->
        <Rule Id="SA1015" Action="Error"/> <!-- Closing generic brackets must be spaced correctly -->
        <Rule Id="SA1016" Action="Error"/> <!-- Opening attribute brackets must be spaced correctly -->
        <Rule Id="SA1017" Action="Error"/> <!-- Closing attribute brackets must be spaced correctly -->
        <Rule Id="SA1018" Action="Error"/> <!-- Nullable type symbols must be spaced correctly -->
        <Rule Id="SA1019" Action="Error"/> <!-- Member access symbols must be spaced correctly -->
        <Rule Id="SA1020" Action="Error"/> <!-- Increment decrement symbols must be spaced correctly -->
        <Rule Id="SA1021" Action="Error"/> <!-- Negative signs must be spaced correctly -->
        <Rule Id="SA1022" Action="Error"/> <!-- Positive signs must be spaced correctly -->
        <Rule Id="SA1023" Action="Error"/> <!-- Dereference and access of symbols must be spaced correctly -->
        <Rule Id="SA1024" Action="Error"/> <!-- Colons must be spaced correctly -->
        <Rule Id="SA1025" Action="Error"/> <!-- Code must not contain multiple whitespace in a row -->
        <Rule Id="SA1026"
              Action="Error"/> <!-- Code must not contain space after new keyword in implicitly typed array allocation -->
        <Rule Id="SA1027" Action="Error"/> <!-- Use tabs correctly -->
        <Rule Id="SA1028" Action="Error"/> <!-- Code must not contain trailing whitespace -->

        <!-- Readability rules -->
        <Rule Id="SA1100" Action="Error"/> <!-- Do not prefix calls with base unless local implementation exists -->
        <Rule Id="SA1101" Action="None"/> <!-- Prefix local calls with this -->
        <Rule Id="SX1101" Action="None"/> <!-- Do not prefix local calls with this. -->
        <Rule Id="SA1102" Action="Error"/> <!-- Query clause must follow previous clause -->
        <Rule Id="SA1103" Action="Error"/> <!-- Query clauses must be on separate lines or all on one line -->
        <Rule Id="SA1104"
              Action="Error"/> <!-- Query clause must begin on new line when previous clause spans multiple lines -->
        <Rule Id="SA1105" Action="Error"/> <!-- Query clauses spanning multiple lines must begin on own line -->
        <Rule Id="SA1106" Action="Error"/> <!-- Code must not contain empty statements -->
        <Rule Id="SA1107" Action="Error"/> <!-- Code must not contain multiple statements on one line -->
        <Rule Id="SA1108" Action="Error"/> <!-- Block statements must not contain embedded comments -->
        <Rule Id="SA1110" Action="None"/> <!-- Opening parenthesis or bracket must be on declaration line -->
        <Rule Id="SA1111" Action="None"/> <!-- Closing parenthesis must be on line of last parameter -->
        <Rule Id="SA1112" Action="Error"/> <!-- Closing parenthesis must be on line of opening parenthesis -->
        <Rule Id="SA1113" Action="Error"/> <!-- Comma must be on the same line as previous parameter -->
        <Rule Id="SA1114" Action="Error"/> <!-- Parameter list must follow declaration -->
        <Rule Id="SA1115" Action="Error"/> <!-- Parameter must follow comma -->
        <Rule Id="SA1116" Action="Error"/> <!-- Split parameters must start on line after declaration -->
        <Rule Id="SA1117" Action="Error"/> <!-- Parameters must be on same line or separate lines -->
        <Rule Id="SA1118" Action="Error"/> <!-- Parameter must not span multiple lines -->
        <Rule Id="SA1119" Action="Error"/> <!-- Statement must not use unnecessary parenthesis -->
        <Rule Id="SA1120" Action="None"/> <!-- Comments must contain text -->
        <Rule Id="SA1121" Action="Error"/> <!-- Use built-in type alias -->
        <Rule Id="SA1122" Action="Error"/> <!-- Use string.Empty for empty strings -->
        <Rule Id="SA1123" Action="Error"/> <!-- Do not place regions within elements -->
        <Rule Id="SA1124" Action="Error"/> <!-- Do not use regions -->
        <Rule Id="SA1125" Action="Error"/> <!-- Use shorthand for nullable types -->
        <Rule Id="SA1127" Action="None"/> <!-- Generic type constraints must be on their own line -->
        <Rule Id="SA1128" Action="Error"/> <!-- Put constructor initializers on their own line -->
        <Rule Id="SA1129" Action="Error"/> <!-- Do not use default value type constructor -->
        <Rule Id="SA1130" Action="Error"/> <!-- Use lambda syntax -->
        <Rule Id="SA1131" Action="Error"/> <!-- Use readable conditions -->
        <Rule Id="SA1132" Action="Error"/> <!-- Do not combine fields -->
        <Rule Id="SA1133" Action="None"/> <!-- Do not combine attributes -->
        <Rule Id="SA1134" Action="Error"/> <!-- Attributes must not share line -->
        <Rule Id="SA1136" Action="Error"/> <!-- Enum values should be on separate lines -->
        <Rule Id="SA1137" Action="Error"/> <!-- Elements should have the same indentation -->
        <Rule Id="SA1139" Action="Error"/> <!-- Use literals suffix notation instead of casting -->

        <!-- Ordering rules -->
        <Rule Id="SA1200" Action="None"/> <!-- Using directives must be placed correctly -->
        <Rule Id="SA1201" Action="None"/> <!-- Elements must appear in the correct order -->
        <Rule Id="SA1202" Action="None"/> <!-- Elements must be ordered by access -->
        <Rule Id="SA1203" Action="Error"/> <!-- Constants must appear before fields -->
        <Rule Id="SA1204" Action="None"/> <!-- Static elements must appear before instance elements -->
        <Rule Id="SA1205" Action="Error"/> <!-- Partial elements must declare access -->
        <Rule Id="SA1206" Action="Error"/> <!-- Declaration keywords must follow order -->
        <Rule Id="SA1207" Action="Error"/> <!-- Protected must come before internal -->
        <Rule Id="SA1208" Action="Error"/> <!-- System using directives must be placed before other using directives -->
        <Rule Id="SA1209" Action="Error"/> <!-- Using alias directives must be placed after other using directives -->
        <Rule Id="SA1210" Action="Error"/> <!-- Using directives must be ordered alphabetically by namespace -->
        <Rule Id="SA1210" Action="Error"/> <!-- Using directives must be ordered alphabetically by namespace -->
        <Rule Id="SA1211" Action="Error"/> <!-- Using alias directives must be ordered alphabetically by alias name -->
        <Rule Id="SA1212" Action="Error"/> <!-- Property accessors must follow order -->
        <Rule Id="SA1213" Action="Error"/> <!-- Event accessors must follow order -->
        <Rule Id="SA1214" Action="Error"/> <!-- Readonly fields must appear before non-readonly fields -->
        <Rule Id="SA1216" Action="Error"/> <!-- Using static directives must be placed at the correct location. -->
        <Rule Id="SA1217" Action="Error"/> <!-- Using static directives must be ordered alphabetically -->

        <!-- Naming rules -->
        <Rule Id="SA1300" Action="None"/> <!-- Element must begin with upper-case letter -->
        <Rule Id="SA1302" Action="Error"/> <!-- Interface names must begin with I -->
        <Rule Id="SA1303" Action="Error"/> <!-- Const field names must begin with upper-case letter -->
        <Rule Id="SA1304" Action="Error"/> <!-- Non-private readonly fields must begin with upper-case letter -->
        <Rule Id="SA1305" Action="None"/> <!-- Field names must not use Hungarian notation -->
        <Rule Id="SA1306" Action="None"/> <!-- Field names must begin with lower-case letter -->
        <Rule Id="SA1307" Action="Error"/> <!-- Accessible fields must begin with upper-case letter -->
        <Rule Id="SA1308" Action="Error"/> <!-- Variable names must not be prefixed -->
        <Rule Id="SA1309" Action="None"/> <!-- Field names must not begin with underscore -->
        <Rule Id="SX1309" Action="Error"/> <!-- Field names must begin with underscore -->
        <Rule Id="SX1309S" Action="None"/> <!-- Static field names must begin with underscore -->
        <Rule Id="SA1310" Action="None"/> <!-- Field names must not contain underscore -->
        <Rule Id="SA1311" Action="Error"/> <!-- Static readonly fields must begin with upper-case letter -->
        <Rule Id="SA1312" Action="Error"/> <!-- Variable names must begin with lower-case letter -->
        <Rule Id="SA1313" Action="Error"/> <!-- Parameter names must begin with lower-case letter -->
        <Rule Id="SA1314" Action="Error"/> <!-- Type name parameters must begin with T -->

        <!-- Maintainability rules -->
        <Rule Id="SA1400" Action="Error"/> <!-- Access modifier must be declared -->
        <Rule Id="SA1401" Action="Error"/> <!-- Fields must be private -->
        <Rule Id="SA1402" Action="Error"/> <!-- File may only contain a single class -->
        <Rule Id="SA1403" Action="Error"/> <!-- File may only contain a single namespace -->
        <Rule Id="SA1404" Action="Error"/> <!-- Code analysis suppression must have justification -->
        <Rule Id="SA1405" Action="Error"/> <!-- Debug.Assert must provide message text -->
        <Rule Id="SA1406" Action="Error"/> <!-- Debug.Fail must provide message text -->
        <Rule Id="SA1407" Action="Error"/> <!-- Arithmetic expressions must declare precedence -->
        <Rule Id="SA1408" Action="Error"/> <!-- Conditional expressions must declare precedence -->
        <Rule Id="SA1410" Action="Error"/> <!-- Remove delegate parenthesis when possible -->
        <Rule Id="SA1411" Action="Error"/> <!-- Attribute constructor must not use unnecessary parenthesis -->
        <Rule Id="SA1412" Action="None"/> <!-- Store files as UTF-8 with byte order mark -->
        <Rule Id="SA1413" Action="None"/> <!-- Use trailing comma in multi-line initializers -->

        <!-- Layout rules -->
        <Rule Id="SA1500" Action="Error"/> <!-- Braces for multi-line statements must not share line -->
        <Rule Id="SA1501" Action="Error"/> <!-- Statement must not be on a single line -->
        <Rule Id="SA1502" Action="Error"/> <!-- Element must not be on a single line -->
        <Rule Id="SA1503" Action="Error"/> <!-- Braces must not be omitted -->
        <Rule Id="SA1504" Action="Error"/> <!-- All accessors must be single-line or multi-line -->
        <Rule Id="SA1505" Action="Error"/> <!-- Opening braces must not be followed by blank line -->
        <Rule Id="SA1506" Action="Error"/> <!-- Element documentation headers must not be followed by blank line -->
        <Rule Id="SA1507" Action="Error"/> <!-- Code must not contain multiple blank lines in a row -->
        <Rule Id="SA1508" Action="None"/> <!-- Closing braces must not be preceded by blank line -->
        <Rule Id="SA1509" Action="Error"/> <!-- Opening braces must not be preceded by blank line -->
        <Rule Id="SA1510" Action="Error"/> <!-- Chained statement blocks must not be preceded by blank line -->
        <Rule Id="SA1511" Action="Error"/> <!-- While-do footer must not be preceded by blank line -->
        <Rule Id="SA1512" Action="Warning"/> <!-- Single-line comments must not be followed by blank line -->
        <Rule Id="SA1513" Action="None"/> <!-- Closing brace must be followed by blank line -->
        <Rule Id="SA1514" Action="Error"/> <!-- Element documentation header must be preceded by blank line -->
        <Rule Id="SA1515" Action="Error"/> <!-- Single-line comment must be preceded by blank line -->
        <Rule Id="SA1516" Action="Error"/> <!-- Elements must be separated by blank line -->
        <Rule Id="SA1517" Action="Error"/> <!-- Code must not contain blank lines at start of file -->
        <Rule Id="SA1518" Action="Error"/> <!-- Use line endings correctly at end of file -->
        <Rule Id="SA1519" Action="Error"/> <!-- Braces must not be omitted from multi-line child statement -->
        <Rule Id="SA1520" Action="Error"/> <!-- Use braces consistently -->

        <!-- Documentation rules -->
        <Rule Id="SA1600" Action="Error"/> <!-- Elements must be documented -->
        <Rule Id="SA1601" Action="None"/> <!-- Partial elements must be documented -->
        <Rule Id="SA1602" Action="Error"/> <!-- Enumeration items must be documented -->
        <Rule Id="SA1604" Action="Error"/> <!-- Element documentation must have summary -->
        <Rule Id="SA1605" Action="Error"/> <!-- Partial element documentation must have summary -->
        <Rule Id="SA1606" Action="Error"/> <!-- Element documentation must have summary text -->
        <Rule Id="SA1607" Action="Error"/> <!-- Partial element documentation must have summary text -->
        <Rule Id="SA1608" Action="Error"/> <!-- Element documentation must not have default summary -->
        <Rule Id="SA1609" Action="None"/> <!-- Property documentation must have value -->
        <Rule Id="SA1610" Action="None"/> <!-- Property documentation must have value text -->
        <Rule Id="SA1611" Action="Error"/> <!-- Element parameters must be documented -->
        <Rule Id="SA1612" Action="Error"/> <!-- Element parameter documentation must match element parameters -->
        <Rule Id="SA1613" Action="Error"/> <!-- Element parameter documentation must declare parameter name -->
        <Rule Id="SA1614" Action="Error"/> <!-- Element parameter documentation must have text -->
        <Rule Id="SA1615" Action="Error"/> <!-- Element return value must be documented -->
        <Rule Id="SA1616" Action="Error"/> <!-- Element return value documentation must have text -->
        <Rule Id="SA1617" Action="Error"/> <!-- Void return value must not be documented -->
        <Rule Id="SA1618" Action="Error"/> <!-- Generic type parameters must be documented -->
        <Rule Id="SA1619" Action="Error"/> <!-- Generic type parameters must be documented partial class -->
        <Rule Id="SA1620" Action="Error"/> <!-- Generic type parameter documentation must match type parameters -->
        <Rule Id="SA1621" Action="Error"/> <!-- Generic type parameter documentation must declare parameter name -->
        <Rule Id="SA1622" Action="Error"/> <!-- Generic type parameter documentation must have text -->
        <Rule Id="SA1623" Action="None"/> <!-- Property summary documentation must match accessors -->
        <Rule Id="SA1624"
              Action="None"/> <!-- Property summary documentation must omit accessor with restricted access -->
        <Rule Id="SA1625" Action="Error"/> <!-- Element documentation must not be copied and pasted -->
        <Rule Id="SA1626" Action="Error"/> <!-- Single-line comments must not use documentation style slashes -->
        <Rule Id="SA1627" Action="Error"/> <!-- Documentation text must not be empty -->
        <Rule Id="SA1629" Action="Error"/> <!-- Documentation text must end with a period -->
        <Rule Id="SA1633" Action="Error"/> <!-- File must have header -->
        <Rule Id="SA1634" Action="Error"/> <!-- File header must show copyright -->
        <Rule Id="SA1635" Action="Error"/> <!-- File header must have copyright text -->
        <Rule Id="SA1636" Action="Error"/> <!-- File header copyright text must match -->
        <Rule Id="SA1637" Action="Error"/> <!-- File header must contain file name -->
        <Rule Id="SA1638" Action="Error"/> <!-- File header file name documentation must match file name -->
        <Rule Id="SA1639" Action="Error"/> <!-- File header must have summary -->
        <Rule Id="SA1640" Action="Error"/> <!-- File header must have valid company text -->
        <Rule Id="SA1641" Action="Error"/> <!-- File header company name text must match -->
        <Rule Id="SA1642" Action="Error"/> <!-- Constructor summary documentation must begin with standard text -->
        <Rule Id="SA1643" Action="Error"/> <!-- Destructor summary documentation must begin with standard text -->
        <Rule Id="SA1644" Action="Error"/> <!-- Documentation header must not contain blank lines -->
        <Rule Id="SA1648" Action="Error"/> <!-- inheritdoc must be used with inheriting class -->
        <Rule Id="SA1649" Action="Error"/> <!-- File name must match first type name -->
        <Rule Id="SA1651" Action="Error"/> <!-- Do not use placeholder elements -->
    </Rules>
</RuleSet>

Do not follow this link