Skip to content

Instantly share code, notes, and snippets.

@Forkk
Created July 25, 2013 18:41
Show Gist options
  • Save Forkk/6082555 to your computer and use it in GitHub Desktop.
Save Forkk/6082555 to your computer and use it in GitHub Desktop.
MCRewind Design Doc

Goals

During patch generation, the script will need to do the following:

  • Generate a JSON file containing a list of versions we have patches for along with some info about those versions and said patches.
  • Generate bsdiff patches for patching from the current version minecraft.jar to each jar file in the jars folder.
  • Automatically download new versions of Minecraft into the folder of old jars, so that the script can generate patches for them in the future.

Data Structures

The JSON file that lists patches and their info is called the index. This JSON file is simply a dump of an 'index' dict that will be updated with information about the patches as they are generated.

The structure of the index file is as follows:

{
	// Specifies what version of Minecraft these patches are "for".
	// This is the version of Minecraft that the patches should be applied to in order to downgrade the jar.
	"mcversion": "1.5.2",

	// This is an array of objects that contain information about each version provided.
	"versions":
	[
	{
		// This is the name of the version that this patch downgrades to.
		// Names that start with 'a' are alpha versions and names that start with 'b' are beta versions.
		"name": "a1.0.4",

		// This is the MD5sum of the resulting jar file.
		// Once the patch has been applied, the MD5sum of the resulting jar file should match this value.
		"md5sum": "fc56b5270fd47e70557f19d79559371b"
	}
	],

	// This is the version number of the JSON file.
	// This will change whenever the structure of the JSON file is changed.
	"listversion": "1"
}

The script will also have another JSON file called the cache file. This file will store information about the last version of minecraft that was downloaded.

The structure of the cache file is as follows:

{
	// The version name of the last minecraft.jar that was downloaded.
	"mcversion": "1.5.2",

	// The MD5sum of the last minecraft.jar that was downloaded.
	// If this value differs from the jar file's actual MD5sum or the ETag header on the jar's download URL, the jar file will be re-downloaded.
	"md5sum": "6897c3287fb971c9f362eb3ab20f5ddd"
}

Generation Process

The patch generation process can be split up into a couple separate tasks: checking for new versions, downloading new jars, and generating the actual patches.

Checking for new jar files

First, the script needs to check for a new jar file. During this process, the checks listed below are performed. If any of the following checks are false, the script assumes that there is an updated minecraft.jar and moves to the download new jars task.

  1. The script checks if a cache file exists. This is a JSON file containing info about the last version that was downloaded such as its version name (mcversion), and its MD5sum (md5sum).
  2. The script loads the cache file and then goes to Mojang's version list and checks to see what the latest version of Minecraft is.
    • The script checks to make sure that the version name it got from Mojang's version list matches the mcversion field in the cache file.
    • The script checks its jars folder to see if there is a jar file whose filename matches the version name the script got from Mojang's version list. This file shall be referred to as the "latest jar file".
  3. The script goes to the download URL for that version and does a HTTP HEAD request to get the 'ETag' from the header. The ETag should be an MD5sum. If it's not a valid MD5sum, print an error message and exit with nonzero.
    • The script checks to make sure that the ETag it got from the HEAD request matches the md5sum field in the cache file.
  4. The script calculates the MD5sum of the latest jar file.
    • The script checks to make sure the MD5sum of the latest jar file matches the MD5sum it got from the cache file and the HEAD request.
  5. If all of these checks pass as true, the script will skip the "download new jar files" step and move on to generating patches. Otherwise, the script moves on to download new jar files.

Downloading new jar files

The download new jar files step will run after the check for new jar files step. It will be passed a variable containing the version name for the latest minecraft.jar.

  1. The script will download the new minecraft.jar file from Mojang's version list site into a temporary file.
  2. As it is downloading, the script will be calculating the MD5sum of the downloading data.
  3. When the download is done, the script will check the MD5sum it calculated and make sure it matches the "ETag" field in the HTTP header.
  4. If the MD5sum doesn't match the ETag, the script retries the download process. After a certain number of tries, the script gives up and fails with a nonzero exit code.
  5. The script copies the temporary file to its jars folder (the name of the file will be .jar), creating the folder if it doesn't exist, and overwriting any existing jar file with its same name.
  6. The script writes a JSON file (the "cache" file). This file is used to keep track of information about the last version the script downloaded (the format of this cache file is documented in the data structures section above.

Generating patches

The final task is to generate patches. This task needs to be passed the path to the latest downloaded jar file.

  1. The script creates an index dict that will store information about the generated patches. Information about the structure of this file can be found in the data structures section above.
  2. The script then goes through its jars folder and does the following for each jar file:
    1. The script creates a dict for storing information about the patch it's currently generating. Called patch_info
    2. The script stores the version name in the patch_info dict under the name key. The version name is essentially just the filename of the jar file without its file extension.
    3. The script calculates the MD5sum of the jar it's generating a patch for and stores it in the patch_info dict under the md5sum key.
    4. The script runs bsdiff to generate a patch from the latest jar to the jar it's generating a patch for. The patch is saved to the patches folder.
    5. The script then appends the patch_info dict to the versions field in the index and moves on to the next jar file.
  3. If no errors occurred during the patch generation process the script dumps the index dict to the index file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment