Use minimal Project Zomboid RCON protocol client
This commit is contained in:
@@ -11,7 +11,6 @@
|
|||||||
<StripSymbols>true</StripSymbols>
|
<StripSymbols>true</StripSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CoreRCON" Version="5.4.2" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
||||||
<PackageReference Include="NetCord.Hosting" Version="1.0.0-beta.18" />
|
<PackageReference Include="NetCord.Hosting" Version="1.0.0-beta.18" />
|
||||||
<PackageReference Include="NetCord.Hosting.Services" Version="1.0.0-beta.18" />
|
<PackageReference Include="NetCord.Hosting.Services" Version="1.0.0-beta.18" />
|
||||||
|
|||||||
+35
-5
@@ -1,6 +1,8 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using CoreRCON;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using NetCord;
|
using NetCord;
|
||||||
@@ -53,10 +55,38 @@ string Whitelist(ApplicationCommandContext c, string discordId, bool add)
|
|||||||
async Task<string> Command(string command, bool allowDisconnect = false)
|
async Task<string> Command(string command, bool allowDisconnect = false)
|
||||||
{
|
{
|
||||||
var address = (await Dns.GetHostAddressesAsync(Env("RCON_HOST", "zomboid"))).First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
var address = (await Dns.GetHostAddressesAsync(Env("RCON_HOST", "zomboid"))).First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
||||||
using var rcon = new RCON(address, ushort.Parse(Env("RCON_PORT", "27015")), rconPassword, strictCommandPacketIdMatching: false);
|
using var timeout = new CancellationTokenSource(10_000);
|
||||||
await rcon.ConnectAsync();
|
using var tcp = new TcpClient();
|
||||||
try { return await rcon.SendCommandAsync(command); }
|
await tcp.ConnectAsync(address, int.Parse(Env("RCON_PORT", "27015")), timeout.Token);
|
||||||
catch when (allowDisconnect && !rcon.Connected) { return ""; }
|
await using var stream = tcp.GetStream();
|
||||||
|
async Task Write(int id, int type, string body)
|
||||||
|
{
|
||||||
|
byte[] text = Encoding.UTF8.GetBytes(body), packet = new byte[text.Length + 14];
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(packet, packet.Length - 4);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(packet.AsSpan(4), id);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(packet.AsSpan(8), type);
|
||||||
|
text.CopyTo(packet, 12);
|
||||||
|
await stream.WriteAsync(packet, timeout.Token);
|
||||||
|
}
|
||||||
|
async Task<(int Id, int Type, string Body)> Read()
|
||||||
|
{
|
||||||
|
byte[] header = new byte[4];
|
||||||
|
await stream.ReadExactlyAsync(header, timeout.Token);
|
||||||
|
int length = BinaryPrimitives.ReadInt32LittleEndian(header);
|
||||||
|
if (length is < 10 or > 1_048_576) throw new InvalidDataException("Invalid RCON packet");
|
||||||
|
byte[] packet = new byte[length];
|
||||||
|
await stream.ReadExactlyAsync(packet, timeout.Token);
|
||||||
|
return (BinaryPrimitives.ReadInt32LittleEndian(packet), BinaryPrimitives.ReadInt32LittleEndian(packet.AsSpan(4)),
|
||||||
|
Encoding.UTF8.GetString(packet, 8, length - 10));
|
||||||
|
}
|
||||||
|
await Write(1, 3, rconPassword);
|
||||||
|
(int Id, int Type, string Body) response;
|
||||||
|
do response = await Read(); while (response.Type != 2);
|
||||||
|
if (response.Id != 1) throw new UnauthorizedAccessException("RCON authentication failed");
|
||||||
|
await Write(2, 2, command);
|
||||||
|
try { do response = await Read(); while (response.Id != 2); return response.Body; }
|
||||||
|
catch (EndOfStreamException) when (allowDisconnect) { return ""; }
|
||||||
|
catch (IOException) when (allowDisconnect) { return ""; }
|
||||||
}
|
}
|
||||||
async Task<(int Count, string[] Names)> Players()
|
async Task<(int Count, string[] Names)> Players()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user