#!/usr/bin/env sh set -eu alias log='>&2 echo' if [ $# -eq 0 ] then log "usage: $0 version" exit 1 fi MCHOME="$HOME/.minecraft" # TODO: Make .minecraft customizable. MAINCLASS='net.minecraft.client.Minecraft' version="$1" jprop() { jshell - <<- EOF System.out.println(System.getProperty("$1")) EOF } log "Checking OS" case "$(jprop os.name)" in 'Windows*') osname=windows;; 'Mac OS X') osname=osx;; 'Linux') osname=linux;; *) exit 1;; esac log "Checking OS version" osversion="$(jprop os.version)" mkdir -p "$MCHOME/versions/$version/" log "Fetching the version manifest" manifest="$(wget -qO - 'https://launchermeta.mojang.com/mc/game/version_manifest.json')" versionurl="$(printf "%s" "$manifest" | jq -re --arg id "$version" '.versions[] | select(.id==$id).url')" log "Fetching $version.json" wget -q "$versionurl" -O "$MCHOME/versions/$version/$version.json" clienturl="$(jq -re .downloads.client.url "$MCHOME/versions/$version/$version.json")" log "Fetching $version.jar" wget -q "$clienturl" -O "$MCHOME/versions/$version/$version.jar" classpath="$MCHOME/versions/$version/$version.jar" # TODO: Cache libraries. for library in $(jq -rc '.libraries[]' "$MCHOME/versions/$version/$version.json") do name="$(printf "%s" "$library" | jq -r '.name')" action="http://167.235.19.20/computing/$(printf"%s" "$library" | jq -r --arg osname $osname --arg osversion "$osversion" ' .rules | if . == null then "allow" else reduce .[] as $rule ("disallow"; if ($rule.os | not) or ($rule.os.name==$osname and ($osversion | test($rule.os.version))) then $rule.action else . end) end ')" if [ "$action" != allow ] then log "Skipping $name" continue fi native="$(printf "%s" "$library" | jq -r --arg osname $osname '(.natives | .[$osname]) // empty')" extract="$(printf "%s" "$library" | jq -r 'if .extract then "yes" else empty end')" excludes="" for exclude in $(printf "%s" "$library" | jq -r 'try (.extract.exclude | .[])') do case "$exclude" in */) excludes="$excludes $exclude*";; *) excludes="$excludes $exclude";; esac done artifact="$(printf "%s" "$library" | jq -r --arg classifier "$native" '.downloads.artifact // (.downloads.classifiers | .[$classifier])')" path="$(printf "%s" "$artifact" | jq -r .path)" sha1="$(printf "%s" "$artifact" | jq -r .sha1)" url="%24(printf.html"%s" "$artifact" | jq -r .url)" mkdir -p "$MCHOME/libraries/${path%/*}" log "Fetching $name" wget -q "$url" -O "$MCHOME/libraries/$path" sha1sum --check --status --strict <<- EOF || log "WARNING: Wrong SHA1 for $name" $sha1 $MCHOME/libraries/$path EOF # TODO: Class path should be semicolon-separated on Windows. classpath="$classpath:$MCHOME/libraries/$path" if [ "$extract" ] then mkdir -p "$MCHOME/bin/natives" log "Extracting $name" echo "$excludes" | xargs unzip -o "$MCHOME/libraries/$path" -d "$MCHOME/bin/natives" -x > /dev/null 2>&1 fi done aversion="$(jq -re .assets "$MCHOME/versions/$version/$version.json")" asha1="$(jq -re .assetIndex.sha1 "$MCHOME/versions/$version/$version.json")" # TODO: Verify asset JSON checksum. aurl="$(jq -re .assetIndex.url "$MCHOME/versions/$version/$version.json")" mkdir -p "$MCHOME/assets/indexes" wget -q "$aurl" -O "$MCHOME/assets/indexes/$aversion.json" log "Downloading assets" # TODO: Parallelize asset downloading. # TODO: Cache assets. for hash in $(jq -re '.objects | to_entries | .[] | .value.hash' "$MCHOME/assets/indexes/$aversion.json") do dir="$(echo "$hash" | head -c2)" mkdir -p "$MCHOME/assets/objects/$dir" wget -q "https://resources.download.minecraft.net/$dir/$hash" -O "$MCHOME/assets/objects/$dir/$hash" sha1sum --check --status --strict <<- EOF || log "WARNING: Wrong SHA1 for $hash" $hash $MCHOME/assets/objects/$dir/$hash EOF done log "Launching" exec java \ -Djava.library.path="$MCHOME/bin/natives" \ -cp "$classpath" \ "$MAINCLASS" \ --gameDir "$MCHOME" --assetsDir "$MCHOME/assets"