Skip to content

Commit

Permalink
Version 8 [NugetDeploy]
Browse files Browse the repository at this point in the history
- Switch to ValueTasks.
- Switch to v8 MS dependencies.
- Update to Stylecop beta.
- Fix double data read.
- Switch to records and structs where appropriate.
  • Loading branch information
Cyberboss committed Dec 16, 2023
1 parent 8f0ed3f commit 59433bd
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 35 deletions.
10 changes: 5 additions & 5 deletions Byond.TopicSender/Byond.TopicSender.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>7.1.0</Version>
<Version>8.0.0</Version>
<Authors>Jordan Dominion</Authors>
<Description>Library for asynchronously sending valid topic strings to /world/Topic() in BYOND servers.</Description>
<PackageProjectUrl>https://Cyberboss.github.io/Byond.TopicSender</PackageProjectUrl>
<RepositoryUrl>https://github.com/Cyberboss/Byond.TopicSender</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<Copyright>Copyright (c) Jordan Brown 2018</Copyright>
<PackageReleaseNotes>Multi-targeted across .NET 6-8. Fixed issue with partial reads.</PackageReleaseNotes>
<PackageReleaseNotes>Fixed potential double data reads occurring. Switched to using ValueTask return values. Switched to using records ans d structs where appropriate. Updated Microsoft packages to version 8.</PackageReleaseNotes>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageTags>byond topic packet sender</PackageTags>
<IncludeSymbols>true</IncludeSymbols>
Expand All @@ -36,11 +36,11 @@

<ItemGroup>
<!-- Usage: Logging -->
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<!-- Usage: Sourcelink -->
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<!-- Usage: Linting -->
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
12 changes: 6 additions & 6 deletions Byond.TopicSender/ITopicClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public interface ITopicClient
/// <param name="queryString">The query string to send.</param>
/// <param name="port">The port of the <paramref name="destinationServer"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
Task<TopicResponse> SendTopic(string destinationServer, string queryString, ushort port, CancellationToken cancellationToken = default);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
ValueTask<TopicResponse> SendTopic(string destinationServer, string queryString, ushort port, CancellationToken cancellationToken = default);

/// <summary>
/// Send a <paramref name="queryString"/> to the /world/Topic of a server at a given <paramref name="address"/>.
Expand All @@ -26,17 +26,17 @@ public interface ITopicClient
/// <param name="queryString">The query string to send.</param>
/// <param name="port">The port of the <paramref name="address"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
Task<TopicResponse> SendTopic(IPAddress address, string queryString, ushort port, CancellationToken cancellationToken = default);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
ValueTask<TopicResponse> SendTopic(IPAddress address, string queryString, ushort port, CancellationToken cancellationToken = default);

/// <summary>
/// Send a <paramref name="queryString"/> to the /world/Topic of a server at a given <paramref name="endPoint"/>.
/// </summary>
/// <param name="endPoint">The <see cref="IPEndPoint"/> of the server.</param>
/// <param name="queryString">The query string to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
Task<TopicResponse> SendTopic(IPEndPoint endPoint, string queryString, CancellationToken cancellationToken = default);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the returned <see cref="TopicResponse"/> from the server.</returns>
ValueTask<TopicResponse> SendTopic(IPEndPoint endPoint, string queryString, CancellationToken cancellationToken = default);

