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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ The release workflow publishes the section that matches the tag as the
GitHub release notes, so each version gets a heading of the form
`## X.Y.Z (date)`.

## 4.1.1 (2026-10-02)

- Setting up a Windows PC that an earlier version had set up (when the
app was called BroLink) now registers the `LatchStream` service. Setup
moved the engine's folder and removed the old service, but treated the
running engine as needing nothing more, so the new service was never
registered and the engine ran only once someone had signed in.

## 4.1.0 (2026-10-02)

- **BroLink is now Latch, by Bardbro.** The app, the repository
Expand Down
10 changes: 5 additions & 5 deletions 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
Expand Up @@ -9,7 +9,7 @@ members = [
]

[workspace.package]
version = "4.1.0"
version = "4.1.1"
edition = "2021"
license = "GPL-3.0-or-later"
authors = ["Latch Contributors"]
Expand Down
2 changes: 1 addition & 1 deletion crates/client/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2105,7 +2105,7 @@ pub(crate) mod tests {
v
},
Extra {
handover: Some("Installing Latch Host 4.1.0 on Gaming-PC: waiting for it to fetch the file…".into()),
handover: Some("Installing Latch Host 4.1.1 on Gaming-PC: waiting for it to fetch the file…".into()),
..windows()
},
None,
Expand Down
7 changes: 5 additions & 2 deletions crates/host/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ impl HostApp {
}
let cfg = self.cfg.clone();
let ctx = ctx.clone();
let install_engine = !status.streamer.installed || migrate || !status.streamer.running;
let install_engine = !status.streamer.installed
|| migrate
|| !status.streamer.running
|| crate::streamer::find().is_some_and(|i| crate::streamer::in_old_folder(&i));
let adapter = status.wake_adapter.clone();
let desc = status.wake_adapter_description.clone();
std::thread::spawn(move || {
Expand Down Expand Up @@ -1249,7 +1252,7 @@ mod snapshots {
}),
setup: vec![],
log: vec![
"Latch 4.1.0 listening on TCP 47850".into(),
"Latch 4.1.1 listening on TCP 47850".into(),
"listening for wake packets on UDP 9".into(),
"Tailscale up as user@example.com (100.64.0.10)".into(),
"the streaming engine is running and Latch is signed in".into(),
Expand Down
3 changes: 2 additions & 1 deletion crates/host/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,9 +692,10 @@ pub fn run_as_admin() -> Result<()> {
let kind = install.as_ref().map(|i| i.kind).unwrap_or("");
let migrate = crate::migrate::uses_old_engine(kind);
let running = install.is_some() && crate::streamer::running();
let old_folder = install.as_ref().is_some_and(crate::streamer::in_old_folder);
let plan = Plan {
exe: &exe,
install_engine: install.is_none() || migrate || !running,
install_engine: install.is_none() || migrate || !running || old_folder,
migrate,
dry_run: false,
sunshine_user: &cfg.sunshine_user,
Expand Down
23 changes: 22 additions & 1 deletion crates/host/src/streamer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use anyhow::{bail, Context, Result};
use latch_core::{SUNSHINE_PORT, SUNSHINE_WEB_PORT};
use std::net::{SocketAddr, TcpStream};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Duration;

Expand All @@ -25,6 +25,13 @@ pub const INSTALL_DIRS: [(&str, &str); 4] = [
("Apollo", r"C:\Program Files\Apollo"),
];

/// Whether `install` is the engine in the folder an earlier version used.
/// Setup moves it to [`ENGINE_DIR`] and registers the service under its new
/// name, so it has to run the engine step even though an engine is up.
pub fn in_old_folder(install: &Install) -> bool {
install.dir.as_path() == Path::new(crate::legacy::WINDOWS_ENGINE_DIR)
}

/// GitHub repository the setup script fetches the engine archive from.
pub const REPO: &str = "LizardByte/Sunshine";

Expand Down Expand Up @@ -580,6 +587,20 @@ mod tests {
let _ = SUNSHINE_PORT;
}

#[test]
fn an_engine_in_the_old_folder_is_recognised_so_setup_moves_it() {
let old = Install {
kind: "Latch",
dir: PathBuf::from(crate::legacy::WINDOWS_ENGINE_DIR),
};
let new = Install {
kind: "Latch",
dir: PathBuf::from(ENGINE_DIR),
};
assert!(in_old_folder(&old));
assert!(!in_old_folder(&new));
}

#[test]
fn session_listed_reads_current_app_and_running_flags() {
assert!(!session_listed(&serde_json::json!({"apps": []})));
Expand Down
Loading