{ config, pkgs, ... }: let dayzDir = "${config.home.homeDirectory}/.local/share/Steam/steamapps/compatdata/221100/pfx/drive_c/users/steamuser/Documents/DayZ"; in { home.packages = [ (pkgs.writeShellApplication { name = "meowz"; runtimeInputs = with pkgs; [ curl jq fzf steamcmd ]; excludeShellChecks = [ "SC2155" "SC2162" "SC2207" "SC2086" "SC2178" "SC2128" ]; text = '' config="$HOME/.config/dayz" favfile="$config/favorites" logfile="$config/last_launch.log" steamapps="$HOME/.local/share/Steam/steamapps" workshop="$steamapps/workshop/content/221100" d=$'\x1f' mkdir -p "$config" touch "$favfile" sed -i '/^[[:space:]]*$/d' "$favfile" awk -F"$d" 'NF==4' "$favfile" > "$favfile.tmp" && mv "$favfile.tmp" "$favfile" log() { printf '%s\n' "$*"; } err() { printf '%s\n' "$*" >&2; } api() { curl -sg -A "mozilla" "https://api.battlemetrics.com$1" } fetch_servers() { local path="/servers?filter[game]=dayz&page[size]=100$1" local pages=0 while [ -n "$path" ] && [ "$pages" -lt 4 ]; do local resp=$(api "$path") echo "$resp" | jq -e . >/dev/null 2>&1 || break echo "$resp" | jq -r '.data[]? | "\(.id)|\(.attributes.ip):\(.attributes.port)|\(.attributes.name) [\(.attributes.players)/\(.attributes.maxPlayers)]"' path=$(echo "$resp" | jq -r '.links.next // empty' | sed 's|.*battlemetrics.com||') pages=$((pages + 1)) done } if [ "''${1:-}" = "--fetch" ]; then fetch_servers "''${2:+&filter[search]=$2}" exit 0 fi steamuser="" [ -f "$config/steamuser" ] && steamuser=$(cat "$config/steamuser") if [ -z "$steamuser" ]; then read -p "steam username: " steamuser echo "$steamuser" > "$config/steamuser" fi steampass="" [ -f "$config/steampass" ] && steampass=$(cat "$config/steampass") if [ -z "$steampass" ]; then read -rsp "steam password (stored in plaintext, blank to skip caching): " steampass echo if [ -n "$steampass" ]; then (umask 177; echo "$steampass" > "$config/steampass") fi fi loginmarker="$config/steam_authenticated" get_mods_by_id() { api "/servers/$1" | jq -r '.data?.attributes?.details?.modIds[]? // empty' | paste -sd, - } get_mods() { local ip="$1" port="$2" local m=$(curl -s "https://dayzsalauncher.com/api/v1/server/$ip:$port" | jq -r '.result?.mods[]?.steamWorkshopId // empty' | paste -sd, -) if [ -z "$m" ]; then local id=$(api "/servers?filter[game]=dayz&filter[search]=$ip:$port" | jq -r '.data[0]?.id // empty') [ -n "$id" ] && m=$(get_mods_by_id "$id") fi echo "$m" } download_mods() { local mods=($(echo "$1" | tr ',' '\n')) [ "''${#mods[@]}" -eq 0 ] && return 0 touch "$config/blacklist" local missing=() for mod in "''${mods[@]}"; do grep -qx "$mod" "$config/blacklist" && continue find "$workshop/$mod" -mindepth 1 2>/dev/null | grep -q . || missing+=("$mod") done [ "''${#missing[@]}" -eq 0 ] && { log "mods already present"; return 0; } local runner="steamcmd" command -v steamcmd >/dev/null 2>&1 || { err "steamcmd not found"; return 1; } mods=("''${missing[@]}") local cmds="" for mod in "''${mods[@]}"; do cmds="$cmds +workshop_download_item 221100 $mod" done if pgrep -x steam >/dev/null; then log "closing steam..." steam -shutdown while pgrep -x steam >/dev/null; do sleep 1; done fi log "downloading mods..." local attempt=1 while [ "$attempt" -le 4 ]; do [ "$attempt" -gt 1 ] && log "retry $attempt/4, ''${#mods[@]} mods" local out local login_args=("$steamuser") if [ ! -f "$loginmarker" ] && [ -n "$steampass" ]; then login_args=("$steamuser" "$steampass") fi out=$($runner +@sSteamCmdForcePlatformType linux -forceipv4 +login "''${login_args[@]}" $cmds +quit | tee /dev/tty) || true if echo "$out" | grep -q "Waiting for user info"; then touch "$loginmarker" fi if echo "$out" | grep -q "FAILED"; then rm -f "$loginmarker" fi if echo "$out" | grep -q "Invalid Password"; then err "wrong password, clearing cache" rm -f "$config/steampass" steampass="" fi local failed=() for mod in "''${mods[@]}"; do if echo "$out" | grep -q "Success. Downloaded item $mod "; then continue fi if echo "$out" | grep -qE "Download item $mod failed \((Access Denied|No match|File Not Found)\)"; then err "mod $mod unavailable, blacklisting" grep -qx "$mod" "$config/blacklist" || echo "$mod" >> "$config/blacklist" continue fi failed+=("$mod") done [ "''${#failed[@]}" -eq 0 ] && { log "mods downloaded"; return 0; } mods=("''${failed[@]}") cmds="" for mod in "''${mods[@]}"; do cmds="$cmds +workshop_download_item 221100 $mod" done attempt=$((attempt + 1)) sleep 3 done err "failed to download: ''${mods[*]}" return 1 } launch() { local ip="$1" port="$2" mods="$3" mod_arg="" if [ -z "$ip" ] || [ -z "$port" ]; then err "invalid ip/port" return 1 fi log "verifying server..." local verify verify=$(api "/servers?filter[game]=dayz&filter[search]=$ip:$port" | jq -r --arg ip "$ip" --arg port "$port" '.data[]? | select(.attributes.ip == $ip and (.attributes.port | tostring) == $port) | "\(.attributes.name) [\(.attributes.players)/\(.attributes.maxPlayers)]"' | head -n 1) if [ -n "$verify" ]; then log "found: $verify" else err "server not found on battlemetrics, connecting anyway" fi if [ -z "$mods" ]; then log "no mod list found, may still require mods" else log "downloading mods..." download_mods "$mods" || err "some mods failed, launching anyway" for m in $(echo "$mods" | tr ',' '\n'); do [ -z "$m" ] && continue local folder=$(find "$workshop/$m" -maxdepth 1 -type d -name "@*" 2>/dev/null | head -n 1) [ -z "$folder" ] && folder="$workshop/$m" find "$folder" -iname "*.pbo" 2>/dev/null | grep -q . || { err "missing pbo for $m, skipping"; continue; } mod_arg="$mod_arg;z:''${folder//\//\\}" done mod_arg="''${mod_arg#;}" [ -n "$mod_arg" ] && mod_arg="-mod=$mod_arg" sleep 3 fi echo "$(date '+%F %T') connect=$ip:$port $mod_arg" >> "$logfile" if ! pgrep -x steam >/dev/null; then log "starting steam..." if [ ! -f "$loginmarker" ]; then if [ -n "$steampass" ]; then steam -login "$steamuser" "$steampass" >>"$config/steam.log" 2>&1 & disown else steam -login "$steamuser" >>"$config/steam.log" 2>&1 & disown fi else steam >>"$config/steam.log" 2>&1 & disown fi while ! pgrep -x steam >/dev/null; do sleep 1; done sleep 5 fi log "connecting to $ip:$port..." if [ -n "$mod_arg" ]; then steam -applaunch 221100 -noLauncher "-connect=$ip" "-port=$port" "$mod_arg" >>"$config/steam.log" 2>&1 else steam -applaunch 221100 -noLauncher "-connect=$ip" "-port=$port" >>"$config/steam.log" 2>&1 fi } pick_server() { fzf --prompt="server> " --delimiter="|" --with-nth=3.. --layout=reverse \ --disabled --ansi \ --bind "start:reload:$0 --fetch" \ --bind "change:reload:sleep 0.4; $0 --fetch {q}" } save_favorite() { local ip="$1" port="$2" name="$3" if grep -qF "$ip$d$port$d" "$favfile"; then err "already saved" return fi local mods=$(get_mods "$ip" "$port") printf "%s%s%s%s%s%s%s\n" "$ip" "$d" "$port" "$d" "$mods" "$d" "$name" >> "$favfile" log "saved $name" } show_favorites() { grep -vE '^[[:space:]]*$' "$favfile" | fzf --prompt="fav> " --delimiter="$d" --with-nth=4 --layout=reverse } while true; do action=$(printf "favorites\nbrowse\nadd manual\nremove\nrefresh mods\nquit\n" | fzf --prompt="dayz> " --layout=reverse) case "$action" in browse) selection=$(pick_server) [ -z "$selection" ] && continue id=$(echo "$selection" | cut -d'|' -f1) ip_port=$(echo "$selection" | cut -d'|' -f2) name=$(echo "$selection" | cut -d'|' -f3- | sed 's/ \[[0-9]*\/[0-9]*\]$//') ip=''${ip_port%%:*} port=''${ip_port##*:} confirm=$(printf "play\nsave to favorites\n" | fzf --prompt="$name> " --layout=reverse) case "$confirm" in play) mods=$(get_mods_by_id "$id"); launch "$ip" "$port" "$mods"; exit 0 ;; "save to favorites") save_favorite "$ip" "$port" "$name"; show_favorites ;; esac ;; favorites) selection=$(show_favorites) [ -z "$selection" ] && continue ip=$(echo "$selection" | cut -d"$d" -f1) port=$(echo "$selection" | cut -d"$d" -f2) mods=$(echo "$selection" | cut -d"$d" -f3) launch "$ip" "$port" "$mods" exit 0 ;; "add manual") read -p "ip: " ip read -p "port: " port read -p "name: " name save_favorite "$ip" "$port" "$name" show_favorites ;; remove) selection=$(show_favorites) [ -z "$selection" ] && continue grep -vF "$selection" "$favfile" | grep -vE '^[[:space:]]*$' > "$favfile.tmp" && mv "$favfile.tmp" "$favfile" show_favorites ;; "refresh mods") selection=$(show_favorites) [ -z "$selection" ] && continue ip=$(echo "$selection" | cut -d"$d" -f1) port=$(echo "$selection" | cut -d"$d" -f2) name=$(echo "$selection" | cut -d"$d" -f4) mods=$(get_mods "$ip" "$port") grep -vF "$selection" "$favfile" | grep -vE '^[[:space:]]*$' > "$favfile.tmp" && mv "$favfile.tmp" "$favfile" printf "%s%s%s%s%s%s%s\n" "$ip" "$d" "$port" "$d" "$mods" "$d" "$name" >> "$favfile" show_favorites ;; quit|"") break ;; esac done ''; }) ]; xdg.desktopEntries.meowz = { name = "MeowZ"; comment = "dayz server launcher"; exec = "kitty --hold -e meowz"; icon = "applications-games"; terminal = false; categories = [ "Game" ]; }; home.file."${dayzDir}/DayZ.cfg".text = '' language="English"; adapter=-1; 3D_Performance=57692; Resolution_Bpp=32; WinX=0; WinY=0; WindowWidth=1920; WindowHeight=1080; MSAA=0; PostFX=0; VSync=1; FXAO=0; AToC=0; AnisoFilter=4; TerrainDetail=2; FXAA=0; refreshMode=200; maxGPUToRenderFrames=1; ''; home.file."${dayzDir}/steamuser_settings.DayZProfile".text = '' version=1; blood=1; singleVoice=0; gamma=1; lastMPServer=""; lastMPServerName=""; lastMPMission=""; inputVersion=1; perspective=1; trackIR=1; freeTrack=1; mouseSmoothing=0; maxSamplesPlayed=96; vonInputMode=0; TexQuality=1; WaterQuality=1; tripleHead=0; showTitles=1; vehicleFreelook=0; pauseMode=0; shadowQuality=1; headBob=0; fov=0.95993; sceneComplexity=150000; shadowZDistance=80; viewDistance=1200; preferredObjectViewDistance=1200; terrainGrid=12.5; volumeMaster=5; volumeCD=0.5; volumeFX=4; volumeSpeech=5; volumeVoN=10; vonRecThreshold=0.029999999; brightness=1; uiTopLeftX=0.12500001; uiTopLeftY=0; uiBottomRightX=0.875; uiBottomRightY=1; IGUIScale=0.55000001; showHUD=1; hudBrightness=1.0; showCrosshair=0; showQuickbar=1; showVehicleHUD=1; showServerInfo=0; ''; home.file."${dayzDir}/steamuser.dayz_preset_User.xml".text = '' ''; }