X-Git-Url: https://vilimpoc.org/repos/bl2-split/blobdiff_plain/cb05cf8e0e0cab3cbd1b77f233aaedbd30ecffa2..5765b872626e0ee9da7d0311906bcd91429b9e69:/bl2-splitscreen.sh diff --git a/bl2-splitscreen.sh b/bl2-splitscreen.sh index 8406345..ed4e040 100755 --- a/bl2-splitscreen.sh +++ b/bl2-splitscreen.sh @@ -107,11 +107,47 @@ detect_mode() { MODE="$(detect_mode)" [ "$MODE" = unknown ] && die "Cannot find BL2 - neither $NATIVE_BIN nor $WIN_EXE_REL under $GAME_DIR" if [ -z "$ISOLATION" ]; then ISOLATION=bwrap; fi # device-masking works for both modes -GOLDBERG_SO="$BASE/goldberg/libsteam_api.so" # native -GOLDBERG_DLL="$BASE/goldberg/steam_api.dll" # proton +GOLDBERG_SO="$BASE/goldberg/libsteam_api.so" # native (classic Mr_Goldberg) +GOLDBERG_DLL="$BASE/goldberg/steam_api.dll" # proton (gbe_fork) + +# Goldberg emulator downloads (NOT committed to git - fetched by `fetch`). Pinned +# to known-good releases; override via env if these ever move. +GBE_FORK_TAG="${GBE_FORK_TAG:-release-2026_05_30}" +GBE_FORK_WIN_URL="${GBE_FORK_WIN_URL:-https://github.com/Detanup01/gbe_fork/releases/download/$GBE_FORK_TAG/emu-win-release.7z}" +CLASSIC_GOLDBERG_URL="${CLASSIC_GOLDBERG_URL:-https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/2524331e488ec6399c396cf48bbe9903/Goldberg_Lan_Steam_Emu_v0.2.5.zip}" resolve_pad() { local m; for m in $1; do [ -e "$m" ] && { readlink -f "$m"; return; }; done; } +# --------------------------------------------------------------- fetch deps - +# Download the Goldberg emulator binaries (kept out of git). Proton needs the +# gbe_fork Windows steam_api.dll; native needs the classic Mr_Goldberg Linux .so. +cmd_fetch() { # downloads the Goldberg binary the detected MODE needs + command -v bsdtar >/dev/null || die "bsdtar missing (sudo pacman -S libarchive)" + command -v curl >/dev/null || die "curl missing (sudo pacman -S curl)" + mkdir -p "$BASE/goldberg" + local tmp; tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' RETURN + + if [ "$MODE" = proton ]; then + if [ -f "$GOLDBERG_DLL" ]; then ok "gbe_fork steam_api.dll already present"; return; fi + say "Downloading gbe_fork (Windows) $GBE_FORK_TAG ..." + curl -fsSL -o "$tmp/win.7z" "$GBE_FORK_WIN_URL" || die "download failed: $GBE_FORK_WIN_URL" + bsdtar -xf "$tmp/win.7z" -C "$tmp" release/experimental/x86/steam_api.dll \ + || die "could not extract steam_api.dll from the archive" + cp "$tmp/release/experimental/x86/steam_api.dll" "$GOLDBERG_DLL" + file "$GOLDBERG_DLL" | grep -qi "PE32 .*i386" && ok "steam_api.dll (proton) fetched" \ + || die "fetched steam_api.dll is not the expected 32-bit PE" + else + if [ -f "$GOLDBERG_SO" ]; then ok "classic goldberg libsteam_api.so already present"; return; fi + say "Downloading classic Mr_Goldberg (Linux) ..." + curl -fsSL -o "$tmp/lin.zip" "$CLASSIC_GOLDBERG_URL" || die "download failed: $CLASSIC_GOLDBERG_URL" + bsdtar -xf "$tmp/lin.zip" -C "$tmp" linux/x86/libsteam_api.so \ + || die "could not extract libsteam_api.so from the archive" + cp "$tmp/linux/x86/libsteam_api.so" "$GOLDBERG_SO" + file "$GOLDBERG_SO" | grep -q "ELF 32-bit" && ok "libsteam_api.so (native) fetched" \ + || die "fetched libsteam_api.so is not the expected 32-bit ELF" + fi +} + # ------------------------------------------------------------------ check --- cmd_check() { local fail=0 @@ -175,6 +211,13 @@ EOF # other via UDP broadcast, so co-op hangs at "Creating online session". Tell the # emu to send discovery directly to localhost so they always find each other. printf '127.0.0.1\n' > "$ss/custom_broadcasts.txt" + # BL2 stalls on "Creating online session" waiting on (unreachable) online Steam + # servers; offline mode makes the emu report Steam offline so it skips that wait + # and goes to LAN. LAN co-op still works. Remove this file if it ever breaks join. + cat > "$ss/configs.main.ini" < sets LAUNCHED_PID else wd="$pg/Binaries/Win32" patch_proton_config "$BASE/p$n/prefix" # skip intro movies (if prefix exists) - env=( "WINEPREFIX=$BASE/p$n/prefix" "GAMEID=0" "STORE=none" ) + mkdir -p "$BASE/dxvk-cache" + env=( "WINEPREFIX=$BASE/p$n/prefix" "GAMEID=0" "STORE=none" + "UMU_RUNTIME_UPDATE=0" # skip umu's startup update check + # Shared D3D shader cache: the 2nd instance reuses shaders the 1st already + # compiled instead of recompiling them while the 1st is using the GPU. + "DXVK_STATE_CACHE_PATH=$BASE/dxvk-cache" ) # Only use the SDL VID/PID filter in "sdl" mode. With bwrap masking it is # redundant AND can over-filter (it wrongly dropped the 360 pad), so skip it. [ "$ISOLATION" = sdl ] && env+=( "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT=$vidpid" ) @@ -390,18 +439,47 @@ cmd_run() { say "${c_dim}Windows settle onto $where ~${SPLASH_WAIT}s in (after the splash)." say "Re-place any time with: ./bl2-splitscreen.sh tile${c_rst}" say ""; say "Press Ctrl-C to kill both instances." - trap 'kill "$pid1" "$pid2" 2>/dev/null' INT TERM + # Killing $pid1/$pid2 only hits the top of each tree; the game runs deeper inside + # umu/pressure-vessel and survives. kill_instances tears down the whole tree. + trap 'echo; kill_instances; exit 130' INT TERM wait "$pid1" "$pid2" } +# ------------------------------------------------------------------ kill ---- +# Terminate every Borderlands 2 process we launched (game exe + umu/Proton/Wine +# tree for our prefixes), SIGTERM first then SIGKILL. Scoped to our BASE dir so it +# won't touch unrelated Wine apps. +kill_instances() { + local sig + for sig in TERM KILL; do + pkill -"$sig" -f 'Borderlands2\.exe' 2>/dev/null # proton game + pkill -"$sig" -x 'Borderlands2' 2>/dev/null # native game + pkill -"$sig" -f "$BASE/p[12]/" 2>/dev/null # umu/proton/wine for our copies+prefixes + [ "$sig" = TERM ] && sleep 2 + done +} + +cmd_kill() { + # Match the process NAME (comm) as a substring, not the full cmdline - otherwise + # helper shells that merely have the game path in their args get counted. This + # matches "Borderlands2.ex" (Proton, 15-char truncated) and "Borderlands2". + pgrep 'Borderlands2' >/dev/null || { ok "No Borderlands 2 instances running."; return 0; } + say "Killing Borderlands 2 instances..." + kill_instances + local left; left=$(pgrep -c 'Borderlands2' 2>/dev/null || echo 0) + [ "$left" = 0 ] && ok "All instances killed." || warn "$left process(es) still lingering." +} + # ------------------------------------------------------------------ clean --- -cmd_clean() { say "Removing generated copies/saves (real game untouched)..."; rm -rf "$BASE/p1" "$BASE/p2"; ok "Done."; } +cmd_clean() { cmd_kill; say "Removing generated copies/saves (real game untouched)..."; rm -rf "$BASE/p1" "$BASE/p2"; ok "Done."; } case "${1:-}" in + fetch) cmd_fetch ;; check) cmd_check ;; setup) cmd_setup ;; run) cmd_run ;; tile) cmd_tile ;; + kill) cmd_kill ;; clean) cmd_clean ;; - *) say "Usage: $0 {check|setup|run|tile|clean} (MODE=native|proton to force)"; exit 1 ;; + *) say "Usage: $0 {fetch|check|setup|run|tile|kill|clean} (MODE=native|proton to force)"; exit 1 ;; esac