mc-server-manager/source/server_protocol.d

118 lines
3.9 KiB
D

module server_protocol;
import std.socket;
import std.algorithm;
import std.conv;
import std.stdio;
import streams;
const int SEGMENT_BITS = 0x7F;
const int CONTINUE_BIT = 0x80;
struct ServerStatus {
bool online;
int playersOnline;
int maxPlayers;
string[] playerNames;
}
ServerStatus fetchStatus(string ipAndPort) {
ptrdiff_t portIdx = countUntil(ipAndPort, ":");
if (portIdx == -1) throw new Exception("Invalid IP address. Port is required.");
string ip = ipAndPort[0..portIdx];
ushort port = ipAndPort[portIdx + 1 .. $].to!ushort;
Address address = new InternetAddress(ip, port);
writeln(address);
Socket socket = new TcpSocket(address);
auto sIn = SocketInputStream(socket);
auto sOut = SocketOutputStream(socket);
auto bufferedOut = bufferedOutputStreamFor(sOut);
auto arrayOut = byteArrayOutputStream();
writeVarInt(&arrayOut, 0x00);
writeVarInt(&arrayOut, 763);
writeString(&arrayOut, ip);
auto dOut = dataOutputStreamFor(&arrayOut, Endianness.BigEndian);
dOut.writeToStream(port);
writeVarInt(&arrayOut, 1);
ubyte[] handshakePacket = arrayOut.toArray();
writeln(handshakePacket);
writeVarInt(&bufferedOut, cast(int) handshakePacket.length);
bufferedOut.writeToStream(handshakePacket);
bufferedOut.flushStream();
writeln("Sent handshake packet.");
auto arrayOut2 = byteArrayOutputStream();
writeVarInt(arrayOut2, 0x00);
ubyte[] statusRequestPacket = arrayOut2.toArray();
writeVarInt(bufferedOut, cast(int) statusRequestPacket.length);
bufferedOut.writeToStream(statusRequestPacket);
bufferedOut.flushStream();
writeln("Sent status request packet.");
int responsePacketSize = readVarInt(sIn);
writefln!"Got response of %d bytes"(responsePacketSize);
ubyte[] packetIdAndData = new ubyte[responsePacketSize];
StreamResult result = sIn.readFromStream(packetIdAndData);
if (result.hasError || result.count != responsePacketSize) throw new Exception("Failed to read response packet.");
auto packetIn = arrayInputStreamFor(packetIdAndData);
int packetId = readVarInt(packetIn);
if (packetId != 0x00) throw new Exception("Received invalid packetId when receiving status response.");
string jsonStr = readString(packetIn);
writeln(jsonStr);
ServerStatus status;
return status;
}
int readVarInt(S)(S s) if (isByteInputStream!S) {
int value = 0;
int position = 0;
ubyte[1] buf;
while (true) {
writeln("Attempting to read from stream...");
StreamResult result = s.readFromStream(buf);
writeln(result);
if (result.hasError) throw new Exception(cast(string) result.error.message);
ubyte currentByte = buf[0];
value |= (currentByte & SEGMENT_BITS) << position;
if ((currentByte & CONTINUE_BIT) == 0) break;
position += 7;
if (position >= 32) throw new Exception("VarInt is too big.");
}
return value;
}
void writeVarInt(S)(S s, int value) if (isByteOutputStream!S) {
while (true) {
if ((value & ~SEGMENT_BITS) == 0) {
StreamResult r = s.writeToStream([cast(ubyte) value]);
if (r.hasError || r.count != 1) throw new Exception("Failed to write byte to stream");
return;
}
StreamResult r = s.writeToStream([(value & SEGMENT_BITS) | CONTINUE_BIT]);
if (r.hasError || r.count != 1) throw new Exception("Failed to write byte to stream");
value >>>= 7;
}
}
string readString(S)(S s) if (isByteInputStream!S) {
int length = readVarInt(s);
ubyte[] data = new ubyte[length];
StreamResult result = s.readFromStream(data);
if (result.hasError || result.count != length) throw new Exception("Couldn't read string.");
return cast(string) data.idup;
}
void writeString(S)(S s, string str) if (isByteOutputStream!S) {
ubyte[] bytes = cast(ubyte[]) str;
writeVarInt(s, cast(int) bytes.length);
s.writeToStream(bytes);
}