Use minimal Project Zomboid RCON protocol client
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
<StripSymbols>true</StripSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CoreRCON" Version="5.4.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
||||
<PackageReference Include="NetCord.Hosting" 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.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CoreRCON;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using NetCord;
|
||||
@@ -53,10 +55,38 @@ string Whitelist(ApplicationCommandContext c, string discordId, bool add)
|
||||
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);
|
||||
using var rcon = new RCON(address, ushort.Parse(Env("RCON_PORT", "27015")), rconPassword, strictCommandPacketIdMatching: false);
|
||||
await rcon.ConnectAsync();
|
||||
try { return await rcon.SendCommandAsync(command); }
|
||||
catch when (allowDisconnect && !rcon.Connected) { return ""; }
|
||||
using var timeout = new CancellationTokenSource(10_000);
|
||||
using var tcp = new TcpClient();
|
||||
await tcp.ConnectAsync(address, int.Parse(Env("RCON_PORT", "27015")), timeout.Token);
|
||||
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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user