#!/usr/bin/env bash # Instalator Linux pentru EzWeb METIN2 (client Metin2 Windows prin Wine). # # curl -fsSL https://m2.ezweb.ro/install.sh | bash # curl -fsSL https://m2.ezweb.ro/install.sh | bash -s -- --yes # fara intrebari # bash install.sh [--yes] [--no-deps] [--no-desktop-icon] [--reinstall] [--uninstall] # # Ce face (doar in directorul tau HOME, in afara de instalarea pachetelor cu sudo): # 1. detecteaza distributia si, daca lipseste, instaleaza Wine (+ glxinfo) # cu managerul de pachete (apt / pacman / dnf / zypper), dupa confirmare; # 2. descarca clientul de pe site, verifica SHA-256; # 3. il instaleaza in ~/.local/share// (rularea din nou = actualizare, # setarile tale din metin2.cfg se pastreaza), cu prefix Wine propriu; daca # versiunea instalata e deja aceeasi, nu mai descarca nimic si actualizeaza doar # lansatorul (--reinstall = dezarhiveaza din nou clientul); # 4. creeaza lansatorul in meniu (+ pe desktop), "Setari grafice" (rezolutie / # ecran complet) si un script de dezinstalare. # Generat de tools/build-client-release.py; adresa site-ului e completata de site. set -euo pipefail SITE_URL='https://m2.ezweb.ro' SERVER_NAME='EzWeb METIN2' SLUG=ezweb-metin2 VERSION=2026.10.29.30 TARBALL=ezweb-metin2-2026.10.29.30-linux.tar.gz TARBALL_SHA256=fb441bb603667d47cae9821e0537c69125c22dffdc8643a3eeaaf601eea37545 TARBALL_SIZE=1370422357 ASSUME_YES=0; NO_DEPS=0; DESKTOP_ICON=1; UNINSTALL=0; REINSTALL=0 for arg in "$@"; do case $arg in -y|--yes) ASSUME_YES=1 ;; --no-deps) NO_DEPS=1 ;; --no-desktop-icon) DESKTOP_ICON=0 ;; --uninstall) UNINSTALL=1 ;; --reinstall) REINSTALL=1 ;; -h|--help) sed -n 2,19p "$0" 2>/dev/null || true; exit 0 ;; *) echo "optiune necunoscuta: $arg" >&2; exit 2 ;; esac done DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache} DEST=$DATA_HOME/$SLUG CACHE=$CACHE_HOME/$SLUG APPS=$DATA_HOME/applications ICONS=$DATA_HOME/icons/hicolor/48x48/apps say() { printf '\033[1;33m==>\033[0m %s\n' "$*"; } ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; } die() { printf '\033[1;31mEROARE:\033[0m %s\n' "$*" >&2; exit 1; } # When piped (curl | bash) stdin is the script itself: ask on the terminal. ask() { [ "$ASSUME_YES" = 1 ] && return 0 local reply if [ -r /dev/tty ] && { exec 3/dev/null; then printf '%s [D/n] ' "$1" > /dev/tty read -r reply <&3 || reply=n exec 3<&- case $reply in ''|d|D|y|Y|da|DA|yes) return 0 ;; *) return 1 ;; esac fi die "nu pot intreba (fara terminal). Ruleaza cu: bash -s -- --yes" } [ "$(id -u)" != 0 ] || die "nu rula ca root; ruleaza ca utilizatorul tau (sudo e folosit doar pentru pachete)" case $SLUG in ''|*/*|.*) die "nume de instalare invalid" ;; esac if [ "$UNINSTALL" = 1 ]; then [ -x "$DEST/uninstall.sh" ] || die "$SERVER_NAME nu este instalat in $DEST" exec "$DEST/uninstall.sh" fi say "$SERVER_NAME — instalare client $VERSION (Linux, prin Wine)" [ "$(uname -m)" = x86_64 ] || die "este necesar un PC x86_64 (detectat: $(uname -m))" for tool in curl tar sha256sum gzip; do command -v "$tool" >/dev/null || die "lipseste '$tool'"; done # ---------------------------------------------------------------- dependencies distro_id=unknown; distro_like="" if [ -r /etc/os-release ]; then # shellcheck source=/dev/null distro_id=$(. /etc/os-release; echo "${ID:-unknown}") distro_like=$(. /etc/os-release; echo "${ID_LIKE:-}") fi family=unknown case " $distro_id $distro_like " in *" arch "*|*" cachyos "*|*" manjaro "*|*" endeavouros "*) family=arch ;; *" fedora "*|*" rhel "*|*" nobara "*) family=fedora ;; *" debian "*|*" ubuntu "*) family=debian ;; *" opensuse"*|*" suse "*) family=suse ;; esac pkg_cmds() { # the exact commands we will run with sudo case $family in debian) echo "sudo dpkg --add-architecture i386" echo "sudo apt-get update" echo "sudo apt-get install -y --install-recommends wine wine64 wine32:i386 mesa-utils" ;; arch) echo "sudo pacman -S --needed --noconfirm wine mesa-utils" ;; fedora) echo "sudo dnf install -y wine glx-utils" ;; suse) echo "sudo zypper --non-interactive install wine Mesa-demo-x" ;; esac } need_wine=0 command -v wine >/dev/null && command -v wineserver >/dev/null || need_wine=1 if [ "$need_wine" = 1 ]; then [ "$NO_DEPS" = 0 ] || die "Wine lipseste si --no-deps a fost dat. Instaleaza Wine (>= 9) si ruleaza din nou." [ "$family" != unknown ] || die "distributie necunoscuta ($distro_id). Instaleaza Wine >= 9 (cu suport 32-bit) si glxinfo, apoi ruleaza din nou cu --no-deps." command -v sudo >/dev/null || die "lipseste sudo; instaleaza Wine ca root si ruleaza din nou cu --no-deps" say "Wine nu este instalat. Pe $distro_id voi rula:" pkg_cmds | sed 's/^/ /' ask "Instalez acum pachetele?" || die "anulat. Instaleaza Wine si ruleaza din nou." while IFS= read -r cmd; do say "$cmd" tty_in=/dev/null if [ -r /dev/tty ] && { : /dev/null; then tty_in=/dev/tty; fi # shellcheck disable=SC2086 $cmd <"$tty_in" || { [ "$family" = arch ] && echo "Sfat: pe Arch mai vechi, Wine e in [multilib]; activeaza-l in /etc/pacman.conf." >&2 die "comanda a esuat: $cmd" } done < <(pkg_cmds) command -v wine >/dev/null || die "Wine tot nu este disponibil dupa instalare" fi wine_version=$(wine --version 2>/dev/null || echo unknown) ok "$wine_version" major=$(printf '%s' "$wine_version" | sed -n 's/^wine-\([0-9]*\).*/\1/p') if [ -n "$major" ] && [ "$major" -lt 9 ]; then echo "atentie: $wine_version este vechi; recomandat Wine 9 sau mai nou (testat: 11.20)" >&2 fi command -v glxinfo >/dev/null || echo "atentie: glxinfo lipseste; lansatorul nu poate verifica daca placa video e folosita" >&2 # Launcher and uninstaller are embedded here (not only in the archive), so running # the installer again updates them without downloading the client again. write_scripts() { cat > "$DEST/launch.sh.new" <<'M2_EMBEDDED_LAUNCH_SH' #!/usr/bin/env bash # Lansator Linux pentru clientul Metin2 (Windows Client.exe prin Wine). # Instalat de install.sh in ~/.local/share//launch.sh. Nu e un client # Linux nativ: ruleaza acelasi Client.exe ca pe Windows, intr-un prefix Wine # propriu (/prefix). Nu modifica ~/.wine sau setari globale. # # launch.sh [--settings] [--desktop] [--allow-software] [--check] [--screen] # --settings fereastra "Setari grafice": ecran complet sau fereastra + marime # --desktop ruleaza intr-un desktop virtual Wine (vechiul mod; diagnostic) # --allow-software permite randare pe CPU (doar diagnostic; foarte lent) # --check doar verifica cerintele # --screen afiseaza rezolutia detectata a monitorului si alegerea curenta # Variabile: M2_DESKTOP=1 (la fel ca --desktop), WINEDLLOVERRIDES (adaugat). # # Alegerea de ecran se pastreaza in /display.conf; inainte de fiecare pornire # WIDTH/HEIGHT/WINDOWED din client/metin2.cfg sunt scrise conform ei (restul # setarilor raman neatinse). Fara alegere explicita, marimea implicita se calculeaza # din monitor (cea mai mare fereastra care incape, de ex. 1600x900 pe 1920x1080). set -uo pipefail DIR=$(cd "$(dirname "$(readlink -f "$0")")" && pwd) # shellcheck source=/dev/null . "$DIR/release.env" CLIENT=$DIR/client CFG=$CLIENT/metin2.cfg PREFIX=$DIR/prefix LOGDIR=$DIR/logs CONF=$DIR/display.conf DESKTOP=0; ALLOW_SOFTWARE=0; CHECK_ONLY=0; SETTINGS=0; SHOW_SCREEN=0 [ "${M2_DESKTOP:-0}" = 1 ] && DESKTOP=1 for arg in "$@"; do case $arg in --settings) SETTINGS=1 ;; --desktop) DESKTOP=1 ;; --no-desktop) DESKTOP=0 ;; # implicit acum; acceptat pentru compatibilitate --allow-software) ALLOW_SOFTWARE=1 ;; --check) CHECK_ONLY=1 ;; --screen) SHOW_SCREEN=1 ;; *) echo "optiune necunoscuta: $arg" >&2; exit 2 ;; esac done # Errors also reach players who started the game from the menu (no terminal). msg() { echo "$M2_SERVER_NAME: $*" >&2 if [ ! -t 2 ]; then if command -v kdialog >/dev/null; then kdialog --title "$M2_SERVER_NAME" --error "$*" 2>/dev/null & elif command -v zenity >/dev/null; then zenity --error --title="$M2_SERVER_NAME" --text="$*" 2>/dev/null & elif command -v notify-send >/dev/null; then notify-send "$M2_SERVER_NAME" "$*" 2>/dev/null fi fi } # ------------------------------------------------------------------ screen size # Prints "W H" of the primary monitor as the game will see it. Wine draws through # X11 (XWayland on a Wayland desktop), so the X view (xrandr) comes first; the # Wayland tools are fallbacks for when xrandr is missing. screen_size() { local out line if [ -n "${DISPLAY:-}" ] && command -v xrandr >/dev/null; then out=$(timeout 5 xrandr --current 2>/dev/null) line=$(printf '%s\n' "$out" | grep -m1 -E ' connected primary [0-9]+x[0-9]+\+') [ -n "$line" ] || line=$(printf '%s\n' "$out" | grep -m1 -E ' connected [0-9]+x[0-9]+\+') if [ -n "$line" ]; then printf '%s\n' "$line" | sed -E 's/.* ([0-9]+)x([0-9]+)\+.*/\1 \2/'; return 0 fi fi if [ -n "${WAYLAND_DISPLAY:-}" ] && command -v kscreen-doctor >/dev/null; then # Text output; the primary output has "priority 1". Geometry is in logical px. out=$(timeout 5 kscreen-doctor -o 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g') line=$(printf '%s\n' "$out" | awk ' /^Output:/ { if (geo != "" && en && first == "") first = geo; if (geo != "" && en && prio) best = geo; en = 0; prio = 0; geo = "" } /^[[:space:]]*enabled/ { en = 1 } /^[[:space:]]*priority 1$/ { prio = 1 } /Geometry:/ { geo = $NF } END { if (geo != "" && en && first == "") first = geo; if (geo != "" && en && prio) best = geo; print (best != "" ? best : first) }') if printf '%s' "$line" | grep -qE '^[0-9]+x[0-9]+$'; then echo "${line%x*} ${line#*x}"; return 0; fi fi if [ -n "${WAYLAND_DISPLAY:-}" ] && command -v wlr-randr >/dev/null; then line=$(timeout 5 wlr-randr 2>/dev/null | grep -m1 -E '^[[:space:]]+[0-9]+x[0-9]+ px.*current' | awk '{print $1}') if [ -n "$line" ]; then echo "${line%x*} ${line#*x}"; return 0; fi fi if [ -n "${DISPLAY:-}" ] && command -v xdpyinfo >/dev/null; then # Whole X screen (all monitors together): last resort. line=$(timeout 5 xdpyinfo 2>/dev/null | sed -n 's/^ *dimensions: *\([0-9]*\)x\([0-9]*\) pixels.*/\1 \2/p' | head -1) if [ -n "$line" ]; then echo "$line"; return 0; fi fi return 1 } # Window sizes offered; a windowed size must fit the screen with title bar + panel. SIZES="800x600 1024x768 1280x720 1280x800 1280x960 1366x768 1440x900 1600x900 1680x1050 1920x1080 2560x1080 2560x1440 3440x1440 3840x2160" WIN_MARGIN=100 fitting_sizes() { # $1 $2 = screen W H local s w h for s in $SIZES; do w=${s%x*}; h=${s#*x} [ "$w" -le "$1" ] && [ "$h" -le $(( $2 - WIN_MARGIN )) ] && echo "$s" done } # First start: the largest window that fits and has the monitor's shape (within 10 %), # at least 1280 wide (1600x900 on 1920x1080, 1920x1080 on 2560x1440). Small screens # (1366x768, 1280x800, ...) have no such window: there the default is fullscreen. default_choice() { # -> "mode W H" local scr best="" area=0 s w h d if scr=$(screen_size); then # shellcheck disable=SC2086 set -- $scr for s in $(fitting_sizes "$1" "$2"); do w=${s%x*}; h=${s#*x} d=$(( w * $2 - h * $1 )); [ "$d" -lt 0 ] && d=$(( -d )) [ "$w" -ge 1280 ] && [ $(( d * 10 )) -le $(( h * $1 )) ] || continue [ $(( w * h )) -gt "$area" ] && { area=$(( w * h )); best="$w $h"; } done if [ -n "$best" ]; then echo "windowed $best"; else echo "fullscreen $1 $2"; fi else echo "windowed 1280 720" fi } cfg_get() { sed -n "s/^$1[[:space:]]\{1,\}\([0-9.]*\).*/\1/p" "$CFG" 2>/dev/null | head -1; } cfg_set() { # KEY VALUE ... ; keeps CRLF, the other keys and their order local tmp=$CFG.tmp.$$ key val [ -f "$CFG" ] || : > "$CFG" cp -f "$CFG" "$tmp" || return 1 while [ $# -ge 2 ]; do key=$1; val=$2; shift 2 if grep -q "^${key}[[:space:]]" "$tmp"; then sed -i -E "s/^(${key}[[:space:]]+)[^[:space:]]*/\1$val/" "$tmp" else printf '%s\t\t\t\t%s\r\n' "$key" "$val" >> "$tmp" fi done mv -f "$tmp" "$CFG" } save_conf() { # mode W H chosen cat > "$CONF.tmp" </dev/null || M2_WIDTH=1280 [ "${M2_HEIGHT:-0}" -ge 600 ] 2>/dev/null || M2_HEIGHT=720 MODE=$M2_MODE; WIDTH=$M2_WIDTH; HEIGHT=$M2_HEIGHT } apply_cfg() { local windowed=1 [ "$MODE" = fullscreen ] && windowed=0 # Inside a Wine virtual desktop the client fills the desktop (no Wine-drawn title bar). [ "$DESKTOP" = 1 ] && windowed=0 cfg_set WIDTH "$WIDTH" HEIGHT "$HEIGHT" WINDOWED "$windowed" } # ------------------------------------------------------------------ settings UI settings_dialog() { local scr sw="" sh="" cur_mode cur_w cur_h s choice="" tags=() labels=() i if scr=$(screen_size); then set -- $scr; sw=$1; sh=$2; fi cur_mode=$(sed -n 's/^M2_MODE=\([a-z]*\)$/\1/p' "$CONF" 2>/dev/null) cur_w=$(cfg_get WIDTH); cur_h=$(cfg_get HEIGHT) if [ -n "$sw" ]; then local rec; rec=$(default_choice) tags+=(fullscreen); labels+=("Ecran complet — ${sw}x${sh} (rezoluția monitorului$([ "${rec%% *}" = fullscreen ] && echo ', recomandat'))") for s in $(fitting_sizes "$sw" "$sh"); do tags+=("$s"); labels+=("Fereastră ${s/x/ x }$([ "windowed ${s/x/ }" = "$rec" ] && echo ' (recomandat)')") done else tags+=(fullscreen); labels+=("Ecran complet (rezoluția monitorului)") for s in $SIZES; do tags+=("$s"); labels+=("Fereastră ${s/x/ x }"); done fi local current=fullscreen [ "$cur_mode" != fullscreen ] && current="${cur_w}x${cur_h}" local text="Alege cum pornește jocul.${sw:+ Monitor detectat: ${sw}x${sh}.}" if [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && command -v kdialog >/dev/null; then local args=() for i in "${!tags[@]}"; do args+=("${tags[$i]}" "${labels[$i]}" "$([ "${tags[$i]}" = "$current" ] && echo on || echo off)"); done choice=$(kdialog --title "$M2_SERVER_NAME — Setări grafice" --geometry 460x420 \ --radiolist "$text" "${args[@]}" 2>/dev/null) || return 1 elif [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && command -v zenity >/dev/null; then local args=() for i in "${!tags[@]}"; do args+=("$([ "${tags[$i]}" = "$current" ] && echo TRUE || echo FALSE)" "${tags[$i]}" "${labels[$i]}"); done choice=$(zenity --list --radiolist --title="$M2_SERVER_NAME — Setări grafice" --text="$text" \ --width=460 --height=460 --column="" --column=cod --column=Mod --hide-column=2 \ --print-column=2 "${args[@]}" 2>/dev/null) || return 1 elif [ -t 0 ]; then echo "$text" for i in "${!tags[@]}"; do printf ' %2d) %s%s\n' $(( i + 1 )) "${labels[$i]}" "$([ "${tags[$i]}" = "$current" ] && echo ' [actual]')"; done printf 'Numarul ales (Enter = anulare): ' read -r i || return 1 [ -n "$i" ] && [ "$i" -ge 1 ] 2>/dev/null && [ "$i" -le "${#tags[@]}" ] || return 1 choice=${tags[$(( i - 1 ))]} else msg "Nu am gasit kdialog sau zenity. Ruleaza intr-un terminal: $DIR/launch.sh --settings" return 1 fi choice=${choice//[$'\r\n ']/} case $choice in fullscreen) save_conf fullscreen "${sw:-$cur_w}" "${sh:-$cur_h}" 1 ;; [0-9]*x[0-9]*) save_conf windowed "${choice%x*}" "${choice#*x}" 1 ;; *) return 1 ;; esac resolve_display; apply_cfg local done_text="Salvat: " if [ "$MODE" = fullscreen ]; then done_text+="ecran complet ${WIDTH}x${HEIGHT}."; else done_text+="fereastră ${WIDTH}x${HEIGHT}."; fi echo "$done_text" if [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && command -v kdialog >/dev/null; then kdialog --title "$M2_SERVER_NAME" --yesno "$done_text Pornești jocul acum?" 2>/dev/null && return 0 elif [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && command -v zenity >/dev/null; then zenity --question --title="$M2_SERVER_NAME" --text="$done_text Pornești jocul acum?" 2>/dev/null && return 0 fi return 1 } if [ "$SHOW_SCREEN" = 1 ]; then if scr=$(screen_size); then echo "monitor: ${scr/ /x}"; else echo "monitor: nedetectat"; fi resolve_display echo "ales: $MODE ${WIDTH}x${HEIGHT} (display.conf: $(sed -n 's/^M2_CHOSEN=//p' "$CONF"))" echo "ferestre posibile: $( [ -n "${scr:-}" ] && fitting_sizes ${scr} | tr '\n' ' ')" exit 0 fi if [ "$SETTINGS" = 1 ]; then settings_dialog || exit 0 fi command -v wine >/dev/null || { msg "Wine nu este instalat. Ruleaza din nou comanda de instalare de pe site."; exit 1; } [ -f "$CLIENT/Client.exe" ] || { msg "Lipseste $CLIENT/Client.exe. Reinstaleaza jocul."; exit 1; } if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]; then msg "Nu exista o sesiune grafica (DISPLAY/WAYLAND_DISPLAY)."; exit 1 fi # Same renderer guard as tools/client-wine.sh: software GL is unplayable (15-23 FPS, # ~11 CPU cores in docs/LINUX_GPU.md), so refuse it unless asked for explicitly. if command -v glxinfo >/dev/null && [ -n "${DISPLAY:-}" ]; then if report=$(timeout 10 glxinfo -B 2>&1); then renderer=$(printf '%s\n' "$report" | sed -n 's/^OpenGL renderer string: //p') [ -n "$renderer" ] && echo "renderer OpenGL: $renderer" if printf '%s\n' "$report" | grep -qiE 'llvmpipe|lavapipe|softpipe|swrast|software rasterizer|Accelerated: no'; then if [ "$ALLOW_SOFTWARE" = 1 ]; then echo "atentie: randare pe CPU permisa explicit; jocul va fi lent" >&2 else msg "Placa video nu este folosita (randare software: ${renderer:-necunoscut}). Instaleaza driverul GPU (Mesa/NVIDIA) si pachetele 32/64-bit pentru OpenGL." exit 1 fi fi fi else echo "atentie: renderer neverificat (instaleaza mesa-utils / glx-utils pentru glxinfo)" >&2 fi export WINEPREFIX=$PREFIX export WINEDEBUG=${WINEDEBUG:--all} # Mono/Gecko are not needed by the client; skip their installer prompts. No # winemenubuilder: Wine must not add its own menu entries/file associations. export WINEDLLOVERRIDES="mscoree,mshtml=;winemenubuilder.exe=d${WINEDLLOVERRIDES:+;$WINEDLLOVERRIDES}" if [ ! -f "$PREFIX/system.reg" ]; then echo "creez prefixul Wine: $PREFIX" wineboot -i >/dev/null 2>&1 || { msg "wineboot a esuat (prefix $PREFIX)"; exit 1; } wineserver -w fi [ "$CHECK_ONLY" = 1 ] && { echo "ok: $(wine --version) prefix $PREFIX"; exit 0; } resolve_display apply_cfg echo "ecran: $MODE ${WIDTH}x${HEIGHT}$([ "$DESKTOP" = 1 ] && echo ' (desktop virtual Wine)')" mkdir -p "$LOGDIR" log=$LOGDIR/wine-$(date +%Y%m%d-%H%M%S).log ls -1t "$LOGDIR"/wine-*.log 2>/dev/null | tail -n +11 | xargs -r rm -f cd "$CLIENT" || exit 1 rm -f syserr.txt # The client requires Direct3D to list an 800x600x32 mode (GrpDevice.cpp # Find(800, 600, 32)). Wine 9+ always adds the standard modes (800x600 included) # even when the X server lists only the native one (checked under gamescope, whose # XWayland offers 1920x1080 and 1440x1080 only), so no virtual desktop is needed. # WINDOWED 0 = popup window of the monitor's size, which Wine/KWin show fullscreen # without a mode change. if [ "$DESKTOP" = 1 ]; then exe=$(winepath -w "$CLIENT/Client.exe" 2>/dev/null) wine explorer "/desktop=${M2_SLUG},${WIDTH}x${HEIGHT}" "$exe" >"$log" 2>&1 rc=$? else wine Client.exe >"$log" 2>&1 rc=$? fi wineserver -w if [ -s syserr.txt ]; then echo "--- syserr.txt ---" >&2 tail -n 20 syserr.txt >&2 fi exit $rc M2_EMBEDDED_LAUNCH_SH cat > "$DEST/uninstall.sh.new" <<'M2_EMBEDDED_UNINSTALL_SH' #!/usr/bin/env bash # Dezinstalare client: sterge doar ce a creat install.sh in directorul tau HOME # (jocul, prefixul Wine propriu, lansatorul, iconita, arhiva descarcata). # Pachetul Wine instalat de sistem ramane (dezinstaleaza-l manual daca vrei). set -euo pipefail DIR=$(cd "$(dirname "$(readlink -f "$0")")" && pwd) # shellcheck source=/dev/null . "$DIR/release.env" DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache} case $M2_SLUG in ''|*/*|.*) echo "release.env invalid" >&2; exit 1 ;; esac [ "$DIR" = "$DATA_HOME/$M2_SLUG" ] || { echo "refuz: $DIR nu este $DATA_HOME/$M2_SLUG" >&2; exit 1; } case $DIR in "$HOME"/*) ;; *) echo "refuz: $DIR nu este in $HOME" >&2; exit 1 ;; esac if [ "${1:-}" != --yes ] && [ -t 0 ]; then printf 'Dezinstalez %s din %s (inclusiv setarile si capturile de ecran)? [d/N] ' "$M2_SERVER_NAME" "$DIR" read -r reply case $reply in d|D|y|Y|da|yes) ;; *) echo "anulat"; exit 0 ;; esac fi if [ -d "$DIR/prefix" ] && command -v wineserver >/dev/null; then WINEPREFIX="$DIR/prefix" wineserver -k 2>/dev/null || true fi if [ -f "$DIR/desktop-icon.path" ]; then icon=$(cat "$DIR/desktop-icon.path") case $icon in "$HOME"/*"/$M2_SLUG.desktop") rm -f "$icon" ;; esac fi rm -f "$DATA_HOME/applications/$M2_SLUG.desktop" "$DATA_HOME/applications/$M2_SLUG-settings.desktop" "$DATA_HOME/icons/hicolor/48x48/apps/$M2_SLUG.png" command -v update-desktop-database >/dev/null && update-desktop-database -q "$DATA_HOME/applications" 2>/dev/null || true rm -rf "${CACHE_HOME:?}/$M2_SLUG" rm -rf "$DIR" echo "$M2_SERVER_NAME a fost dezinstalat." M2_EMBEDDED_UNINSTALL_SH chmod 755 "$DEST/launch.sh.new" "$DEST/uninstall.sh.new" mv -f "$DEST/launch.sh.new" "$DEST/launch.sh" mv -f "$DEST/uninstall.sh.new" "$DEST/uninstall.sh" } up_to_date=0 if [ "$REINSTALL" = 0 ] && [ -f "$DEST/client/Client.exe" ] && [ -f "$DEST/release.env" ] \ && grep -q "^M2_SLUG=$SLUG\$" "$DEST/release.env" \ && [ "$(cut -d' ' -f1 "$DEST/installed-archive.sha256" 2>/dev/null)" = "$TARBALL_SHA256" ]; then up_to_date=1 ok "clientul $VERSION este deja instalat in $DEST; actualizez doar lansatorul" fi # -------------------------------------------------------------------- download if [ "$up_to_date" = 0 ]; then mkdir -p "$CACHE" archive=$CACHE/$TARBALL check_sum() { [ -f "$1" ] && [ "$(sha256sum "$1" | cut -d' ' -f1)" = "$TARBALL_SHA256" ]; } if check_sum "$archive"; then ok "arhiva exista deja in $CACHE (SHA-256 corect)" else rm -f "$archive" avail=$(df -Pk "$CACHE" | awk 'NR==2 {print $4}') want=$(( TARBALL_SIZE / 1024 * 3 )) [ "${avail:-0}" -gt "$want" ] || die "spatiu insuficient: ~$(( want / 1048576 + 1 )) GiB necesari in $CACHE_HOME si $DATA_HOME" say "descarc $TARBALL ($(( TARBALL_SIZE / 1048576 )) MiB)" curl -fL --retry 3 --retry-delay 2 -C - -o "$archive.part" "$SITE_URL/downloads/$TARBALL" \ || curl -fL --retry 3 -o "$archive.part" "$SITE_URL/downloads/$TARBALL" \ || die "descarcarea a esuat" mv -f "$archive.part" "$archive" check_sum "$archive" || { rm -f "$archive"; die "SHA-256 gresit pentru $TARBALL (descarcare corupta); ruleaza din nou"; } ok "SHA-256 verificat: $TARBALL_SHA256" fi # ---------------------------------------------------------------------- install mkdir -p "$DEST" if [ -f "$DEST/release.env" ] && ! grep -q "^M2_SLUG=$SLUG\$" "$DEST/release.env"; then die "$DEST contine alta instalare; nu o suprascriu" fi staging=$DEST/.staging rm -rf "$staging"; mkdir -p "$staging" say "dezarhivez in $DEST" tar -xzf "$archive" -C "$staging" top=$staging/$SLUG [ -f "$top/release.env" ] && [ -f "$top/client/Client.exe" ] || die "arhiva are un continut neasteptat" if [ -d "$DEST/client" ]; then # Keep the player's own settings and screenshots across updates. for keep in metin2.cfg mouse.cfg channel.inf; do [ -f "$DEST/client/$keep" ] && cp -f "$DEST/client/$keep" "$top/client/$keep" done if [ -d "$DEST/client/screenshot" ]; then cp -a "$DEST/client/screenshot/." "$top/client/screenshot/" 2>/dev/null || true fi mv "$DEST/client" "$DEST/client.old" fi mv "$top/client" "$DEST/client" for f in launch.sh release.env icon.png uninstall.sh; do mv -f "$top/$f" "$DEST/$f"; done rm -rf "$DEST/client.old" "$staging" printf '%s %s\n' "$TARBALL_SHA256" "$TARBALL" > "$DEST/installed-archive.sha256" ok "client $VERSION instalat in $DEST/client" fi # up_to_date = 0 write_scripts ok "lansator: $DEST/launch.sh (setari grafice: $DEST/launch.sh --settings)" if [ ! -f "$DEST/prefix/system.reg" ]; then say "creez prefixul Wine propriu ($DEST/prefix) — dureaza putin la prima instalare" env WINEPREFIX="$DEST/prefix" WINEDEBUG=-all WINEDLLOVERRIDES="mscoree,mshtml=;winemenubuilder.exe=d" wineboot -i >/dev/null 2>&1 \ || die "wineboot a esuat" WINEPREFIX="$DEST/prefix" wineserver -w || true fi ok "prefix Wine: $DEST/prefix" # --------------------------------------------------------------------- launcher mkdir -p "$APPS" "$ICONS" cp -f "$DEST/icon.png" "$ICONS/$SLUG.png" desktop_file=$APPS/$SLUG.desktop cat > "$desktop_file" < "$APPS/$SLUG-settings.desktop" </dev/null && update-desktop-database -q "$APPS" 2>/dev/null || true ok "lansator in meniu: $desktop_file (+ \"$SERVER_NAME — Setări grafice\")" desktop_dir="" if command -v xdg-user-dir >/dev/null; then desktop_dir=$(xdg-user-dir DESKTOP 2>/dev/null || true); fi [ -n "$desktop_dir" ] || desktop_dir=$HOME/Desktop if [ "$DESKTOP_ICON" = 1 ] && [ -d "$desktop_dir" ] && [ "$desktop_dir" != "$HOME" ]; then install -m 755 "$desktop_file" "$desktop_dir/$SLUG.desktop" command -v gio >/dev/null && gio set "$desktop_dir/$SLUG.desktop" metadata::trusted true 2>/dev/null || true printf '%s\n' "$desktop_dir/$SLUG.desktop" > "$DEST/desktop-icon.path" ok "scurtatura pe desktop: $desktop_dir/$SLUG.desktop" fi echo ok "Gata! Porneste \"$SERVER_NAME\" din meniul de aplicatii sau cu: $DEST/launch.sh" echo " Rezolutie / ecran complet: \"$SERVER_NAME — Setări grafice\" din meniu (sau clic dreapta pe joc)." echo " Actualizare: ruleaza din nou aceeasi comanda. Dezinstalare: $DEST/uninstall.sh"