Skip to content

Commit

Permalink
Update Protocols and Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
BattlefieldDuck committed Jan 19, 2024
1 parent 3abdfbc commit 24ca65e
Show file tree
Hide file tree
Showing 72 changed files with 3,047 additions and 1,732 deletions.
49 changes: 0 additions & 49 deletions OpenGSQ/Exceptions.cs

This file was deleted.

18 changes: 18 additions & 0 deletions OpenGSQ/Exceptions/AuthenticationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace OpenGSQ.Exceptions
{
/// <summary>
/// Represents errors that occur during application execution.
/// </summary>
public class AuthenticationException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class with a specified error message.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
public AuthenticationException(string message) : base(message)
{
}
}
}
63 changes: 63 additions & 0 deletions OpenGSQ/Exceptions/InvalidPacketException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Linq;

namespace OpenGSQ.Exceptions
{
/// <summary>
/// Represents errors that occur during application execution when a packet is invalid.
/// </summary>
public class InvalidPacketException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="InvalidPacketException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public InvalidPacketException(string message) : base(message)
{

}

/// <summary>
/// Checks if the received value is equal to the expected value.
/// </summary>
/// <typeparam name="T">The type of the values to compare.</typeparam>
/// <param name="received">The received value.</param>
/// <param name="expected">The expected value.</param>
/// <exception cref="InvalidPacketException">
/// Thrown when the received value does not match the expected value.
/// </exception>
public static void ThrowIfNotEqual<T>(T received, T expected)
{
if (typeof(T) == typeof(byte[]))
{
if (!(received as byte[]).SequenceEqual(expected as byte[]))
{
throw new InvalidPacketException(GetMessage(received, expected));
}
}
else if (!received.Equals(expected))
{
throw new InvalidPacketException(GetMessage(received, expected));
}
}

private static string GetMessage<T>(T received, T expected)
{
string receivedStr;
string expectedStr;

if (typeof(T) == typeof(byte[]))
{
receivedStr = BitConverter.ToString(received as byte[]);
expectedStr = BitConverter.ToString(expected as byte[]);
}
else
{
receivedStr = received.ToString();
expectedStr = expected.ToString();
}

return $"Packet header mismatch. Received: {receivedStr}. Expected: {expectedStr}.";
}
}
}
19 changes: 19 additions & 0 deletions OpenGSQ/Exceptions/ServerNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace OpenGSQ.Exceptions
{
/// <summary>
/// Represents errors that occur during application execution when a server is not found.
/// </summary>
public class ServerNotFoundException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ServerNotFoundException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public ServerNotFoundException(string message) : base(message)
{

}
}
}
18 changes: 18 additions & 0 deletions OpenGSQ/Exceptions/TimeoutException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace OpenGSQ.Exceptions
{
/// <summary>
/// Represents errors that occur during application execution related to timeouts.
/// </summary>
public class TimeoutException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeoutException"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public TimeoutException(string message) : base(message)
{
}
}
}
4 changes: 2 additions & 2 deletions OpenGSQ/OpenGSQ.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

<PropertyGroup>
<AssemblyTitle>OpenGSQ</AssemblyTitle>
<VersionPrefix>2.0.1</VersionPrefix>
<Version>2.0.1</Version>
<VersionPrefix>2.1.0</VersionPrefix>
<Version>2.1.0</Version>
<Authors>OpenGSQ, BattlefieldDuck</Authors>
<TargetFrameworks>netstandard2.1;netstandard2.0</TargetFrameworks>
<AssemblyName>OpenGSQ</AssemblyName>
Expand Down
14 changes: 8 additions & 6 deletions OpenGSQ/ProtocolBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,20 @@ public ProtocolBase(string host, int port, int timeout = 5000)
}

/// <summary>
/// Asynchronously gets the Internet Protocol (IP) endpoint.
/// Retrieves the IP address of the host.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains the IP endpoint.</returns>
protected async Task<IPEndPoint> GetIPEndPoint()
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the IP address of the host.
/// </returns>
protected async Task<string> GetIPAddress()
{
if (IPAddress.TryParse(Host, out var ipAddress))
if (IPAddress.TryParse(Host, out var address))
{
return new IPEndPoint(ipAddress, Port);
return await Task.FromResult(address.ToString());
}
else
{
return new IPEndPoint((await Dns.GetHostAddressesAsync(Host))[0], Port);
return (await Dns.GetHostAddressesAsync(Host))[0].ToString();
}
}
}
Expand Down
117 changes: 68 additions & 49 deletions OpenGSQ/Protocols/ASE.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenGSQ.Exceptions;
using OpenGSQ.Responses.ASE;

