Class ConnectionProbe
- Namespace
- McServerLauncher.Services
- Assembly
- McServerLauncher.dll
The measurements behind the connection test: a Minecraft status ping, a Bedrock (RakNet) ping, a bare TCP round trip, and a download and an upload to time the line.
public static class ConnectionProbe
- Inheritance
-
ConnectionProbe
- Inherited Members
Remarks
Each tells apart one stretch of the way a player's packets travel. The status ping to
127.0.0.1 is the server alone; the TCP round trip to Cloudflare is this machine's internet;
the status ping to the tunnel's public address goes out to Playit and back in through the agent,
which is the path a player's packets take. Comparing them says where the time goes.
The status ping is the one the game's server list sends (handshake with next state 1, status request, then a ping whose echo is timed), so any server that shows up in the list answers it, whatever its type. Only the echo is timed — the connection and the status JSON are not, as the game itself does.
The speed test uses Cloudflare's public endpoints (speed.cloudflare.com, the ones its own
web test calls). No account and no key; what is sent up is random bytes.
Fields
FallbackProtocol
A protocol number to put in the handshake when the server's own is not known: a real one, recent (1.21.1). See PingJavaAsync(string, int, TimeSpan, int, CancellationToken) for why -1 will not do.
public const int FallbackProtocol = 767
Field Value
LatencyHost
public const string LatencyHost = "1.1.1.1"
Field Value
SpeedHost
public const string SpeedHost = "speed.cloudflare.com"
Field Value
Methods
DownloadAsync(long, TimeSpan, CancellationToken)
Downloads up to bytes, for at most limit.
public static Task<Throughput> DownloadAsync(long bytes, TimeSpan limit, CancellationToken ct = default)
Parameters
byteslonglimitTimeSpanctCancellationToken
Returns
PingBedrockAsync(IPEndPoint, TimeSpan, CancellationToken)
A RakNet unconnected ping, which is what the Bedrock client sends to fill its server list. Milliseconds, or null when nothing answered in time. The address comes already resolved: see ResolveAsync(string, CancellationToken).
public static Task<double?> PingBedrockAsync(IPEndPoint server, TimeSpan timeout, CancellationToken ct = default)
Parameters
serverIPEndPointtimeoutTimeSpanctCancellationToken
Returns
PingJavaAsync(string, int, TimeSpan, int, CancellationToken)
A server-list ping. Null when nothing answered in time.
public static Task<SlpReply?> PingJavaAsync(string host, int port, TimeSpan timeout, int protocol = 767, CancellationToken ct = default)
Parameters
hoststringWhere to connect, and what the handshake says it connected to. A name, not an IP, when going through Playit: its edge routes by it, and an IP there gets the connection reset.
portintThe port to connect to, also written into the handshake.
timeoutTimeSpanFor the whole exchange: connecting, the status and the ping.
protocolintThe protocol number in the handshake. Never -1, the "whatever you speak" that status tools send: Playit's Minecraft edge reads the handshake before passing it on and resets a connection whose protocol is not a real one (found on 2026-10-10 against a live tunnel: -1 was reset every time within a few milliseconds, 776 and 767 answered every time), and a server asked with -1 may report -1 as its own protocol, which is no use either. Asked with a real number, a server answers with its own, so the local ping finds out what the tunnel ping then sends.
ctCancellationTokenStops it early; a cancellation is not reported as "no answer".
Returns
ResolveAsync(string, CancellationToken)
What host points to, or null when it does not resolve: asked once and
without holding the thread, to be pinged as often as needed.
public static Task<IPAddress?> ResolveAsync(string host, CancellationToken ct = default)
Parameters
hoststringctCancellationToken
Returns
Remarks
Connect(string, int) resolves the name itself, synchronously, and the
test pings eight times from the UI thread: eight lookups that froze the window while each
waited on a slow DNS. TCP does not need this — its ConnectAsync resolves asynchronously,
and the Java ping must keep the name, which Playit's edge routes by.
ResolveJavaAsync(string, CancellationToken)
Where a Java address really points: host:port as written, or the
_minecraft._tcp SRV record, which is how Playit's domains carry their port.
public static Task<(string Host, int Port)> ResolveJavaAsync(string address, CancellationToken ct = default)
Parameters
addressstringctCancellationToken
Returns
Remarks
.NET has no SRV lookup of its own, so it is asked of Cloudflare's DNS over HTTPS — the same provider the speed test already talks to. Without an answer the game's default port is what a client would try, so it is what is tried here.
SampleAsync(Func<CancellationToken, Task<double?>>, int, TimeSpan, IProgress<double>?, CancellationToken)
Runs probe count times, a little apart.
public static Task<LatencyStats> SampleAsync(Func<CancellationToken, Task<double?>> probe, int count, TimeSpan gap, IProgress<double>? each = null, CancellationToken ct = default)
Parameters
probeFunc<CancellationToken, Task<double?>>countintgapTimeSpaneachIProgress<double>ctCancellationToken
Returns
TcpRoundTripAsync(string, int, TimeSpan, CancellationToken)
How long a TCP connection takes to open: one round trip, and nothing else.
public static Task<double?> TcpRoundTripAsync(string host, int port, TimeSpan timeout, CancellationToken ct = default)
Parameters
hoststringportinttimeoutTimeSpanctCancellationToken
Returns
UploadAsync(long, TimeSpan, CancellationToken)
Uploads up to bytes of random data, for at most limit.
public static Task<Throughput> UploadAsync(long bytes, TimeSpan limit, CancellationToken ct = default)
Parameters
byteslonglimitTimeSpanctCancellationToken