LibAC-dart/lib/utils/rcon/Rcon.dart
2024-06-04 15:34:07 -07:00

124 lines
3.4 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'RconDecoder.dart';
abstract class RconClient {
void exit();
void send(String cmd);
factory RconClient() => RconClientImpl();
Stream<String> connect(String host, int port, String password);
}
class RconType {
/// RCON Packet Type 0: Server response
static const int SERVERDATA_RESPONSE_VALUE = 0;
/// RCON Packet Type 2: Server auth response
static const int SERVERDATA_AUTH_RESPONSE = 2;
/// RCON Packet Type 2: Client execute command
static const int SERVERDATA_EXECCOMMAND = 2;
/// RCON Packet Type 3: Client send authentication data
static const int SERVERDATA_AUTH = 3;
}
/// Client for the rcon protocol
class RconClientImpl implements RconClient {
/// Socket used to write and read TCP packets.
late Socket _socket;
/// Disconnect from the a RCON server.
///
/// Usage:
///
/// ```dart
/// RconClient client = RconClient.connect(print, 'localhost', '25575', 'password');
/// client.exit();
/// ```
@override
void exit() => _socket.destroy();
/// Send commands
@override
void send(String cmd) => _send(RconType.SERVERDATA_EXECCOMMAND, cmd);
/// Authenticate to the server
void _authenticate(String password) =>
_send(RconType.SERVERDATA_AUTH, password);
/// Build and send a packet to the rcon server
///
/// | Field | Type | Value |
/// |--------------|-------------------------------------|-------|
/// | Size | 32-bit little-endian Signed Integer | |
/// | ID | 32-bit little-endian Signed Integer | |
/// | Type | 32-bit little-endian Signed Integer | |
/// | Body | Null-terminated ASCII String | |
/// | Empty String | Null-terminated ASCII String | 0x00 |
void _send(int type, String payload) {
final Uint8List outSize = Uint8List(4);
final Uint8List outId = Uint8List(4); // Empty: 00 00 00 00
final Uint8List outType = Uint8List(4);
// Write type in the Type field
final ByteData typeData = ByteData.view(outType.buffer);
typeData.setInt32(0, type, Endian.little);
// Build the body
final List<int> outBody = utf8.encode(payload);
// Build ID + Type + Body + Empty String
final Uint8List outPacketBody =
Uint8List.fromList(outId + outType + outBody + Uint8List(2));
// Calculate the Size field
final ByteData sizeData = ByteData.view(outSize.buffer);
sizeData.setInt32(0, outPacketBody.length, Endian.little);
// Build the packet
final Uint8List packet = Uint8List.fromList(outSize + outPacketBody);
// View packet
// print(packet);
// print(packet.map((int i) => i.toRadixString(16)).toList());
// print(utf8.decode(packet));
// Send the packet
_socket.add(packet);
}
/// Connect to the a RCON server.
///
/// Usage:
///
/// ```dart
/// final RconClient client = RconClient();
/// final Stream<String> response = client.connect(
/// 'localhost',
/// 25575,
/// 'password',
/// );
/// ```
@override
Stream<String> connect(String host, int port, String password) async* {
print("Trying to connect...");
_socket = await Socket.connect(host, port);
print("Connected.");
final Stream<String> stream = _socket.transform(RconDecoder());
print("Trying to authenticate");
_authenticate(password);
yield* stream;
}
}