mirror of
https://github.com/aclist/dztui.git
synced 2026-11-07 10:30:40 +01:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84c75173a0 | ||
|
|
7528ab6fa3 | ||
|
|
fa43459a78 | ||
|
|
252f07cbec | ||
|
|
a0f610ead7 | ||
|
|
ca4bcfc1a5 | ||
|
|
ac4892d4f0 | ||
|
|
240b660923 | ||
|
|
150dfbca72 | ||
|
|
4b78114174 | ||
|
|
d1bde9901d | ||
|
|
c5eba364d9 | ||
|
|
5460b53c26 | ||
|
|
99301b3312 | ||
|
|
8d30af9dee | ||
|
|
8a8109b7df | ||
|
|
e4eaf25c8f | ||
|
|
0daf6089b9 |
No files matched your search
@@ -8,7 +8,7 @@ jobs:
|
||||
mirror-to-codeberg:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: pixta-dev/repository-mirroring-action@v1
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
tags
|
||||
__pycache__
|
||||
pre-commit
|
||||
tests/
|
||||
+30
-3
@@ -1,5 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## [6.0.6-beta.1] 2026-05-17
|
||||
## Changed
|
||||
- Update symlink hash logic to align with DZGUI 7 beta
|
||||
- Update A2S helper to latest commit
|
||||
|
||||
## [6.0.5-beta.2] 2026-04-17
|
||||
## Changed
|
||||
- During server connection attempts, only download mods local to specific server
|
||||
|
||||
## [6.0.5-beta.1] 2026-04-13
|
||||
## Fixed
|
||||
- Update A2S module checksums to support servers with missing description field
|
||||
## Changed
|
||||
- Bump version to reflect hotfixes on stable branch
|
||||
|
||||
## [6.0.2-beta.5] 2026-04-12
|
||||
## Fixed
|
||||
- Use conditional logic when testing GLibUnix namespaces
|
||||
|
||||
## [6.0.2-beta.4] 2026-04-10
|
||||
## Fixed
|
||||
- Use GLibUnix namespace when setting up SIGINT handling
|
||||
|
||||
## [6.0.2-beta.3] 2026-01-26
|
||||
## Fixed
|
||||
- Raise error correctly from module
|
||||
|
||||
## [6.0.2-beta.2] 2026-01-18
|
||||
## Fixed
|
||||
- Helpers not being downloaded (uncommented boilerplate)
|
||||
@@ -589,7 +616,7 @@ As part of this change, version 4.0.0 introduces the ability to connect to or ad
|
||||
|
||||
If you encounter any problems with this new release or with the migration of configs, please do not hesitate to submit a bug report.
|
||||
|
||||
Attention Fedora 38 users: problems with upstream GNOME packages causing crashes have been reported to GNOME development and a fix has been issued. You have the choice of compiling the zenity package
|
||||
Attention Fedora 38 users: problems with upstream GNOME packages causing crashes have been reported to GNOME development and a fix has been issued. You have the choice of compiling the zenity package
|
||||
from source or waiting until the latest version is merged into Fedora's package manager.
|
||||
|
||||
### Added
|
||||
@@ -614,7 +641,7 @@ from source or waiting until the latest version is merged into Fedora's package
|
||||
- Store complete IP:Port instead of server IDs
|
||||
- Make Battlemetrics API key optional: this is only used for the 'Connect by ID' and 'Add server by ID' methods and is not required. If you prefer, you can simply connect/add by IP.
|
||||
- Prevent the application from launching in Game Mode on Steam Deck: Steam Deck's kiosk mode has problems sending keyboard input to third party applications. To prevent unintended usage, DZGUI now warns the user to launch the app in Desktop Mode if they attempt to use it from Game Mode. Adding DZGUI as a Non-Steam Game does work on desktop PCs, but is not recommended due to the way Steam handles subshells. For best results, launch DZGUI directly via the script/applications menu (PC) or via the desktop icon (Steam Deck).
|
||||
- Omit null servers from list: servers that time out or send an empty response are now omitted entirely from the My Servers list, as they will not return meaningful metadata unless they are online.
|
||||
- Omit null servers from list: servers that time out or send an empty response are now omitted entirely from the My Servers list, as they will not return meaningful metadata unless they are online.
|
||||
The My Servers list thus shows online and accessible servers
|
||||
|
||||
## [3.3.0] 2023-05-16
|
||||
@@ -633,7 +660,7 @@ from source or waiting until the latest version is merged into Fedora's package
|
||||
- First-time setup: sudo escalation when checking system map count for the first time
|
||||
|
||||
### Fixed
|
||||
- Steam Deck: non-ASCII delimiter causing setup menu to despawn on some devices
|
||||
- Steam Deck: non-ASCII delimiter causing setup menu to despawn on some devices
|
||||
- Don't add items in My Servers multiple times to array when the list of favorites is paginated
|
||||
- Trigger progress dialogs sooner and in sequence to reduce appearance of visual lag
|
||||
- First-time setup: break out of dialogs correctly when user backs out
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ If you are a developer, you can skip to the end.
|
||||
# Requesting help
|
||||
|
||||
If you encounter a problem with DZGUI, you can submit tickets on the GitHub
|
||||
(issue tracker)[https://github.com/aclist/dzgui-devel/issues] under the
|
||||
[issue tracker](https://github.com/aclist/dztui/issues) under the
|
||||
`troubleshooting` tag.
|
||||
|
||||
# How can I help the project?
|
||||
@@ -24,7 +24,7 @@ accordingly.
|
||||
|
||||
## Submitting a ticket
|
||||
|
||||
Navigate to the GitHub (issue tracker)[https://github.com/aclist/dzgui-devel/issues].
|
||||
Navigate to the GitHub [issue tracker](https://github.com/aclist/dztui/issues).
|
||||
From there, follow the onscreen prompts. You will be asked questions such as:
|
||||
|
||||
- What version are you using?
|
||||
@@ -67,5 +67,5 @@ will be merged to `prerelease/<latest-version>`, then `testing`, and after a liv
|
||||
testing phase, to `stable` as well.
|
||||
|
||||
It is recommended to follow
|
||||
(Conventional Commits)[https://www.conventionalcommits.org/en/v1.0.0/], as this
|
||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), as this
|
||||
integrates well with tooling and helps the project review your code.
|
||||
@@ -11,6 +11,15 @@ This overcomes certain limitations in the Linux client and helps prepare the gam
|
||||
- Concatenate launch options to pass to Steam
|
||||
- Connect to mod-enabled LAN servers
|
||||
|
||||
## Design philosophy
|
||||
|
||||
DZGUI follows a specific set of guidelines, enumerated below:
|
||||
|
||||
- Provide a free and open-source tool under GPL
|
||||
- Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server
|
||||
- All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints
|
||||
- Target Linux desktop and Steam deck specifically; not a cross-platform tool
|
||||
|
||||
## Setup and usage
|
||||
|
||||
Refer to the documentation for installation and setup instructions:
|
||||
@@ -18,7 +27,6 @@ Refer to the documentation for installation and setup instructions:
|
||||
- [GitHub](https://aclist.github.io/dzgui/index.html)
|
||||
- [Mirror (Codeberg)](https://aclist.codeberg.page)
|
||||
|
||||
|
||||

|
||||
|
||||
## Attribution
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -o pipefail
|
||||
|
||||
src_path="$(readlink -e "$0")"
|
||||
version=6.0.2-beta.2
|
||||
version=6.0.6-beta.1
|
||||
|
||||
#CONSTANTS
|
||||
aid=221100
|
||||
@@ -386,7 +386,7 @@ prompt_dl(){
|
||||
Your branch: $branch
|
||||
Your version: $version
|
||||
Upstream version: $upstream
|
||||
|
||||
|
||||
Version updates introduce important bug fixes and are encouraged. Attempt to download the latest version?
|
||||
EOF
|
||||
}
|
||||
@@ -567,9 +567,12 @@ get_hash(){
|
||||
md5sum "$1" | awk '{print $1}'
|
||||
}
|
||||
fetch_a2s(){
|
||||
# this file is currently monolithic
|
||||
[[ -d $helpers_path/a2s ]] && { logger INFO "A2S helper is current"; return 0; }
|
||||
local sha=c7590ffa9a6d0c6912e17ceeab15b832a1090640
|
||||
sum="d26da2fe03df2b95436f823f0496887b"
|
||||
if [[ -d $helpers_path/a2s ]] && [[ $(get_hash "$helpers_path/a2s/info.py") == $sum ]]; then
|
||||
logger INFO "A2S helper is current"
|
||||
return 0
|
||||
fi
|
||||
local sha=b40eb24cdbb06ebd08272f224257fe5a81610e86
|
||||
local author="yepoleb"
|
||||
local repo="python-a2s"
|
||||
local url="https://github.com/$author/$repo/tarball/$sha"
|
||||
@@ -583,13 +586,13 @@ fetch_a2s(){
|
||||
logger INFO "Updated A2S helper to sha '$sha'"
|
||||
}
|
||||
fetch_dzq(){
|
||||
local sum="0a334e1e144e76e560419d155435c91e"
|
||||
local sum="0d316badbb8d943c5f078e73862cac3e"
|
||||
local file="$helpers_path/a2s/dayzquery.py"
|
||||
if [[ -f $file ]] && [[ $(get_hash "$file") == $sum ]]; then
|
||||
logger INFO "DZQ is current"
|
||||
return 0
|
||||
fi
|
||||
local sha=a22a9f428cbe075d7dda62f78000296955eea92a
|
||||
local sha="07483b88ed096327ebca752f5e177011b604d7fe"
|
||||
local author="yepoleb"
|
||||
local repo="dayzquery"
|
||||
local url="https://raw.githubusercontent.com/$author/$repo/$sha/dayzquery.py"
|
||||
@@ -626,12 +629,12 @@ fetch_helpers_by_sum(){
|
||||
[[ -f "$config_file" ]] && source "$config_file"
|
||||
declare -A sums
|
||||
sums=(
|
||||
["funcs"]="69e83db00cbc7674e241565c88aed83d"
|
||||
["funcs"]="1cd26343cd68a5382e3377a9757497fa"
|
||||
["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
|
||||
["servers.py"]="ed442c3aecf33f777d59dcf53650d263"
|
||||
["ui.py"]="d09b9d8bed3854efd51377289786c5ac"
|
||||
["ui.py"]="cf52a3d5883afe02a013d8673df494fa"
|
||||
["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
|
||||
["pefile.py"]="21531f2c0d9dfa5f110cf6779f9d22c0"
|
||||
["pefile.py"]="b452974a84bff1d821872fcebf59e380"
|
||||
)
|
||||
local author="aclist"
|
||||
local repo="dztui"
|
||||
|
||||
+145
-147
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o pipefail
|
||||
version="6.0.2-beta.1"
|
||||
version="6.0.5-beta.2"
|
||||
|
||||
#CONSTANTS
|
||||
aid=221100
|
||||
@@ -690,6 +690,7 @@ toggle(){
|
||||
auto_install="1"
|
||||
else
|
||||
auto_install=""
|
||||
rm "$versions_file"
|
||||
fi
|
||||
;;
|
||||
Toggle[[:space:]]debug[[:space:]]mode)
|
||||
@@ -929,13 +930,19 @@ query_defunct(){
|
||||
<<< "$result" awk '{print $2}'
|
||||
}
|
||||
encode(){
|
||||
echo "$1" | md5sum | cut -c -8
|
||||
echo -n "$1" | md5sum | cut -c -8
|
||||
}
|
||||
compare(){
|
||||
local modlist="$@"
|
||||
diff=$(comm -23 <(<<< "$modlist" sort -u) <(installed_mods | sort))
|
||||
echo "$diff"
|
||||
}
|
||||
unlink_all(){
|
||||
readarray -t links < <(find "$game_dir" -maxdepth 1 -mindepth 1 -type l)
|
||||
for link in "${links[@]}"; do
|
||||
unlink "$link"
|
||||
done
|
||||
}
|
||||
legacy_symlinks(){
|
||||
logger INFO "Removing legacy symlinks"
|
||||
|
||||
@@ -1026,7 +1033,10 @@ update_symlinks(){
|
||||
if [[ ! -d "$workshop_dir" ]]; then
|
||||
return
|
||||
fi
|
||||
legacy_symlinks
|
||||
if [[ ! -d "$game_dir" ]]; then
|
||||
return
|
||||
fi
|
||||
unlink_all
|
||||
symlinks
|
||||
}
|
||||
try_fallback(){
|
||||
@@ -1077,41 +1087,42 @@ try_connect(){
|
||||
local appid="$2"
|
||||
local binary="$3"
|
||||
|
||||
local ip=$(<<< $record awk -F: '{print $1}')
|
||||
local gameport=$(<<< $record awk -F: '{print $2}')
|
||||
local qport=$(<<< $record awk -F: '{print $3}')
|
||||
IFS=":" read -ra address <<< "$record"
|
||||
local ip
|
||||
local gameport
|
||||
local qport
|
||||
ip=${address[0]}
|
||||
gameport=${address[1]}
|
||||
qport=${address[2]}
|
||||
|
||||
[[ $appid -eq $exp ]] && echo "$binary" > $_cache_binary
|
||||
[[ $appid -eq $exp ]] && echo "$binary" > "$_cache_binary"
|
||||
|
||||
local remote_mods
|
||||
remote_mods=$(a2s $ip $qport rules)
|
||||
remote_mods=$(a2s "$ip" "$qport" rules)
|
||||
if [[ $? -eq 1 ]]; then
|
||||
printf "Failed to fetch server modlist, possibly timed out"
|
||||
return 1
|
||||
fi
|
||||
logger INFO "Server returned modlist: $(<<< $remote_mods tr '\n' ' ')"
|
||||
local sanitized_mods=$(query_defunct "$remote_mods")
|
||||
local diff=$(compare "$sanitized_mods")
|
||||
logger INFO "Server returned modlist: $(<<< "$remote_mods" tr '\n' ' ')"
|
||||
|
||||
local sanitized_mods
|
||||
sanitized_mods=$(query_defunct "$remote_mods")
|
||||
|
||||
logger INFO "Connection attempt for $ip:$qport"
|
||||
update_history "$record"
|
||||
|
||||
if [[ -n $auto_install ]]; then
|
||||
logger INFO "Merging modlists"
|
||||
diff=$(merge_modlists "$diff")
|
||||
diff=$(query_defunct "$diff")
|
||||
fi
|
||||
if [[ -n $diff ]]; then
|
||||
if [[ $(check_architecture) -eq 1 ]] && [[ $(test_display_mode) == "gm" ]]; then
|
||||
printf "Use Desktop Mode to download mods on Steam Deck"
|
||||
return 1
|
||||
fi
|
||||
case $auto_install in
|
||||
"") manual_mod_install "$ip" "$gameport" "$diff" "$sanitized_mods" "" "$appid";;
|
||||
1|2) manual_mod_install "$ip" "$gameport" "$diff" "$sanitized_mods" "auto" "$appid" ;;
|
||||
esac
|
||||
else
|
||||
case $auto_install in
|
||||
"") install_mods "$sanitized_mods" "manual" ;;
|
||||
1|2) install_mods "$sanitized_mods" "auto" ;;
|
||||
esac
|
||||
|
||||
# NOTE: clean diff indicates that all requisite mods were downloaded
|
||||
diff=$(compare "$sanitized_mods")
|
||||
if [[ -z $diff ]]; then
|
||||
launch "$ip" "$gameport" "$sanitized_mods" "$appid"
|
||||
else
|
||||
printf "User aborted download process, or some mods may have failed to download. Try connecting again to resync."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
check_architecture(){
|
||||
@@ -1127,13 +1138,15 @@ force_update(){
|
||||
printf "Only available when mod auto-install is ON"
|
||||
return 1
|
||||
fi
|
||||
# NOTE: forcibly regenerate signatures by removing file
|
||||
rm "$versions_file"
|
||||
local update=$(check_timestamps)
|
||||
manual_mod_install "null" "null" "$update" "null" "force" "null"
|
||||
local mods
|
||||
mods=$(ls -1 "$workshop_dir")
|
||||
install_mods "$mods" "force"
|
||||
echo "Finished requesting mod updates."
|
||||
return 0
|
||||
}
|
||||
get_local_stamps(){
|
||||
get_timestamps(){
|
||||
readarray -t modlist < <(printf "%s\n" "$@")
|
||||
local max="${#modlist[@]}"
|
||||
_concat(){
|
||||
@@ -1158,67 +1171,6 @@ update_stamps(){
|
||||
printf "%s\n" "${stamps[$i]}" >> $versions_file
|
||||
done
|
||||
}
|
||||
check_timestamps(){
|
||||
readarray -t local_modlist < <(ls -1 $workshop_dir)
|
||||
local max=${#local_modlist[@]}
|
||||
logger INFO "Local mod count: $max"
|
||||
[[ $max -eq 0 ]] && return 1
|
||||
local local_stamps=$(get_local_stamps "${local_modlist[@]}")
|
||||
if [[ -z $local_stamps ]]; then
|
||||
logger WARN "Timestamp query returned empty response"
|
||||
return 1
|
||||
fi
|
||||
local aligned=$(<<< "$local_stamps" jq -r '.response.publishedfiledetails[]|"\(.publishedfileid),\(.time_updated)"')
|
||||
readarray -t remote_ids < <(<<< "$aligned" awk -F, '{print $1}')
|
||||
readarray -t remote_times < <(<<< "$aligned" awk -F, '{print $2}')
|
||||
|
||||
if [[ ! -f $versions_file ]]; then
|
||||
logger INFO "No prior versions file found, creating"
|
||||
update_stamps "$aligned"
|
||||
#force refresh all mods if versions file was missing
|
||||
printf "%s\n" "${remote_ids[@]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
readarray -t old_ids < <(< $versions_file awk -F, '{print $1}')
|
||||
readarray -t old_times < <(< $versions_file awk -F, '{print $2}')
|
||||
|
||||
declare -A remote_version
|
||||
declare -A local_version
|
||||
|
||||
for((i = 0; i < ${#remote_ids[@]}; ++i)); do
|
||||
remote_version[${remote_ids[$i]}]=${remote_times[$i]}
|
||||
done
|
||||
|
||||
needs_update=()
|
||||
for((i=0;i<${#old_ids[@]};i++)); do
|
||||
local id=${old_ids[$i]}
|
||||
local time=${old_times[$i]}
|
||||
if [[ $time != ${remote_version[$id]} ]]; then
|
||||
logger WARN "Mod '$id' timestamp '$time' != '${remote_version[$id]}'"
|
||||
needs_update+=($id)
|
||||
fi
|
||||
done
|
||||
printf "%s\n" "${needs_update[@]}"
|
||||
}
|
||||
merge_modlists(){
|
||||
local diff="$1"
|
||||
_sort(){
|
||||
printf "%s\n" "$diff"
|
||||
printf "%s\n" "${needs_update[@]}"
|
||||
}
|
||||
readarray -t needs_update < <(check_timestamps)
|
||||
if [[ -z ${needs_update[@]} ]]; then
|
||||
echo "$diff"
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$diff" ]] && [[ ${#needs_update[@]} -gt 0 ]]; then
|
||||
printf "%s\n" "${needs_update[@]}"
|
||||
else
|
||||
# remove duplicates
|
||||
_sort | sort -u
|
||||
fi
|
||||
}
|
||||
concat_mods(){
|
||||
readarray -t concat_arr <<< "$@"
|
||||
local id
|
||||
@@ -1260,7 +1212,7 @@ launch(){
|
||||
|
||||
if [[ $debug -eq 1 ]]; then
|
||||
local launch_options="$steam_cmd -applaunch $appid -connect=$ip:$gameport -nolauncher -nosplash -name=$name -skipintro -mod=$concat"
|
||||
printf "Debug mode: these options would have been used to launch the game: $launch_options"
|
||||
printf "Debug mode: these options would have been used to launch the game: %s" "$launch_options"
|
||||
return 0
|
||||
fi
|
||||
echo "$concat" > "$_cache_launch"
|
||||
@@ -1296,75 +1248,121 @@ final_handshake(){
|
||||
printf "\n"
|
||||
return 6
|
||||
}
|
||||
manual_mod_install(){
|
||||
local ip="$1"
|
||||
local gameport="$2"
|
||||
local diff="$3"
|
||||
local sanitized_mods="$4"
|
||||
local mode="$5"
|
||||
local appid="$6"
|
||||
local ex="$state_path/dzg.watcher"
|
||||
watcher(){
|
||||
local -n to_download="$1"
|
||||
local -n signatures="$2"
|
||||
local mode="$3"
|
||||
|
||||
readarray -t stage_mods <<< "$diff"
|
||||
[[ -f $ex ]] && rm $ex
|
||||
declare -i i=0
|
||||
local remote_time
|
||||
local length
|
||||
length=${#to_download[@]}
|
||||
for mod in "${!to_download[@]}"; do
|
||||
remote_time=${to_download[$mod]}
|
||||
[[ -f $ex ]] && return 1
|
||||
case $mode in
|
||||
"auto" | "force")
|
||||
$steam_cmd "steam://url/CommunityFilePage/${mod}+workshop_download_item ${aid} ${mod}"
|
||||
echo "# Opening workshop page for ${mod}. If you see no progress, you may be out of disk space."
|
||||
;;
|
||||
*)
|
||||
$steam_cmd "steam://url/CommunityFilePage/${mod}"
|
||||
echo "# Opening workshop page for ${mod}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
|
||||
;;
|
||||
esac
|
||||
sleep 1s
|
||||
foreground
|
||||
|
||||
_watcher(){
|
||||
for((i=0;i<${#stage_mods[@]};i++)); do
|
||||
until [[ -d $downloads_dir/${mod} ]]; do
|
||||
[[ -f $ex ]] && return 1
|
||||
|
||||
if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
|
||||
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}+workshop_download_item $aid ${stage_mods[$i]}"
|
||||
echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress, you may be out of disk space."
|
||||
else
|
||||
$steam_cmd "steam://url/CommunityFilePage/${stage_mods[$i]}"
|
||||
echo "# Opening workshop page for ${stage_mods[$i]}. If you see no progress after subscribing, try unsubscribing and resubscribing again until the download commences."
|
||||
fi
|
||||
sleep 1s
|
||||
foreground
|
||||
|
||||
until [[ -d $downloads_dir/${stage_mods[$i]} ]]; do
|
||||
[[ -f $ex ]] && return 1
|
||||
sleep 0.1s
|
||||
if [[ -d $workshop_dir/${stage_mods[$i]} ]]; then
|
||||
sleep 0.1s
|
||||
# NOTE: fallback for instantaneous downloads
|
||||
if [[ $mode == "auto" || $mode == "force" ]]; then
|
||||
if [[ -d $workshop_dir/${mod} ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
foreground
|
||||
if [[ $mode == "auto" ]] || [[ $mode == "force" ]]; then
|
||||
echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]}). You do not need to manually Subscribe."
|
||||
else
|
||||
echo "# Steam is downloading ${stage_mods[$i]} (mod $((i+1)) of ${#stage_mods[@]})"
|
||||
fi
|
||||
until [[ -d $workshop_dir/${stage_mods[$i]} ]]; do
|
||||
[[ -f $ex ]] && return 1
|
||||
sleep 0.1s
|
||||
done
|
||||
|
||||
foreground
|
||||
echo "# ${stage_mods[$i]} moved to mods dir"
|
||||
done
|
||||
echo "100"
|
||||
}
|
||||
_watcher > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch $ex)
|
||||
|
||||
if [[ $mode == "force" ]]; then
|
||||
rm "$versions_file"
|
||||
check_timestamps
|
||||
return 0
|
||||
foreground
|
||||
case $mode in
|
||||
"auto" | "force")
|
||||
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length}). You do not need to manually Subscribe."
|
||||
;;
|
||||
*)
|
||||
echo "# Steam is downloading ${mod} (mod $((i+1)) of ${length})"
|
||||
;;
|
||||
esac
|
||||
|
||||
until [[ -d $workshop_dir/${mod} ]]; do
|
||||
[[ -f $ex ]] && return 1
|
||||
sleep 0.1s
|
||||
done
|
||||
|
||||
foreground
|
||||
if [[ ! -v signatures["${mod}"] ]]; then
|
||||
echo "$mod,$remote_time" >> "$versions_file"
|
||||
else
|
||||
sed -i "s/$mod,${signatures[$mod]}/$mod,$remote_time/" "$versions_file"
|
||||
fi
|
||||
echo "# ${mod} moved to mods dir"
|
||||
i+=1
|
||||
done
|
||||
echo "100"
|
||||
}
|
||||
install_mods(){
|
||||
local sanitized_mods="$1"
|
||||
local mode="$2"
|
||||
local ex="$state_path/dzg.watcher"
|
||||
|
||||
[[ -f $ex ]] && rm "$ex"
|
||||
|
||||
if [[ -f "$versions_file" ]]; then
|
||||
readarray -t local_mods < "$versions_file"
|
||||
else
|
||||
local_mods=()
|
||||
fi
|
||||
|
||||
local diff=$(compare "$sanitized_mods")
|
||||
if [[ -z $diff ]]; then
|
||||
if [[ $mode == "auto" ]]; then
|
||||
rm "$versions_file"
|
||||
check_timestamps
|
||||
declare -A mod_signatures
|
||||
local local_id
|
||||
local local_time
|
||||
for mod in "${local_mods[@]}"; do
|
||||
IFS="," read -ra fields <<< "$mod"
|
||||
local_id="${fields[0]}"
|
||||
local_time="${fields[1]}"
|
||||
mod_signatures[$local_id]=$local_time
|
||||
done
|
||||
|
||||
readarray -t remote_mods <<< "$sanitized_mods"
|
||||
declare -A download
|
||||
local remote_timestamps
|
||||
local remote_id
|
||||
local remote_time
|
||||
remote_timestamps=$(get_timestamps "${remote_mods[@]}")
|
||||
for mod in $(<<< "$remote_timestamps" jq -r '.response.publishedfiledetails[]|"\(.publishedfileid),\(.time_updated)"'); do
|
||||
IFS="," read -ra fields <<< "$mod"
|
||||
remote_id="${fields[0]}"
|
||||
remote_time="${fields[1]}"
|
||||
|
||||
# NOTE: mod is not present in versions file
|
||||
if [[ ! -v mod_signatures["$remote_id"] ]]; then
|
||||
download["$remote_id"]=$remote_time
|
||||
continue
|
||||
fi
|
||||
launch "$ip" "$gameport" "$sanitized_mods" "$appid"
|
||||
else
|
||||
printf "User aborted download process, or some mods may have failed to download. Try connecting again to resync."
|
||||
# NOTE: mod timestamp is out of date
|
||||
local_time="${mod_signatures["$remote_id"]}"
|
||||
if [[ $remote_time != "$local_time" ]]; then
|
||||
download["$remote_id"]=$remote_time
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
[[ ${#download[@]} -lt 1 ]] && return
|
||||
if [[ $(check_architecture) -eq 1 ]] && [[ $(test_display_mode) == "gm" ]]; then
|
||||
printf "Use Desktop Mode to download mods on Steam Deck"
|
||||
exit 1
|
||||
fi
|
||||
watcher download mod_signatures "$mode" > >($steamsafe_zenity --pulsate --progress --auto-close --title="DZG Watcher" --width=500 2>/dev/null; rc=$?; [[ $rc -eq 1 ]] && touch "$ex")
|
||||
}
|
||||
test_display_mode(){
|
||||
pgrep -a gamescope | grep -q "generate-drm-mode"
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ def get_dayz_version(file: Path) -> DayZVersion | Exception:
|
||||
try:
|
||||
version = get_version(file)
|
||||
except Exception as e:
|
||||
return e
|
||||
raise e
|
||||
patch = str(version.build) + str(version.revision)
|
||||
dz_vers = DayZVersion(version.major, version.minor, int(patch))
|
||||
return dz_vers
|
||||
|
||||
+86
-184
@@ -38,8 +38,10 @@ if TYPE_CHECKING:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
|
||||
import gi # noqa E402
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib, Gdk, GObject, Pango # noqa E402
|
||||
gi.require_version("GLibUnix", "2.0")
|
||||
from gi.repository import Gtk, GLib, GLibUnix, Gdk, GObject, Pango # noqa E402
|
||||
|
||||
# https://bugzilla.gnome.org/show_bug.cgi?id=708676
|
||||
warnings.filterwarnings("ignore", ".*g_value_get_int", Warning)
|
||||
@@ -852,9 +854,7 @@ def call_out(command: str, *args: str) -> subprocess.CompletedProcess:
|
||||
arg_ar = []
|
||||
for i in args:
|
||||
arg_ar.append(i)
|
||||
logger.info(
|
||||
f"Context '{name}' calling subprocess '{command}' with args '{arg_ar}'"
|
||||
)
|
||||
logger.info(f"Context '{name}' calling subprocess '{command}' with args '{arg_ar}'")
|
||||
proc = subprocess.run(
|
||||
["/usr/bin/env", "bash", funcs, command] + arg_ar,
|
||||
capture_output=True,
|
||||
@@ -880,9 +880,7 @@ def spawn_dialog(msg: str, mode: Popup) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def process_shell_return_code(
|
||||
msg: str, code: int, original_input: RowType
|
||||
) -> None:
|
||||
def process_shell_return_code(msg: str, code: int, original_input: RowType) -> None:
|
||||
logger.info(
|
||||
f"Processing return code '{code}' for the input "
|
||||
f"'{original_input}', returned message '{msg}'"
|
||||
@@ -909,9 +907,7 @@ def process_shell_return_code(
|
||||
spawn_dialog("Invalid Steam API key, reverting", Popup.NOTIFY)
|
||||
App.notebook.settings.revert(Preferences.STEAM)
|
||||
case 79: # failed settings update (bm)
|
||||
spawn_dialog(
|
||||
"Invalid Battlemetrics API key, reverting", Popup.NOTIFY
|
||||
)
|
||||
spawn_dialog("Invalid Battlemetrics API key, reverting", Popup.NOTIFY)
|
||||
App.notebook.settings.revert(Preferences.BM)
|
||||
case 80: # pop toast after successful settings change
|
||||
config_vals.clear()
|
||||
@@ -977,9 +973,7 @@ def call_on_thread(
|
||||
if state:
|
||||
wait_dialog = GenericDialog(msg, Popup.WAIT)
|
||||
wait_dialog.show_all()
|
||||
thread = threading.Thread(
|
||||
target=_background, args=(subproc, args, wait_dialog)
|
||||
)
|
||||
thread = threading.Thread(target=_background, args=(subproc, args, wait_dialog))
|
||||
thread.start()
|
||||
else:
|
||||
"""
|
||||
@@ -1004,18 +998,14 @@ def process_tree_option(choice: RowType) -> None:
|
||||
record = App.treeview.get_record_string()
|
||||
wait_msg = command.dict["wait_msg"]
|
||||
show_wait_dialog = True
|
||||
call_on_thread(
|
||||
show_wait_dialog, cmd_string, wait_msg, record, choice=choice
|
||||
)
|
||||
call_on_thread(show_wait_dialog, cmd_string, wait_msg, record, choice=choice)
|
||||
return
|
||||
|
||||
# modlist highlight stale action
|
||||
if context == WindowContext.TABLE_MODS and command == RowType.HIGHLIGHT:
|
||||
wait_msg = command.dict["wait_msg"]
|
||||
show_wait_dialog = True
|
||||
call_on_thread(
|
||||
show_wait_dialog, cmd_string, wait_msg, "", choice=choice
|
||||
)
|
||||
call_on_thread(show_wait_dialog, cmd_string, wait_msg, "", choice=choice)
|
||||
return
|
||||
|
||||
if command == RowType.CHANGELOG:
|
||||
@@ -1083,12 +1073,8 @@ def prepare_connection(rowtype: RowType, record: Record) -> None:
|
||||
thread = threading.Thread(target=background, args=(rowtype, record))
|
||||
thread.start()
|
||||
|
||||
def try_connect(
|
||||
addr: str,
|
||||
appid: str,
|
||||
path: str,
|
||||
row: RowType
|
||||
) -> None:
|
||||
|
||||
def try_connect(addr: str, appid: str, path: str, row: RowType) -> None:
|
||||
def background(addr, appid, path):
|
||||
def cleanup():
|
||||
App.treeview.dialog_hide()
|
||||
@@ -1151,6 +1137,7 @@ def connect_by_ip(enum: RowType, response: str) -> None:
|
||||
return
|
||||
prepare_connection(enum, record)
|
||||
|
||||
|
||||
def connect_by_id(enum: RowType, response: str, key: str) -> None:
|
||||
def _prep(key: str, response: str) -> None:
|
||||
def cleanup() -> None:
|
||||
@@ -1179,9 +1166,7 @@ def process_user_input(enum: RowType) -> None:
|
||||
if enum == RowType.CONN_BY_ID:
|
||||
key = query_config("api_key")[0]
|
||||
if len(key) == 0:
|
||||
spawn_dialog(
|
||||
"No Battlemetrics API key is set; see Options", Popup.NOTIFY
|
||||
)
|
||||
spawn_dialog("No Battlemetrics API key is set; see Options", Popup.NOTIFY)
|
||||
return
|
||||
|
||||
user_entry = EntryDialog(prompt, Popup.ENTRY, link_label)
|
||||
@@ -1202,9 +1187,7 @@ def process_user_input(enum: RowType) -> None:
|
||||
|
||||
show_wait_dialog = True
|
||||
wait_msg = "Working"
|
||||
call_on_thread(
|
||||
show_wait_dialog, cmd_string, wait_msg, response, choice=enum
|
||||
)
|
||||
call_on_thread(show_wait_dialog, cmd_string, wait_msg, response, choice=enum)
|
||||
return
|
||||
|
||||
|
||||
@@ -1292,9 +1275,7 @@ class OuterWindow(Gtk.Window):
|
||||
logger.info(f"Using default window size {w},{h}")
|
||||
self.set_default_size(w, h)
|
||||
|
||||
def _on_delete_event(
|
||||
self, window: "OuterWindow", event: Gdk.EventKey
|
||||
) -> None:
|
||||
def _on_delete_event(self, window: "OuterWindow", event: Gdk.EventKey) -> None:
|
||||
self.halt_proc_and_quit()
|
||||
|
||||
def halt_proc_and_quit(self) -> None:
|
||||
@@ -1361,8 +1342,7 @@ class RightPanel(Gtk.Box):
|
||||
self.pack_start(self.filters_vbox, False, False, 0)
|
||||
|
||||
debug_tooltip = (
|
||||
"Used to perform a dry run without\n"
|
||||
"actually connecting to a server"
|
||||
"Used to perform a dry run without\n" "actually connecting to a server"
|
||||
)
|
||||
ping_tooltip = (
|
||||
"Refresh the ping for visible servers.\n"
|
||||
@@ -1431,8 +1411,7 @@ class RightPanel(Gtk.Box):
|
||||
rows = ModelManager.get_filtered()
|
||||
with ThreadPoolExecutor(100) as executor:
|
||||
futures = [
|
||||
executor.submit(Servers.ping, i, row)
|
||||
for i, row in enumerate(rows)
|
||||
executor.submit(Servers.ping, i, row) for i, row in enumerate(rows)
|
||||
]
|
||||
wait(futures)
|
||||
for future in futures:
|
||||
@@ -1808,9 +1787,7 @@ class ModelManagerSingleton:
|
||||
|
||||
|
||||
class TreeView(Gtk.TreeView):
|
||||
__gsignals__ = {
|
||||
"on_distcalc_started": (GObject.SignalFlags.RUN_FIRST, None, ())
|
||||
}
|
||||
__gsignals__ = {"on_distcalc_started": (GObject.SignalFlags.RUN_FIRST, None, ())}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -1929,9 +1906,7 @@ class TreeView(Gtk.TreeView):
|
||||
if self.current_proc and self.current_proc.is_alive():
|
||||
self.current_proc.terminate()
|
||||
|
||||
def _delete_note(
|
||||
self, button: Gtk.Button, user_entry: Gtk.Box, addr: str
|
||||
) -> None:
|
||||
def _delete_note(self, button: Gtk.Button, user_entry: Gtk.Box, addr: str) -> None:
|
||||
box = button.get_parent()
|
||||
dialog = box.get_parent()
|
||||
try:
|
||||
@@ -2023,9 +1998,7 @@ class TreeView(Gtk.TreeView):
|
||||
model.remove(it)
|
||||
|
||||
block_signals()
|
||||
thread = threading.Thread(
|
||||
target=ModelManager.resync_model, args=(addr, qport)
|
||||
)
|
||||
thread = threading.Thread(target=ModelManager.resync_model, args=(addr, qport))
|
||||
thread.start()
|
||||
|
||||
def remove_from_history(self) -> None:
|
||||
@@ -2049,16 +2022,12 @@ class TreeView(Gtk.TreeView):
|
||||
if hasattr(TreeView, menu_item.action):
|
||||
func = getattr(TreeView, menu_item.action)
|
||||
msg = (
|
||||
f"User clicked context menu '{menu_item.get_label()}', "
|
||||
f"calls {func}"
|
||||
f"User clicked context menu '{menu_item.get_label()}', " f"calls {func}"
|
||||
)
|
||||
logger.info(msg)
|
||||
func(self)
|
||||
else:
|
||||
msg = (
|
||||
f"Context menu function for '{menu_item.action}' "
|
||||
f"does not exist"
|
||||
)
|
||||
msg = f"Context menu function for '{menu_item.action}' " f"does not exist"
|
||||
u_msg = (
|
||||
f"Something went wrong when accessing the method "
|
||||
f"'{menu_item.action}'"
|
||||
@@ -2099,9 +2068,7 @@ class TreeView(Gtk.TreeView):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _on_button_release(
|
||||
self, widget: Gtk.Widget, event: Gdk.EventButton
|
||||
) -> None:
|
||||
def _on_button_release(self, widget: Gtk.Widget, event: Gdk.EventButton) -> None:
|
||||
if event.type is Gdk.EventType.BUTTON_RELEASE and event.button != 3:
|
||||
return
|
||||
|
||||
@@ -2185,9 +2152,7 @@ class TreeView(Gtk.TreeView):
|
||||
if event.type is Gdk.EventType.KEY_PRESS and event.keyval is Gdk.KEY_l:
|
||||
if self.is_selection_empty():
|
||||
return
|
||||
self.menu.popup_at_widget(
|
||||
widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST
|
||||
)
|
||||
self.menu.popup_at_widget(widget, Gdk.Gravity.CENTER, Gdk.Gravity.WEST)
|
||||
else:
|
||||
self.menu.popup_at_pointer(event)
|
||||
self.menu.select_first(False)
|
||||
@@ -2201,9 +2166,7 @@ class TreeView(Gtk.TreeView):
|
||||
return None
|
||||
call_out("start_cooldown", "", "")
|
||||
|
||||
thread = threading.Thread(
|
||||
target=self._background_player_count, args=()
|
||||
)
|
||||
thread = threading.Thread(target=self._background_player_count, args=())
|
||||
thread.start()
|
||||
|
||||
def get_current_iter(self) -> Gtk.TreeIter | None:
|
||||
@@ -2218,9 +2181,7 @@ class TreeView(Gtk.TreeView):
|
||||
|
||||
context = App.treeview.get_subpage_label()
|
||||
row_sel = self.get_value_at_index(0)
|
||||
logger.info(
|
||||
f"Tree selection for context '{context}' changed to '{row_sel}'"
|
||||
)
|
||||
logger.info(f"Tree selection for context '{context}' changed to '{row_sel}'")
|
||||
|
||||
if self.current_proc and self.current_proc.is_alive():
|
||||
self.current_proc.terminate()
|
||||
@@ -2278,9 +2239,7 @@ class TreeView(Gtk.TreeView):
|
||||
self.set_cursor(path)
|
||||
return None
|
||||
|
||||
def _on_keypress(
|
||||
self, treeview: Gtk.TreeView, event: Gdk.EventKey
|
||||
) -> bool | None:
|
||||
def _on_keypress(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> bool | None:
|
||||
keyname = Gdk.keyval_name(event.keyval)
|
||||
grid = App.grid
|
||||
if event.state is Gdk.ModifierType.CONTROL_MASK:
|
||||
@@ -2335,9 +2294,7 @@ class TreeView(Gtk.TreeView):
|
||||
return False
|
||||
return None
|
||||
|
||||
def _on_key_release(
|
||||
self, treeview: Gtk.TreeView, event: Gdk.EventKey
|
||||
) -> None:
|
||||
def _on_key_release(self, treeview: Gtk.TreeView, event: Gdk.EventKey) -> None:
|
||||
"""
|
||||
Suppresses spamming on keydown
|
||||
"""
|
||||
@@ -2452,10 +2409,7 @@ class TreeView(Gtk.TreeView):
|
||||
params = Servers.params
|
||||
serv = []
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures = [
|
||||
executor.submit(job, key, APPID_DAYZ, param)
|
||||
for param in params
|
||||
]
|
||||
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
|
||||
wait(futures)
|
||||
for future in futures:
|
||||
res = future.result()
|
||||
@@ -2477,10 +2431,7 @@ class TreeView(Gtk.TreeView):
|
||||
|
||||
def _dump_lan(self, port: int) -> list | None:
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures = [
|
||||
executor.submit(Servers.test_ip, i, port)
|
||||
for i in range(1, 256)
|
||||
]
|
||||
futures = [executor.submit(Servers.test_ip, i, port) for i in range(1, 256)]
|
||||
wait(futures)
|
||||
servers = []
|
||||
for future in futures:
|
||||
@@ -2500,7 +2451,7 @@ class TreeView(Gtk.TreeView):
|
||||
if len(ips) == 0:
|
||||
return []
|
||||
# NOTE: block malformed records
|
||||
ips = [ip for ip in ips if len(ip.split(":")) == 3 and ip.split(":")[2] != "" ]
|
||||
ips = [ip for ip in ips if len(ip.split(":")) == 3 and ip.split(":")[2] != ""]
|
||||
with ThreadPoolExecutor() as executor:
|
||||
futures = [
|
||||
executor.submit(
|
||||
@@ -2605,9 +2556,7 @@ class TreeView(Gtk.TreeView):
|
||||
ModelManager.set_store(App.treeview.get_model())
|
||||
App.treeview.set_model(None)
|
||||
|
||||
thread = threading.Thread(
|
||||
target=ModelManager.filter, args=(mode, *args)
|
||||
)
|
||||
thread = threading.Thread(target=ModelManager.filter, args=(mode, *args))
|
||||
thread.start()
|
||||
|
||||
def _background_quad(self, dialog: "GenericDialog", mode: RowType) -> None:
|
||||
@@ -2766,13 +2715,9 @@ class TreeView(Gtk.TreeView):
|
||||
self.initialize_columns()
|
||||
self.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||
|
||||
self.wait_dialog = GenericDialog(
|
||||
"Fetching server metadata", Popup.WAIT
|
||||
)
|
||||
self.wait_dialog = GenericDialog("Fetching server metadata", Popup.WAIT)
|
||||
self.wait_dialog.show_all()
|
||||
thread = threading.Thread(
|
||||
target=self._query_servers, args=(mode, port)
|
||||
)
|
||||
thread = threading.Thread(target=self._query_servers, args=(mode, port))
|
||||
thread.start()
|
||||
|
||||
def _format_float(
|
||||
@@ -2951,9 +2896,7 @@ class TreeView(Gtk.TreeView):
|
||||
def get_view(self):
|
||||
return self.view
|
||||
|
||||
def get_prereqs(
|
||||
self, record: Record
|
||||
) -> tuple[bool, str, str|None, "Prereqs"]:
|
||||
def get_prereqs(self, record: Record) -> tuple[bool, str, str | None, "Prereqs"]:
|
||||
"""
|
||||
Always called on a thread with a dialog on the transient parent window
|
||||
"""
|
||||
@@ -2967,18 +2910,14 @@ class TreeView(Gtk.TreeView):
|
||||
steam_path = query_config("default_steam_path")[0]
|
||||
|
||||
if len(steam_path) < 1:
|
||||
logger.critical(
|
||||
"Config file has no value set for 'default_steam_path'"
|
||||
)
|
||||
logger.critical("Config file has no value set for 'default_steam_path'")
|
||||
msg = "Local Steam installation is not set, possibly malformed config file."
|
||||
return (False, msg, None, prereqs)
|
||||
|
||||
try:
|
||||
pefile_path = PeFile.get_pefile_path(steam_path, prereqs.appid)
|
||||
except AppNotInstalledError:
|
||||
logger.critical(
|
||||
f"'{prereqs.appid}' not found in user's libraryfolders"
|
||||
)
|
||||
logger.critical(f"'{prereqs.appid}' not found in user's libraryfolders")
|
||||
msg = (
|
||||
f"This server is running {build}. You can install "
|
||||
f"{build} by searching for it in your Steam library. "
|
||||
@@ -2987,9 +2926,7 @@ class TreeView(Gtk.TreeView):
|
||||
)
|
||||
return (False, msg, None, prereqs)
|
||||
except AppMovedError:
|
||||
logger.critical(
|
||||
f"Library folder synch error for '{prereqs.appid}'"
|
||||
)
|
||||
logger.critical(f"Library folder synch error for '{prereqs.appid}'")
|
||||
msg = (
|
||||
f"Steam is reporting that {build} is installed at a non-existent location. "
|
||||
f"If you recently installed {build} or moved it to a different drive, "
|
||||
@@ -3044,7 +2981,6 @@ class TreeView(Gtk.TreeView):
|
||||
|
||||
return (True, "", pefile_path, prereqs)
|
||||
|
||||
|
||||
@signal_emission
|
||||
@update_window_labels
|
||||
def _on_row_activated(
|
||||
@@ -3198,7 +3134,7 @@ class GenericDialog(Gtk.MessageDialog):
|
||||
self.outer.set_margin_end(30)
|
||||
|
||||
def _on_dialog_delete(
|
||||
self, response_id: Gtk.ResponseType, event: Gdk.Event
|
||||
self, response_id: Gtk.ResponseType, event: Gdk.Event
|
||||
) -> Literal[True]:
|
||||
"""
|
||||
Prevent manual dialog destruction
|
||||
@@ -3277,9 +3213,7 @@ class LanDialog(Gtk.MessageDialog):
|
||||
self.connect("delete-event", self.restore_context)
|
||||
|
||||
self.entry.connect("insert-text", self._on_text_typed)
|
||||
self.entry.get_property("buffer").connect(
|
||||
"deleted-text", self._on_text_deleted
|
||||
)
|
||||
self.entry.get_property("buffer").connect("deleted-text", self._on_text_deleted)
|
||||
|
||||
def _validate(self, text: str) -> None:
|
||||
if self._is_invalid(text):
|
||||
@@ -3395,9 +3329,7 @@ class DetailsDialog(GenericDialog):
|
||||
for i, column_title in enumerate(["Item", "Details"]):
|
||||
renderer = Gtk.CellRendererText(xalign=0)
|
||||
if i == 0:
|
||||
column = Gtk.TreeViewColumn(
|
||||
column_title, renderer, text=i, weight=2
|
||||
)
|
||||
column = Gtk.TreeViewColumn(column_title, renderer, text=i, weight=2)
|
||||
else:
|
||||
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
@@ -3413,12 +3345,8 @@ class DetailsDialog(GenericDialog):
|
||||
scrollable_message = Gtk.ScrolledWindow()
|
||||
desc = Gtk.Label(label="Server message", valign=Gtk.Align.START)
|
||||
add_class(desc, "details-heading")
|
||||
box = Gtk.Box(
|
||||
orientation=Gtk.Orientation.VERTICAL, halign=Gtk.Align.CENTER
|
||||
)
|
||||
self.description = Gtk.Label(
|
||||
justify=Gtk.Justification.CENTER, wrap=True
|
||||
)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, halign=Gtk.Align.CENTER)
|
||||
self.description = Gtk.Label(justify=Gtk.Justification.CENTER, wrap=True)
|
||||
sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
sep.set_margin_bottom(10)
|
||||
for el in desc, sep, self.description:
|
||||
@@ -3456,9 +3384,7 @@ class DetailsDialog(GenericDialog):
|
||||
self.run()
|
||||
self.destroy()
|
||||
|
||||
def _background(
|
||||
self, dialog: "GenericDialog", ip: str, qport: int
|
||||
) -> None:
|
||||
def _background(self, dialog: "GenericDialog", ip: str, qport: int) -> None:
|
||||
response = Servers.details(self.ip, self.qport)
|
||||
if response.success:
|
||||
for row in response.data:
|
||||
@@ -3515,9 +3441,7 @@ class ModDialog(GenericDialog):
|
||||
|
||||
wait_dialog = GenericDialog("Fetching modlist", Popup.WAIT)
|
||||
wait_dialog.show_all()
|
||||
thread = threading.Thread(
|
||||
target=self._background, args=(wait_dialog, record)
|
||||
)
|
||||
thread = threading.Thread(target=self._background, args=(wait_dialog, record))
|
||||
thread.start()
|
||||
|
||||
def _background(self, dialog: "GenericDialog", record: str) -> None:
|
||||
@@ -3542,9 +3466,7 @@ class ModDialog(GenericDialog):
|
||||
self.view.set_model(modlist_store)
|
||||
GLib.idle_add(_load)
|
||||
|
||||
def _parse_modlist_rows(
|
||||
self, data: subprocess.CompletedProcess
|
||||
) -> bool | int:
|
||||
def _parse_modlist_rows(self, data: subprocess.CompletedProcess) -> bool | int:
|
||||
lines = data.stdout.splitlines()
|
||||
hits = len(lines)
|
||||
reader = csv.reader(lines, delimiter=delimiter)
|
||||
@@ -3574,9 +3496,7 @@ class ModDialog(GenericDialog):
|
||||
|
||||
|
||||
class LinkDialog(GenericDialog):
|
||||
def __init__(
|
||||
self, text: str, link: str | None, command: RowType, uid: str = ""
|
||||
):
|
||||
def __init__(self, text: str, link: str | None, command: RowType, uid: str = ""):
|
||||
super().__init__(text, Popup.NOTIFY)
|
||||
|
||||
text = textwrap.dedent(text)
|
||||
@@ -3794,7 +3714,12 @@ class Options(Gtk.Box):
|
||||
client_store = Gtk.ListStore(str, str)
|
||||
client_store.append(["steam", "steam"])
|
||||
client_store.append(["flatpak", "flatpak run com.valvesoftware.Steam"])
|
||||
client_store.append(["flatpak (container)", "flatpak-spawn --host flatpak run com.valvesoftware.Steam"])
|
||||
client_store.append(
|
||||
[
|
||||
"flatpak (container)",
|
||||
"flatpak-spawn --host flatpak run com.valvesoftware.Steam",
|
||||
]
|
||||
)
|
||||
|
||||
renderer_text = Gtk.CellRendererText(ellipsize=Pango.EllipsizeMode.END)
|
||||
self.client_combo = Gtk.ComboBox.new_with_model(client_store)
|
||||
@@ -3818,8 +3743,7 @@ class Options(Gtk.Box):
|
||||
self.force_button.set_sensitive(False)
|
||||
|
||||
msg = (
|
||||
"Manual: prompt to subscribe to mods in Steam. "
|
||||
"Auto: unmanned downloads."
|
||||
"Manual: prompt to subscribe to mods in Steam. " "Auto: unmanned downloads."
|
||||
)
|
||||
eb = InfoEventBox(msg)
|
||||
|
||||
@@ -3878,9 +3802,7 @@ class Options(Gtk.Box):
|
||||
self.make_frame(mods_grid, "Mods"),
|
||||
self.make_frame(version_grid, "Version"),
|
||||
]:
|
||||
grid.attach(
|
||||
frame, col, row, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT
|
||||
)
|
||||
grid.attach(frame, col, row, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
|
||||
row += 1
|
||||
|
||||
self.scrollable = Gtk.ScrolledWindow(vexpand=True)
|
||||
@@ -3926,9 +3848,7 @@ class Options(Gtk.Box):
|
||||
|
||||
return box
|
||||
|
||||
def _on_field_activated(
|
||||
self, entry: Gtk.Entry, context: Preferences
|
||||
) -> None:
|
||||
def _on_field_activated(self, entry: Gtk.Entry, context: Preferences) -> None:
|
||||
text = entry.get_text()
|
||||
button = entry.sibling
|
||||
if not self._is_valid_text(text, context):
|
||||
@@ -3949,9 +3869,7 @@ class Options(Gtk.Box):
|
||||
for record in rows:
|
||||
col = 1
|
||||
for el in record:
|
||||
grid.attach(
|
||||
el, col, row, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT
|
||||
)
|
||||
grid.attach(el, col, row, self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
|
||||
col += 1
|
||||
row += 1
|
||||
return grid
|
||||
@@ -4004,9 +3922,7 @@ class Options(Gtk.Box):
|
||||
return
|
||||
process_toggle(RowType.TGL_BRANCH)
|
||||
|
||||
def _on_radio_toggled(
|
||||
self, button: Gtk.RadioButton, context: Preferences
|
||||
) -> None:
|
||||
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
|
||||
if App.treeview.subpage is not RowType.OPTIONS:
|
||||
return
|
||||
match context:
|
||||
@@ -4150,18 +4066,14 @@ class Options(Gtk.Box):
|
||||
field[1].get_children()[1].set_sensitive(False)
|
||||
|
||||
try:
|
||||
pe_file_path = PeFile.get_pefile_path(
|
||||
default_steam_path, APPID_DAYZ
|
||||
)
|
||||
pe_file_path = PeFile.get_pefile_path(default_steam_path, APPID_DAYZ)
|
||||
vers = PeFile.get_dayz_version(pe_file_path)
|
||||
dayz_version = PeFile.dayz_version_to_str(vers)
|
||||
except Exception:
|
||||
dayz_version = "-"
|
||||
|
||||
try:
|
||||
exp_file_path = PeFile.get_pefile_path(
|
||||
default_steam_path, APPID_DAYZ_EXP
|
||||
)
|
||||
exp_file_path = PeFile.get_pefile_path(default_steam_path, APPID_DAYZ_EXP)
|
||||
vers = PeFile.get_dayz_version(exp_file_path)
|
||||
dayz_exp_version = PeFile.dayz_version_to_str(vers)
|
||||
except Exception:
|
||||
@@ -4270,9 +4182,7 @@ class KeybindingsDialog(Gtk.Box):
|
||||
if not sep:
|
||||
grid.attach(desc, col, row, w, h)
|
||||
else:
|
||||
grid.attach_next_to(
|
||||
desc, sep, Gtk.PositionType.BOTTOM, w, h
|
||||
)
|
||||
grid.attach_next_to(desc, sep, Gtk.PositionType.BOTTOM, w, h)
|
||||
row += 1
|
||||
sep = None
|
||||
grid.attach_next_to(frame, desc, Gtk.PositionType.RIGHT, w, h)
|
||||
@@ -4280,9 +4190,7 @@ class KeybindingsDialog(Gtk.Box):
|
||||
l_spacer = Gtk.Label(label="")
|
||||
r_spacer = Gtk.Label(label="")
|
||||
grid.attach(l_spacer, col, row + 1, w, h)
|
||||
grid.attach_next_to(
|
||||
r_spacer, l_spacer, Gtk.PositionType.RIGHT, w, h
|
||||
)
|
||||
grid.attach_next_to(r_spacer, l_spacer, Gtk.PositionType.RIGHT, w, h)
|
||||
row += 1
|
||||
return grid
|
||||
|
||||
@@ -4291,9 +4199,7 @@ class KeybindingsDialog(Gtk.Box):
|
||||
col = 0
|
||||
w = 1
|
||||
h = 1
|
||||
sidebar = Gtk.Grid(
|
||||
row_homogeneous=True, orientation=Gtk.Orientation.VERTICAL
|
||||
)
|
||||
sidebar = Gtk.Grid(row_homogeneous=True, orientation=Gtk.Orientation.VERTICAL)
|
||||
for cat in categories:
|
||||
label = Gtk.Label(label=cat)
|
||||
add_class(label, "left-label")
|
||||
@@ -4313,9 +4219,7 @@ class KeybindingsDialog(Gtk.Box):
|
||||
column = 1
|
||||
w = 1
|
||||
h = 1
|
||||
sidebar = self.build_sidebar(
|
||||
["Servers", "Navigation", "Vim-style keys"]
|
||||
)
|
||||
sidebar = self.build_sidebar(["Servers", "Navigation", "Vim-style keys"])
|
||||
separator = Gtk.Separator()
|
||||
keys_box = self.build_keys(items)
|
||||
|
||||
@@ -4533,9 +4437,7 @@ class Statusbar(Gtk.Statusbar):
|
||||
|
||||
players_pretty = pluralize("players", players)
|
||||
hits_pretty = pluralize("matches", hits)
|
||||
formatted = (
|
||||
f"Found {hits:n} {hits_pretty} with {players:n} {players_pretty}"
|
||||
)
|
||||
formatted = f"Found {hits:n} {hits_pretty} with {players:n} {players_pretty}"
|
||||
suffix = "| Distance: calculating..."
|
||||
|
||||
if players == 0:
|
||||
@@ -4587,9 +4489,7 @@ class Grid(Gtk.Grid):
|
||||
self.breadcrumbs = Gtk.Label(label="Main menu", halign=Gtk.Align.START)
|
||||
|
||||
self.attach(self.notebook, 0, 0, 3, 1)
|
||||
self.attach_next_to(
|
||||
self.breadcrumbs, self.notebook, Gtk.PositionType.TOP, 3, 1
|
||||
)
|
||||
self.attach_next_to(self.breadcrumbs, self.notebook, Gtk.PositionType.TOP, 3, 1)
|
||||
self.attach_next_to(
|
||||
self.statusbar, self.notebook, Gtk.PositionType.BOTTOM, 3, 1
|
||||
)
|
||||
@@ -4651,11 +4551,23 @@ class App(Gtk.Application):
|
||||
)
|
||||
self.win.add_accel_group(accel)
|
||||
|
||||
GLib.unix_signal_add(
|
||||
GLib.PRIORITY_DEFAULT, signal.SIGINT, self._catch_sigint
|
||||
)
|
||||
self.setup_signals()
|
||||
Gtk.main()
|
||||
|
||||
def setup_signals(self) -> None:
|
||||
SIGNAL_ADD = "signal_add"
|
||||
SIGNAL_ADD_FULL = "signal_add_full"
|
||||
try:
|
||||
if SIGNAL_ADD in dir(GLibUnix):
|
||||
func = GLibUnix.signal_add
|
||||
elif SIGNAL_ADD_FULL in dir(GLibUnix):
|
||||
func = GLibUnix.signal_add_full
|
||||
else:
|
||||
func = GLib.unix_signal_add
|
||||
func(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._catch_sigint)
|
||||
except Exception as e:
|
||||
logger.critical(e)
|
||||
|
||||
def _catch_sigint(self) -> None:
|
||||
self.win.halt_proc_and_quit()
|
||||
|
||||
@@ -4740,9 +4652,7 @@ class ModSelectionPanel(Gtk.Box):
|
||||
|
||||
def toggle_select_stale_button(self, state: bool) -> None:
|
||||
if state:
|
||||
button = Gtk.Button(
|
||||
label="Select stale", margin_start=10, margin_end=10
|
||||
)
|
||||
button = Gtk.Button(label="Select stale", margin_start=10, margin_end=10)
|
||||
text = "Bulk selects all currently highlighted mods"
|
||||
button.set_tooltip_text(text)
|
||||
button.connect("clicked", self._on_button_clicked)
|
||||
@@ -4862,9 +4772,7 @@ class FilterPanel(Gtk.Box):
|
||||
self.keyword_entry = Gtk.Entry()
|
||||
self.keyword_entry.set_placeholder_text("Filter by keyword")
|
||||
self.keyword_entry.connect("activate", self._on_keyword_enter)
|
||||
self.keyword_entry.connect(
|
||||
"key-press-event", self._on_keyword_keypress
|
||||
)
|
||||
self.keyword_entry.connect("key-press-event", self._on_keyword_keypress)
|
||||
|
||||
completion = Gtk.EntryCompletion(inline_completion=True)
|
||||
completion.set_text_column(0)
|
||||
@@ -4932,9 +4840,7 @@ class FilterPanel(Gtk.Box):
|
||||
state = self.default_filters[label]
|
||||
check.set_active(state)
|
||||
|
||||
def _on_map_entry_keypress(
|
||||
self, entry: Gtk.Entry, event: Gdk.EventKey
|
||||
) -> None:
|
||||
def _on_map_entry_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> None:
|
||||
match event.keyval:
|
||||
case Gdk.KEY_Return:
|
||||
text = entry.get_text()
|
||||
@@ -4977,9 +4883,7 @@ class FilterPanel(Gtk.Box):
|
||||
App.treeview.grab_focus()
|
||||
return False
|
||||
|
||||
def _on_keyword_keypress(
|
||||
self, entry: Gtk.Entry, event: Gdk.EventKey
|
||||
) -> bool:
|
||||
def _on_keyword_keypress(self, entry: Gtk.Entry, event: Gdk.EventKey) -> bool:
|
||||
match event.keyval:
|
||||
case Gdk.KEY_Up:
|
||||
return True
|
||||
@@ -4990,9 +4894,7 @@ class FilterPanel(Gtk.Box):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _on_combo_keypress(
|
||||
self, combo: Gtk.ComboBox, event: Gdk.EventKey
|
||||
) -> bool:
|
||||
def _on_combo_keypress(self, combo: Gtk.ComboBox, event: Gdk.EventKey) -> bool:
|
||||
match event.keyval:
|
||||
case Gdk.KEY_Down:
|
||||
self.maps_combo.popup()
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 238 KiB |
Reference in new issue
Block a user