diff options
| author | Caroline Larimore <caroline@larimo.re> | 2025-08-17 14:59:02 -0700 |
|---|---|---|
| committer | Caroline Larimore <caroline@larimo.re> | 2025-08-17 14:59:02 -0700 |
| commit | 9e1e7bec816a249dd26abb5fcb53e70e898859a3 (patch) | |
| tree | cecfcc90d660c15f21e4e16d7e0489d008446b74 | |
Initial Commit
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | LICENCE | 21 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | auth.json | 7 | ||||
| -rw-r--r-- | info.json | 15 | ||||
| -rwxr-xr-x | mcsh.sh | 58 | ||||
| -rw-r--r-- | meta.jq | 64 |
7 files changed, 174 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93ad9bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +assets +libraries +logs +natives +run +versions @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Caroline Larimore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ec0235 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# mcsh + +a minecraft launcher written in bash (and jq), for some reason diff --git a/auth.json b/auth.json new file mode 100644 index 0000000..2f3a7c6 --- /dev/null +++ b/auth.json @@ -0,0 +1,7 @@ +{ + "player_name": "mcsh", + "uuid": "00000000-0000-0000-0000-000000000000", + "xuid": "00000000-0000-0000-0000-000000000000", + "access_token": "0", + "type": "mojang" +} diff --git a/info.json b/info.json new file mode 100644 index 0000000..6e3bd0e --- /dev/null +++ b/info.json @@ -0,0 +1,15 @@ +{ + "os": { + "name": "linux", + "arch": "x64" + }, + "features": { + "is_demo_user": false, + "has_custom_resolution": false, + "has_quick_plays_support": false, + + "is_quick_play_singleplayer": false, + "is_quick_play_multiplayer": false, + "is_quick_play_realms": false + } +} @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +VERSION="1.21.8" + +function download_library { + if ! [[ -e "${2:-}" ]]; then + printf '%s\n' "${1:-}" + mkdir -p "$(dirname "${2:-}")" + curl --silent -o "${2:-}" "${3:-}" + fi +} +export -f download_library + +function download_asset { + if ! [[ -e "assets/objects/${2:-}" ]]; then + printf '%s\n' "${1:-}" + mkdir -p "$(dirname "assets/objects/${2:-}")" + curl --silent -o "assets/objects/${2:-}" "https://resources.download.minecraft.net/${2:-}" + fi +} +export -f download_asset + +printf 'updating version manifest\n' +mkdir -p versions +curl --silent "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" | jq '{"latest": .latest, "versions": [.versions[] | {"key": .id, "value": .url}] | from_entries}' > versions/manifest.json + + +printf 'updating %s meta %s\n' "$VERSION" +mkdir -p "versions/$VERSION" +curl --silent -o "versions/$VERSION/meta.json" "$(jq -r --arg version "$VERSION" '.versions[$version]' versions/manifest.json)" +meta="$(jq -c --slurpfile info info.json --slurpfile auth auth.json -f meta.jq "versions/$VERSION/meta.json")" + +printf 'updating %s asset index...\n' "$VERSION" +mkdir -p assets/indexes +assets_url="$(<<<"$meta" jq -r '.asset_index')" +curl --silent -o "assets/indexes/$(basename "$assets_url")" "$assets_url" + +#TODO: check hash +if ! [[ -e "versions/$VERSION/client.jar" ]]; then + printf 'downloading %s client jar...\n' "$VERSION" + <<<"$meta" jq -r '.client_url' | xargs curl --silent -o "versions/$VERSION/client.jar" +fi + +printf 'downloading libraries...\n' +<<<"$meta" jq -r '.libraries[] | "\(.name) libraries/\(.path) \(.url)"' | xargs -I {} "$SHELL" -c "download_library {}" + +printf 'downloading assets...\n' +jq -r '.objects | to_entries[] | "\(.key) \(.value.hash[:2])/\(.value.hash)"' "assets/indexes/$(basename "$assets_url")" | xargs -P 10 -I {} "$SHELL" -c "download_asset {}" + +# if ! [[ -e "versions/$VERSION/log4j.xml" ]]; then +# printf 'downloading %s log4j config...\n' "$VERSION" +# <<<"$meta" jq -r '.logging_config' | xargs curl --silent -o "versions/$VERSION/log4j.xml" +# fi + +printf 'starting game :3\n' +<<<"$meta" jq -r '.args.jvm + [.main_class] + .args.game | join(" ")' | xargs java @@ -0,0 +1,64 @@ +def apply_optional: if has("rules") then + { + "rules": [.rules[] | { + "action": .action, + "match": del(.action) + }], + "value": del(.rules) + } as $optional + | if $optional.rules[].match | inside($info[0]) then + $optional.value + else + empty + end +end; + +def replace_placeholders: { + "auth_player_name": $auth[0].player_name, + "auth_uuid": $auth[0].uuid, + "auth_xuid": $auth[0].xuid, + "auth_access_token": $auth[0].access_token, + "user_type": $auth[0].type, + + "version_name": .id, + "version_type": .type, + + "assets_index_name": .assets, + "assets_root": "assets", + "game_directory": "run", + "natives_directory": "natives", + + "classpath": (. as $root | [ + .libraries[] | apply_optional | "libraries/" + .downloads.artifact.path + ] + ["versions/\($root.id)/client.jar"] | join(":")), + "clientid": "idfk", + + # log4j config path + "path": "versions/\(.id)/log4j.xml", + + "launcher_name": "mcsh", + "launcher_version": "v0.1.0" +} as $strings | walk(if type == "string" then + gsub("\\${(?<key>.*)}"; "\($strings[.key] // "${\(.key)}")") +end); + +replace_placeholders | { + "args": .arguments | to_entries | [.[] | { + "key": .key, + "value": .value | [ + .[] | if type == "object" then + apply_optional | .value + end + ] | flatten + }] | from_entries, # as $args | $args + {"jvm": [.logging.client.argument] + $args.jvm}), + "libraries": [.libraries[] | apply_optional | { + "name": .name, + "path": .downloads.artifact.path, + "url": .downloads.artifact.url + }], + "asset_index": .assetIndex.url, + "java_version": .javaVersion.majorVersion, + "client_url": .downloads.client.url, + "main_class": .mainClass, + "logging_config": .logging.client.file.url +} |