Rcon was not working
This commit is contained in:
parent
167c27848a
commit
2a5774bd6d
3 changed files with 153 additions and 76 deletions
|
@ -1,95 +1,124 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
class RCONPacket {
|
||||
int id;
|
||||
int type;
|
||||
String body;
|
||||
import 'RconDecoder.dart';
|
||||
|
||||
RCONPacket(this.id, this.type, this.body);
|
||||
abstract class RconClient {
|
||||
void exit();
|
||||
|
||||
List<int> toBytes() {
|
||||
List<int> bodyBytes = utf8.encode(body);
|
||||
int size = 10 + bodyBytes.length;
|
||||
List<int> packet = [];
|
||||
packet.addAll(_intToBytes(size));
|
||||
packet.addAll(_intToBytes(id));
|
||||
packet.addAll(_intToBytes(type));
|
||||
packet.addAll(bodyBytes);
|
||||
packet.addAll([0, 0]);
|
||||
return packet;
|
||||
}
|
||||
void send(String cmd);
|
||||
|
||||
static RCONPacket fromBytes(List<int> bytes) {
|
||||
int size = _bytesToInt(bytes.sublist(0, 4));
|
||||
int id = _bytesToInt(bytes.sublist(4, 8));
|
||||
int type = _bytesToInt(bytes.sublist(8, 12));
|
||||
String body =
|
||||
utf8.decode(bytes.sublist(12, size + 2).sublist(0, size - 10));
|
||||
return RCONPacket(id, type, body);
|
||||
}
|
||||
factory RconClient() => RconClientImpl();
|
||||
|
||||
static List<int> _intToBytes(int value) {
|
||||
return [
|
||||
value & 0xFF,
|
||||
(value >> 8) & 0xFF,
|
||||
(value >> 16) & 0xFF,
|
||||
(value >> 24) & 0xFF
|
||||
];
|
||||
}
|
||||
|
||||
static int _bytesToInt(List<int> bytes) {
|
||||
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
|
||||
}
|
||||
Stream<String> connect(String host, int port, String password);
|
||||
}
|
||||
|
||||
class RCONClient {
|
||||
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;
|
||||
late int _requestId;
|
||||
|
||||
RCONClient(this.host, this.port, this.password) {
|
||||
_requestId = 0;
|
||||
/// 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);
|
||||
}
|
||||
|
||||
Future<void> connect() async {
|
||||
/// 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);
|
||||
if (await _authenticate()) {
|
||||
print('Authenticated successfully');
|
||||
} else {
|
||||
print('Authentication failed');
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _authenticate() async {
|
||||
RCONPacket packet = RCONPacket(_requestId++, 3, password);
|
||||
_socket.add(packet.toBytes());
|
||||
await _socket.flush();
|
||||
RCONPacket response = await _readPacket();
|
||||
return response.id == packet.id && response.type == 2;
|
||||
}
|
||||
print("Connected.");
|
||||
|
||||
Future<RCONPacket> _readPacket() async {
|
||||
List<int> sizeBytes = await _socket.first;
|
||||
int size = RCONPacket._bytesToInt(sizeBytes);
|
||||
List<int> dataBytes = [];
|
||||
while (dataBytes.length < size) {
|
||||
dataBytes.addAll(await _socket.first);
|
||||
}
|
||||
return RCONPacket.fromBytes(sizeBytes + dataBytes);
|
||||
}
|
||||
final Stream<String> stream = _socket.transform(RconDecoder());
|
||||
|
||||
Future<String> sendCommand(String command) async {
|
||||
RCONPacket packet = RCONPacket(_requestId++, 2, command);
|
||||
_socket.add(packet.toBytes());
|
||||
await _socket.flush();
|
||||
RCONPacket response = await _readPacket();
|
||||
return response.body;
|
||||
}
|
||||
print("Trying to authenticate");
|
||||
|
||||
void close() {
|
||||
_socket.close();
|
||||
_authenticate(password);
|
||||
|
||||
yield* stream;
|
||||
}
|
||||
}
|
||||
|
|
48
lib/utils/rcon/RconDecoder.dart
Normal file
48
lib/utils/rcon/RconDecoder.dart
Normal file
|
@ -0,0 +1,48 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'Rcon.dart';
|
||||
|
||||
class RconDecoder extends Converter<Uint8List, String> {
|
||||
/// Transform packets into a String.
|
||||
///
|
||||
/// | 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 |
|
||||
@override
|
||||
String convert(Uint8List data) {
|
||||
final byteData = ByteData.sublistView(data);
|
||||
// final inLength = byteData.getInt32(0, Endian.little);
|
||||
final inId = byteData.getInt32(4, Endian.little);
|
||||
final inType = byteData.getInt32(8, Endian.little);
|
||||
final inBody = data.sublist(12);
|
||||
|
||||
if (inType == RconType.SERVERDATA_AUTH_RESPONSE) {
|
||||
if (inId == -1) {
|
||||
throw const SocketException('Bad login.');
|
||||
} else if (inId == 0) {
|
||||
return 'Authentication successful. You can now write commands.';
|
||||
}
|
||||
}
|
||||
|
||||
return utf8.decode(inBody);
|
||||
}
|
||||
|
||||
@override
|
||||
Sink<Uint8List> startChunkedConversion(Sink<String> sink) {
|
||||
return ChunkedConversionSink.withCallback((chunk) {
|
||||
for (final data in chunk) {
|
||||
sink.add(convert(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Override the base class's bind, to provide a better type.
|
||||
@override
|
||||
Stream<String> bind(Stream<Uint8List> stream) => super.bind(stream);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
name: libac_dart
|
||||
description: "Aria's Creations code library"
|
||||
version: 1.0.28
|
||||
version: 1.0.29
|
||||
homepage: "https://zontreck.com"
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue