From ddd9df5b369a63331e84d51134334d9083bb88a2 Mon Sep 17 00:00:00 2001 From: wytch Date: Fri, 27 Feb 2026 21:19:38 -0600 Subject: [PATCH] Speedy yet? --- pub_event[rungame,endgame](sync).ash | 39 +++++----- savesync[start](permanent).ash | 107 ++++++++++++--------------- 2 files changed, 65 insertions(+), 81 deletions(-) diff --git a/pub_event[rungame,endgame](sync).ash b/pub_event[rungame,endgame](sync).ash index 6ded79a..bfaecaf 100644 --- a/pub_event[rungame,endgame](sync).ash +++ b/pub_event[rungame,endgame](sync).ash @@ -1,51 +1,48 @@ #!/bin/ash # shellcheck shell=dash -# 1. Load Configs - Use absolute paths . /recalbox/share/system/configs/savesync/savesync.conf -# 2. Setup Variables +# Constants EVENT_FILE="/tmp/es_state.inf" -RESPONSE_TOPIC="recalbox/savesync/response" TIMEOUT_SEC=10 log() { - # Ensure port is 1883 - mosquitto_pub -h 127.0.0.1 -p 1883 -q 0 -t "$LOG_TOPIC" -m "SaveLog=[$(date '+%H:%M:%S')] [$1] $2" 2>/dev/null + mosquitto_pub -h 127.0.0.1 -p 1883 -q 0 -t "$LOG_TOPIC" -m "SaveLog=[$(date '+%H:%M:%S')] [PUB] $1" 2>/dev/null } -# 3. Check Payload +# 1. Validation [ ! -s "$EVENT_FILE" ] && exit 0 PAYLOAD=$(cat "$EVENT_FILE") -# 4. Check Connection quickly (1 second max) -#nc -z -w 1 "$RCLONE_ENDPOINT" "$RCLONE_PORT" || { log "ERROR" "Offline"; exit 1; } -log "DEBUG" "Checking connection to: ${RCLONE_ENDPOINT}:${RCLONE_PORT}" -nc -zv -w 1 "$RCLONE_ENDPOINT" "$RCLONE_PORT" 2>&1 | log "DEBUG" "$(cat)" +# 2. Network Check (Short-circuit exit) +nc -z -w 1 "$RCLONE_ENDPOINT" "$RCLONE_PORT" || { + log "Offline: Skipping sync" + exit 0 +} -# 5. Publish Request -if mosquitto_pub -h 127.0.0.1 -p 1883 -t "$TOPIC" -m "$PAYLOAD"; then - log "INFO" "Event published. Waiting for Daemon..." -else - log "ERROR" "MQTT Broker unreachable." +# 3. Publish Event +mosquitto_pub -h 127.0.0.1 -p 1883 -t "$TOPIC" -m "$PAYLOAD" || { + log "MQTT Fail" exit 1 -fi +} -# 6. The Wait (With Timeout) -# We listen for the daemon to say "SaveContinue=0" +# 4. Wait for Response (Crucial: Using -C 1 and the same Response Key) ( sleep "$TIMEOUT_SEC" mosquitto_pub -h 127.0.0.1 -t "$RESPONSE_TOPIC" -m "SaveSync=timeout" ) & TIMER_PID=$! -# Wait for exactly 1 message +log "Waiting for Daemon response..." +# Listen for the specific "SaveSync" key RESPONSE=$(mosquitto_sub -h 127.0.0.1 -p 1883 -t "$RESPONSE_TOPIC" -C 1) kill "$TIMER_PID" 2>/dev/null case "$RESPONSE" in -*"timeout"*) log "ERROR" "Daemon timed out. Starting game with local save." ;; -*) log "INFO" "Daemon finished. Proceeding." ;; +*"timeout"*) log "WARN: Daemon timed out. Starting game anyway." ;; +*"0"*) log "INFO: Sync Complete. Launching game." ;; +*) log "INFO: Received response: $RESPONSE" ;; esac exit 0 diff --git a/savesync[start](permanent).ash b/savesync[start](permanent).ash index 94cbb38..f4435a2 100644 --- a/savesync[start](permanent).ash +++ b/savesync[start](permanent).ash @@ -1,88 +1,75 @@ #!/bin/ash -# shellcheck shell=ash +# shellcheck shell=dash -# --- Configuration --- . /recalbox/share/system/configs/savesync/savesync.conf log() { - local level="$1" - local msg="$2" - local timestamp=$(date '+%Y-%m-%d %H:%M:%S') - local log_line="[$timestamp] [$level] $msg" - - if [ "$level" = "ERROR" ]; then - mkdir -p "$(dirname "$ERROR_LOG")" - printf "%s\n" "$log_line" >>"$ERROR_LOG" - fi - - # FIX: Corrected port to 1883 and added -m - mosquitto_pub -h 127.0.0.1 -p 1883 -q 0 -t "$LOG_TOPIC" -m "SaveLog=$log_line" 2>/dev/null - - [ "${DEBUG_MODE:-0}" -eq 1 ] && printf "%s\n" "$log_line" + mosquitto_pub -h 127.0.0.1 -p 1883 -q 0 -t "$LOG_TOPIC" -m "SaveLog=[$(date '+%H:%M:%S')] [DAEMON] $1" 2>/dev/null } -sleep 2 -log "INFO" "--- ES Event Daemon Started ---" +log "INFO" "--- Daemon Active ---" -# --- Main Listener Loop --- -mosquitto_sub -h 127.0.0.1 -p 1883 -q 0 -t "$TOPIC" | while IFS="=" read -r key value; do - # 1. Clean input - value=$(echo "$value" | tr -d '\r') +mosquitto_sub -h 127.0.0.1 -p 1883 -t "$TOPIC" | while IFS="=" read -r key value; do + # Clean input + val=$(echo "$value" | tr -d '\r') case "$key" in - "SystemId") this_system_id="$value" ;; + "SystemId") sid="$val" ;; "GamePath") - this_game_path="$value" - # Replace 'roms' with 'saves' and change extension to .srm - tmp_path="${value/roms/saves}" - this_save_path="${tmp_path%.*}.srm" - # Replace 'saves' with 'archives' for backup - this_backup_path="${this_save_path/saves/archives}" + gp="$val" + # Fast path transformation using sed + sp=$(echo "$val" | sed 's|roms|saves|; s|\.[^.]*$|.srm|') + bp=$(echo "$sp" | sed 's|saves|archives|') ;; - "Action") this_action="$value" ;; + "Action") act="$val" ;; "State") - this_state="$value" + st="$val" - if [ "$this_state" = "playing" ] && [ "$this_action" = "rungame" ]; then - # PASS CURRENT VARS TO SUBSHELL + # Check if we have the full "Start Game" context + if [ "$st" = "playing" ] && [ "$act" = "rungame" ]; then + # FORK to background, but keep current variables! ( - # We use local copies inside the subshell to be safe - sid="$this_system_id" - gp="$this_game_path" - sp="$this_save_path" - bp="$this_backup_path" + log "INFO" "Syncing START for $(basename "$gp")" - log "INFO" "Game Started: $(basename "$gp")" - filename=$(basename "$sp") - remote_full="$REMOTE_BASE/$sid/$filename" + remote_f="$REMOTE_BASE/$sid/$(basename "$sp")" + loc_sz=$(stat -c %s "$sp" 2>/dev/null || echo 0) - # rclone operations... - local_size=$(stat -c %s "$sp" 2>/dev/null || echo 0) - remote_size=$(rclone lsjson "$remote_full" 2>/dev/null | grep -o '"Size":[0-9]*' | cut -d: -f2) - : "${remote_size:=0}" + # Fetch remote size + rem_sz=$(rclone lsjson "$remote_f" 2>/dev/null | grep -o '"Size":[0-9]*' | cut -d: -f2) + : "${rem_sz:=0}" - if [ "$local_size" -lt "$remote_size" ]; then - log "WARN" "Cloud save LARGER. Restoring..." + if [ "$loc_sz" -lt "$rem_sz" ]; then + log "WARN" "Cloud save larger. Restoring..." mkdir -p "$(dirname "$bp")" - rclone copyto "$remote_full" "$sp" --backup-dir "$(dirname "$bp")" + rclone copyto "$remote_f" "$sp" --backup-dir "$(dirname "$bp")" else - log "INFO" "Local save safe. Updating..." - rclone update "$remote_full" "$sp" + log "INFO" "Local save current. Updating..." + rclone update "$remote_f" "$sp" fi - # FIX: Ensure you are sending the EXACT key the publisher is waiting for - # If your publisher is looking for "SaveSync", change "SaveContinue" below - mosquitto_pub -h 127.0.0.1 -p 1883 -t "$RESPONSE_TOPIC" -m "SaveSync=ok" + # SIGNAL SUCCESS (Key must match Publisher's expectation) + mosquitto_pub -h 127.0.0.1 -p 1883 -t "$RESPONSE_TOPIC" -m "SaveSync=0" + ) & + + elif [ "$st" = "endgame" ]; then + # Syncing on end - we don't necessarily need to block here + ( + log "INFO" "Syncing END for $(basename "$sp")" + rclone update "$sp" "$REMOTE_BASE/$sid/" + log "INFO" "Final Sync Done." + + # Optional: Tell publisher we are done (though ES usually doesn't wait on endgame) + mosquitto_pub -h 127.0.0.1 -p 1883 -t "$RESPONSE_TOPIC" -m "SaveSync=0" ) & - log "DEBUG" "Sync backgrounded." fi - # Variable resets happen here, safely outside the child process - this_system_id="" - this_game_path="" - this_save_path="" - this_action="" - this_backup_path="" + # Clear variables for next burst of events + sid="" + gp="" + sp="" + bp="" + act="" + st="" ;; esac done