Internal versioning

From wiki.vg
Jump to navigation Jump to search

The version used by the GameUpdate procedure differs from what the end-user sees, as many may have noticed by viewing the contents of the version file. One who is up-to-date with version 1.7.3 as an example would have it containing the number 1310111031000 with an additional nul character prepended.

This number does not in any way relate or resolve to the update version, but is in fact a UNIX timestamp telling when the update was uploaded.

It might not come to you as a surprise that Minecraft primarily uses Amazon services for both their homepage and update storage location (downloading from https://s3.amazonaws.com/MinecraftDownload/minecraft.jar might've give that away).

Truth be told, Amazon maintains a quite extensive HTTP based API to interface with their service, both for end-user and administration purposes. However, the MinecraftDownload bucket [1] is privilege limited with Amazons internal Access Control Lists.

We can however utilize what is described as the HEAD [2] operation to determine the timestamp of the latest update.

The HEAD operation returns a fair bit of HTTP response headers, including one named Last-Modified. This header contains a RFC1123 formatted date string that easily can be parsed into a UNIX timestamp.

Java POC

<source lang="java"> import java.net.URL; import java.net.HttpURLConnection; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;

class VersionPOC {

 public static String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z";
 public static main(String args[])
 {
   long              timestamp  = 0;
   HttpURLConnection connection = null;
   try {
     URL minecraftJar = new URL("http://s3.amazonaws.com/MinecraftDownload/minecraft.jar");
     connection = (HttpURLConnection)minecraftJar.openConnection();
     connection.setUseCaches(false);
     connection.setRequestMethod("HEAD");
     timestamp = new SimpleDateFormat(RFC1123_PATTERN, Locale.US)      .
                     parse(connection.getHeaderField("Last-Modified")) .
                     getTime();
     System.out.println("Latest internal version is " + timestamp);
     connection.disconnect();
   } catch (Exception except) {
     except.printStackTrace();
   }
 }

} </source>