/// <summary>
/// Properly escapes characters for a Byond Topic() packet. Performs URL encoding. See http://www.byond.com/docs/ref/info.html#/proc/list2params.
Expand Down
2 changes: 1 addition & 1 deletion Byond.TopicSender/SocketParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Byond.TopicSender
/// <summary>
/// <see cref="System.Net.Sockets.Socket"/> parameters used by the <see cref="TopicClient"/>.
/// </summary>
public sealed class SocketParameters
public readonly record struct SocketParameters
{
/// <summary>
/// The timeout for the send operation.
Expand Down
42 changes: 22 additions & 20 deletions Byond.TopicSender/TopicClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,43 +33,41 @@ public sealed class TopicClient : ITopicClient
/// </summary>
/// <param name="socketParameters">The <see cref="SocketParameters"/> to use.</param>
/// <param name="logger">The optional <see cref="ILogger"/> to use.</param>
public TopicClient(SocketParameters socketParameters, ILogger? logger = null)
public TopicClient(SocketParameters socketParameters, ILogger? logger)
{
this.socketParameters = socketParameters ?? throw new ArgumentNullException(nameof(socketParameters));
this.socketParameters = socketParameters;
this.logger = logger ?? new NullLogger<TopicClient>();
}

/// <inheritdoc />
public async Task<TopicResponse> SendTopic(string destinationServer, string queryString, ushort port, CancellationToken cancellationToken = default)
public async ValueTask<TopicResponse> SendTopic(string destinationServer, string queryString, ushort port, CancellationToken cancellationToken = default)
{
if (destinationServer == null)
throw new ArgumentNullException(nameof(destinationServer));
ArgumentNullException.ThrowIfNull(destinationServer);
var hostEntries = await Dns.GetHostAddressesAsync(destinationServer, cancellationToken).ConfigureAwait(false);

// pick the first IPV4 entry
return await SendTopic(hostEntries.First(x => x.AddressFamily == AddressFamily.InterNetwork), queryString, port, cancellationToken).ConfigureAwait(false);
}

/// <inheritdoc />
public Task<TopicResponse> SendTopic(IPAddress address, string queryString, ushort port, CancellationToken cancellationToken = default)
public ValueTask<TopicResponse> SendTopic(IPAddress address, string queryString, ushort port, CancellationToken cancellationToken = default)
{
if (address == null)
throw new ArgumentNullException(nameof(address));
ArgumentNullException.ThrowIfNull(address);

return SendTopic(new IPEndPoint(address, port), queryString, cancellationToken);
}

/// <inheritdoc />
public async Task<TopicResponse> SendTopic(IPEndPoint endPoint, string queryString, CancellationToken cancellationToken = default)
public async ValueTask<TopicResponse> SendTopic(IPEndPoint endPoint, string queryString, CancellationToken cancellationToken = default)
{
if (endPoint == null)
throw new ArgumentNullException(nameof(endPoint));
if (queryString == null)
throw new ArgumentNullException(nameof(queryString));
ArgumentNullException.ThrowIfNull(endPoint);
ArgumentNullException.ThrowIfNull(queryString);

using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
{
// prepare
var needsQueryToken = queryString.Length == 0 || queryString[0] != '?';
const char QueryToken = '?';
var needsQueryToken = queryString.Length == 0 || queryString[0] != QueryToken;

var queryStringByteLength = Encoding.UTF8.GetByteCount(queryString);

Expand Down Expand Up @@ -119,7 +117,7 @@ struct byond_msg_topic {
writer.Write((byte)0);

if (needsQueryToken)
writer.Write('?');
writer.Write(QueryToken);

writer.Seek(queryStringByteLength, SeekOrigin.Current);
writer.Write((byte)0);
Expand Down Expand Up @@ -197,12 +195,16 @@ struct byond_msg_topic {
throw;
}

receiveOffset += read;
if (!peeking && read == 0)
if (!peeking)
{
if (receiveOffset < returnedData.Length)
logger.LogDebug("Zero bytes read at offset {receiveOffset} before expected length of {expectedLength}.", receiveOffset, returnedData.Length);
break;
if (read == 0)
{
if (receiveOffset < returnedData.Length)
logger.LogDebug("Zero bytes read at offset {receiveOffset} before expected length of {expectedLength}.", receiveOffset, returnedData.Length);
break;
}

receiveOffset += read;
}

if (header == null && receiveOffset >= TopicResponseHeader.HeaderLength)
Expand Down
2 changes: 1 addition & 1 deletion Byond.TopicSender/TopicResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Byond.TopicSender
/// <summary>
/// Represents a topic response from BYOND.
/// </summary>
public sealed class TopicResponse : TopicResponseHeader
public sealed record TopicResponse : TopicResponseHeader
{
/// <summary>
/// The <see cref="TopicResponseType"/> of the header.
Expand Down
2 changes: 1 addition & 1 deletion Byond.TopicSender/TopicResponseHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Byond.TopicSender
/// <summary>
/// Represents the header data of a <see cref="TopicResponse"/>.
/// </summary>
public class TopicResponseHeader
public record TopicResponseHeader
{
/// <summary>
/// The <see cref="Array.Length"/> of a topic response header.
Expand Down
6 changes: 5 additions & 1 deletion Byond.TopicSender/analyzers.ruleset
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="myrules" Description="My rule set" ToolsVersion="16.0">
<RuleSet Name="myrules" Description="My rule set" ToolsVersion="17.0">
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
<Rule Id="UseConfigureAwait" Action="Warning" />
</Rules>
Expand Down Expand Up @@ -667,6 +667,9 @@
<Rule Id="CS8020" Action="Warning" />
<Rule Id="CS8040" Action="Warning" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.CodeStyle" RuleNamespace="Microsoft.CodeAnalysis.CSharp.CodeStyle">
<Rule Id="IDE0290" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.EditorFeatures" RuleNamespace="Microsoft.CodeAnalysis.CSharp.EditorFeatures">
<Rule Id="IDE0032" Action="Warning" />
<Rule Id="IDE0032WithoutSuggestion" Action="Warning" />
Expand Down Expand Up @@ -751,6 +754,7 @@
<Rule Id="IDE0048WithoutSuggestion" Action="Warning" />
<Rule Id="IDE0049" Action="None" />
<Rule Id="IDE0049WithoutSuggestion" Action="None" />
<Rule Id="IDE0290" Action="None" />
<Rule Id="IDE1005" Action="Warning" />
<Rule Id="IDE1005WithoutSuggestion" Action="Warning" />
<Rule Id="IDE1006" Action="Warning" />
Expand Down

0 comments on commit 59433bd

Please sign in to comment.