Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package comet for openSUSE:Factory checked in at 2026-01-17 14:53:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/comet (Old) and /work/SRC/openSUSE:Factory/.comet.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "comet" Sat Jan 17 14:53:04 2026 rev:2 rq:1327482 version:0.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/comet/comet.changes 2025-11-28 16:52:58.498393760 +0100 +++ /work/SRC/openSUSE:Factory/.comet.new.1928/comet.changes 2026-01-17 14:53:52.220973559 +0100 @@ -1,0 +2,14 @@ +Thu Jan 15 22:28:31 UTC 2026 - Jonatas GonΓ§alves <[email protected]> + +- Update to 0.3.2 + * fix: create named pipes immediately instead of polling for pipe name by @imLinguin in 7f71ff5 + * chore: improve code readability by @imLinguin in 1d5290a + * Add Heretic + Hexen to Game-Compatibility.md by @nemesisx00 in #74 + * docs: add flashback to game-compatibility.md by @mrcaique in #76 + * Add Streets of Rage 4 to Game-Compatibility.md by @nemesisx00 in #77 + * Update Game-Compatibility.md by @AaronEldreth in #78 + * Added "X4: Foundations" to the game compatibility list. by @gabriel-eladi in #81 + * Add Indiana Jones and The Great Circle to Game Compatability List by @TheManInBlack-Gaming in #84 + + +------------------------------------------------------------------- Old: ---- comet-0.3.1.tar.xz New: ---- comet-0.3.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ comet.spec ++++++ --- /var/tmp/diff_new_pack.kwBOPy/_old 2026-01-17 14:53:56.853166666 +0100 +++ /var/tmp/diff_new_pack.kwBOPy/_new 2026-01-17 14:53:56.853166666 +0100 @@ -21,7 +21,7 @@ %endif Name: comet -Version: 0.3.1 +Version: 0.3.2 Release: 0 Summary: Communication service for Heroic Games Launcher License: GPL-3.0-only ++++++ _service ++++++ --- /var/tmp/diff_new_pack.kwBOPy/_old 2026-01-17 14:53:56.893168333 +0100 +++ /var/tmp/diff_new_pack.kwBOPy/_new 2026-01-17 14:53:56.897168500 +0100 @@ -4,7 +4,7 @@ <param name="scm">git</param> <param name="submodules">enable</param> <param name="filename">comet</param> - <param name="revision">v0.3.1</param> + <param name="revision">v0.3.2</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v([\.\d]+)</param> <param name="versionrewrite-replacement">\1</param> @@ -17,8 +17,8 @@ </service> <service name="cargo_vendor" mode="manual"> - <param name="src">comet-0.3.1.tar.xz</param> - <param name="revision">v0.3.1</param> + <param name="src">comet-0.3.2.tar.xz</param> + <param name="revision">v0.3.2</param> <param name="compression">zst</param> <param name="tag">comet</param> <param name="update">true</param> ++++++ comet-0.3.1.tar.xz -> comet-0.3.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/Cargo.lock new/comet-0.3.2/Cargo.lock --- old/comet-0.3.1/Cargo.lock 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/Cargo.lock 2026-01-04 18:46:58.000000000 +0100 @@ -317,7 +317,7 @@ [[package]] name = "comet" -version = "0.3.1" +version = "0.3.2" dependencies = [ "async_zip", "base64 0.22.1", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/Cargo.toml new/comet-0.3.2/Cargo.toml --- old/comet-0.3.1/Cargo.toml 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/Cargo.toml 2026-01-04 18:46:58.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "comet" -version = "0.3.1" +version = "0.3.2" edition = "2021" description = "" authors = ["PaweΕ Lidwin <[email protected]>"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/docs/wiki/Game-Compatibility.md new/comet-0.3.2/docs/wiki/Game-Compatibility.md --- old/comet-0.3.1/docs/wiki/Game-Compatibility.md 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/docs/wiki/Game-Compatibility.md 2026-01-04 18:46:58.000000000 +0100 @@ -60,24 +60,31 @@ |[BATTLETECH](https://www.gog.com/game/battletech_game)|1.9.1-686R (2/26/2020)|β Not Working|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Multiplayer is untested.| |[Crypt of the NecroDancer](https://www.gog.com/game/crypt_of_the_necrodancer)|4.1.0-b5142 (4/3/2024)|π© Yes|[commit `ed38c3d`](https://github.com/kevin-wijnen/comet/commit/ed38c3d5253893779ba3d7ab828af442652f6044)|π² No|π© Achievements π© Leaderboard|Achievements do work. Leaderboard support works as of Comet version `ed38c3d`. Tested with game + all DLCs.| |[Cuphead](https://www.gog.com/game/cuphead)|1.3.4 (8/19/2022)|π² Not Available|[commit `55e4025`](https://github.com/imLinguin/comet/commit/55e402538df3bff354bf2e1e9a54fa4e5e091122)|π© Yes|π© Achievements π² Leaderboard|GalaxyCommunication.exe service required for game to start communicating with GOG. Otherwise, Achievements won't work. No Leaderboards present in-game.| -|[Cyberpunk 2077](https://www.gog.com/en/game/cyberpunk_2077)|2.21 (1/19/2025)|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Achievements work in [Heroic v2.16.1](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/tag/v2.16.1). This was tested with the regular version of the game, not the [Ultimate Edition](https://www.gog.com/en/game/cyberpunk_2077_ultimate_edition) or [Phantom Liberty DLC](https://www.gog.com/en/game/cyberpunk_2077_phantom_liberty).| +|[Cyberpunk 2077](https://www.gog.com/game/cyberpunk_2077)|2.21 (1/19/2025)|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Achievements work in [Heroic v2.16.1](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/tag/v2.16.1). This was tested with the regular version of the game, not the [Ultimate Edition](https://www.gog.com/game/cyberpunk_2077_ultimate_edition) or [Phantom Liberty DLC](https://www.gog.com/game/cyberpunk_2077_phantom_liberty).| |[DOOM + DOOM II](https://www.gog.com/game/doom_doom_ii)|Version 2265 - 8/7/2024|π² Not Available|[`v0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π² Leaderboard|Achievements & Multiplayer do work as of `v0.1.2`.| |[DOOM 3: BFG Edition](https://www.gog.com/game/doom_3)|1.14 (7/14/2017)|π² Not Available|[version `0.1.1`](https://github.com/imLinguin/comet/releases/tag/v0.1.1)|π© Yes|π© Achievements π² Leaderboard|GalaxyCommunication.exe service required for game to start communicating with GOG. Otherwise, Achievements won't work. Mutliplayer isn't supported in GOG version of the game.| |[DOOM (2016)](https://www.gog.com/game/doom_2016)|Version 20240321-110145-gentle-wolf - 2/25/2025|π² Not Available|[`v0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π² Leaderboard|Achievements does work as of `v0.1.2`.| |[Duck Detective: The Secret Salami](https://www.gog.com/game/duck_detective_the_secret_salami)|1.1.0|β Not Working|[version `v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Windows build of the game is required to unlock achievements when on Linux.| -|[DUSK](https://www.gog.com/en/game/dusk)|1.8.25|π© Yes|[`v0.3.0`](https://github.com/imLinguin/comet/releases/tag/v0.3.0)|π² No|π© Achievements π² Leaderboard|Linux version has a bug that causes the game to launch in Chinese, and cannot be changed. This makes testing difficult. Windows version works fine.| +|[DUSK](https://www.gog.com/game/dusk)|1.8.25|π© Yes|[`v0.3.0`](https://github.com/imLinguin/comet/releases/tag/v0.3.0)|π² No|π© Achievements π² Leaderboard|Linux version has a bug that causes the game to launch in Chinese, and cannot be changed. This makes testing difficult. Windows version works fine.| +|[Flashback](https://www.gog.com/game/flashback)|1.0.0 - 30/11/2018|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|| |[Ghostrunner](https://www.gog.com/game/ghostrunner)|42507_446 (6/24/2022)|π² Not Available|[version `0.1.0`](https://github.com/imLinguin/comet/releases/tag/v0.1.0)|π² No|π© Achievements π© Leaderboard|Achievements and Leaderboards work as expected. The game seems to separate saves based on Galaxy user id. Saves may need to be moved manually to be available.| -|[Homeworld: Deserts of Kharak](https://www.gog.com/en/game/homeworld_deserts_of_kharak)|1.3.0|π² Not Available|[`v0.3.0`](https://github.com/imLinguin/comet/releases/tag/v0.3.0)|π© Yes|π© Achievements π² Leaderboard| Game has buggy achievements that fail to unlock sometimes, even when played on Windows on the official GOG app. Game has leaderboards, but untested. | +|[Heretic + Hexen](https://www.gog.com/game/heretic_hexen)|Version 4577-c7e7b84-170 (7/25/2025)|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Crossplay multiplayer works.| +|[Homeworld: Deserts of Kharak](https://www.gog.com/game/homeworld_deserts_of_kharak)|1.3.0|π² Not Available|[`v0.3.0`](https://github.com/imLinguin/comet/releases/tag/v0.3.0)|π© Yes|π© Achievements π² Leaderboard| Game has buggy achievements that fail to unlock sometimes, even when played on Windows on the official GOG app. Game has leaderboards, but untested. | |[Horizon Zero Dawn Complete Edition](https://gog.com/game/horizon_zero_dawn_complete_edition)|7517962 (1/18/2022)|π² Not Available|[commit `c4715bf`](https://github.com/imLinguin/comet/commit/c4715bfa186f9b8955b842d57fd6f17fc5209f26)|π² No|π© Achievements π² Leaderboard| Achievements do work.| +|[Indiana Jones and the Great Circle](https://www.gog.com/en/game/indiana_jones_and_the_great_circle)|gog v6 (12/25/2025)|π² Not Available|[v0.2.0](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π© Yes|π© Achievements|Works with the comet version bundled with Heroic Games Launcher 1.18.1.| |[Indivisible](https://www.gog.com/game/indivisible)|42940 (6/22/2020)|π© Yes|[version 0.1.0](https://github.com/imLinguin/comet/releases/tag/v0.1.0)|π² No|π© Achievements π² Leaderboard|Achievements do work.| |[Kingdom Come: Deliverance](https://www.gog.com/game/kingdom_come_deliverance)|1.9.6-404-504czi3 |π² Not available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π© Yes|π© Achievements π² Leaderboard|| +|[Kingdom Come: Deliverance II](https://www.gog.com/game/kingdom_come_deliverance_ii_gold_edition)|12540_release_1_3_30 (5/29/2025)|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|β Unknown|π© Achievements π² Leaderboard|Initially, it did not work on Steam Deck running Bazzite. However, when I installed KCD2 on Legion Go running Bazzite, achievements began working. Using Comet bundled with Heroic Games Launcher 2.18.1| |[Metal Slug](https://www.gog.com/game/metal_slug)|Version gog-3 - 26/05/2017|π² Not Available|[version `v0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π© Leaderboard|Achievements and leaderboard do work as of `v0.1.2`. Multiplayer not tested yet.| |[Metal Slug 2](https://www.gog.com/game/metal_slug_2)|gog-3 - 26/05/2017|π² Not Available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π© Leaderboard|Achievements and leaderboard do work as of `v0.1.2`.| |[Metal Slug 3](https://www.gog.com/game/metal_slug_3)|gog-5 - 26/05/2017|π² Not Available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π© Leaderboard|Achievements and leaderboard do work as of `v0.1.2`. Multiplayer not tested yet.| |[Metal Slug X](https://www.gog.com/game/metal_slug_x)|gog-6 - 02/06/2017|π² Not Available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π© Leaderboard|Achievements and leaderboard do work as of `v0.1.2`. Multiplayer not tested yet.| |[Quake II](https://www.gog.com/game/quake_ii_quad_damage)|5984 (11/01/2023)|π² Not available|[version `0.1.1`](https://github.com/imLinguin/comet/releases/tag/v0.1.1)|π² No|π© Achievements π² Leaderboard|Achievements work as expected. The game needs OpenID support introduced in comet v0.1.1| +|[Severed Steel](https://www.gog.com/game/severed_steel)|Version 5.5 (2/9/2024)|π² Not Available|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|Achievements work as expected.| |[STONKS-9800: Stock Market Simulator](https://www.gog.com/game/stonks9800_stock_market_simulator)|0.4.2.5 (04/04/2024)|π² Not Available|[commit `55e4025`](https://github.com/imLinguin/comet/commit/55e402538df3bff354bf2e1e9a54fa4e5e091122)|β Unknown|β Achievements π² Leaderboard|Game specific issue related to the GOG SDK library used. See [#26](https://github.com/imLinguin/comet/issues/26#issuecomment-2053667485) for any information and updates. Game did not connect to GOG via Comet with and without the dummy Service.| |[Stardew Valley](https://www.gog.com/game/stardew_valley)|1.6.5.24110.6670590629 (4/19/2024)|π© Yes|[commit `c4715bf`](https://github.com/imLinguin/comet/commit/c4715bfa186f9b8955b842d57fd6f17fc5209f26)|π² No|π² Achievements π² Leaderboard|The game uses Galaxy SDK for multiplayer only.| +|[Streets of Rage 4](https://www.gog.com/game/streets_of_rage_4)|Version v08g-r18163 (3/14/2023)|π© Yes|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π© Leaderboard|Native Linux Version tested: v08g-18208| |[Tomb Raider GOTY](https://www.gog.com/game/tomb_raider_goty)|1.0|π² Not available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π² Leaderboard|| |[Wolfenstein: The New Order](https://www.gog.com/game/wolfenstein_the_new_order)|1.0.0.2 - 06/02/2020|π² Not Available|[version `0.1.2`](https://github.com/imLinguin/comet/releases/tag/v0.1.2)|π² No|π© Achievements π² Leaderboard|Switch to older version without hotfix for the achievements, however the game is prone to crash. Refer to [this issue](https://github.com/imLinguin/comet/issues/57).| +|[X4: Foundations](https://www.gog.com/en/game/x4_foundations)|Version 8.00 Hotfix 3 - 10/16/2025|β Not Working|[`v0.2.0`](https://github.com/imLinguin/comet/releases/tag/v0.2.0)|π² No|π© Achievements π² Leaderboard|_(tested on Heroic 2.18.1)_ The game has a working native Linux version, but achievements don't work with it (they work fine on the Windows version). If you're using Heroic, I recommend the Windows version, since it runs just as well if not slightly better, and you get Comet and Cloud Saves support with it (and also: the native Linux and Windows save files are incompatible with one another).| |[Xeno Crisis](https://www.gog.com/game/xeno_crisis)|1.0.4 (2/11/2020)|β Not Working|[commit `55e4025`](https://github.com/imLinguin/comet/commit/55e402538df3bff354bf2e1e9a54fa4e5e091122)|π² No|π© Achievements π² Leaderboard|Achievement connection does work. The GOG Galaxy communications are not present in the Linux version, thus the macOS or Windows version needs to be used with Comet to work.| diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/dummy-service/README.md new/comet-0.3.2/dummy-service/README.md --- old/comet-0.3.1/dummy-service/README.md 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/dummy-service/README.md 2026-01-04 18:46:58.000000000 +0100 @@ -37,4 +37,4 @@ ```shell meson compile -C builddir -`` +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/gog/achievements.rs new/comet-0.3.2/src/api/gog/achievements.rs --- old/comet-0.3.1/src/api/gog/achievements.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/gog/achievements.rs 2026-01-04 18:46:58.000000000 +0100 @@ -1,8 +1,8 @@ use crate::api::handlers::context::HandlerContext; -use crate::api::handlers::error::{MessageHandlingError, MessageHandlingErrorKind}; +use crate::api::handlers::error::MessageHandlingError; use crate::constants::TokenStorage; use derive_getters::Getters; -use reqwest::{Client, Error}; +use reqwest::Client; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Debug, Clone, Getters)] @@ -66,14 +66,12 @@ user_id: &str, reqwest_client: &Client, ) -> Result<(Vec<Achievement>, String), MessageHandlingError> { - let lock = token_store.lock().await; - let token = lock - .get(client_id) - .ok_or(MessageHandlingError::new( - MessageHandlingErrorKind::Unauthorized, - ))? - .clone(); - drop(lock); + let token = { + let lock = token_store.lock().await; + lock.get(client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/achievements", @@ -85,12 +83,12 @@ .header("X-Gog-Lc", crate::LOCALE.as_str()) .send() .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .map_err(MessageHandlingError::network)?; let achievements_data = response .json::<AchievementsResponse>() .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .map_err(MessageHandlingError::network)?; Ok((achievements_data.items, achievements_data.achievements_mode)) } @@ -112,11 +110,17 @@ user_id: &str, achievement_id: &str, date_unlocked: Option<String>, -) -> Result<(), Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<(), MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/achievements/{}", &client_id, user_id, achievement_id @@ -128,8 +132,13 @@ .json(&body) .bearer_auth(token.access_token) .send() - .await?; - response.error_for_status()?; + .await + .map_err(MessageHandlingError::network)?; + + response + .error_for_status() + .map_err(MessageHandlingError::network)?; + Ok(()) } @@ -137,11 +146,17 @@ context: &HandlerContext, reqwest_client: &Client, user_id: &str, -) -> Result<(), Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<(), MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/achievements", &client_id, user_id @@ -151,8 +166,12 @@ .delete(url) .bearer_auth(token.access_token) .send() - .await?; - response.error_for_status()?; + .await + .map_err(MessageHandlingError::network)?; + + response + .error_for_status() + .map_err(MessageHandlingError::network)?; Ok(()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/gog/leaderboards.rs new/comet-0.3.2/src/api/gog/leaderboards.rs --- old/comet-0.3.1/src/api/gog/leaderboards.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/gog/leaderboards.rs 2026-01-04 18:46:58.000000000 +0100 @@ -1,4 +1,5 @@ use crate::api::handlers::context::HandlerContext; +use crate::api::handlers::error::{MessageHandlingError, MessageHandlingErrorKind}; use derive_getters::Getters; use log::debug; use reqwest::{Client, Url}; @@ -42,16 +43,23 @@ context: &HandlerContext, reqwest_client: &Client, params: I, -) -> Result<Vec<LeaderboardDefinition>, reqwest::Error> +) -> Result<Vec<LeaderboardDefinition>, MessageHandlingError> where I: IntoIterator<Item = (K, V)>, K: AsRef<str>, V: AsRef<str>, { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); + let client_id = context.client_id().await.ok_or(MessageHandlingError::new( + MessageHandlingErrorKind::Unauthorized, + ))?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::new( + MessageHandlingErrorKind::Unauthorized, + ))? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/leaderboards", &client_id @@ -64,9 +72,13 @@ .bearer_auth(token.access_token) .header("X-Gog-Lc", crate::LOCALE.as_str()) .send() - .await?; + .await + .map_err(MessageHandlingError::network)?; - let response_data: LeaderboardsResponse = response.json().await?; + let response_data: LeaderboardsResponse = response + .json() + .await + .map_err(MessageHandlingError::network)?; debug!("Got {} leaderboards", response_data.items.len()); @@ -92,16 +104,22 @@ reqwest_client: &Client, leaderboard_id: u64, params: I, -) -> Result<LeaderboardEntriesResponse, reqwest::Error> +) -> Result<LeaderboardEntriesResponse, MessageHandlingError> where I: IntoIterator<Item = (K, V)>, K: AsRef<str>, V: AsRef<str>, { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/leaderboards/{}/entries", @@ -115,11 +133,14 @@ .bearer_auth(token.access_token) .header("X-Gog-Lc", crate::LOCALE.as_str()) .send() - .await?; + .await + .map_err(MessageHandlingError::network)?; - let response = response.error_for_status()?; + let response = response + .error_for_status() + .map_err(MessageHandlingError::network)?; - response.json().await + response.json().await.map_err(MessageHandlingError::network) } #[derive(Serialize)] @@ -145,11 +166,17 @@ score: i32, force_update: bool, details: Option<String>, -) -> Result<LeaderboardScoreUpdateResponse, reqwest::Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<LeaderboardScoreUpdateResponse, MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/leaderboards/{}", @@ -167,10 +194,16 @@ .json(&payload) .bearer_auth(token.access_token) .send() - .await?; + .await + .map_err(MessageHandlingError::network)?; - let response = response.error_for_status()?; - let data = response.json().await?; + let response = response + .error_for_status() + .map_err(MessageHandlingError::network)?; + let data = response + .json() + .await + .map_err(MessageHandlingError::network)?; Ok(data) } @@ -189,11 +222,17 @@ name: String, sort_method: String, display_type: String, -) -> Result<String, reqwest::Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<String, MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let payload = CreateLeaderboardPayload { key, @@ -212,10 +251,16 @@ .json(&payload) .bearer_auth(token.access_token) .send() - .await?; - let response = response.error_for_status()?; - - let definition: LeaderboardDefinition = response.json().await?; + .await + .map_err(MessageHandlingError::network)?; + let response = response + .error_for_status() + .map_err(MessageHandlingError::network)?; + + let definition: LeaderboardDefinition = response + .json() + .await + .map_err(MessageHandlingError::network)?; Ok(definition.id) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/gog/overlay.rs new/comet-0.3.2/src/api/gog/overlay.rs --- old/comet-0.3.1/src/api/gog/overlay.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/gog/overlay.rs 2026-01-04 18:46:58.000000000 +0100 @@ -2,6 +2,7 @@ #[derive(Clone, Debug)] pub enum OverlayPeerMessage { + InitConnection(String), Achievement(Achievement), DisablePopups(Vec<u8>), OpenWebPage(String), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/gog/stats.rs new/comet-0.3.2/src/api/gog/stats.rs --- old/comet-0.3.1/src/api/gog/stats.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/gog/stats.rs 2026-01-04 18:46:58.000000000 +0100 @@ -1,8 +1,8 @@ use crate::api::handlers::context::HandlerContext; -use crate::api::handlers::error::{MessageHandlingError, MessageHandlingErrorKind}; +use crate::api::handlers::error::MessageHandlingError; use crate::constants::TokenStorage; use derive_getters::Getters; -use reqwest::{Client, Error}; +use reqwest::Client; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Debug)] @@ -72,14 +72,12 @@ user_id: &str, reqwest_client: &Client, ) -> Result<Vec<Stat>, MessageHandlingError> { - let lock = token_store.lock().await; - let token = lock - .get(client_id) - .ok_or(MessageHandlingError::new( - MessageHandlingErrorKind::Unauthorized, - ))? - .clone(); - drop(lock); + let token = { + let lock = token_store.lock().await; + lock.get(client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/stats", @@ -90,12 +88,12 @@ .bearer_auth(token.access_token) .send() .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .map_err(MessageHandlingError::network)?; let stats_data = response .json::<StatsResponse>() .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .map_err(MessageHandlingError::network)?; Ok(stats_data.items) } @@ -123,11 +121,17 @@ reqwest_client: &Client, user_id: &str, stat: &Stat, -) -> Result<(), Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<(), MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/stats/{}", @@ -147,9 +151,13 @@ .json(&payload) .bearer_auth(token.access_token) .send() - .await?; + .await + .map_err(MessageHandlingError::network)?; + + response + .error_for_status() + .map_err(MessageHandlingError::network)?; - response.error_for_status()?; Ok(()) } @@ -157,11 +165,17 @@ context: &HandlerContext, reqwest_client: &Client, user_id: &str, -) -> Result<(), Error> { - let lock = context.token_store().lock().await; - let client_id = context.client_id().await.unwrap(); - let token = lock.get(&client_id).unwrap().clone(); - drop(lock); +) -> Result<(), MessageHandlingError> { + let client_id = context + .client_id() + .await + .ok_or(MessageHandlingError::unauthorized())?; + let token = { + let lock = context.token_store().lock().await; + lock.get(&client_id) + .ok_or(MessageHandlingError::unauthorized())? + .clone() + }; let url = format!( "https://gameplay.gog.com/clients/{}/users/{}/stats", @@ -172,8 +186,12 @@ .delete(url) .bearer_auth(token.access_token) .send() - .await?; + .await + .map_err(MessageHandlingError::network)?; + + response + .error_for_status() + .map_err(MessageHandlingError::network)?; - response.error_for_status()?; Ok(()) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/communication_service.rs new/comet-0.3.2/src/api/handlers/communication_service.rs --- old/comet-0.3.1/src/api/handlers/communication_service.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/communication_service.rs 2026-01-04 18:46:58.000000000 +0100 @@ -76,9 +76,7 @@ "Unhandled communication service message type {}", message_type ); - Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )) + Err(MessageHandlingError::not_implemented()) } } @@ -89,8 +87,7 @@ _reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request_data = LibraryInfoRequest::parse_from_bytes(&payload.payload); - let request_data = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let request_data = request_data.map_err(MessageHandlingError::proto)?; let compiler_type = request_data.compiler_type(); let compiler_version = request_data.compiler_version(); @@ -131,8 +128,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request_data = AuthInfoRequest::parse_from_bytes(&payload.payload); - let request_data = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let request_data = request_data.map_err(MessageHandlingError::proto)?; let client_id = request_data.client_id(); let client_secret = request_data.client_secret(); @@ -145,13 +141,15 @@ } info!("Game PID: {}", pid); + let refresh_token = { + let token_storage = context.token_store().lock().await; - let token_storage = context.token_store().lock().await; - let galaxy_token = token_storage - .get(constants::GALAXY_CLIENT_ID) - .expect("Failed to get Galaxy token from store"); - let refresh_token = galaxy_token.refresh_token.clone(); - drop(token_storage); + token_storage + .get(constants::GALAXY_CLIENT_ID) + .expect("Failed to get Galaxy token from store") + .refresh_token + .clone() + }; // Obtain the token (at least attempt to) let new_token = gog::users::get_token_for( @@ -207,7 +205,6 @@ Ok(token) => { let mut token_storage = context.token_store().lock().await; token_storage.insert(String::from(client_id), token.clone()); - drop(token_storage); content.set_refresh_token(token.refresh_token); context.set_online().await; } @@ -216,9 +213,7 @@ if let Some(status) = err.status() { // user doesn't own the game if StatusCode::FORBIDDEN == status { - return Err(MessageHandlingError::new( - MessageHandlingErrorKind::Unauthorized, - )); + return Err(MessageHandlingError::unauthorized()); } } // Check if we can continue offline @@ -378,8 +373,7 @@ _reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request_data = UpdateUserStatRequest::parse_from_bytes(&proto_payload.payload); - let request_data = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let request_data = request_data.map_err(MessageHandlingError::proto)?; let stat_id: u64 = request_data.stat_id(); let stat_id: i64 = stat_id.try_into().unwrap(); @@ -389,13 +383,13 @@ let value = request_data.float_value(); set_stat_float(context, stat_id, value) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; } VALUE_TYPE_INT => { let value = request_data.int_value(); set_stat_int(context, stat_id, value) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; } VALUE_TYPE_UNDEFINED => { warn!("Undefined value type, ignoring"); @@ -424,13 +418,11 @@ user_info: Arc<UserInfo>, reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { - gog::stats::delete_stats(context, reqwest_client, &user_info.galaxy_user_id) - .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + gog::stats::delete_stats(context, reqwest_client, &user_info.galaxy_user_id).await?; db::gameplay::reset_stats(context) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; let mut header = Header::new(); header.set_type( @@ -536,8 +528,7 @@ _reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request_data = UnlockUserAchievementRequest::parse_from_bytes(&proto_payload.payload); - let request_data = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let request_data = request_data.map_err(MessageHandlingError::proto)?; let ach_id: i64 = request_data.achievement_id().try_into().unwrap(); let timestamp = request_data.time(); @@ -588,8 +579,7 @@ _reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request_data = ClearUserAchievementRequest::parse_from_bytes(&proto_payload.payload); - let request_data = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let request_data = request_data.map_err(MessageHandlingError::proto)?; let ach_id: i64 = request_data.achievement_id().try_into().unwrap(); @@ -619,12 +609,11 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { gog::achievements::delete_achievements(context, reqwest_client, &user_info.galaxy_user_id) - .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .await?; db::gameplay::reset_achievements(context) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; let mut header = Header::new(); header.set_type( @@ -656,7 +645,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = GetLeaderboardsByKeyRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let keys = request.key.join(","); super::utils::handle_leaderboards_query(context, reqwest_client, [("keys", keys)]).await @@ -669,7 +658,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = GetLeaderboardEntriesGlobalRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let params = [ ("range_start", request.range_start().to_string()), @@ -691,7 +680,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = GetLeaderboardEntriesAroundUserRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let user_id = IDType::parse(request.user_id()); @@ -717,7 +706,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = GetLeaderboardEntriesForUsersRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let user_ids: String = request .user_ids @@ -744,7 +733,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = SetLeaderboardScoreRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let id = request.leaderboard_id().to_string(); info!( @@ -755,7 +744,7 @@ let current_score = match db::gameplay::get_leaderboard_score(context, &id).await { Ok((score, _old_rank, _entry_total_count, _force, _details)) => score, Err(sqlx::Error::RowNotFound) => 0, - Err(err) => return Err(MessageHandlingError::new(MessageHandlingErrorKind::DB(err))), + Err(err) => return Err(MessageHandlingError::db(err)), }; let mut header = Header::new(); header.set_type( @@ -804,7 +793,7 @@ &details, ) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; db::gameplay::set_leaderboard_rank( context, &id, @@ -812,7 +801,7 @@ data.leaderboard_entry_total_count, ) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; let mut proto_data = SetLeaderboardScoreResponse::new(); proto_data.set_score(request.score()); @@ -821,7 +810,7 @@ proto_data.set_leaderboard_entry_total_count(data.leaderboard_entry_total_count); let payload = proto_data .write_to_bytes() - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; header.set_size(payload.len().try_into().unwrap()); return Ok(ProtoPayload { header, payload }); } @@ -836,12 +825,14 @@ &details, ) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; - if err.status().is_none() || err.status().is_some_and(|s| s.as_u16() != 409) { - db::gameplay::set_leaderboad_changed(context, &id, true) - .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; - context.set_updated_leaderboards(true).await; + .map_err(MessageHandlingError::db)?; + if let MessageHandlingErrorKind::Network(err) = err.kind { + if err.status().is_none() || err.status().is_some_and(|s| s.as_u16() != 409) { + db::gameplay::set_leaderboad_changed(context, &id, true) + .await + .map_err(MessageHandlingError::db)?; + context.set_updated_leaderboards(true).await; + } } } } @@ -849,7 +840,7 @@ let (_score, old_rank, entry_total_count, _force, _details) = db::gameplay::get_leaderboard_score(context, &id) .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::DB(err)))?; + .map_err(MessageHandlingError::db)?; let new_rank = if old_rank != 0 { old_rank } else { 1 }; let entry_total_count = if old_rank != 0 { entry_total_count @@ -864,7 +855,7 @@ proto_data.set_leaderboard_entry_total_count(entry_total_count); let payload = proto_data .write_to_bytes() - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; header.set_size(payload.len().try_into().unwrap()); Ok(ProtoPayload { header, payload }) } @@ -876,7 +867,7 @@ reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let mut request = CreateLeaderboardRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; let key = request.take_key(); let name = request.take_name(); @@ -904,8 +895,7 @@ sort_method, display_type, ) - .await - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Network(err)))?; + .await?; let mut response = CreateLeaderboardResponse::new(); response.set_leaderboard_id(leaderboard_id.parse().unwrap()); @@ -920,7 +910,7 @@ let payload = response .write_to_bytes() - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; header.set_size(payload.len().try_into().unwrap()); Ok(ProtoPayload { header, payload }) @@ -933,7 +923,7 @@ _reqwest_client: &Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = StartGameSessionRequest::parse_from_bytes(&proto_payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; info!("Requested session start for {}", request.game_pid()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/context.rs new/comet-0.3.2/src/api/handlers/context.rs --- old/comet-0.3.1/src/api/handlers/context.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/context.rs 2026-01-04 18:46:58.000000000 +0100 @@ -26,7 +26,6 @@ socket: Mutex<TcpStream>, token_store: TokenStorage, overlay_sender: broadcast::Sender<(u32, OverlayPeerMessage)>, - overlay_listener: Mutex<String>, #[getter(skip)] db_connection: Mutex<Option<SqlitePool>>, #[getter(skip)] @@ -55,7 +54,6 @@ token_store, overlay_sender: achievement_sender, db_connection: Mutex::new(None), - overlay_listener: Mutex::new(String::default()), state, } } @@ -161,8 +159,10 @@ } pub async fn register_overlay_listener(&self, pid: u32, listener: String) { - let mut ov_listener = self.overlay_listener.lock().await; self.state.lock().await.pid = pid; - *ov_listener = listener; + + self.overlay_sender + .send((pid, OverlayPeerMessage::InitConnection(listener))) + .ok(); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/error.rs new/comet-0.3.2/src/api/handlers/error.rs --- old/comet-0.3.1/src/api/handlers/error.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/error.rs 2026-01-04 18:46:58.000000000 +0100 @@ -21,6 +21,54 @@ pub fn new(kind: MessageHandlingErrorKind) -> MessageHandlingError { MessageHandlingError { kind } } + + pub fn not_implemented() -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::NotImplemented, + } + } + + pub fn ignored() -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::Ignored, + } + } + + pub fn unauthorized() -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::Unauthorized, + } + } + + pub fn io(err: tokio::io::Error) -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::IO(err), + } + } + + pub fn db(err: sqlx::Error) -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::DB(err), + } + } + + pub fn network(err: reqwest::Error) -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::Network(err), + } + } + + pub fn proto(err: protobuf::Error) -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::Proto(err), + } + } + + pub fn json(err: serde_json::Error) -> MessageHandlingError { + MessageHandlingError { + kind: MessageHandlingErrorKind::Json(err), + } + } } impl std::fmt::Display for MessageHandlingError { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/overlay_client.rs new/comet-0.3.2/src/api/handlers/overlay_client.rs --- old/comet-0.3.1/src/api/handlers/overlay_client.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/overlay_client.rs 2026-01-04 18:46:58.000000000 +0100 @@ -33,9 +33,7 @@ "Received unsupported ov_client message type {}", message_type ); - Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )) + Err(MessageHandlingError::not_implemented()) } } @@ -190,9 +188,9 @@ reqwest_client: &reqwest::Client, ) -> Result<ProtoPayload, MessageHandlingError> { let request = OverlayToClientRequest::parse_from_bytes(&payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; - let parsed_request: serde_json::Value = serde_json::from_str(request.data()) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Json(err)))?; + .map_err(MessageHandlingError::proto)?; + let parsed_request: serde_json::Value = + serde_json::from_str(request.data()).map_err(MessageHandlingError::json)?; let command = parsed_request.get("Command"); let json_data: serde_json::Value = match command { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/overlay_peer.rs new/comet-0.3.2/src/api/handlers/overlay_peer.rs --- old/comet-0.3.1/src/api/handlers/overlay_peer.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/overlay_peer.rs 2026-01-04 18:46:58.000000000 +0100 @@ -4,7 +4,7 @@ use log::warn; use protobuf::{Enum, Message}; -use super::{context::HandlerContext, MessageHandlingError, MessageHandlingErrorKind}; +use super::{context::HandlerContext, MessageHandlingError}; pub async fn entry_point( payload: &ProtoPayload, @@ -26,11 +26,9 @@ let _ = overlay_initialized(payload, context).await; } else { warn!("Received unsupported peer message type {}", message_type); - return Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )); + return Err(MessageHandlingError::not_implemented()); } - Err(MessageHandlingError::new(MessageHandlingErrorKind::Ignored)) + Err(MessageHandlingError::ignored()) } async fn show_web_page( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/overlay_service.rs new/comet-0.3.2/src/api/handlers/overlay_service.rs --- old/comet-0.3.1/src/api/handlers/overlay_service.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/overlay_service.rs 2026-01-04 18:46:58.000000000 +0100 @@ -1,4 +1,4 @@ -use super::{context::HandlerContext, MessageHandlingError, MessageHandlingErrorKind}; +use super::{context::HandlerContext, MessageHandlingError}; use crate::api::gog::achievements::Achievement; use crate::constants; use crate::proto::common_utils::ProtoPayload; @@ -19,15 +19,13 @@ access_token(payload, context).await } else if message_type == MessageType::OVERLAY_INITIALIZATION_NOTIFICATION.value() { init_notification(payload).await?; - Err(MessageHandlingError::new(MessageHandlingErrorKind::Ignored)) + Err(MessageHandlingError::ignored()) } else { warn!( "Received unsupported ov_service message type {}", message_type ); - Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )) + Err(MessageHandlingError::not_implemented()) } } @@ -85,9 +83,7 @@ if let Some(token) = galaxy_access_token { res.set_access_token(token.access_token.clone()); } - let payload = res - .write_to_bytes() - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let payload = res.write_to_bytes().map_err(MessageHandlingError::proto)?; let mut header = gog_protocols_pb::Header::new(); header.set_sort(MessageSort::MESSAGE_SORT.value().try_into().unwrap()); header.set_type( @@ -103,7 +99,7 @@ async fn init_notification(payload: &ProtoPayload) -> Result<(), MessageHandlingError> { let message = OverlayInitializationNotification::parse_from_bytes(&payload.payload) - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; info!( "Overlay notified if it successfully initialized - {}", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/utils.rs new/comet-0.3.2/src/api/handlers/utils.rs --- old/comet-0.3.1/src/api/handlers/utils.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/utils.rs 2026-01-04 18:46:58.000000000 +0100 @@ -98,7 +98,7 @@ let payload = payload_data .write_to_bytes() - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + .map_err(MessageHandlingError::proto)?; header.set_size(payload.len().try_into().unwrap()); @@ -160,11 +160,13 @@ } Err(err) => { warn!("Leaderboards request error: {}", err); - if err.is_status() && err.status().unwrap() == reqwest::StatusCode::NOT_FOUND { - header - .mut_special_fields() - .mut_unknown_fields() - .add_varint(101, 404); + if let MessageHandlingErrorKind::Network(err) = err.kind { + if err.is_status() && err.status().unwrap() == reqwest::StatusCode::NOT_FOUND { + header + .mut_special_fields() + .mut_unknown_fields() + .add_varint(101, 404); + } } Vec::new() } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers/webbroker.rs new/comet-0.3.2/src/api/handlers/webbroker.rs --- old/comet-0.3.1/src/api/handlers/webbroker.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers/webbroker.rs 2026-01-04 18:46:58.000000000 +0100 @@ -25,9 +25,7 @@ "Received unsupported webbroker message type {}", message_type ); - Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )) + Err(MessageHandlingError::not_implemented()) } } @@ -39,8 +37,7 @@ // This is the stub that just responds with success let request_data = SubscribeTopicRequest::parse_from_bytes(&payload.payload); - let proto = request_data - .map_err(|err| MessageHandlingError::new(MessageHandlingErrorKind::Proto(err)))?; + let proto = request_data.map_err(MessageHandlingError::proto)?; let topic = String::from(proto.topic()); context.subscribe_topic(topic.clone()).await; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/api/handlers.rs new/comet-0.3.2/src/api/handlers.rs --- old/comet-0.3.1/src/api/handlers.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/api/handlers.rs 2026-01-04 18:46:58.000000000 +0100 @@ -9,7 +9,7 @@ use crate::{constants::TokenStorage, proto::common_utils::ProtoPayload}; use error::*; -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; use crate::api::gog; use crate::api::notification_pusher::PusherEvent; @@ -160,19 +160,36 @@ let user_clone = user_info.clone(); let overlay_thread = tokio::spawn(async move { let mut overlay_receiver = overlay_event_receiver; - #[cfg(unix)] - let overlay_listener: tokio::net::UnixListener = loop { - if shutdown_token_clone.is_cancelled() { - return; - } - tokio::time::sleep(Duration::from_secs(2)).await; - let lock = context_clone.overlay_listener().lock().await; - if !lock.is_empty() { - if let Ok(list) = tokio::net::UnixListener::bind(&*lock) { - break list; + + let pipe_name = loop { + tokio::select! { + Ok((_pid, msg)) = overlay_receiver.recv() => { + if let OverlayPeerMessage::InitConnection(socket) = msg { + break socket; + } + }, + _ = shutdown_token_clone.cancelled() => { + return; } } }; + + #[cfg(unix)] + let Ok(overlay_listener) = tokio::net::UnixListener::bind(&pipe_name) else { + return; + }; + + #[cfg(windows)] + let Ok(mut current_socket) = tokio::net::windows::named_pipe::ServerOptions::new() + .first_pipe_instance(true) + .create(&pipe_name) + else { + return; + }; + + #[cfg(windows)] + current_socket.connect().await.ok(); + #[cfg(unix)] let mut current_socket = { debug!("Waiting for overlay connection"); @@ -186,24 +203,7 @@ } } }; - #[cfg(windows)] - let mut current_socket: tokio::net::windows::named_pipe::NamedPipeServer = loop { - if shutdown_token_clone.is_cancelled() { - return; - } - tokio::time::sleep(Duration::from_secs(2)).await; - let lock = context_clone.overlay_listener().lock().await; - if !lock.is_empty() { - if let Ok(list) = tokio::net::windows::named_pipe::ServerOptions::new() - .first_pipe_instance(true) - .create(&*lock) - { - if list.connect().await.is_ok() { - break list; - } - } - } - }; + let game_pid = context_clone.get_pid().await; loop { tokio::select! { @@ -235,7 +235,7 @@ OverlayPeerMessage::OpenWebPage(page) => overlay_peer::encode_open_web_page(page).await, OverlayPeerMessage::InvitationDialog(con) => overlay_peer::encode_game_invite(con).await, OverlayPeerMessage::DisablePopups(data) => overlay_peer::encode_overlay_initialized(data).await, - _ => Err(MessageHandlingError::new(MessageHandlingErrorKind::Ignored).into()) + _ => Err(MessageHandlingError::ignored().into()) }; if let Ok(data) = data { if let Err(err) = current_socket.write_all(&data).await { @@ -277,10 +277,6 @@ let _ = main_socket.await; let _ = overlay_thread.await; - let lock = context.overlay_listener().lock().await; - if !lock.is_empty() { - let _ = tokio::fs::remove_file(&*lock).await; - } sync_routine(&context, &reqwest_client, user_info.clone()).await; } @@ -303,9 +299,7 @@ 7 => overlay_client::entry_point(&payload, context, user_info, reqwest_client).await, _ => { warn!("Unhandled sort {}", sort); - Err(MessageHandlingError::new( - MessageHandlingErrorKind::NotImplemented, - )) + Err(MessageHandlingError::not_implemented()) } }?; result.header.set_sort(sort); @@ -515,8 +509,9 @@ .expect("Failed to update leaderboard state"); } Err(err) => { - if let Some(status) = err.status() { - if status.as_u16() == 409 { + warn!("More details {}", err); + if let MessageHandlingErrorKind::Network(networ_error) = err.kind { + if networ_error.status().is_some_and(|s| s == 409) { warn!("Leaderboard conflict for {}", id); let entries = gog::leaderboards::get_leaderboards_entries( context, @@ -550,7 +545,6 @@ } } } - warn!("More details {}", err); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/comet-0.3.1/src/main.rs new/comet-0.3.2/src/main.rs --- old/comet-0.3.1/src/main.rs 2025-07-21 21:19:35.000000000 +0200 +++ new/comet-0.3.2/src/main.rs 2026-01-04 18:46:58.000000000 +0100 @@ -19,6 +19,7 @@ use comet::api::notification_pusher::NotificationPusherClient; use comet::api::notification_pusher::PusherEvent; use comet::api::structs::{Token, UserInfo}; +use tokio::task::JoinHandle; #[derive(Subcommand, Debug)] enum SubCommand { @@ -86,7 +87,11 @@ #[tokio::main] async fn main() { let args = Args::parse(); - let env = Env::new().filter_or("COMET_LOG", "info"); + #[cfg(debug_assertions)] + let log_level = "debug"; + #[cfg(not(debug_assertions))] + let log_level = "info"; + let env = Env::new().filter_or("COMET_LOG", log_level); Builder::from_env(env) .target(Target::Stderr) .filter_module("h2::codec", log::LevelFilter::Off) @@ -112,9 +117,10 @@ let token_store: constants::TokenStorage = Arc::new(Mutex::new(HashMap::new())); let galaxy_token = Token::new(access_token.clone(), refresh_token.clone()); - let mut store_lock = token_store.lock().await; - store_lock.insert(String::from(constants::GALAXY_CLIENT_ID), galaxy_token); - drop(store_lock); + { + let mut store_lock = token_store.lock().await; + store_lock.insert(String::from(constants::GALAXY_CLIENT_ID), galaxy_token); + } let client_clone = reqwest_client.clone(); tokio::spawn(async move { @@ -170,12 +176,13 @@ if !db::gameplay::has_achievements(&database).await || !db::gameplay::has_statistics(&database).await { - let mut connection = database.acquire().await.unwrap(); - sqlx::query(db::gameplay::SETUP_QUERY) - .execute(&mut *connection) - .await - .expect("Failed to setup the database"); - drop(connection); + { + let mut connection = database.acquire().await.unwrap(); + sqlx::query(db::gameplay::SETUP_QUERY) + .execute(&mut *connection) + .await + .expect("Failed to setup the database"); + } let new_token = api::gog::users::get_token_for( &client_id, @@ -187,9 +194,10 @@ .await .expect("Failed to obtain credentials"); - let mut tokens = token_store.lock().await; - tokens.insert(client_id.clone(), new_token); - drop(tokens); + { + let mut tokens = token_store.lock().await; + tokens.insert(client_id.clone(), new_token); + } let new_achievements = api::gog::achievements::fetch_achievements( &token_store, @@ -323,6 +331,7 @@ } } _ = tokio::time::sleep(Duration::from_secs(comet_idle_wait)) => { + handlers.retain(|handler: &JoinHandle<()>| !handler.is_finished()); if active_clients == 0 && ever_connected { socket_shutdown.cancel(); break ++++++ vendor-comet.tar.zst ++++++ /work/SRC/openSUSE:Factory/comet/vendor-comet.tar.zst /work/SRC/openSUSE:Factory/.comet.new.1928/vendor-comet.tar.zst differ: char 7, line 1
