-
-
Save csh/2480d14fbbb33b4bbae3 to your computer and use it in GitHub Desktop.
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Net.Sockets; | |
| using System.Text; | |
| using System.Threading; | |
| using Newtonsoft.Json; | |
| #if DEBUG | |
| using System.Diagnostics; | |
| #endif | |
| namespace MCServerPing | |
| { | |
| class ServerPing | |
| { | |
| private static readonly Dictionary<char, ConsoleColor> Colours = new Dictionary<char, ConsoleColor> | |
| { | |
| { '0', ConsoleColor.Black }, | |
| { '1', ConsoleColor.DarkBlue }, | |
| { '2', ConsoleColor.DarkGreen }, | |
| { '3', ConsoleColor.DarkCyan }, | |
| { '4', ConsoleColor.DarkRed }, | |
| { '5', ConsoleColor.DarkMagenta }, | |
| { '6', ConsoleColor.Yellow }, | |
| { '7', ConsoleColor.Gray }, | |
| { '8', ConsoleColor.DarkGray }, | |
| { '9', ConsoleColor.Blue }, | |
| { 'a', ConsoleColor.Green }, | |
| { 'b', ConsoleColor.Cyan }, | |
| { 'c', ConsoleColor.Red }, | |
| { 'd', ConsoleColor.Magenta }, | |
| { 'e', ConsoleColor.Yellow }, | |
| { 'f', ConsoleColor.White }, | |
| { 'k', Console.ForegroundColor }, | |
| { 'l', Console.ForegroundColor }, | |
| { 'm', Console.ForegroundColor }, | |
| { 'n', Console.ForegroundColor }, | |
| { 'o', Console.ForegroundColor }, | |
| { 'r', ConsoleColor.White } | |
| }; | |
| private static NetworkStream _stream; | |
| private static List<byte> _buffer; | |
| private static int _offset; | |
| private static void Main(string[] args) | |
| { | |
| Console.Title = "Minecraft Server Ping"; | |
| var client = new TcpClient(); | |
| var task = client.ConnectAsync("localhost", 25565); | |
| Console.WriteLine("Connecting to Minecraft server.."); | |
| while (!task.IsCompleted) | |
| { | |
| #if DEBUG | |
| Debug.WriteLine("Connecting.."); | |
| #endif | |
| Thread.Sleep(250); | |
| } | |
| if (!client.Connected) | |
| { | |
| Console.ForegroundColor = ConsoleColor.Red; | |
| Console.WriteLine("Unable to connect to the server"); | |
| Console.ResetColor(); | |
| Console.ReadKey(true); | |
| Environment.Exit(1); | |
| } | |
| _buffer = new List<byte>(); | |
| _stream = client.GetStream(); | |
| Console.WriteLine("Sending status request"); | |
| /* | |
| * Send a "Handshake" packet | |
| * http://wiki.vg/Server_List_Ping#Ping_Process | |
| */ | |
| WriteVarInt(47); | |
| WriteString("localhost"); | |
| WriteShort(25565); | |
| WriteVarInt(1); | |
| Flush(0); | |
| /* | |
| * Send a "Status Request" packet | |
| * http://wiki.vg/Server_List_Ping#Ping_Process | |
| */ | |
| Flush(0); | |
| /* | |
| * If you are using a modded server then use a larger buffer to account, | |
| * see link for explanation and a motd to HTML snippet | |
| * https://gist.github.com/csh/2480d14fbbb33b4bbae3#gistcomment-2672658 | |
| */ | |
| var buffer = new byte[Int16.MaxValue]; | |
| // var buffer = new byte[4096]; | |
| _stream.Read(buffer, 0, buffer.Length); | |
| try | |
| { | |
| var length = ReadVarInt(buffer); | |
| var packet = ReadVarInt(buffer); | |
| var jsonLength = ReadVarInt(buffer); | |
| #if DEBUG | |
| Console.WriteLine("Received packet 0x{0} with a length of {1}", packet.ToString("X2"), length); | |
| #endif | |
| var json = ReadString(buffer, jsonLength); | |
| var ping = JsonConvert.DeserializeObject<PingPayload>(json); | |
| Console.WriteLine("Software: {0}", ping.Version.Name); | |
| Console.WriteLine("Protocol: {0}", ping.Version.Protocol); | |
| Console.WriteLine("Players Online: {0}/{1}", ping.Players.Online, ping.Players.Max); | |
| WriteMotd(ping); | |
| Console.ReadKey(true); | |
| } | |
| catch (IOException ex) | |
| { | |
| /* | |
| * If an IOException is thrown then the server didn't | |
| * send us a VarInt or sent us an invalid one. | |
| */ | |
| Console.ForegroundColor = ConsoleColor.Red; | |
| Console.WriteLine("Unable to read packet length from server,"); | |
| Console.WriteLine("are you sure it's a Minecraft server?"); | |
| #if DEBUG | |
| Console.WriteLine("Here are the details:"); | |
| Console.WriteLine(ex.ToString()); | |
| #endif | |
| Console.ResetColor(); | |
| } | |
| } | |
| private static void WriteMotd(PingPayload ping) | |
| { | |
| Console.Write("Motd: "); | |
| var chars = ping.Motd.ToCharArray(); | |
| for (var i = 0; i < ping.Motd.Length; i++) | |
| { | |
| try | |
| { | |
| if (chars[i] == '\u00A7' && Colours.ContainsKey(chars[i + 1])) | |
| { | |
| Console.ForegroundColor = Colours[chars[i + 1]]; | |
| continue; | |
| } | |
| if (chars[i - 1] == '\u00A7' && Colours.ContainsKey(chars[i])) | |
| { | |
| continue; | |
| } | |
| } | |
| catch (IndexOutOfRangeException) | |
| { | |
| // End of string | |
| } | |
| Console.Write(chars[i]); | |
| } | |
| Console.WriteLine(); | |
| Console.ResetColor(); | |
| } | |
| #region Read/Write methods | |
| internal static byte ReadByte(byte[] buffer) | |
| { | |
| var b = buffer[_offset]; | |
| _offset += 1; | |
| return b; | |
| } | |
| internal static byte[] Read(byte[] buffer, int length) | |
| { | |
| var data = new byte[length]; | |
| Array.Copy(buffer, _offset, data, 0, length); | |
| _offset += length; | |
| return data; | |
| } | |
| internal static int ReadVarInt(byte[] buffer) | |
| { | |
| var value = 0; | |
| var size = 0; | |
| int b; | |
| while (((b = ReadByte(buffer)) & 0x80) == 0x80) | |
| { | |
| value |= (b & 0x7F) << (size++*7); | |
| if (size > 5) | |
| { | |
| throw new IOException("This VarInt is an imposter!"); | |
| } | |
| } | |
| return value | ((b & 0x7F) << (size*7)); | |
| } | |
| internal static string ReadString(byte[] buffer, int length) | |
| { | |
| var data = Read(buffer, length); | |
| return Encoding.UTF8.GetString(data); | |
| } | |
| internal static void WriteVarInt(int value) | |
| { | |
| while ((value & 128) != 0) | |
| { | |
| _buffer.Add((byte) (value & 127 | 128)); | |
| value = (int) ((uint) value) >> 7; | |
| } | |
| _buffer.Add((byte) value); | |
| } | |
| internal static void WriteShort(short value) | |
| { | |
| _buffer.AddRange(BitConverter.GetBytes(value)); | |
| } | |
| internal static void WriteString(string data) | |
| { | |
| var buffer = Encoding.UTF8.GetBytes(data); | |
| WriteVarInt(buffer.Length); | |
| _buffer.AddRange(buffer); | |
| } | |
| internal static void Write(byte b) | |
| { | |
| _stream.WriteByte(b); | |
| } | |
| internal static void Flush(int id = -1) | |
| { | |
| var buffer = _buffer.ToArray(); | |
| _buffer.Clear(); | |
| var add = 0; | |
| var packetData = new[] {(byte) 0x00}; | |
| if (id >= 0) | |
| { | |
| WriteVarInt(id); | |
| packetData = _buffer.ToArray(); | |
| add = packetData.Length; | |
| _buffer.Clear(); | |
| } | |
| WriteVarInt(buffer.Length + add); | |
| var bufferLength = _buffer.ToArray(); | |
| _buffer.Clear(); | |
| _stream.Write(bufferLength, 0, bufferLength.Length); | |
| _stream.Write(packetData, 0, packetData.Length); | |
| _stream.Write(buffer, 0, buffer.Length); | |
| } | |
| #endregion | |
| } | |
| #region Server ping | |
| /// <summary> | |
| /// C# represenation of the following JSON file | |
| /// https://gist.github.com/thinkofdeath/6927216 | |
| /// </summary> | |
| class PingPayload | |
| { | |
| /// <summary> | |
| /// Protocol that the server is using and the given name | |
| /// </summary> | |
| [JsonProperty(PropertyName = "version")] | |
| public VersionPayload Version { get; set; } | |
| [JsonProperty(PropertyName = "players")] | |
| public PlayersPayload Players { get; set; } | |
| [JsonProperty(PropertyName = "description")] | |
| public string Motd { get; set; } | |
| /// <summary> | |
| /// Server icon, important to note that it's encoded in base 64 | |
| /// </summary> | |
| [JsonProperty(PropertyName = "favicon")] | |
| public string Icon { get; set; } | |
| } | |
| class VersionPayload | |
| { | |
| [JsonProperty(PropertyName = "protocol")] | |
| public int Protocol { get; set; } | |
| [JsonProperty(PropertyName = "name")] | |
| public string Name { get; set; } | |
| } | |
| class PlayersPayload | |
| { | |
| [JsonProperty(PropertyName = "max")] | |
| public int Max { get; set; } | |
| [JsonProperty(PropertyName = "online")] | |
| public int Online { get; set; } | |
| [JsonProperty(PropertyName = "sample")] | |
| public List<Player> Sample { get; set; } | |
| } | |
| class Player | |
| { | |
| [JsonProperty(PropertyName = "name")] | |
| public string Name { get; set; } | |
| [JsonProperty(PropertyName = "id")] | |
| public string Id { get; set; } | |
| } | |
| #endregion | |
| } |
Hi, I'm having a problem with the response JSON.
The response that the server sent to the client is incomplete
I'm trying this on 1.20.1 on a Spigot server
Recieved JSON Data:
{"version":{"name":"Paper 1.20.1","protocol":763},"favicon":"data:image/png;base64,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
The error is given by "favicon" because it doesnt have a " to end the String.
The problem isnt byte array length since there is still left space.
Why isnt the server sending me the whole json data/cutting it out?
Hi, I placed the code outside the Main method so I can call it again as my intention was to ping the server at regular interval (say every five minutes).
It works like a charm on first ping, the succeeding ping delivers no payload. How can I get around this?