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

Reply via email to