Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "deadlock-api-ingest"
version = "0.2.3"
version = "0.2.4"
description = "Deadlock API Ingest"
repository = "https://github.com/deadlock-api/deadlock-api-ingest"
license = "MIT"
Expand Down
71 changes: 67 additions & 4 deletions install-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,24 @@ manage_service() {
;;
"create")
local executable_path="$2"
local service_extra_args="${3:-}"

# Create systemd user directory if it doesn't exist
mkdir -p "$SYSTEMD_USER_DIR"

local exec_start="$executable_path"
if [[ -n "$service_extra_args" ]]; then
exec_start="$executable_path $service_extra_args"
fi

cat > "$SYSTEMD_SERVICE_FILE" << EOF
[Unit]
Description=Deadlock API Ingest - Monitors Steam cache for match replays
Documentation=https://github.com/deadlock-api/deadlock-api-ingest

[Service]
Type=simple
ExecStart=$executable_path
ExecStart=$exec_start
Restart=on-failure
RestartSec=10
StandardOutput=journal
Expand Down Expand Up @@ -331,6 +337,50 @@ prompt_for_autostart() {
return 0
}

# Function to prompt user for Statlocker integration
prompt_for_statlocker() {
# Check if we're running in an interactive terminal
if [[ ! -t 0 ]] || [[ ! -t 1 ]]; then
log "INFO" "Non-interactive mode detected. Enabling Statlocker integration by default."
return 0
fi

log "INFO" "Statlocker integration sends match IDs to statlocker.gg after each ingestion."
log "INFO" "This helps track community match statistics. No personal data is sent."
echo >&2

local attempts=0
local max_attempts=2

while [[ $attempts -lt $max_attempts ]]; do
echo -n "Would you like to enable Statlocker integration? (y/n): " >&2

local response
if read -t 10 -r response; then
case "${response,,}" in
y|yes)
return 0
;;
n|no)
return 1
;;
*)
attempts=$((attempts + 1))
if [[ $attempts -lt $max_attempts ]]; then
echo "Invalid response. Please enter 'y' for yes or 'n' for no." >&2
fi
;;
esac
else
log "INFO" "No response received within 10 seconds. Enabling Statlocker integration by default."
return 0
fi
done

log "INFO" "Maximum attempts reached. Enabling Statlocker integration by default."
return 0
}