namespace OpenGSQ.Protocols
{
Expand All @@ -15,8 +16,8 @@ public class ASE : ProtocolBase
/// <inheritdoc/>
public override string FullName => "All-Seeing Eye Protocol";

private readonly byte[] _request = Encoding.ASCII.GetBytes("s");
private readonly byte[] _response = Encoding.ASCII.GetBytes("EYE1");
private readonly byte[] requestHeader = Encoding.ASCII.GetBytes("s");
private readonly byte[] responseHeader = Encoding.ASCII.GetBytes("EYE1");

/// <summary>
/// Initializes a new instance of the <see cref="ASE"/> class.
Expand All @@ -29,38 +30,38 @@ public ASE(string host, int port, int timeout = 5000) : base(host, port, timeout
}

/// <summary>
/// Gets the status of the server.
/// Asynchronously gets the status of the server.
/// </summary>
/// <returns>A dictionary containing the server status.</returns>
public async Task<Dictionary<string, object>> GetStatus()
/// <returns>
/// A task that represents the asynchronous operation. The task result contains a Status object with the status information.
/// </returns>
/// <exception cref="InvalidPacketException">
/// Thrown when the received packet header does not match the expected header.
/// </exception>
/// <exception cref="TimeoutException">Thrown when the operation times out.</exception>
public async Task<Status> GetStatus()
{
byte[] response = await UdpClient.CommunicateAsync(this, _request);
byte[] response = await UdpClient.CommunicateAsync(this, requestHeader);

using (var br = new BinaryReader(new MemoryStream(response), Encoding.UTF8))
using (var br = new BinaryReader(new MemoryStream(response)))
{
byte[] header = br.ReadBytes(4);
InvalidPacketException.ThrowIfNotEqual(header, responseHeader);

if (!header.SequenceEqual(_response))
return new Status
{
throw new InvalidPacketException($"Packet header mismatch. Received: {BitConverter.ToString(header)}. Expected: {BitConverter.ToString(_response)}.");
}

var result = new Dictionary<string, object>
{
["gamename"] = ReadString(br),
["gameport"] = ReadString(br),
["hostname"] = ReadString(br),
["gametype"] = ReadString(br),
["map"] = ReadString(br),
["version"] = ReadString(br),
["password"] = ReadString(br),
["numplayers"] = ReadString(br),
["maxplayers"] = ReadString(br),
["rules"] = ParseRules(br),
["players"] = ParsePlayers(br)
GameName = ReadString(br),
GamePort = int.Parse(ReadString(br)),
HostName = ReadString(br),
GameType = ReadString(br),
Map = ReadString(br),
Version = ReadString(br),
Password = ReadString(br) != "0",
NumPlayers = int.Parse(ReadString(br)),
MaxPlayers = int.Parse(ReadString(br)),
Rules = ParseRules(br),
Players = ParsePlayers(br),
};

return result;
}
}

Expand All @@ -83,36 +84,54 @@ private Dictionary<string, string> ParseRules(BinaryReader br)
return rules;
}

private List<Dictionary<string, string>> ParsePlayers(BinaryReader br)
private List<Player> ParsePlayers(BinaryReader br)
{
var players = new List<Dictionary<string, string>>();
var keys = new Dictionary<int, string>
{
[1] = "name",
[2] = "team",
[4] = "skin",
[8] = "score",
[16] = "ping",
[32] = "time"
};
var players = new List<Player>();

while (!br.IsEnd())
{
byte flags = br.ReadByte();
var player = new Dictionary<string, string>();
players.Add(ParsePlayer(br));
}

foreach (var key in keys)
{
if ((flags & key.Key) == key.Key)
{
player[key.Value] = ReadString(br);
}
}
return players;
}

private Player ParsePlayer(BinaryReader br)
{
byte flags = br.ReadByte();
var player = new Player();

players.Add(player);
if ((flags & 1) == 1)
{
player.Name = ReadString(br);
}

return players;
if ((flags & 2) == 2)
{
player.Team = ReadString(br);
}

if ((flags & 4) == 4)
{
player.Skin = ReadString(br);
}

if ((flags & 8) == 8)
{
player.Score = int.TryParse(ReadString(br), out int result) ? result : 0;
}

if ((flags & 16) == 16)
{
player.Ping = int.TryParse(ReadString(br), out int result) ? result : 0;
}

if ((flags & 32) == 32)
{
player.Time = int.TryParse(ReadString(br), out int result) ? result : 0;
}

return player;
}

private string ReadString(BinaryReader br)
Expand Down
Loading

0 comments on commit 24ca65e

Please sign in to comment.