Skip to content

Commit

Permalink
Communication Protocol Refactoring (kwsch#104)
Browse files Browse the repository at this point in the history
* Refactoring

- Implement functionality for Synchronous TCP connection (same as existing async)
- Implement flexible config->connection flow, allows adding different connection types (like USB)

* Prettify json
can easily edit it now in notepad

* Add GUI to create USB bot
* Include default values in settings json

* Add some xmldoc
  • Loading branch information
kwsch authored Jan 17, 2021
1 parent 67fb80f commit a989c1b
Showing 57 changed files with 1,261 additions and 340 deletions.
23 changes: 23 additions & 0 deletions SysBot.Base/Connection/Console/BotConfigUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Net;

namespace SysBot.Base
{
/// <summary>
/// Stored config of a bot
/// </summary>
public static class BotConfigUtil
{
/// <summary>
/// Parses the input details into an <see cref="IWirelessConnectionConfig"/> object.
/// </summary>
/// <typeparam name="T">Type of config object that implements <see cref="IWirelessConnectionConfig"/></typeparam>
/// <param name="ip">IP address string for the connection</param>
/// <param name="port">Port of the connection</param>
/// <returns></returns>
public static T GetConfig<T>(string ip, int port) where T : IWirelessConnectionConfig, new() => new()
{
IP = IPAddress.Parse(ip).ToString(), // sanitize leading zeroes out for paranoia's sake
Port = port,
};
}
}
13 changes: 13 additions & 0 deletions SysBot.Base/Connection/Console/IAsyncConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace SysBot.Base
{
/// <summary>
/// Asynchronous notifications when calling <see cref="IConsoleConnection.Connect"/> and <see cref="IConsoleConnection.Disconnect"/>.
/// </summary>
public interface IAsyncConnection
{
void ConnectCallback(IAsyncResult ar);
void DisconnectCallback(IAsyncResult ar);
}
}
44 changes: 44 additions & 0 deletions SysBot.Base/Connection/Console/IConsoleBotConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace SysBot.Base
{
/// <summary>
/// Bare minimum details for a saved configuration object.
/// </summary>
public interface IConsoleBotConfig
{
/// <summary>
/// Checks if the <see cref="IConsoleBotConfig"/> is valid or not.
/// </summary>
bool IsValid();

/// <summary>
/// Checks if the config matches the input <see cref="magic"/>.
/// </summary>
/// <param name="magic">Magic value to be used by the comparison to see if it matches.</param>
bool Matches(string magic);
}

/// <summary>
/// Exposes methods to obtain the communication object that interacts with the console.
/// </summary>
/// <typeparam name="TSync"></typeparam>
/// <typeparam name="TAsync"></typeparam>
public interface IConsoleBotConnector<out TSync, out TAsync>
{
/// <summary>
/// Obtains the synchronous communication implementation for this console.
/// </summary>
TSync CreateSync();

/// <summary>
/// Obtains the asynchronous communication implementation for this console.
/// </summary>
TAsync CreateAsynchronous();
}

/// <summary>
/// Combined interface for less verbosity of implementers
/// </summary>
public interface IConsoleBotManaged<out TSync, out TAsync> : IConsoleBotConfig, IConsoleBotConnector<TSync, TAsync>
{
}
}
57 changes: 57 additions & 0 deletions SysBot.Base/Connection/Console/IConsoleConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace SysBot.Base
{
/// <summary>
/// Concepts and properties to describe a connection with a console, without explicitly interacting with it.
/// </summary>
public interface IConsoleConnection
{
/// <summary>
/// Internal differentiation for the Bot
/// </summary>
string Name { get; }

/// <summary>
/// Customized Label for the bot (potentially based on in-game values).
/// </summary>
string Label { get; set; }

/// <summary>
/// Connects to the device.
/// </summary>
void Connect();

/// <summary>
/// Resets the connection to the device, usually by calling <see cref="Disconnect"/> then <see cref="Connect"/> in a clean manner.
/// </summary>
void Reset();

/// <summary>
/// Disconnects from the device.
/// </summary>
void Disconnect();

/// <summary>
/// Indicates if the device is currently connected.
/// </summary>
/// <remarks>Sometimes might not check the connection status, rather if it has been called to <see cref="Connect"/> without <see cref="Disconnect"/> being called yet.</remarks>
bool Connected { get; }

/// <summary>
/// Logs a message for the connection.
/// </summary>
/// <param name="message">Anything you want the bot to log.</param>
abstract void Log(string message);

/// <summary>
/// Logs an information message for the connection.
/// </summary>
/// <param name="message"></param>
abstract void LogInfo(string message);

/// <summary>
/// Logs an error message for the connection.
/// </summary>
/// <param name="message"></param>
abstract void LogError(string message);
}
}
16 changes: 16 additions & 0 deletions SysBot.Base/Connection/Console/IConsoleConnectionAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Threading;
using System.Threading.Tasks;

namespace SysBot.Base
{
/// <summary>
/// Bare minimum methods required to interact with a <see cref="IConsoleConnection"/> in an asynchronous manner.
/// </summary>
public interface IConsoleConnectionAsync : IConsoleConnection
{
Task<int> SendAsync(byte[] buffer, CancellationToken token);

Task<byte[]> ReadBytesAsync(uint offset, int length, CancellationToken token);
Task WriteBytesAsync(byte[] data, uint offset, CancellationToken token);
}
}
13 changes: 13 additions & 0 deletions SysBot.Base/Connection/Console/IConsoleConnectionSync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace SysBot.Base
{
/// <summary>
/// Bare minimum methods required to interact with a <see cref="IConsoleConnection"/> in a synchronous manner.
/// </summary>
public interface IConsoleConnectionSync : IConsoleConnection
{
int Send(byte[] buffer);

byte[] ReadBytes(uint offset, int length);
void WriteBytes(byte[] data, uint offset);
}
}
14 changes: 14 additions & 0 deletions SysBot.Base/Connection/Console/IWirelessConnectionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace SysBot.Base
{
/// <summary>
/// Contains details for communicating with another wireless device.
/// </summary>
public interface IWirelessConnectionConfig
{
/// <summary> IP Address (X.X.X.X) </summary>
string IP { get; set; }

/// <summary> Port </summary>
int Port { get; set; }
}
}
20 changes: 20 additions & 0 deletions SysBot.Base/Connection/Switch/ISwitchConnectionAsync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Threading;
using System.Threading.Tasks;

namespace SysBot.Base
{
/// <summary>
/// Exposes the available interactions for asynchronous communications with a Nintendo Switch.
/// </summary>
public interface ISwitchConnectionAsync : IConsoleConnectionAsync
{
Task<ulong> GetMainNsoBaseAsync(CancellationToken token);
Task<ulong> GetHeapBaseAsync(CancellationToken token);

Task<byte[]> ReadBytesMainAsync(ulong offset, int length, CancellationToken token);
Task<byte[]> ReadBytesAbsoluteAsync(ulong offset, int length, CancellationToken token);

Task WriteBytesMainAsync(byte[] data, ulong offset, CancellationToken token);
Task WriteBytesAbsoluteAsync(byte[] data, ulong offset, CancellationToken token);
}
}
18 changes: 18 additions & 0 deletions SysBot.Base/Connection/Switch/ISwitchConnectionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace SysBot.Base
{
/// <summary>
/// Defines how the Nintendo Switch is to be communicated with.
/// </summary>
public interface ISwitchConnectionConfig : IConsoleBotManaged<ISwitchConnectionSync, ISwitchConnectionAsync>
{
/// <summary>
/// Communication Protocol in use
/// </summary>
SwitchProtocol Protocol { get; }

/// <summary>
/// Determines if the communication uses 0D 0A (CRLF) to end a command.
/// </summary>
bool UseCRLF { get; }
}
}
17 changes: 17 additions & 0 deletions SysBot.Base/Connection/Switch/ISwitchConnectionSync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace SysBot.Base
{
/// <summary>
/// Exposes the available interactions for synchronous communications with a Nintendo Switch.
/// </summary>
public interface ISwitchConnectionSync : IConsoleConnectionSync
{
ulong GetMainNsoBase();
ulong GetHeapBase();

byte[] ReadBytesMain(ulong offset, int length);
byte[] ReadBytesAbsolute(ulong offset, int length);

void WriteBytesMain(byte[] data, ulong offset);
void WriteBytesAbsolute(byte[] data, ulong offset);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace SysBot.Base
{
/// <summary>
/// Valid configuration request types for the Nintendo Switch to be sent as a <see cref="SwitchCommand"/>.
/// </summary>
public enum SwitchConfigureParameter
{
/// <summary>
@@ -17,26 +20,4 @@ public enum SwitchConfigureParameter
/// </summary>
echoCommands,
}

// Overworld detection using new method depends on Switch console language setting
public enum ConsoleLanguageParameter
{
English,
French,
German,
Spanish,
Italian,
Dutch,
Portuguese,
Russian,
Japanese,
ChineseTraditional,
ChineseSimplified,
Korean,
}
public enum ScreenDetectionMode
{
Original,
ConsoleLanguageSpecific,
}
}
}
68 changes: 68 additions & 0 deletions SysBot.Base/Connection/Switch/SwitchConnectionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Net;
using static SysBot.Base.SwitchProtocol;

namespace SysBot.Base
{
/// <summary>
/// Represents the connection details (but not the communication implementation) for the connection.
/// </summary>
public record SwitchConnectionConfig : ISwitchConnectionConfig, IWirelessConnectionConfig
{
/// <inheritdoc/>
public SwitchProtocol Protocol { get; set; }

/// <inheritdoc/>
public string IP { get; set; } = string.Empty;

/// <inheritdoc/>
public int Port { get; set; } = 6000;

/// <inheritdoc/>
public bool UseCRLF => Protocol is WiFi;

/// <inheritdoc/>
public bool IsValid() => Protocol switch
{
WiFi => IPAddress.TryParse(IP, out _),
USB => Port < ushort.MaxValue,
_ => false,
};

/// <inheritdoc/>
public bool Matches(string magic) => Protocol switch
{
WiFi => IPAddress.TryParse(magic, out var val) && val.ToString() == IP,
USB => magic == Port.ToString(),
_ => false,
};

public override string ToString() => Protocol switch
{
WiFi => IP,
USB => Port.ToString(),
_ => throw new ArgumentOutOfRangeException(nameof(SwitchProtocol)),
};

/// <inheritdoc/>
public ISwitchConnectionAsync CreateAsynchronous() => Protocol switch
{
WiFi => new SwitchSocketAsync(this),
USB => new SwitchUSBAsync(Port),
_ => throw new ArgumentOutOfRangeException(nameof(SwitchProtocol)),
};

public ISwitchConnectionSync CreateSync() => Protocol switch
{
WiFi => new SwitchSocketSync(this),
USB => new SwitchUSBSync(Port),
_ => throw new ArgumentOutOfRangeException(nameof(SwitchProtocol)),
};
}

public enum SwitchProtocol
{
WiFi,
USB,
}
}
Loading

0 comments on commit a989c1b

Please sign in to comment.