# --- Main Installation Logic ---
main() {
log "INFO" "Starting Deadlock API Ingest installation..."
Expand Down Expand Up @@ -395,8 +445,17 @@ main() {
log "INFO" "You can manually download it from: $uninstall_script_url"
fi

# Prompt user for Statlocker integration
local extra_args=""
if ! prompt_for_statlocker; then
extra_args="--no-statlocker"
log "INFO" "Statlocker integration disabled."
else
log "SUCCESS" "Statlocker integration enabled."
fi

# Create the main service (but don't enable/start it yet)
manage_service "create" "$final_executable_path"
manage_service "create" "$final_executable_path" "$extra_args"

# Prompt user for auto-start setup
if prompt_for_autostart; then
Expand Down Expand Up @@ -458,12 +517,16 @@ main() {
local main_created=false
local once_created=false

if create_desktop_shortcut "$final_executable_path" "" "Deadlock API Ingest" "Monitors Steam cache for Deadlock match replays"; then
if create_desktop_shortcut "$final_executable_path" "$extra_args" "Deadlock API Ingest" "Monitors Steam cache for Deadlock match replays"; then
main_created=true
fi

# Create "once" shortcut for initial cache ingest only
if create_desktop_shortcut "$final_executable_path" "--once" "Deadlock API Ingest (Once)" "Scan existing Steam cache once and exit"; then
local once_args="--once"
if [[ -n "$extra_args" ]]; then
once_args="--once $extra_args"
fi
if create_desktop_shortcut "$final_executable_path" "$once_args" "Deadlock API Ingest (Once)" "Scan existing Steam cache once and exit"; then
once_created=true
fi

Expand Down
103 changes: 95 additions & 8 deletions install-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,20 @@ function Set-StartupTask {
$vbsWrapperPath = Join-Path -Path $InstallDir -ChildPath "run-hidden.vbs"
$vbsContent = @"
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run """$ExecutablePath""", 0, False
args = ""
For Each arg In WScript.Arguments
args = args & " " & arg
Next
WshShell.Run """$ExecutablePath""" & args, 0, False
"@
Set-Content -Path $vbsWrapperPath -Value $vbsContent -Force

# Define the action (run the VBS wrapper with wscript.exe to hide the window)
$taskAction = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "`"$vbsWrapperPath`"" -WorkingDirectory $InstallDir
# Define the action (run the VBS wrapper with wscript.exe, forwarding extra args to the exe)
$vbsArgs = "`"$vbsWrapperPath`""
if ($extraArgs -ne "") {
$vbsArgs = "`"$vbsWrapperPath`" $extraArgs"
}
$taskAction = New-ScheduledTaskAction -Execute "wscript.exe" -Argument $vbsArgs -WorkingDirectory $InstallDir

# Define the trigger (when to run it - at user logon)
$taskTrigger = New-ScheduledTaskTrigger -AtLogOn
Expand Down Expand Up @@ -394,6 +402,84 @@ try {

Write-InstallLog -Level 'INFO' "Installing application..."

# Check if running in interactive mode (used by all prompts below)
$isInteractive = [Environment]::UserInteractive -and -not [Console]::IsInputRedirected

# Ask user if they want Statlocker integration
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " STATLOCKER INTEGRATION (OPTIONAL) " -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Statlocker integration sends match IDs to statlocker.gg after each ingestion." -ForegroundColor White
Write-Host "This helps track community match statistics. No personal data is sent." -ForegroundColor White
Write-Host ""
Write-Host "Enable Statlocker integration? (Y/N): " -ForegroundColor Yellow -NoNewline

$enableStatlocker = $false
$statlockerAttempts = 0
$maxStatlockerAttempts = 2

if (-not $isInteractive) {
Write-Host "Y (default in non-interactive mode)" -ForegroundColor Cyan
Write-InstallLog -Level 'INFO' "Non-interactive mode detected. Enabling Statlocker integration by default."
$enableStatlocker = $true
} else {
Comment on lines +423 to +427
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$isInteractive is referenced here before it’s assigned later in the script. As written, this branch will treat interactive runs as non-interactive (since $isInteractive is $null), so the Statlocker prompt won’t appear and will always default to enabled. Initialize $isInteractive before this prompt, or compute a dedicated interactivity flag locally.

Copilot uses AI. Check for mistakes.
$timeoutSeconds = 10
$startTime = Get-Date
$keyPressed = $false

while ($statlockerAttempts -lt $maxStatlockerAttempts -and -not $keyPressed) {
if ([Console]::KeyAvailable) {
$response = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
$key = $response.Character.ToString().ToUpper()

if ($key -eq "Y" -or $key -eq "N") {
Write-Host $key -ForegroundColor Cyan
if ($key -eq "Y") {
$enableStatlocker = $true
}
$keyPressed = $true
break
} else {
$statlockerAttempts++
if ($statlockerAttempts -lt $maxStatlockerAttempts) {
Write-Host ""
Write-Host "Invalid response. Please enter 'Y' for yes or 'N' for no." -ForegroundColor Yellow
Write-Host "Enable Statlocker integration? (Y/N): " -ForegroundColor Yellow -NoNewline
}
}
}

# Check timeout
if (((Get-Date) - $startTime).TotalSeconds -ge $timeoutSeconds -and -not $keyPressed) {
Write-Host "Y (timeout - defaulting to yes)" -ForegroundColor Cyan
Write-InstallLog -Level 'INFO' "No response received within $timeoutSeconds seconds. Enabling Statlocker integration by default."
$enableStatlocker = $true
$keyPressed = $true
break
}

Start-Sleep -Milliseconds 100
}

if (-not $keyPressed) {
Write-Host "Y (max attempts reached - defaulting to yes)" -ForegroundColor Cyan
Write-InstallLog -Level 'INFO' "Maximum attempts reached. Enabling Statlocker integration by default."
$enableStatlocker = $true
}
}

Write-Host ""

$extraArgs = ""
if ($enableStatlocker) {
Write-InstallLog -Level 'SUCCESS' "Statlocker integration enabled."
} else {
$extraArgs = "--no-statlocker"
Write-InstallLog -Level 'INFO' "Statlocker integration disabled."
}

# Ask user if they want auto-start
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Expand All @@ -410,9 +496,6 @@ try {
$autoStartAttempts = 0
$maxAutoStartAttempts = 2

# Check if running in interactive mode
$isInteractive = [Environment]::UserInteractive -and -not [Console]::IsInputRedirected

if (-not $isInteractive) {
Write-Host "Y (default in non-interactive mode)" -ForegroundColor Cyan
Write-InstallLog -Level 'INFO' "Non-interactive mode detected. Enabling auto-start by default."
Expand Down Expand Up @@ -551,11 +634,15 @@ try {

if ($createShortcut) {
# Create main shortcut
New-DesktopShortcut -ExecutablePath $downloadPath
New-DesktopShortcut -ExecutablePath $downloadPath -Arguments $extraArgs

# Create "once" shortcut for initial cache ingest only
$onceArgs = "--once"
if ($extraArgs -ne "") {
$onceArgs = "--once $extraArgs"
}
New-DesktopShortcut -ExecutablePath $downloadPath `
-Arguments "--once" `
-Arguments $onceArgs `
-ShortcutName "$AppName (Once)" `
-Description "Deadlock API Ingest - Scan existing Steam cache once and exit"

Expand Down
8 changes: 7 additions & 1 deletion module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ in {
description = "Group under which deadlock-api-ingest runs";
};

statlocker.enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable Statlocker integration (sends match IDs to statlocker.gg after ingestion)";
};

steamUser = mkOption {
type = types.nullOr types.str;
default = cfg.user;
Expand Down Expand Up @@ -78,7 +84,7 @@ in {
Type = "simple";
User = cfg.user;
Group = cfg.group;
ExecStart = "${cfg.package}/bin/deadlock-api-ingest";
ExecStart = "${cfg.package}/bin/deadlock-api-ingest${lib.optionalString (!cfg.statlocker.enable) " --no-statlocker"}";
Restart = "on-failure";
RestartSec = "10s";

Expand Down
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use tracing_subscriber::util::SubscriberInitExt;
mod error;
mod ingestion_cache;
mod scan_cache;
mod statlocker;
mod utils;

fn init_tracing() {
Expand All @@ -34,6 +35,10 @@ fn init_tracing() {
fn main() {
init_tracing();

if std::env::args().any(|arg| arg == "--no-statlocker") {
statlocker::disable();
}

let Ok(steam_dir) = steamlocate::SteamDir::locate() else {
error!("Could not find Steam directory. Waiting 30s before exiting.");
std::thread::sleep(core::time::Duration::from_secs(30));
Expand Down
4 changes: 4 additions & 0 deletions src/scan_cache.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ingestion_cache;
use crate::statlocker;
use crate::utils::Salts;
use memchr::{memchr, memmem};
use notify::event::{CreateKind, ModifyKind};
Expand Down Expand Up @@ -102,6 +103,8 @@ pub(super) fn initial_cache_dir_ingest(cache_dir: &Path) {
for salt in &salts {
ingestion_cache::mark_ingested(salt);
}
let match_ids: Vec<u64> = salts.iter().map(|s| s.match_id).collect();
statlocker::notify_many(&match_ids);
}
Err(e) => warn!("Failed to ingest salts: {e:?}"),
}
Expand Down Expand Up @@ -141,6 +144,7 @@ pub(super) fn watch_cache_dir(cache_dir: &Path) -> notify::Result<()> {
Ok(..) => {
info!("Ingested salts: {salts:?}");
ingestion_cache::mark_ingested(&salts);
statlocker::notify(salts.match_id);
}
Err(e) => warn!("Failed to ingest salts: {e:?}"),
}
Expand Down
Loading