Created
April 15, 2017 09:03
-
-
Save Andoryuuta/903fd6f3b3ed741a8d21e84283924699 to your computer and use it in GitHub Desktop.
ListRen.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.net.*; | |
import java.io.*; | |
import java.util.*; | |
import java.nio.charset.StandardCharsets; | |
class HostAddr { | |
public HostAddr(String ip, int port){ | |
Ip = ip; | |
Port = port; | |
} | |
public String Ip; | |
public int Port; | |
} | |
class InvalidResponseException extends Exception | |
{ | |
//Parameterless Constructor | |
public InvalidResponseException() {} | |
//Constructor that accepts a message | |
public InvalidResponseException(String message) | |
{ | |
super(message); | |
} | |
} | |
public class ListRen { | |
public static final String RENLIST_SERVER = "master-gsa.renlist.n00b.hk"; | |
public static final int RENLIST_PORT = 28900; | |
private static HashMap<String, String> GetHostStatus(HostAddr host) throws IOException { | |
HashMap<String, String> gameOptions = new HashMap<String, String>(); | |
DatagramSocket sock = new DatagramSocket(); | |
// Prepare and send the \status\ UDP datagram | |
byte[] statusBytes = "\\status\\".getBytes(); | |
DatagramPacket packet = new DatagramPacket(statusBytes, statusBytes.length, InetAddress.getByName(host.Ip), host.Port); | |
sock.send(packet); | |
// Create and read into big buffer for receiving | |
packet.setData(new byte[2048]); | |
sock.setSoTimeout(2000); | |
sock.receive(packet); | |
// Convert the data into a string | |
// (And strip off the first backslash in the process) | |
String respStr = new String(Arrays.copyOfRange(packet.getData(), 0, packet.getLength()), StandardCharsets.UTF_8); | |
// Split by single backslash | |
// escaped once for java and once for regex | |
// resulting in "\\\\" | |
String[] fields = respStr.split("\\\\"); | |
for(int i = 0; i < fields.length;){ | |
if(fields[i].equals("final")){ | |
// Final, exit here. | |
break; | |
}else if(fields[i].equals("")){ | |
// Empty, skip. | |
i++; | |
} else { | |
// Valid, add to map. | |
gameOptions.put(fields[i], fields[i+1]); | |
i+=2; | |
} | |
} | |
sock.close(); | |
return gameOptions; | |
} | |
private static List<HostAddr> GetHostAddrs() throws UnknownHostException, IOException, InvalidResponseException { | |
// Connect to GSA master server | |
Socket sock = null; | |
try { | |
sock = new Socket(RENLIST_SERVER, RENLIST_PORT); | |
} catch (UnknownHostException e) { | |
System.err.println("Unknown host: " + RENLIST_SERVER); | |
throw e; | |
} catch (IOException e) { | |
System.err.println("IOException on creating socket to: " + RENLIST_SERVER); | |
throw e; | |
} | |
// Try to get the host list from the GSA master server. | |
List<HostAddr> hostList = new ArrayList<HostAddr>(); | |
try { | |
InputStream sin = sock.getInputStream(); | |
OutputStream sout = sock.getOutputStream(); | |
// Allocate and read in buffer for the intial "\basic\\secure\IGNORE" query | |
// Sent by the n00b.hk GSA master server. | |
byte[] respData = new byte[21]; | |
sin.read(respData); | |
String query = new String(respData, StandardCharsets.UTF_8); | |
if(query.equals("\\basic\\\\secure\\IGNORE")){ | |
System.out.println("Got basic query."); | |
// Send query to get servers. | |
sout.write("\\gamename\\ccrenegade\\enctype\\0\\validate\\TVJFLK6J\\final\\\\queryid\\1.1\\list\\cmp\\gamename\\ccrenegade\\where\\\\final\\".getBytes()); | |
// Read in each host | |
while(true){ | |
// Read in binary host data (int32 ip, int16 port) | |
byte[] hostData = new byte[6]; | |
sin.read(hostData); | |
String hostDataAsString = new String(hostData, StandardCharsets.UTF_8); | |
// Check if it is the end of the server/host list, | |
// -- HACK | |
if(hostDataAsString.equals("\\final")){ | |
//System.out.println("Got final query. Done reading server list."); | |
break; | |
} | |
// Parse bytes to ip and port, | |
// I don't program in Java, this may be a horrible way to do this. | |
// But it works :) | |
String ip = String.format("%d.%d.%d.%d", hostData[0]&0xff, hostData[1]&0xff, hostData[2]&0xff, hostData[3]&0xff); | |
int port = ((hostData[5]&0xff)|(short) ((hostData[4]&0xff)<<8))&0xffff; | |
hostList.add(new HostAddr(ip, port)); | |
//System.out.printf("%s:%d\n", ip, port); | |
} | |
} else { | |
throw new InvalidResponseException(String.format("Server returned something that wasn't the basic query: %s\n", query)); | |
} | |
sin.close(); | |
sock.close(); | |
} catch (IOException e) { | |
throw e; | |
} | |
return hostList; | |
} | |
public static void main(String[] args) { | |
try{ | |
List<HostAddr> hosts = GetHostAddrs(); | |
for(HostAddr h : hosts){ | |
HashMap<String, String> options; | |
try{ | |
options = GetHostStatus(h); | |
} catch(SocketTimeoutException e){ | |
System.out.printf("GSA Serv: %s:%d timed out.\n", h.Ip, h.Port); | |
continue; | |
} | |
System.out.printf("%s:%s | %s | %s | %s/%s\n", h.Ip, options.get("hostport"), options.get("hostname"), options.get("mapname"), options.get("numplayers"), options.get("maxplayers")); | |
} | |
} catch(Exception e){ | |
System.out.println(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment