diff --git a/CHANGELOG.md b/CHANGELOG.md
index ecfc1b3..14a1345 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Changelog
 
+## [5.6.0-beta.20] 2024-12-23
+### Added
+- Output real and resolved mod ids to logs (temporary)
+- Added -steam launch parameter
+### Fixed
+- Only iterate on missing symlinks
+- Move logging up
+
 ## [5.6.0-beta.19] 2024-12-18
 ### Added
 - Redact usernames in log files
diff --git a/dzgui.sh b/dzgui.sh
index ac2de14..2d3b184 100755
--- a/dzgui.sh
+++ b/dzgui.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -o pipefail
 
-version=5.6.0-beta.19
+version=5.6.0-beta.20
 
 #CONSTANTS
 aid=221100
@@ -587,7 +587,7 @@ fetch_helpers_by_sum(){
         ["ui.py"]="99544ccef6060125509c4b689a808a15"
         ["query_v2.py"]="55d339ba02512ac69de288eb3be41067"
         ["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397"
-        ["funcs"]="05f104fcdf27222f04046d41ec48d692"
+        ["funcs"]="5d69e8e3d7c3b3c499354b0b939ce76b"
         ["lan"]="c62e84ddd1457b71a85ad21da662b9af"
     )
     local author="aclist"
@@ -893,10 +893,10 @@ test_connection(){
     if [[ $res -ne 200 ]]; then
         logger WARN "Remote host '${hr["github.com"]}' unreachable', trying fallback"
         remote_host=cb
+        logger INFO "Set remote host to '${hr["codeberg.org"]}'"
         res=$(get_response_code "${hr["codeberg.org"]}")
         [[ $res -ne 200 ]] && raise_error_and_quit "$str (${hr["codeberg.org"]})"
     fi
-    logger INFO "Set remote host to '${hr["codeberg.org"]}'"
     if [[ $remote_host == "cb" ]]; then
         url_prefix="https://codeberg.org/$author/$repo/raw/branch"
         releases_url="https://codeberg.org/$author/$repo/releases/download/browser"
@@ -1004,6 +1004,9 @@ main(){
         uninstall &&
         exit 0
     fi
+    if [[ $1 == "--steam" ]] || [[ $1 == "-s" ]]; then
+        export STEAM_LAUNCH=1
+    fi
     
     set_im_module
 
diff --git a/helpers/funcs b/helpers/funcs
index 5989793..ea965b3 100755
--- a/helpers/funcs
+++ b/helpers/funcs
@@ -29,6 +29,8 @@ prefix="dzg"
 log_path="$state_path/logs"
 debug_log="$log_path/DZGUI_DEBUG.log"
 system_log="$log_path/DZGUI_SYSTEM.log"
+mod_log="$log_path/DZGUI_MODIDS.log"
+meta_log="$log_path/DZGUI_META.log"
 
 #STATE FILES
 history_file="$state_path/$prefix.history"
@@ -866,12 +868,12 @@ test_connection(){
     if [[ $res -ne 200 ]]; then
         logger WARN "Remote host '${hr["github.com"]}' unreachable', trying fallback"
         remote_host=cb
+        logger INFO "Set remote host to '${hr["codeberg.org"]}'"
         res=$(get_response_code "${hr["codeberg.org"]}")
         if [[ $res -ne 200 ]]; then
             return 1
         fi
     fi
-    logger INFO "Set remote host to '${hr["codeberg.org"]}'"
     if [[ $remote_host == "cb" ]]; then
         url_prefix="https://codeberg.org/$author/$repo/raw/branch"
         releases_url="https://codeberg.org/$author/$repo/releases/download/browser"
@@ -1165,6 +1167,12 @@ $(print_ip_list | sed 's/"//g')
 	Mods:
 $(list_mods | sed 's/^/\t/g')
 	DOC
+    #2024-12-13
+    find $workshop_dir -mindepth 1 -maxdepth 1 -type d | awk -F/ '{print $NF}' | sort > "$mod_log"
+    find $workshop_dir -mindepth 1 -name meta.cpp | while read -r line; do
+        cat "$line" | awk '/publishedid/ {print $3}' | sed 's/;//g;s/\r//g'
+    done | sort > "$meta_log"
+    #END
     printf "Wrote system log to %s" "$system_log"
     return 0
 }
@@ -1220,22 +1228,60 @@ legacy_symlinks(){
     fi
 }
 symlinks(){
-    readarray -t mod_dirs < <(find "$workshop_dir" -maxdepth 1 -mindepth 1 -type d)
-    [[ ${#mod_dirs[@]} -eq 0 ]] && return
-    logger INFO "Generating symlinks in new format"
-    for d in "${mod_dirs[@]}"; do
-        # suppress errors if mods are downloading at boot
-        [[ ! -f "$d/meta.cpp" ]] && continue
-        id=$(awk -F"= " '/publishedid/ {print $2}' "$d"/meta.cpp | awk -F\; '{print $1}')
-        encoded_id=$(encode "$id")
-        link="@$encoded_id"
-        if [[ -h "$game_dir/$link" ]]; then
-            logger INFO "Symlink already exists: '$link' for mod '$id'"
-            continue
+    _merge(){
+        comm -23 <(printf "%s\n" "${mods[@]}" | sort) <(printf "%s\n" "${targets[@]}" | sort)
+    }
+    _pulse(){
+        zenity --pulsate --progress --auto-close --no-cancel --title="DZGUI"
+    }
+    _create_links(){
+        local arr=("$@")
+        local encoded_id
+        local link
+        local mod
+        for ((i=0; i<${#arr[@]}; i++)); do
+            encoded_id=$(encode "${arr[$i]}")
+            link="@$encoded_id"
+            mod="${arr[$i]}"
+            logger INFO "Creating link '$game_dir/$link' for '$workshop_dir/$mod'"
+            [[ $STEAM_LAUNCH -eq 1 ]] && echo "# Creating mod link $((i+1))/${#arr[@]}"
+            ln -s "$workshop_dir/$mod" "$game_dir/$link"
+        done
+    }
+
+    readarray -t mods < <(find $workshop_dir -mindepth 1 -name meta.cpp | awk -F/ 'NF=NF-1{print $NF}')
+    readarray -t links < <(find $game_dir -type l)
+
+    if [[ ${#mods[@]} -eq 0 ]]; then
+        logger INFO "No mods present, aborting"
+        return
+    fi
+
+    if [[ ${#links[@]} -eq 0 ]]; then
+        logger INFO "No symlinks present in '$game_dir', creating them"
+        if [[ $STEAM_LAUNCH -eq 1 ]]; then
+            _create_links "${mods[@]}" > >(_pulse)
+        else
+            _create_links "${mods[@]}"
         fi
-        ln -fs "$d" "$game_dir/$link"
-        logger INFO "Created symlink '$link' for mod '$id'"
-    done
+        return
+    fi
+
+    readarray -t targets < <(printf "%s\n" "${links[@]}" | xargs readlink -f | awk -F/ '{print $NF}')
+    readarray -t hits < <(_merge)
+
+    if [[ ${#hits[@]} -eq 0 ]]; then
+        logger INFO "Symlinks are up to date, skipping"
+        return
+    fi
+
+    # update missing targets
+    logger INFO "Found ${#hits[@]} unlinked mods"
+    if [[ $STEAM_LAUNCH -eq 1 ]]; then
+        _create_links "${hits[@]}" > >(_pulse)
+    else
+        _create_links "${hits[@]}"
+    fi
 }
 update_history(){
     local record="$1"