refactor: prune unconsumed peer lifecycle events

An emit-vs-listen audit of the event surface showed the GUI is
state-as-source-of-truth: useGames renders the complete `games-list`
snapshot (full library + active_operations) and reconstructs status from
it, not from a stream of granular events. Several PeerEvents were emitted
but had no consumer at all -- no frontend `listen()` and no peer-cli
scenario assertion -- so they were pure dead weight that made the backend
look event-driven when it no longer is.

This prunes that dead surface in two parts.

1. Remove three PeerEvent variants with no consumer: InstallGameBegin,
   UninstallGameBegin, and RemoveDownloadedGameBegin. The operation-start
   transition is still observable via ActiveOperationsChanged (the
   snapshot already carries the Installing/Updating/Uninstalling/
   RemovingDownload kind), so nothing is lost. This drops their emit
   sites in handlers.rs, the begin-event assertions in the peer's
   lifecycle unit tests (the asserted sequence is now
   ActiveOperationsChanged(kind) -> LocalLibraryChanged ->
   ActiveOperationsChanged([]) -> *Finished), the peer-cli JSONL
   mappings (install-begin/uninstall-begin/remove-download-begin) plus
   the now-orphaned install_operation_name helper and InstallOperation
   import, and the matching Tauri handler arms.

2. Drop Tauri webview emits that no frontend listener consumed:
   peer-local-ready, game-download-begin, game-download-pre,
   game-download-finished, game-uninstall-finished, and
   peer-connected/-disconnected/-discovered/-lost. The log lines and all
   real side effects are kept (handle_got_game_files still forwards
   PeerCommand::DownloadGameFiles). The orphaned emit_peer_addr_event and
   handle_download_finished helpers and the now-unused SocketAddr import
   are removed. peer-runtime-failed is kept pending a decision on
   surfacing runtime failures in the GUI.

Why not re-wire instead: under state-as-source-of-truth, per-event UI
state is exactly the pattern this project abandoned. Live progress
already flows via game-download-progress, and the peer-cli's chunk,
timing-trigger, and transition assertions read events that are retained
(download-begin, download-chunk-finished, the *-finished/*-failed
terminals), so test coverage is unchanged.

Behavior change: none functional. The Tauri backend no longer emits
events nothing listened to; the GUI is unchanged. The peer-cli no longer
emits the three *-begin JSONL events. PeerEvent is a workspace-internal
UI-reporting type, not a wire-protocol type, so there is no protocol or
version impact and all consumers are updated in this commit.

Docs: PEER_CLI_SCENARIOS.md S39 no longer lists install-begin (with a
note that the start transition is visible via active-operations-changed),
and a dated Run Log entry records the removal. The historical 2026-05-18
run-log note is left intact as a dated observation.

Test Plan:
- just test: pass (incl. peer lifecycle event-sequence tests).
- just clippy: pass (-D warnings, all targets).
- just frontend-test: pass (11/11, incl. streamed-install gating/labels).
- just build: pass (release, no bundle).
- Not run: the Docker S39-S47 matrix (run_extended_scenarios.py); those
  scenarios never asserted the removed *-begin events, so coverage is
  unaffected. just fmt's tombi step needs network and was skipped; no
  TOML changed.

Refs: peer event-surface emit-vs-listen audit; no external consumers of
the removed events.
This commit was merged in pull request #28.
This commit is contained in:
2026-06-20 19:54:53 +02:00
parent c25dc7420e
commit 0b8e1e7f92
5 changed files with 24 additions and 178 deletions
+20 -1
View File
@@ -46,7 +46,7 @@ for deterministic local runs; mDNS/macvlan remains an environment smoke path.
| S36 | Catalog singleton beats stale majority | Five peers advertise one game; one peer has the catalog version and four peers have stale versions. | `list-games` reports `peer_count=1` and the catalog `eti_game_version`; all descriptors and chunks come from the singleton catalog-version peer, while stale peers remain hidden and contribute zero bytes. | | S36 | Catalog singleton beats stale majority | Five peers advertise one game; one peer has the catalog version and four peers have stale versions. | `list-games` reports `peer_count=1` and the catalog `eti_game_version`; all descriptors and chunks come from the singleton catalog-version peer, while stale peers remain hidden and contribute zero bytes. |
| S37 | Single-source download throughput | A source peer advertises a temporary catalog game with one sparse `2 GiB` `.eti`; an empty client downloads it with `install=false`. | The client emits `download-finished` with throughput measurements (`bytes`, `duration_ms`, `mib_per_s`, `mbit_per_s`), and the downloaded archive size matches the source. | | S37 | Single-source download throughput | A source peer advertises a temporary catalog game with one sparse `2 GiB` `.eti`; an empty client downloads it with `install=false`. | The client emits `download-finished` with throughput measurements (`bytes`, `duration_ms`, `mib_per_s`, `mbit_per_s`), and the downloaded archive size matches the source. |
| S38 | First-play launch-setting stamping | `fixture-persona/css` ships a real RAR `.eti` whose tree buries a CRLF `SmartSteamEmu.ini` with a stub `PersonaName` line under `engine/bin/win64/steam_settings/`, plus a stub `account_name.txt` and `language.txt` under `profiles/local/`. A peer installs `css` (with `--unrar`), then sends `play css` with a username and language, then `play css` again. | After install the marker `games/css/launch_settings_applied` is absent and the stub files are intact under `local/`. The first `play` returns `already_applied=false` with `account_name_written`, `language_written`, and `persona_name_written` all true; the deep `SmartSteamEmu.ini` `PersonaName` value becomes the username with its `\r\n` ending and sibling lines preserved, `account_name.txt` becomes the username, `language.txt` becomes the passed language, and the marker now exists. A second `play` returns `already_applied=true`, rewrites nothing, and leaves the files untouched even if their values were reset externally. | | S38 | First-play launch-setting stamping | `fixture-persona/css` ships a real RAR `.eti` whose tree buries a CRLF `SmartSteamEmu.ini` with a stub `PersonaName` line under `engine/bin/win64/steam_settings/`, plus a stub `account_name.txt` and `language.txt` under `profiles/local/`. A peer installs `css` (with `--unrar`), then sends `play css` with a username and language, then `play css` again. | After install the marker `games/css/launch_settings_applied` is absent and the stub files are intact under `local/`. The first `play` returns `already_applied=false` with `account_name_written`, `language_written`, and `persona_name_written` all true; the deep `SmartSteamEmu.ini` `PersonaName` value becomes the username with its `\r\n` ending and sibling lines preserved, `account_name.txt` becomes the username, `language.txt` becomes the passed language, and the marker now exists. A second `play` returns `already_applied=true`, rewrites nothing, and leaves the files untouched even if their values were reset externally. |
| S39 | Streamed install without keeping archive payload | Empty client connects to `fixture-bravo`, then sends `stream-install cnctw`. The source has real RAR `.eti` payload entries under `bin/` and `data/`; the receiver uses the container-bundled `unrar` stream provider. | Client emits `download-begin`, streamed `download-chunk-finished`, `download-finished`, `install-begin`, and `install-finished`. Local `cnctw` is `downloaded=false`, `installed=true`, `availability=LocalOnly`; root `version.ini` and `.eti` are absent; `local/bin/cnctw-payload.bin` and `local/data/cnctw-assets.dat` match `unrar p` output by SHA-256; the source reports no active outbound transfer for `cnctw` after completion. | | S39 | Streamed install without keeping archive payload | Empty client connects to `fixture-bravo`, then sends `stream-install cnctw`. The source has real RAR `.eti` payload entries under `bin/` and `data/`; the receiver uses the container-bundled `unrar` stream provider. | Client emits `download-begin`, streamed `download-chunk-finished`, `download-finished`, and `install-finished` (the install-start transition is observable via `active-operations-changed`; there is no separate `install-begin` event). Local `cnctw` is `downloaded=false`, `installed=true`, `availability=LocalOnly`; root `version.ini` and `.eti` are absent; `local/bin/cnctw-payload.bin` and `local/data/cnctw-assets.dat` match `unrar p` output by SHA-256; the source reports no active outbound transfer for `cnctw` after completion. |
| S40 | Streamed install receiver is not a peer source | After S39, a third peer connects only to the streamed-install receiver. | The third peer may see the receiver's local-only summary in peer snapshots, but `list-games` remote aggregation does not expose `cnctw` as downloadable, `peer_count` remains zero/absent, and attempting `download cnctw` fails with no local files created. | | S40 | Streamed install receiver is not a peer source | After S39, a third peer connects only to the streamed-install receiver. | The third peer may see the receiver's local-only summary in peer snapshots, but `list-games` remote aggregation does not expose `cnctw` as downloadable, `peer_count` remains zero/absent, and attempting `download cnctw` fails with no local files created. |
| S41 | Solid archive streamed install | Empty client connects to a peer serving `fixture-solid/cnctw`, whose `.eti` is a real solid RAR archive. The receiver uses the container-bundled `unrar` stream provider. | The fixture is verified as solid with `unrar lt`; streamed install finishes with `downloaded=false`, `installed=true`, `availability=LocalOnly`; root archive and `version.ini` are absent; streamed byte count equals the extracted solid entries; local payload SHA-256 hashes match `unrar p` output. | | S41 | Solid archive streamed install | Empty client connects to a peer serving `fixture-solid/cnctw`, whose `.eti` is a real solid RAR archive. The receiver uses the container-bundled `unrar` stream provider. | The fixture is verified as solid with `unrar lt`; streamed install finishes with `downloaded=false`, `installed=true`, `availability=LocalOnly`; root archive and `version.ini` are absent; streamed byte count equals the extracted solid entries; local payload SHA-256 hashes match `unrar p` output. |
| S42 | Streamed install whole-stream retry | Empty client connects to two peers serving the same catalog-version `cnctw`: one broken source whose `--unrar` path is missing, followed by one good source. | The broken source sorts before the good source in retry order, contributes zero chunks, and the good source completes a fresh whole-stream attempt. The final state is local-only installed, no root archive/sentinel, no `.local.installing`, byte count matches the extracted entries, and payload hashes match the good source. | | S42 | Streamed install whole-stream retry | Empty client connects to two peers serving the same catalog-version `cnctw`: one broken source whose `--unrar` path is missing, followed by one good source. | The broken source sorts before the good source in retry order, contributes zero chunks, and the good source completes a fresh whole-stream attempt. The final state is local-only installed, no root archive/sentinel, no `.local.installing`, byte count matches the extracted entries, and payload hashes match the good source. |
@@ -143,6 +143,25 @@ Use S39-S41 to pin down low-disk streamed installs:
## Run Log ## Run Log
### 2026-06-20 - Prune Dead Lifecycle Events
- Code under test removed the unconsumed `InstallGameBegin`, `UninstallGameBegin`,
and `RemoveDownloadedGameBegin` `PeerEvent` variants (and their peer-cli JSONL
`install-begin`/`uninstall-begin`/`remove-download-begin` events), plus the
Tauri webview emits that no frontend listener consumed (`peer-local-ready`,
`game-download-begin`, `game-download-pre`, `game-download-finished`,
`game-uninstall-finished`, `peer-connected`/`-disconnected`/`-discovered`/`-lost`).
`peer-runtime-failed` was kept pending a UI decision.
- Rationale: the GUI is state-as-source-of-truth (it renders the `games-list`
snapshot), and no scenario asserted these begin events; the install, uninstall,
and removal start transitions stay observable via `active-operations-changed`.
- Contract update: the S39 row no longer lists `install-begin`. Older run-log
entries below predate the removal and are left intact as historical records.
- Gates: `just test`, `just clippy`, `just frontend-test`, and `just build`
passed. (`just fmt`'s `tombi` step needs network and was skipped; no TOML
changed.) The Docker S39-S47 matrix was not re-run for this cleanup; S39-S47
never asserted the removed begin events, so coverage is unchanged.
### 2026-06-07 - Catalog-Version Matrix Alignment (S1-S47) ### 2026-06-07 - Catalog-Version Matrix Alignment (S1-S47)
- Code under test aligned checked-in fixture `version.ini` sentinels with the - Code under test aligned checked-in fixture `version.ini` sentinels with the
-11
View File
@@ -17,7 +17,6 @@ use lanspread_peer::{
ActiveOperation, ActiveOperation,
ActiveOperationKind, ActiveOperationKind,
ExternalUnrarStreamProvider, ExternalUnrarStreamProvider,
InstallOperation,
NoopStreamInstallProvider, NoopStreamInstallProvider,
OutboundTransfers, OutboundTransfers,
PeerCommand, PeerCommand,
@@ -541,16 +540,10 @@ async fn update_state_from_event(shared: &SharedState, event: PeerEvent) -> (&'s
download_terminal_event(shared, "download-failed", id).await download_terminal_event(shared, "download-failed", id).await
} }
PeerEvent::DownloadGameFilesAllPeersGone { id } => game_id_event("download-peers-gone", id), PeerEvent::DownloadGameFilesAllPeersGone { id } => game_id_event("download-peers-gone", id),
PeerEvent::InstallGameBegin { id, operation } => (
"install-begin",
json!({"game_id": id, "operation": install_operation_name(operation)}),
),
PeerEvent::InstallGameFinished { id } => game_id_event("install-finished", id), PeerEvent::InstallGameFinished { id } => game_id_event("install-finished", id),
PeerEvent::InstallGameFailed { id } => game_id_event("install-failed", id), PeerEvent::InstallGameFailed { id } => game_id_event("install-failed", id),
PeerEvent::UninstallGameBegin { id } => game_id_event("uninstall-begin", id),
PeerEvent::UninstallGameFinished { id } => game_id_event("uninstall-finished", id), PeerEvent::UninstallGameFinished { id } => game_id_event("uninstall-finished", id),
PeerEvent::UninstallGameFailed { id } => game_id_event("uninstall-failed", id), PeerEvent::UninstallGameFailed { id } => game_id_event("uninstall-failed", id),
PeerEvent::RemoveDownloadedGameBegin { id } => game_id_event("remove-download-begin", id),
PeerEvent::RemoveDownloadedGameFinished { id } => { PeerEvent::RemoveDownloadedGameFinished { id } => {
game_id_event("remove-download-finished", id) game_id_event("remove-download-finished", id)
} }
@@ -686,10 +679,6 @@ fn active_operation_name(operation: ActiveOperationKind) -> &'static str {
(&operation).into() (&operation).into()
} }
fn install_operation_name(operation: InstallOperation) -> &'static str {
(&operation).into()
}
fn runtime_component_name(component: PeerRuntimeComponent) -> &'static str { fn runtime_component_name(component: PeerRuntimeComponent) -> &'static str {
(&component).into() (&component).into()
} }
+2 -85
View File
@@ -784,13 +784,6 @@ async fn commit_streamed_install(
ctx.active_operations.clone(), ctx.active_operations.clone(),
tx_notify_ui.clone(), tx_notify_ui.clone(),
); );
events::send(
tx_notify_ui,
PeerEvent::InstallGameBegin {
id: id.clone(),
operation: InstallOperation::Installing,
},
);
match transaction.commit().await { match transaction.commit().await {
Ok(()) => { Ok(()) => {
@@ -918,14 +911,6 @@ async fn run_started_install_operation(
tx_notify_ui.clone(), tx_notify_ui.clone(),
); );
let result = { let result = {
events::send(
tx_notify_ui,
PeerEvent::InstallGameBegin {
id: id.clone(),
operation,
},
);
let state_dir = ctx.state_dir.as_ref(); let state_dir = ctx.state_dir.as_ref();
match operation { match operation {
InstallOperation::Installing => { InstallOperation::Installing => {
@@ -995,14 +980,7 @@ async fn run_uninstall_operation(ctx: &Ctx, tx_notify_ui: &UnboundedSender<PeerE
ctx.active_operations.clone(), ctx.active_operations.clone(),
tx_notify_ui.clone(), tx_notify_ui.clone(),
); );
let result = { let result = install::uninstall(&game_root, ctx.state_dir.as_ref(), &id).await;
events::send(
tx_notify_ui,
PeerEvent::UninstallGameBegin { id: id.clone() },
);
install::uninstall(&game_root, ctx.state_dir.as_ref(), &id).await
};
match result { match result {
Ok(()) => { Ok(()) => {
@@ -1068,14 +1046,7 @@ async fn run_remove_downloaded_operation(
ctx.active_operations.clone(), ctx.active_operations.clone(),
tx_notify_ui.clone(), tx_notify_ui.clone(),
); );
let result = { let result = install::remove_downloaded(&game_dir, &id).await;
events::send(
tx_notify_ui,
PeerEvent::RemoveDownloadedGameBegin { id: id.clone() },
);
install::remove_downloaded(&game_dir, &id).await
};
match result { match result {
Ok(()) => { Ok(()) => {
@@ -2032,13 +2003,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating), &active_update("game", ActiveOperationKind::Updating),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Updating
} if id == "game"
));
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!( assert!(matches!(
recv_event(&mut rx).await, recv_event(&mut rx).await,
@@ -2063,13 +2027,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing), &active_update("game", ActiveOperationKind::Installing),
); );
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Installing);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true); assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!( assert!(matches!(
@@ -2124,13 +2081,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing), &active_update("game", ActiveOperationKind::Installing),
); );
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Installing);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true); assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!( assert!(matches!(
@@ -2184,13 +2134,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating), &active_update("game", ActiveOperationKind::Updating),
); );
match recv_event(&mut rx).await {
PeerEvent::InstallGameBegin { id, operation } => {
assert_eq!(id, "game");
assert_eq!(operation, InstallOperation::Updating);
}
_ => panic!("expected InstallGameBegin"),
}
assert_local_update(recv_event(&mut rx).await, true, true); assert_local_update(recv_event(&mut rx).await, true, true);
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!( assert!(matches!(
@@ -2215,13 +2158,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Installing), &active_update("game", ActiveOperationKind::Installing),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Installing
} if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, true, true); let game = local_update_game(recv_event(&mut rx).await, true, true);
assert_eq!(game.local_version.as_deref(), Some("20240101")); assert_eq!(game.local_version.as_deref(), Some("20240101"));
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2238,13 +2174,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Updating), &active_update("game", ActiveOperationKind::Updating),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::InstallGameBegin {
id,
operation: InstallOperation::Updating
} if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, true, true); let game = local_update_game(recv_event(&mut rx).await, true, true);
assert_eq!(game.local_version.as_deref(), Some("20250101")); assert_eq!(game.local_version.as_deref(), Some("20250101"));
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2258,10 +2187,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Uninstalling), &active_update("game", ActiveOperationKind::Uninstalling),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::UninstallGameBegin { id } if id == "game"
));
let game = local_update_game(recv_event(&mut rx).await, false, true); let game = local_update_game(recv_event(&mut rx).await, false, true);
assert_eq!(game.local_version.as_deref(), Some("20250101")); assert_eq!(game.local_version.as_deref(), Some("20250101"));
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
@@ -2289,10 +2214,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::Uninstalling), &active_update("game", ActiveOperationKind::Uninstalling),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::UninstallGameBegin { id } if id == "game"
));
assert_local_update(recv_event(&mut rx).await, false, true); assert_local_update(recv_event(&mut rx).await, false, true);
assert_active_update(recv_event(&mut rx).await, &[]); assert_active_update(recv_event(&mut rx).await, &[]);
assert!(matches!( assert!(matches!(
@@ -2324,10 +2245,6 @@ mod tests {
recv_event(&mut rx).await, recv_event(&mut rx).await,
&active_update("game", ActiveOperationKind::RemovingDownload), &active_update("game", ActiveOperationKind::RemovingDownload),
); );
assert!(matches!(
recv_event(&mut rx).await,
PeerEvent::RemoveDownloadedGameBegin { id } if id == "game"
));
let PeerEvent::LocalLibraryChanged { games } = recv_event(&mut rx).await else { let PeerEvent::LocalLibraryChanged { games } = recv_event(&mut rx).await else {
panic!("expected LocalLibraryChanged"); panic!("expected LocalLibraryChanged");
}; };
-9
View File
@@ -127,23 +127,14 @@ pub enum PeerEvent {
DownloadGameFilesFailed { id: String }, DownloadGameFilesFailed { id: String },
/// All peers with the game have disconnected during download. /// All peers with the game have disconnected during download.
DownloadGameFilesAllPeersGone { id: String }, DownloadGameFilesAllPeersGone { id: String },
/// Install or update transaction has started for a game.
InstallGameBegin {
id: String,
operation: InstallOperation,
},
/// Install or update transaction has completed successfully. /// Install or update transaction has completed successfully.
InstallGameFinished { id: String }, InstallGameFinished { id: String },
/// Install or update transaction has failed after rollback. /// Install or update transaction has failed after rollback.
InstallGameFailed { id: String }, InstallGameFailed { id: String },
/// Uninstall transaction has started for a game.
UninstallGameBegin { id: String },
/// Uninstall transaction has completed successfully. /// Uninstall transaction has completed successfully.
UninstallGameFinished { id: String }, UninstallGameFinished { id: String },
/// Uninstall transaction has failed after rollback. /// Uninstall transaction has failed after rollback.
UninstallGameFailed { id: String }, UninstallGameFailed { id: String },
/// Downloaded archive removal has started for an uninstalled game.
RemoveDownloadedGameBegin { id: String },
/// Downloaded archive removal has completed successfully. /// Downloaded archive removal has completed successfully.
RemoveDownloadedGameFinished { id: String }, RemoveDownloadedGameFinished { id: String },
/// Downloaded archive removal has failed before deleting the game root. /// Downloaded archive removal has failed before deleting the game root.
@@ -2,7 +2,6 @@ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fs::{self, OpenOptions}, fs::{self, OpenOptions},
io::{self, Read as _, Seek as _, SeekFrom, Write as _}, io::{self, Read as _, Seek as _, SeekFrom, Write as _},
net::SocketAddr,
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
sync::{Arc, Mutex, OnceLock}, sync::{Arc, Mutex, OnceLock},
time::{Duration, SystemTime, UNIX_EPOCH}, time::{Duration, SystemTime, UNIX_EPOCH},
@@ -1970,12 +1969,6 @@ fn emit_game_id_event(app_handle: &AppHandle, event: &str, id: &str, label: &str
} }
} }
fn emit_peer_addr_event(app_handle: &AppHandle, event: &str, addr: SocketAddr) {
if let Err(e) = app_handle.emit(event, Some(addr.to_string())) {
log::error!("Failed to emit {event} event: {e}");
}
}
fn spawn_peer_event_loop(app_handle: AppHandle, mut rx_peer_event: UnboundedReceiver<PeerEvent>) { fn spawn_peer_event_loop(app_handle: AppHandle, mut rx_peer_event: UnboundedReceiver<PeerEvent>) {
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
while let Some(event) = rx_peer_event.recv().await { while let Some(event) = rx_peer_event.recv().await {
@@ -2027,9 +2020,6 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
match event { match event {
PeerEvent::LocalPeerReady { peer_id, addr } => { PeerEvent::LocalPeerReady { peer_id, addr } => {
log::info!("Local peer ready: {peer_id} at {addr}"); log::info!("Local peer ready: {peer_id} at {addr}");
if let Err(e) = app_handle.emit("peer-local-ready", Some((peer_id, addr.to_string()))) {
log::error!("Failed to emit peer-local-ready event: {e}");
}
} }
PeerEvent::ListGames(games) => { PeerEvent::ListGames(games) => {
log::info!("PeerEvent::ListGames received"); log::info!("PeerEvent::ListGames received");
@@ -2068,13 +2058,7 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
); );
} }
PeerEvent::DownloadGameFilesBegin { id } => { PeerEvent::DownloadGameFilesBegin { id } => {
log::info!("PeerEvent::DownloadGameFilesBegin received"); log::info!("PeerEvent::DownloadGameFilesBegin received for {id}");
emit_game_id_event(
app_handle,
"game-download-begin",
&id,
"PeerEvent::DownloadGameFilesBegin",
);
} }
PeerEvent::DownloadGameFileChunkFinished { PeerEvent::DownloadGameFileChunkFinished {
id, id,
@@ -2094,7 +2078,7 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
} }
} }
PeerEvent::DownloadGameFilesFinished { id } => { PeerEvent::DownloadGameFilesFinished { id } => {
handle_download_finished(app_handle, id); log::info!("PeerEvent::DownloadGameFilesFinished received for {id}");
} }
PeerEvent::DownloadGameFilesFailed { id } => { PeerEvent::DownloadGameFilesFailed { id } => {
log::warn!("PeerEvent::DownloadGameFilesFailed received"); log::warn!("PeerEvent::DownloadGameFilesFailed received");
@@ -2114,16 +2098,6 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
"PeerEvent::DownloadGameFilesAllPeersGone", "PeerEvent::DownloadGameFilesAllPeersGone",
); );
} }
PeerEvent::InstallGameBegin { id, operation } => {
let operation_name: &'static str = (&operation).into();
log::info!("PeerEvent::InstallGameBegin received for {id}: {operation_name}");
emit_game_id_event(
app_handle,
"game-install-begin",
&id,
"PeerEvent::InstallGameBegin",
);
}
PeerEvent::InstallGameFinished { id } => { PeerEvent::InstallGameFinished { id } => {
log::info!("PeerEvent::InstallGameFinished received for {id}"); log::info!("PeerEvent::InstallGameFinished received for {id}");
emit_game_id_event( emit_game_id_event(
@@ -2142,23 +2116,8 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
"PeerEvent::InstallGameFailed", "PeerEvent::InstallGameFailed",
); );
} }
PeerEvent::UninstallGameBegin { id } => {
log::info!("PeerEvent::UninstallGameBegin received for {id}");
emit_game_id_event(
app_handle,
"game-uninstall-begin",
&id,
"PeerEvent::UninstallGameBegin",
);
}
PeerEvent::UninstallGameFinished { id } => { PeerEvent::UninstallGameFinished { id } => {
log::info!("PeerEvent::UninstallGameFinished received for {id}"); log::info!("PeerEvent::UninstallGameFinished received for {id}");
emit_game_id_event(
app_handle,
"game-uninstall-finished",
&id,
"PeerEvent::UninstallGameFinished",
);
} }
PeerEvent::UninstallGameFailed { id } => { PeerEvent::UninstallGameFailed { id } => {
log::warn!("PeerEvent::UninstallGameFailed received for {id}"); log::warn!("PeerEvent::UninstallGameFailed received for {id}");
@@ -2169,15 +2128,6 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
"PeerEvent::UninstallGameFailed", "PeerEvent::UninstallGameFailed",
); );
} }
PeerEvent::RemoveDownloadedGameBegin { id } => {
log::info!("PeerEvent::RemoveDownloadedGameBegin received for {id}");
emit_game_id_event(
app_handle,
"game-remove-download-begin",
&id,
"PeerEvent::RemoveDownloadedGameBegin",
);
}
PeerEvent::RemoveDownloadedGameFinished { id } => { PeerEvent::RemoveDownloadedGameFinished { id } => {
log::info!("PeerEvent::RemoveDownloadedGameFinished received for {id}"); log::info!("PeerEvent::RemoveDownloadedGameFinished received for {id}");
emit_game_id_event( emit_game_id_event(
@@ -2198,19 +2148,15 @@ async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
} }
PeerEvent::PeerConnected(addr) => { PeerEvent::PeerConnected(addr) => {
log::info!("Peer connected: {addr}"); log::info!("Peer connected: {addr}");
emit_peer_addr_event(app_handle, "peer-connected", addr);
} }
PeerEvent::PeerDisconnected(addr) => { PeerEvent::PeerDisconnected(addr) => {
log::info!("Peer disconnected: {addr}"); log::info!("Peer disconnected: {addr}");
emit_peer_addr_event(app_handle, "peer-disconnected", addr);
} }
PeerEvent::PeerDiscovered(addr) => { PeerEvent::PeerDiscovered(addr) => {
log::info!("Peer discovered: {addr}"); log::info!("Peer discovered: {addr}");
emit_peer_addr_event(app_handle, "peer-discovered", addr);
} }
PeerEvent::PeerLost(addr) => { PeerEvent::PeerLost(addr) => {
log::info!("Peer lost: {addr}"); log::info!("Peer lost: {addr}");
emit_peer_addr_event(app_handle, "peer-lost", addr);
} }
PeerEvent::PeerCountUpdated(count) => { PeerEvent::PeerCountUpdated(count) => {
log::info!("Peer count updated: {count}"); log::info!("Peer count updated: {count}");
@@ -2237,12 +2183,6 @@ async fn handle_got_game_files(
file_descriptions: Vec<GameFileDescription>, file_descriptions: Vec<GameFileDescription>,
) { ) {
log::info!("PeerEvent::GotGameFiles received"); log::info!("PeerEvent::GotGameFiles received");
emit_game_id_event(
app_handle,
"game-download-pre",
&id,
"PeerEvent::GotGameFiles",
);
let state = app_handle.state::<LanSpreadState>(); let state = app_handle.state::<LanSpreadState>();
let peer_ctrl = state.peer_ctrl.read().await.clone(); let peer_ctrl = state.peer_ctrl.read().await.clone();
@@ -2256,16 +2196,6 @@ async fn handle_got_game_files(
} }
} }
fn handle_download_finished(app_handle: &AppHandle, id: String) {
log::info!("PeerEvent::DownloadGameFilesFinished received");
emit_game_id_event(
app_handle,
"game-download-finished",
&id,
"PeerEvent::DownloadGameFilesFinished",
);
}
#[allow(clippy::missing_panics_doc)] #[allow(clippy::missing_panics_doc)]
#[cfg_attr(mobile, tauri::mobile_entry_point)] #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() { pub fn run() {