#!/bin/bash # Установщик клиента «Мультиверс» для macOS. # Запускать можно сколько угодно раз: сначала он проверяет, что уже # стоит (по именам И размерам файлов), и качает архив ТОЛЬКО если # чего-то не хватает или файл битый. URL="https://multigame.kudabra.us/downloads/multiverse-client.zip" CACHE="/tmp/multiverse-client.zip" HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd)" LOCAL="$HERE/multiverse-client.zip" MC="$HOME/Library/Application Support/minecraft" VER="neoforge-21.1.248" NEED=23 NEEDLIB=243 NEED_KB=1200000 # ~1.2 ГБ свободного места (архив + распаковка) SKINURL='https://multigame.kudabra.us/skins/{USERNAME}.png' # === MANIFEST BEGIN (генерируется build-zip.ps1, не править руками) === MODS='cristellib.jar 583525 customskinloader.jar 218215 dungeonsarise.jar 11842679 dungeons-taverns.jar 24859622 easy_npc.jar 2704420 easy_npc_bundle.jar 25214 easy_npc_config_ui.jar 1064049 explorerscompass.jar 174668 geckolib.jar 630584 lithostitched.jar 852541 lostcities.jar 1385359 moogs-lib.jar 336607 moogs-voyager.jar 1796857 pointblank.jar 17553459 repurposed.jar 8245469 terralith.jar 2814865 townsandtowers.jar 3597353 xaerominimap.jar 2185409 xaeroworldmap.jar 1418918 yung-dungeons.jar 782160 yung-mineshafts.jar 493967 yungs-api.jar 388678 yung-strongholds.jar 461244' # === MANIFEST END === pause_exit(){ echo ""; echo " Нажми Enter, чтобы закрыть окно."; read -r _; exit "${1:-1}"; } fsize(){ wc -c < "$1" 2>/dev/null | tr -d ' '; } # --- полная инвентаризация установки ------------------------------------ scan(){ MOK=0; MMISS=0; MBAD=0; VMISS=0; MISSLIST=""; BADLIST="" while read -r name size; do [ -z "$name" ] && continue f="$MC/mods/$name" if [ ! -f "$f" ]; then MMISS=$((MMISS+1)); MISSLIST="$MISSLIST $name" elif [ "$(fsize "$f")" != "$size" ]; then MBAD=$((MBAD+1)); BADLIST="$BADLIST $name" else MOK=$((MOK+1)) fi done </dev/null | wc -l | tr -d ' ') } report_scan(){ echo " моды: $MOK из $NEED целых" [ "$MMISS" != "0" ] && echo " не хватает:$MISSLIST" [ "$MBAD" != "0" ] && echo " битые: $BADLIST" echo " библиотеки: $LIBHAVE из $NEEDLIB" [ "$VMISS" != "0" ] && echo " файлы версии $VER: не хватает $VMISS" return 0 } # --- чужие моды уезжают в mods_backup_*, наши остаются ------------------- cleanforeign(){ BK=""; FCNT=0; FFAIL=0; FFLIST="" [ -d "$MC/mods" ] || return 0 for f in "$MC/mods/"*.jar; do [ -e "$f" ] || continue b=$(basename "$f") if echo "$MODS" | awk '{print $1}' | grep -qxF "$b"; then continue; fi if [ -z "$BK" ]; then BK="$MC/mods_backup_$(date +%Y%m%d_%H%M%S)_$$" mkdir -p "$BK" fi if mv -f "$f" "$BK/" 2>/dev/null && [ ! -e "$f" ]; then FCNT=$((FCNT+1)) else FFAIL=1; FFLIST="$FFLIST $b" fi done [ -z "$BK" ] && return 0 if [ "$FCNT" -gt 0 ]; then echo " Чужие моды - $FCNT шт. - переложены в резервную папку:" echo " $BK" echo " Они никуда не делись, но игру больше не ломают." else rmdir "$BK" 2>/dev/null fi if [ "$FFAIL" = "1" ]; then echo " ВНИМАНИЕ: чужие моды убрать НЕ удалось - закрой игру и лаунчер" echo " и запусти установщик ещё раз, иначе игра может упасть:" echo " $FFLIST" fi return 0 } # --- проверка архива ---------------------------------------------------- zip_ok(){ if command -v unzip >/dev/null 2>&1; then unzip -tq "$1" >/dev/null 2>&1 else tar -tf "$1" >/dev/null 2>&1; fi } unpack(){ if command -v unzip >/dev/null 2>&1; then unzip -o -q "$1" -d "$MC" else tar -xf "$1" -C "$MC"; fi } # --- где взять архив ---------------------------------------------------- getzip(){ SRC=""; KEEP=1 if [ "$SKIPLOCAL" != "1" ]; then if [ -n "$MV_ZIP" ] && [ -f "$MV_ZIP" ]; then echo " Использую указанный архив: $MV_ZIP" SRC="$MV_ZIP"; return 0 fi if [ -f "$LOCAL" ]; then echo " Нашёл архив рядом с установщиком - использую его, качать не буду." SRC="$LOCAL"; return 0 fi fi if [ -f "$CACHE" ]; then if zip_ok "$CACHE"; then echo " Нашёл уже скачанный целый архив с прошлого раза - использую его." SRC="$CACHE"; KEEP=0; return 0 fi rm -f "$CACHE" fi download || return 1 if [ -f "$CACHE" ]; then SRC="$CACHE"; KEEP=0; return 0; fi return 1 } dropsrc(){ if [ "$KEEP" = "0" ]; then rm -f "$CACHE"; else SKIPLOCAL=1; fi SRC="" } download(){ AVAIL_KB=$(df -k "$HOME" | awk 'NR==2{print $4}') if [ -n "$AVAIL_KB" ] && [ "$AVAIL_KB" -lt "$NEED_KB" ]; then echo "" echo " ------------------------------------------------------------" echo " !! МАЛО МЕСТА НА ДИСКЕ" echo " ------------------------------------------------------------" echo "" echo " Нужно примерно 1.2 ГБ свободного места, а сейчас свободно" echo " около $((AVAIL_KB/1024)) МБ." echo "" echo " Освободи место (корзина, папка Загрузки, старые фильмы)" echo " и запусти установщик ещё раз." pause_exit 1 fi echo "" echo " [2/5] Качаю клиент (~384 МБ). Это самая долгая часть," echo " на медленном интернете может занять 10-30 минут." echo " Если загрузка оборвётся - я сам продолжу с того же места." echo "" for TRY in 1 2 3; do if curl -L --fail -C - --progress-bar -o "$CACHE" "$URL"; then return 0; fi echo "" echo " ...обрыв. Пробую продолжить (попытка $TRY из 3)" sleep 3 done return 1 } # --- настройки скинов --------------------------------------------------- checkskin(){ CSLOK=0 [ -f "$MC/CustomSkinLoader/CustomSkinLoader.json" ] || return 0 grep -qF "$SKINURL" "$MC/CustomSkinLoader/CustomSkinLoader.json" && CSLOK=1 return 0 } # конфиг скинов пишем сами: из-за файла на 1 КБ качать 380 МБ незачем writeskin(){ mkdir -p "$MC/CustomSkinLoader" cat > "$MC/CustomSkinLoader/CustomSkinLoader.json" <<'CSLEOF' { "version": "15.0.1", "buildNumber": 40, "loadlist": [ { "name": "Multiverse", "type": "Legacy", "checkPNG": false, "skin": "https://multigame.kudabra.us/skins/{USERNAME}.png", "model": "auto" }, { "name": "GameProfile", "type": "GameProfile" }, { "name": "LocalSkin", "type": "Legacy", "checkPNG": false, "skin": "LocalSkin/skins/{USERNAME}.png", "model": "auto", "cape": "LocalSkin/capes/{USERNAME}.png", "elytra": "LocalSkin/elytras/{USERNAME}.png" }, { "name": "Mojang", "type": "MojangAPI", "apiRoot": "https://api.mojang.com/", "sessionRoot": "https://sessionserver.mojang.com/" }, { "name": "OptiFine", "type": "Legacy", "checkPNG": false, "model": "auto", "cape": "https://optifine.net/capes/{USERNAME}.png" } ], "enableTransparentSkin": true, "forceLoadAllTextures": true, "enableCape": true, "threadPoolSize": 8, "enableLogStdOut": false, "cacheExpiry": 30, "forceUpdateSkull": false, "enableLocalProfileCache": false, "enableCacheAutoClean": true, "forceDisableCache": false } CSLEOF } # ======================================================================== clear 2>/dev/null echo "" echo " ============================================================" echo "" echo " М У Л Ь Т И В Е Р С - у с т а н о в щ и к" echo "" echo " Minecraft 1.21.1 + NeoForge 21.1.248" echo " 23 модов" echo " (включая скины, карту мира и оружейный мод Point Blank)" echo "" echo " ============================================================" echo "" echo " Привет! Сейчас я проверю, что уже стоит, и доставлю недостающее." echo " Ничего нажимать не надо — просто подожди." echo "" echo " Папка установки:" echo " $MC" echo "" if [ ! -d "/Applications/TLauncher.app" ] && ! ls "$HOME/Applications" 2>/dev/null | grep -qi tlauncher; then echo " Подсказка: я не нашёл TLauncher в папке «Программы»." echo " Если он ещё не установлен — скачай с tlauncher.org" echo " (подойдёт и обычная .jar-версия). Это можно сделать" echo " параллельно, пока я качаю клиент." echo "" fi mkdir -p "$MC/mods" # --- 1. что уже стоит --------------------------------------------------- echo " [1/5] Проверяю, что уже установлено..." scan report_scan # --- 2. чужие моды в сторонку ------------------------------------------- cleanforeign # --- 3. нужен ли архив -------------------------------------------------- NEEDZIP=0 [ "$MMISS" != "0" ] && NEEDZIP=1 [ "$MBAD" != "0" ] && NEEDZIP=1 [ "$VMISS" != "0" ] && NEEDZIP=1 [ "$LIBHAVE" -lt "$NEEDLIB" ] && NEEDZIP=1 SKIPLOCAL=0 PASS=0 while [ "$NEEDZIP" = "1" ]; do PASS=$((PASS+1)) if ! getzip; then echo "" echo " ------------------------------------------------------------" echo " !! НЕ ПОЛУЧИЛОСЬ СКАЧАТЬ АРХИВ" echo " ------------------------------------------------------------" echo "" echo " Что можно сделать:" echo " * проверь, что интернет вообще работает (открой любой сайт);" echo " * если включён VPN — попробуй выключить, и наоборот;" echo " * запусти установщик ещё раз — он продолжит загрузку" echo " с места обрыва, а не с нуля;" echo " * скачай архив браузером:" echo " $URL" echo " положи его РЯДОМ с этим установщиком и запусти снова —" echo " он возьмёт готовый файл и качать не будет." pause_exit 1 fi echo "" echo " [3/5] Распаковываю недостающее..." if ! unpack "$SRC"; then echo " Архив не распаковался — похоже, он битый." dropsrc if [ "$PASS" -ge 2 ]; then echo " Запусти установщик ещё раз — он скачает архив заново." pause_exit 1 fi continue fi scan NEEDZIP=0 [ "$MMISS" != "0" ] && NEEDZIP=1 [ "$MBAD" != "0" ] && NEEDZIP=1 [ "$VMISS" != "0" ] && NEEDZIP=1 [ "$LIBHAVE" -lt "$NEEDLIB" ] && NEEDZIP=1 if [ "$NEEDZIP" = "1" ]; then echo " После распаковки всё ещё не всё на месте — архив был неполный." dropsrc if [ "$PASS" -ge 2 ]; then echo "" echo " !! Что-то встало не полностью (модов: $MOK из $NEED)." [ -n "$MISSLIST" ] && echo " Не хватает:$MISSLIST" [ -n "$BADLIST" ] && echo " Битые файлы:$BADLIST" echo " Запусти установщик ещё раз — он перепишет всё поверх." pause_exit 1 fi continue fi # архив из /tmp больше не нужен, свой (рядом со скриптом) не трогаем [ "$KEEP" = "0" ] && rm -f "$CACHE" done if [ "$PASS" = "0" ]; then echo " [2/5] Все $NEED модов на месте и целые — архив качать не нужно." fi # --- 4. настройки скинов ------------------------------------------------ echo " [4/5] Проверяю настройки скинов..." checkskin if [ "$CSLOK" = "1" ]; then echo " скины настроены правильно" else writeskin checkskin if [ "$CSLOK" = "1" ]; then echo " настройки скинов восстановлены" else echo " ВНИМАНИЕ: не удалось записать настройки скинов" fi fi # --- 5. дублируем моды во все возможные игровые каталоги ---------------- # TLauncher умеет запускать версию в «отдельной папке» — тогда игра читает # моды НЕ из основного каталога, и сервер ругается на отсутствующие моды. echo " [5/5] Раскладываю моды по игровым каталогам TLauncher..." EXTRA=0 for CAND in "$HOME/.minecraft" "$MC/versions/$VER" "$MC/$VER"; do [ -d "$CAND" ] || continue [ "$CAND" = "$MC" ] && continue mkdir -p "$CAND/mods" "$CAND/CustomSkinLoader" 2>/dev/null cp -f "$MC/mods/"*.jar "$CAND/mods/" 2>/dev/null cp -f "$MC/CustomSkinLoader/"*.json "$CAND/CustomSkinLoader/" 2>/dev/null CN=$(ls "$CAND/mods/"*.jar 2>/dev/null | wc -l | tr -d ' ') echo " + $CAND (jar: $CN)" EXTRA=$((EXTRA+1)) done if [ "$EXTRA" = "0" ]; then echo " (дополнительных каталогов нет — всё в основном)" fi scan echo "" echo " ============================================================" echo "" echo " ГОТОВО! Всё установилось (модов: $MOK из $NEED)." echo "" echo " ============================================================" echo "" if [ "$FFAIL" = "1" ]; then echo " НО: чужой мод остался в папке mods, убрать его не вышло:" echo " $FFLIST" echo " Закрой игру и лаунчер и запусти установщик ещё раз," echo " иначе игра может упасть на старте." echo "" fi echo " Что делать дальше:" echo "" echo " 1. Открой TLauncher." echo "" echo " 2. В списке версий выбери: $VER" echo " (не видно — включи показ установленных версий" echo " или просто перезапусти TLauncher)." echo "" echo " 3. Совет для ноутбука: в настройках TLauncher (шестерёнка)" echo " выдели игре 4 ГБ памяти, если ноут позволяет —" echo " с модами так сильно плавнее." echo "" echo " 4. Нажми «Играть». ПЕРВЫЙ запуск дольше обычного:" echo " TLauncher сам докачает ваниль 1.21.1, звуки и текстуры." echo " Это нормально — дай ему доделать." echo "" echo " 5. В игре: Сетевая игра -> Добавить сервер, адрес:" echo "" echo " multiverse.kudabra.us" echo "" echo " ------------------------------------------------------------" echo " ОРУЖИЕ" echo " ------------------------------------------------------------" echo " В сборку входит оружейный мод Point Blank: винтовки," echo " пистолеты, снайперки, дробовики, патроны и прицелы." echo " Оружие не выдаётся само — оно крафтится на верстаке мода" echo " (Arms Dealer Workstation). Мод GeckoLib рядом с ним —" echo " служебная библиотека анимаций, она нужна для работы оружия." echo "" echo " ------------------------------------------------------------" echo " КАРТА И МИНИКАРТА" echo " ------------------------------------------------------------" echo " В сборку входят Xaero's Minimap и Xaero's World Map:" echo " миникарта в углу экрана и полноэкранная карта мира," echo " которая рисуется сама, пока ты ходишь по миру." echo " Клавиши по умолчанию:" echo " M — открыть полноэкранную карту мира" echo " B — поставить точку (waypoint) там, где стоишь" echo " U — список точек (Open Waypoint Screen)" echo " Y — настройки миникарты" echo " Z — увеличить миникарту (пока держишь клавишу)" echo " Num + — быстрая точка, без окошка" echo " Всё это меняется в Настройки -> Управление, разделы" echo " «Xaero's Minimap» и «Xaero's World Map»." echo " Точки лагерей NPC вбивать руками не надо: напиши в чат" echo " «Войс, где лагеря» — он пришлёт их прямо в чат, и точка" echo " добавляется одним кликом по сообщению." echo "" echo " ------------------------------------------------------------" echo " СКИНЫ" echo " ------------------------------------------------------------" echo " В сборку входит поддержка скинов (мод CustomSkinLoader)." echo " Свой скин загружаешь на сайте https://multigame.kudabra.us" echo " в разделе «Профиль» — там же и превью. Скин виден тебе и" echo " другим игрокам с клиентом Мультиверса. После смены скина" echo " перезайди на сервер или подожди пару минут." echo "" echo " ------------------------------------------------------------" echo " ЕСЛИ НЕ ПУСКАЕТ НА СЕРВЕР (пишет про «каналы модов»)" echo " ------------------------------------------------------------" echo " Ошибка вида «Канал мода ... отсутствует на стороне клиента»" echo " значит, что игра читает ДРУГУЮ папку mods." echo "" echo " * В окне ошибки нажми кнопку «Папка с модами» — откроется" echo " тот каталог, который игра использует на самом деле." echo " В нём должно быть $NEED jar-файлов." echo "" echo " * Если папка пустая или файлов меньше — скопируй туда все" echo " jar-файлы вот отсюда:" echo " $MC/mods" echo "" echo " * Либо выключи в настройках версии TLauncher галочку" echo " «Отдельная папка/директория» и запусти установщик ещё раз." echo "" echo " Установщик можно запускать сколько угодно раз: он проверяет" echo " каждый мод по размеру и молча доставляет только то, чего" echo " не хватает. Если всё на месте — ничего не качает." echo "" echo " Увидимся в Мультиверсе!" echo " ------------------------------------------------------------" pause_exit 0