Compare commits

...
26 Commits
Author SHA1 Message Date
aclist e99178711a docs: update disclaimers 2023-01-18 23:57:28 +09:00
aclist f8f90c1c0a Edit heading 2022-10-03 07:10:14 +09:00
aclist 59ddc1be3e Clean up readme 2022-08-23 20:32:47 +09:00
aclist e1b87f75e6 Remove extraneous comments 2022-08-23 20:30:15 +09:00
aclist 619e23f174 Remove newlines 2022-08-01 04:13:01 +09:00
aclist b6336fc987 Clean up format 2022-08-01 04:11:38 +09:00
aclist 8044bedca8 Create feature_request.md 2022-07-18 16:37:37 +09:00
aclist e1763c8128 Merge pull request #18 from aclist/aclist-patch-1
Update issue templates
2022-07-18 16:35:13 +09:00
aclist d383760e10 Update issue templates 2022-07-18 16:33:51 +09:00
aclist c5569dfd1a Reword readme 2022-07-18 01:30:31 +09:00
aclist 3836a50098 Unlink legacy symlinks 2022-06-22 23:44:37 +09:00
aclist 6b5a904a62 Add heading 2022-06-20 15:20:44 +09:00
aclist ee41d73679 Mention DZGUI 2022-06-20 15:20:14 +09:00
aclist 69dec73c6d Add extended debug options 2022-06-19 12:14:17 +09:00
aclist 5539dd4eec Fix reserved chars in modlist 2022-06-19 11:17:12 +09:00
aclist 7989ab36f8 Add auto-download 2022-06-18 01:02:41 +09:00
aclist e289a3fc43 Fallback to manual mode if mods exceed /tmp space 2022-06-18 00:35:55 +09:00
aclist 6a9e4f547d Force steamcmd keepalive 2022-06-17 16:12:30 +09:00
aclist 788ee990a2 Revert login method 2022-06-16 20:15:35 +09:00
aclist 319fd420a2 Bump version 2022-06-16 00:13:14 +09:00
aclist 0f36a54722 Drop credential based login 2022-06-16 00:12:58 +09:00
aclist a143cc4496 Hotfix for long concatenations of mods 2022-06-15 23:13:03 +09:00
aclist 94cbfd3f62 Fix steamcmd invocation parameters 2022-06-15 10:14:46 +09:00
aclist 9c5b00fa21 Revert default steamcmd flag to 0 2022-06-15 10:03:38 +09:00
aclist b1d7e89f02 Improve steamcmd detection 2022-06-15 10:02:35 +09:00
aclist 05cee7b70d Fix array bug 2022-06-09 00:25:08 +09:00
4 changed files with 207 additions and 66 deletions

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
---
name: Bug report
about: Create a report to improve the app
title: "[BUG] Give a title to the report"
labels: ''
assignees: ''
---
**Info**
DZGUI version:
Mode (normal/debug):
Server ID:
Linux distribution:
**Bug description**
A clear and concise description of what the bug is.
**Steps to reproduce the behavior:**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
+14
View File
@@ -0,0 +1,14 @@
---
name: Feature request
about: Submit a request for a new feature
title: "[FEAT]"
labels: ''
assignees: ''
---
**Feature description**
A clear and concise description of the proposed feature.
**Objective**
What does this feature attempt to solve?
+15 -28
View File
@@ -1,5 +1,14 @@
# DZTUI # DZTUI
**NOTE:** this project has been superseded by [DZGUI](https://github.com/aclist/dztui/tree/dzgui), which brings numerous hotfixes and quality of life features.
Development of DZGUI is ongoing and has been driven by the needs of desktop, Steam Deck, and GUI users, and supports many more use cases and idiosyncrasies of DayZ servers.
By comparison, the DZTUI codebase is quite ancient now and has served mainly as a proof of concept for the implementation of DZGUI. If you continue to use DZTUI in its current state, be aware that it
lacks critical features.
## What this is:
A TUI pseudo-browser for listing heads-up information on DayZ community servers, fetching and synchronizing mods, and connecting to these servers automatically without manually setting launch A TUI pseudo-browser for listing heads-up information on DayZ community servers, fetching and synchronizing mods, and connecting to these servers automatically without manually setting launch
parameters. parameters.
@@ -53,31 +62,17 @@ See https://developer.valvesoftware.com/wiki/SteamCMD for additional details.
|auto_install_mods|if set to 1, uses steamcmd to automatically stage mods without manual subscription necessary| |auto_install_mods|if set to 1, uses steamcmd to automatically stage mods without manual subscription necessary|
|steam_cmd_user|the name of the local system user that invokes steamcmd (recommended user is `steam`)| |steam_cmd_user|the name of the local system user that invokes steamcmd (recommended user is `steam`)|
|steam_username|the login handle of the real steam account that owns DayZ (this is required for mods; you cannot anonymously download mods without a license)| |steam_username|the login handle of the real steam account that owns DayZ (this is required for mods; you cannot anonymously download mods without a license)|
|staging_dir|the intermediate path to stage downloaded mods to before moving them to the real user's `workshop_dir`. `/tmp` is used by default (see explanation below). |staging_dir|the intermediate path to stage downloaded mods to before moving them to the real user's `workshop_dir`. `/tmp` is used by default. This must be a writable directory.
### Some explanation of how steamcmd works follows: ### Some explanation of how steamcmd works follows:
`steamcmd` is chiefly designed for updating content on game servers. As such, it is shipped as a command-line application and is designed to be run by a user isolated from the rest of the system. It allows for programmatic login to a Steam account and fetching/updating content in a headless fashion. While this can be done anonymously for downloading game server content, to download mods tied to an actual game, you need the game license (i.e. own the game). This means you must log in to retrieve mods. `steamcmd` is chiefly designed for updating content on game servers. As such, it is shipped as a command-line application and is designed to be run by a user isolated from the rest of the system. It allows for programmatic login to a Steam account and fetching/updating content in a headless fashion. While this can be done anonymously for downloading game server content, to download mods tied to an actual game, you need the game license (i.e. own the game). This means you must log in to retrieve mods.
Valve recommends creating an isolated user called `steam` that is used to invoke `steamcmd` and fetch content. This is a reasonable suggestion and makes sense for a server context in particular. Once logged in, `steamcmd` by design stores and hashes the login credentials to uniquely identify the user and obviate further manual password entry from the same account. After prompting for a password (and 2FA code, where applicable), it won't ask for them again unless the hash is removed or the login user is changed. Valve recommends creating an isolated user called `steam` that is used to invoke `steamcmd` and fetch content. Once logged in, `steamcmd` by design stores and hashes the login credentials to uniquely identify the user and obviate further manual password entry from the same account. After prompting for a password (and 2FA code, where applicable), it won't ask for them again until the hash expires or the login user is changed.
You must create the `steam` user (or some other user of your choice) on the local system, and install `steamcmd` from your repositories or download the upstream binary. You must create the `steam` user (or some other user of your choice; default configuration assumes a user named `steam`) on the local system, and install `steamcmd` from your repositories or download the upstream binary.
For our purposes, isolating the user still seems like a good idea, but this introduces a challenge: how to fetch the content as the user `steam` and then make it available to the real user in the least kludgy manner possible? Obviously, if the users are isolated from each other, the `steam` user shouldn't be allowed to wantonly write files to the real user's space, and vice versa, the real user shouldn't be able to go into the `steam` user's space. Credentials go through `steamcmd` directly to Valve and not through this script. See the function `auto_mod_download()` to see the exact command being used.
I initially thought making use of a common/shared directory that both users have group permissions to would be good: the `steam` user could fetch the mods, then move them to this common directory, which is set to recursively inherit permissions. The real user then grabs the content out of there and puts it in their real mod path. This seemed overengineered, though, and you have to mess about with group permissions and umasks, and would a user create a totally clean directory for this purpose, as I did, or just set their `$HOME`? It seemed rife with problems. Another alternative here would be to use access control lists (ACL), but, again, this just seemed like overkill, and I didn't like the idea of users wantonly moving files in and out of some unspecified directory, especially if someone chose to set this as an important directory path.
Eventually, I chose something more streamlined:
1. We use `sudo` to perform a one-time invocation of `steamcmd` as the `steam` user (`sudo -u $steamcmd_user ...`)
2. steamcmd fetches the mods as a concatenated list in one go, and writes them to `/tmp` (`+force_install_dir $staging_dir`). `/tmp` seemed like the ideal choice because, by design, all users can see this directory, and it uses the sticky bit, so it's painless to change ownership of the incoming files to the real user and move them out. Lastly, `/tmp` is the de facto place for ephemeral files, so there is no worry about destructively changing files if the write operation is malformed for some reason--although it is unlikely to be. Note that `steamcmd` does not have great error handling or provide correct return codes even if the wrong credentials are supplied, and likes to write a `steamapps`directory (albeit empty) into the destination directory when failing. Taking this into account, `/tmp` seemed like a good place.
3. `sudo` is used to change ownership of the incoming files back to the real user and its effective group id (`sudo chown -R $USER:$gid $staging_dir/steamapps`)
4. The individual mod ID directories are copied out of `/tmp` to the real mod path.
4. Once finished, the directory leftovers are removed from `/tmp` (`rm -r $staging_dir/steamapps`). The `steamapps` root may contain some files we don't want to conflict with the real workshop path, so we just take the mods, not the whole directory stack. Later invocations of `steamcmd` may fail if there is some detritus in `steamapps`, so it needs to be removed.
There are other ways of solving this, but in terms of skipping group and user management and keeping things fast and simple, this seems reasonable. At most, the user will be asked to input their Stream credentials once and never again until the hash is revoked, and their password (sudo) once. In addition, mod downloading stops becoming a recurring need once it's been done for a few servers.
Obviously, those credentials go through `steamcmd` directly to Valve and not through this script. See the function `auto_mod_download()` to see the exact command being used.
## Usage: ## Usage:
@@ -104,8 +99,7 @@ Lastly, the launch options and mod list will be concatenated and used to launch
- Servers don't verbosely report the version of mods they are running, so other than checking that the mod exists, we can't easily compare version differences. This means you may get booted on connect if your mod is not up to date. (Shouldn't happen due to Steam updates, but it's theoretically possible if a server is pegged to some older version). If that happens, you could manually install the desired version and reconnect. - Servers don't verbosely report the version of mods they are running, so other than checking that the mod exists, we can't easily compare version differences. This means you may get booted on connect if your mod is not up to date. (Shouldn't happen due to Steam updates, but it's theoretically possible if a server is pegged to some older version). If that happens, you could manually install the desired version and reconnect.
- The server protocol enforces a maximum length of 64 characters, so server names should never exceed the acceptable width of the table, making truncation unneccessary. - The server protocol enforces a maximum length of 64 characters, so server names should never exceed the acceptable width of the table, making truncation unneccessary.
- This was not tested with Flatpak steam or Steam native runtime. If there is a particular method of invocation for those, submit an issue. - This was not tested with Flatpak steam or Steam native runtime. If there is a particular method of invocation for those, submit an issue.
- Official servers do not get indexed by server trackers in the usual manner, so this works only with custom servers. It might be possible to include a manifest of official servers in a future revision, however. - Official servers do not get indexed by server trackers in the usual manner, so this works only with custom servers. (DZGUI supports official servers)
- ~~In testing, at least one server behaved strangely despite the correct launch parameters being given and refused to start. (Investigating)~~ **Thanks to the user scandalouss for help in resolving this issue**
- You may still encounter weird bugs from DayZ itself due to the unstable nature of the client. (E.g., "User is not connected to Steam" when loading into a map.) - You may still encounter weird bugs from DayZ itself due to the unstable nature of the client. (E.g., "User is not connected to Steam" when loading into a map.)
- Wasn't tested on esoteric terminal emulators or fancy configs. (Tested against urxvt) - Wasn't tested on esoteric terminal emulators or fancy configs. (Tested against urxvt)
@@ -117,11 +111,4 @@ A. It is technically feasible using A2S, but is complicated and offers little be
Q. Why not allow browsing all servers in real time? Q. Why not allow browsing all servers in real time?
A. It greatly complicates the scope. There are 10K+ servers that would have to be queried and some kind of manifest created, to say nothing of sorting, searching, pagination, etc. Having a simple heads-up list of your servers of interest should suffice for most use cases. I am thinking of rewriting this in curses as a fully-featured browser at some point, but we would have to see what improvements are made to the in-game browser and launcher to see if this is maintainable or even necessary in the first place. A. This feature exists in DZGUI. It is not ideal for a TUI client due to the need for pagination/scrolling.
Q. What about adding the field «insert some field name here»?
A. See above. "Number of players in queue" could be added to help decide whether to join a full server, but this requires a custom API.
+152 -38
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
set -eo pipefail set -eo pipefail
version=0.3.0 version=0.7.1
release_url="https://raw.githubusercontent.com/aclist/dztui/main/dztui.sh" release_url="https://raw.githubusercontent.com/aclist/dztui/main/dztui.sh"
aid=221100 aid=221100
game="dayz" game="dayz"
@@ -12,8 +12,8 @@ steam_path="/path/to/steam"
workshop_dir="$steam_path/steamapps/workshop/content/$aid" workshop_dir="$steam_path/steamapps/workshop/content/$aid"
game_dir="$steam_path/steamapps/common/DayZ" game_dir="$steam_path/steamapps/common/DayZ"
key="APIKEY" key="APIKEY"
whitelist="8039514,8789747,4363928,8199330,11359652,12862329" whitelist=""
fav="8789747" fav=""
name="player" name="player"
separator="│" separator="│"
ping=1 ping=1
@@ -21,7 +21,7 @@ debug=0
#END CONFIG================ #END CONFIG================
#STEAMCMD CONFIG=========== #STEAMCMD CONFIG===========
auto_install_mods=0 auto_install_mods=1
steamcmd_user="steam" steamcmd_user="steam"
steam_username="STEAMUSER" steam_username="STEAMUSER"
staging_dir="/tmp" staging_dir="/tmp"
@@ -30,22 +30,40 @@ staging_dir="/tmp"
declare -A deps declare -A deps
deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [column]="2.37.2" [tr]="9.0" [comm]="9.0") deps=([awk]="5.1.1" [curl]="7.80.0" [jq]="1.6" [column]="2.37.2" [tr]="9.0" [comm]="9.0")
max_range=$(awk -F, '{print NF}' <<< $whitelist) max_range=$(awk -F, '{print NF}' <<< $whitelist)
[[ $debug -eq 0 ]] && mode=Normal || mode=Debug
err(){ err(){
printf "[ERROR] %s\n" "$1" printf "[ERROR] %s\n" "$1"
return 1 return 1
} }
download_new_version(){
source_script=$(realpath "$0")
source_dir=$(dirname "$source_script")
mv $source_script $source_script.old
curl -Ls "$release_url" > $source_script
rc=$?
if [[ $rc -eq 0 ]]; then
printf "[INFO] Wrote %s to %s\n" $upstream "$source_script"
printf "[INFO] Restart app to use new version\n"
chmod +x $source_script
exit
else
mv $source_script.old $source_script
err "Failed to download new version. Reverting to prior version and quitting."
exit
fi
}
version_check(){ version_check(){
upstream=$(curl -Ls "$release_url" | awk -F= 'NR==3 {print $2}') upstream=$(curl -Ls "$release_url" | awk -F= 'NR==3 {print $2}')
printf "[INFO] Checking for new version\n" printf "[INFO] Checking for new version\n"
if [[ ! $upstream == "$version" ]]; then if [[ ! $upstream == "$version" ]]; then
printf "[INFO] A newer version of DZTUI is available at:\n$release_url.\n" printf "[INFO] A newer version of DZTUI is available (%s)\n" $upstream
while true; do while true; do
read -p "Continue anyway? [Y/n] " response read -p "Attempt to download new version? [Y/n] " response
if [[ $response == "Y" ]]; then if [[ $response == "Y" ]]; then
break download_new_version
elif [[ $response == "n" ]]; then elif [[ $response == "n" ]]; then
exit_msg break
else else
: :
fi fi
@@ -79,7 +97,7 @@ checks() {
check_ping(){ check_ping(){
if [[ $ping -eq 1 ]]; then if [[ $ping -eq 1 ]]; then
ping_ip=$(echo -e "$i" | awk -F'\t' '{print $2}' | awk -F: '{print $1}') ping_ip=$(echo -e "$i" | awk -F'\t' '{print $2}' | awk -F: '{print $1}')
ms=$(ping -c 1 "$ping_ip" | awk -Ftime= '/time=/ {print $2}') ms=$(ping -c 1 -W 1 "$ping_ip" | awk -Ftime= '/time=/ {print $2}')
[[ -z $ms ]] && ms="Timeout" || : [[ -z $ms ]] && ms="Timeout" || :
printf "%s\t%s\n" "$i" "$ms" printf "%s\t%s\n" "$i" "$ms"
else else
@@ -93,11 +111,22 @@ parse_json(){
check_ping check_ping
done done
} }
encode(){
echo "$1" | awk '{printf("%c",$1)}' | base64 | sed 's/\//_/g; s/=//g; s/+/]/g'
}
legacy_symlinks(){
for d in "$game_dir"/*; do
if [[ $d =~ @[0-9]+-.+ ]]; then
unlink "$d"
fi
done
}
symlinks(){ symlinks(){
for d in "$workshop_dir"/*; do for d in "$workshop_dir"/*; do
id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}') id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
encoded_id=$(encode "$id")
mod=$(awk -F\" '/name/ {print $2}' "$d"/meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g') mod=$(awk -F\" '/name/ {print $2}' "$d"/meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g')
link="@$id-$mod" link="@$encoded_id"
[[ -h "$game_dir/$link" ]] && : || [[ -h "$game_dir/$link" ]] && : ||
printf "[INFO] Creating symlink for $mod\n" printf "[INFO] Creating symlink for $mod\n"
ln -fs "$d" "$game_dir/$link" ln -fs "$d" "$game_dir/$link"
@@ -136,11 +165,11 @@ table(){
done done
} }
concat_mods(){ concat_mods(){
readarray -t serv <<< "$remote_mods" readarray -t serv <<< "$(server_modlist)"
for i in "${serv[@]}"; do for i in "${serv[@]}"; do
id=$(awk -F"= " '/publishedid/ {print $2}' "$workshop_dir"/$i/meta.cpp | awk -F\; '{print $1}') id=$(awk -F"= " '/publishedid/ {print $2}' "$workshop_dir"/$i/meta.cpp | awk -F\; '{print $1}')
mod=$(awk -F\" '/name/ {print $2}' "$workshop_dir"/$i/meta.cpp | sed -E 's/[^[:alpha:]0-9]+/_/g; s/^_|_$//g') encoded_id=$(encode $id)
link="@$id-$mod;" link="@$encoded_id;"
echo -e "$link" echo -e "$link"
done | tr -d '\n' | perl -ple 'chop' done | tr -d '\n' | perl -ple 'chop'
} }
@@ -167,13 +196,13 @@ launch_fav(){
} }
manual_mod_install(){ manual_mod_install(){
printf "[ERROR] Missing mods. Open these links and subscribe to each one, then reconnect\n" printf "[ERROR] Missing mods. Open these links and subscribe to each one, then reconnect\n"
for i in $diff; do for i in $(diff); do
printf "%s%s\n" "$workshop" $i printf "%s%s\n" "$workshop" $i
done done
} }
steamcmd_modlist(){ steamcmd_modlist(){
for i in $diff; do for i in $(diff); do
printf "+workshop_download_item %s %s " $aid $i printf "+workshop_download_item %s %s validate " $aid $i
done done
} }
move_files(){ move_files(){
@@ -182,41 +211,95 @@ move_files(){
rm -r "$staging_dir"/steamapps rm -r "$staging_dir"/steamapps
} }
auto_mod_download(){ auto_mod_download(){
sudo su -c "steamcmd +force_install_dir $staging_dir +login $steam_username $(steamcmd_modlist) +quit" $steamcmd_user if [[ -d "$staging_dir/steamapps" ]]; then
sudo chown -R $USER:$gid "$staging_dir"/steamapps
rm -r "$staging_dir"/steamapps
fi
until [[ -z $(diff) ]]; do
printf "[INFO] Downloading missing mods\n"
sudo -iu steam bash -c "$steamcmd_path +force_install_dir $staging_dir +login $steam_username $(steamcmd_modlist) +quit" $steamcmd_user
printf "\n"
[[ "$(ls -A $staging_dir/steamapps)" ]] && move_files || return 1 [[ "$(ls -A $staging_dir/steamapps)" ]] && move_files || return 1
done
}
find_steam_cmd(){
for i in "/home/steam" "/usr/share" "/usr/bin" "/"; do
steamcmd_path=$(sudo find "$i" -name steamcmd.sh 2>/dev/null | grep -v linux32 | head -n1)
if [[ -n "$steamcmd_path" ]]; then
printf "[INFO] Found steamcmd at '$steamcmd_path'\n"
return 0
else
return 1
fi
done
} }
auto_mod_install(){ auto_mod_install(){
printf "[ERROR] Missing mods. Invoking steamcmd for user $steamcmd_user\n" printf "[ERROR] Missing mods. Checking for steamcmd user '$steamcmd_user'\n"
if [[ -z $steamcmd_user ]]; then if [[ -z $steamcmd_user ]]; then
err "steamcmd user value was empty. Reverting to manual mode" err "steamcmd user value was empty. Reverting to manual mode"
elif elif
id $steamcmd_user &>/dev/null id $steamcmd_user &>/dev/null
[[ $? -eq 1 ]]; then [[ $? -eq 1 ]]; then
err "Invalid steamcmd user. Reverting to manual mode" err "Invalid steamcmd user. Reverting to manual mode"
elif
command -v steamcmd &>/dev/null
[[ $? -eq 1 ]]; then
err "steamcmd not installed. See: https://developer.valvesoftware.com/wiki/SteamCMD"
else else
printf "[INFO] Found steamcmd user. Downloading mods\n" printf "[INFO] Found steamcmd user '$steamcmd_user'\n"
fi
find_steam_cmd
if [[ $? -eq 1 ]]; then
err "steamcmd not found. See: https://developer.valvesoftware.com/wiki/SteamCMD"
else
revert_msg="Something went wrong. Reverting to manual mode" revert_msg="Something went wrong. Reverting to manual mode"
auto_mod_download auto_mod_download
[[ $? -eq 0 ]] && printf "\n"; init_table || err "$revert_msg" if [[ $? -eq 0 ]]; then
printf "\n"
passed_mod_check
else
err "$revert_msg"
fi
fi fi
} }
failed_mod_check(){ failed_mod_check(){
[[ $auto_install_mods -eq 1 ]] && auto_mod_install || manual_mod_install disksize=$(df $staging_dir --output=avail | tail -n1)
bytewise=$((disksize * 1024))
hr=$(echo $(numfmt --to=iec --format "%8.1f" $bytewise $totalmodsize) | sed 's/ /\//')
if [[ $auto_install_mods -eq 1 ]]; then
if [[ $totalmodsize -gt $bytewise ]]; then
printf "[ERROR] Not enough space in /tmp to automatically stage mods: %s\n" $hr
manual_mod_install
else
auto_mod_install
fi
fi
} }
passed_mod_check(){ passed_mod_check(){
printf "[INFO] Mod check passed\n" printf "[INFO] Mod check passed\n"
connecting_to=$(echo -e "${tabled[$sel]}" | awk -F'\t' '{print $1,$2}') connecting_to=$(echo -e "${tabled[$sel]}" | awk -F'\t' '{print $1,$2}')
legacy_symlinks
symlinks symlinks
launch launch
} }
check_workshop(){ query_defunct(){
curl -Ls "$url${modlist[$i]}" | grep data-appid | awk -F\" '{print $8}' max=${#modlist[@]}
printf "[INFO] Verifying integrity of server modlist manifest\n"
tput cnorm
#printf "\n"
concat(){
for ((i=0;i<$max;i++)); do
echo "publishedfileids[$i]=${modlist[$i]}&"
done | awk '{print}' ORS=''
}
payload(){
echo -e "itemcount=${max}&$(concat)"
}
post(){
curl -s -X POST -H "Content-Type:application/x-www-form-urlencoded" -d "$(payload)" 'https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/?format=json'
}
result=$(post | jq -r '.[].publishedfiledetails[] | select(.result==1) | "\(.file_size) \(.publishedfileid)"')
readarray -t newlist <<< $(echo -e "$result" | awk '{print $2}')
totalmodsize=$(echo -e "$result" | awk '{s+=$1}END{print s}')
} }
validate_mods(){ validate_mods(){
url="https://steamcommunity.com/sharedfiles/filedetails/?id=" url="https://steamcommunity.com/sharedfiles/filedetails/?id="
@@ -224,27 +307,23 @@ validate_mods(){
tput civis tput civis
newlist=() newlist=()
readarray -t modlist <<< $remote_mods readarray -t modlist <<< $remote_mods
for ((i=0;i<=${#modlist[@]};i++)); do query_defunct
printf "[INFO] Verifying integrity of server modlist manifest [$i/${#modlist[@]}]\r"
[[ $(check_workshop) -eq $aid ]] && newlist+=("${modlist[$i]}") || :
sleep 2s
done
tput cnorm
printf "\n"
} }
server_modlist(){ server_modlist(){
for i in "${newlist[@]}"; do for i in "${newlist[@]}"; do
printf "$i\n" printf "$i\n"
done done
} }
diff(){
comm -23 <(server_modlist | sort) <(installed_mods | sort)
}
compare(){ compare(){
fetch_mods fetch_mods
validate_mods validate_mods
diff=$(comm -23 <(server_modlist | sort) <(installed_mods | sort))
} }
connect(){ connect(){
compare compare
if [[ -n $diff ]]; then if [[ -n $(diff) ]]; then
failed_mod_check failed_mod_check
else else
passed_mod_check passed_mod_check
@@ -258,22 +337,26 @@ query_api(){
response=$(curl -s "$api" -H "Authorization: Bearer "$key"" -G -d "sort=-players" \ response=$(curl -s "$api" -H "Authorization: Bearer "$key"" -G -d "sort=-players" \
-d "filter[game]=$game" -d "filter[ids][whitelist]=$whitelist") -d "filter[game]=$game" -d "filter[ids][whitelist]=$whitelist")
if [[ "$(jq -r 'keys[]' <<< $response)" == "errors" ]]; then if [[ "$(jq -r 'keys[]' <<< $response)" == "errors" ]]; then
printf "\n"
printf "[ERROR] %s: check API key\n" "$(jq -r '.errors[] .status' <<< $response)" printf "[ERROR] %s: check API key\n" "$(jq -r '.errors[] .status' <<< $response)"
tput cnorm
return 1 return 1
elif elif
[[ -z "$(jq -r '.data[]' <<< $response)" ]]; then [[ -z "$(jq -r '.data[]' <<< $response)" ]]; then
printf "[ERROR] Check server ID\n" printf "[ERROR] Check server ID\n"
tput cnorm
return 1 return 1
fi fi
} }
init_table(){ init_table(){
tput civis tput civis
printf "[INFO] Polling servers. Please wait.\r" printf "[INFO] Polling servers. Please wait.\n"
query_api query_api
readarray -t tabled <<< $(parse_json <<< $response) readarray -t tabled <<< $(parse_json <<< $response)
tput cnorm tput cnorm
tput cuu1 tput cuu1
tput el tput el
printf "\n"
table | columnize table | columnize
} }
get_sorted_id(){ get_sorted_id(){
@@ -284,11 +367,13 @@ menu(){
printf "f$separator Launch favorite\n" printf "f$separator Launch favorite\n"
printf "l$separator List installed mods\n" printf "l$separator List installed mods\n"
printf "r$separator Refresh\n" printf "r$separator Refresh\n"
if [[ $debug -eq 1 ]]; then
printf "d$separator Debug options\n"
fi
printf "q$separator Quit\n" printf "q$separator Quit\n"
printf "\n" printf "\n"
} }
exit_msg(){ exit_msg(){
printf "DZTUI $version\n"
exit exit
} }
forced_exit(){ forced_exit(){
@@ -296,11 +381,39 @@ forced_exit(){
printf "\n" printf "\n"
exit_msg exit_msg
} }
list_mod_names(){
cd $game_dir
for i in $(find * -maxdepth 1 -type l); do
awk -F"[=;]" -v var="$i" '{OFS="\t"}/name/ {a=$2}END{print var,a}' "$i"/meta.cpp \
| sed 's/;$//g'
done | column -t -s$'\t' -o$'\t' | less
}
debug_options(){
if [[ $debug -eq 1 ]]; then
while true; do
printf "\n"
printf "1$separator List human readable mod paths\n"
printf "q$separator Back\n"
printf "\n"
read -p "Selection: " option
if [[ $option == 1 ]]; then
list_mod_names
elif [[ $option == q ]]; then
return
else
:
fi
done
else
:
fi
}
main(){ main(){
checks checks
init_table init_table
while true; do while true; do
menu menu
printf "[DZTUI $version/$mode]\n"
read -p "Selection: " sel read -p "Selection: " sel
if [[ $sel =~ ^[0-9]+$ ]]; then if [[ $sel =~ ^[0-9]+$ ]]; then
if [[ $sel -gt $max_range ]]; then if [[ $sel -gt $max_range ]]; then
@@ -314,6 +427,7 @@ main(){
r) init_table ;; r) init_table ;;
f) launch_fav ;; f) launch_fav ;;
l) list_mods ;; l) list_mods ;;
d) debug_options;;
q) exit_msg ;; q) exit_msg ;;
*) : ;; *) : ;;
esac esac