Use signals for updating and saving games
This commit is contained in:
@@ -205,6 +205,7 @@ class DetailsWindow(Adw.Window):
|
|||||||
self.game.save()
|
self.game.save()
|
||||||
self.game.update()
|
self.game.update()
|
||||||
|
|
||||||
|
# TODO: this is fucked up
|
||||||
# Get a cover from SGDB if none is present
|
# Get a cover from SGDB if none is present
|
||||||
if not self.game_cover.get_pixbuf():
|
if not self.game_cover.get_pixbuf():
|
||||||
self.game.set_loading(1)
|
self.game.set_loading(1)
|
||||||
|
|||||||
79
src/game.py
79
src/game.py
@@ -17,7 +17,6 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
@@ -25,10 +24,9 @@ import subprocess
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from gi.repository import Adw, Gtk
|
from gi.repository import Adw, GObject, Gtk
|
||||||
|
|
||||||
from src import shared # pylint: disable=no-name-in-module
|
from src import shared # pylint: disable=no-name-in-module
|
||||||
from src.game_cover import GameCover
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
@@ -86,74 +84,15 @@ class Game(Gtk.Box):
|
|||||||
|
|
||||||
shared.schema.connect("changed", self.schema_changed)
|
shared.schema.connect("changed", self.schema_changed)
|
||||||
|
|
||||||
def update(self):
|
|
||||||
if self.get_parent():
|
|
||||||
self.get_parent().get_parent().remove(self)
|
|
||||||
if self.get_parent():
|
|
||||||
self.get_parent().set_child()
|
|
||||||
|
|
||||||
self.menu_button.set_menu_model(
|
|
||||||
self.hidden_game_options if self.hidden else self.game_options
|
|
||||||
)
|
|
||||||
|
|
||||||
self.title.set_label(self.name)
|
|
||||||
|
|
||||||
self.menu_button.get_popover().connect(
|
|
||||||
"notify::visible", self.toggle_play, None
|
|
||||||
)
|
|
||||||
self.menu_button.get_popover().connect(
|
|
||||||
"notify::visible", self.win.set_active_game, self
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.game_id in self.win.game_covers:
|
|
||||||
self.game_cover = self.win.game_covers[self.game_id]
|
|
||||||
self.game_cover.add_picture(self.cover)
|
|
||||||
else:
|
|
||||||
self.game_cover = GameCover({self.cover}, self.get_cover_path())
|
|
||||||
self.win.game_covers[self.game_id] = self.game_cover
|
|
||||||
|
|
||||||
if (
|
|
||||||
self.win.stack.get_visible_child() == self.win.details_view
|
|
||||||
and self.win.active_game == self
|
|
||||||
):
|
|
||||||
self.win.show_details_view(self)
|
|
||||||
|
|
||||||
if not self.removed and not self.blacklisted:
|
|
||||||
if self.hidden:
|
|
||||||
self.win.hidden_library.append(self)
|
|
||||||
else:
|
|
||||||
self.win.library.append(self)
|
|
||||||
self.get_parent().set_focusable(False)
|
|
||||||
|
|
||||||
self.win.set_library_child()
|
|
||||||
|
|
||||||
def update_values(self, data):
|
def update_values(self, data):
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.emit("update-ready", {})
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
shared.games_dir.mkdir(parents=True, exist_ok=True)
|
self.emit("save-ready", {})
|
||||||
|
|
||||||
attrs = (
|
|
||||||
"added",
|
|
||||||
"executable",
|
|
||||||
"game_id",
|
|
||||||
"source",
|
|
||||||
"hidden",
|
|
||||||
"last_played",
|
|
||||||
"name",
|
|
||||||
"developer",
|
|
||||||
"removed",
|
|
||||||
"blacklisted",
|
|
||||||
"version",
|
|
||||||
)
|
|
||||||
|
|
||||||
json.dump(
|
|
||||||
{attr: getattr(self, attr) for attr in attrs if attr},
|
|
||||||
(shared.games_dir / f"{self.game_id}.json").open("w"),
|
|
||||||
indent=4,
|
|
||||||
sort_keys=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_toast(self, title, action=None):
|
def create_toast(self, title, action=None):
|
||||||
toast = Adw.Toast.new(title.format(self.name))
|
toast = Adw.Toast.new(title.format(self.name))
|
||||||
@@ -270,3 +209,11 @@ class Game(Gtk.Box):
|
|||||||
def schema_changed(self, _settings, key):
|
def schema_changed(self, _settings, key):
|
||||||
if key == "cover-launches-game":
|
if key == "cover-launches-game":
|
||||||
self.set_play_icon()
|
self.set_play_icon()
|
||||||
|
|
||||||
|
@GObject.Signal(name="update-ready", arg_types=[object])
|
||||||
|
def update_ready(self, _additional_data) -> None:
|
||||||
|
"""Signal emitted when the game needs updating"""
|
||||||
|
|
||||||
|
@GObject.Signal(name="save-ready", arg_types=[object])
|
||||||
|
def save_ready(self, _additional_data) -> None:
|
||||||
|
"""Signal emitted when the game needs saving"""
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class CartridgesApplication(Adw.Application):
|
|||||||
# Create the games store ready to load games from disk
|
# Create the games store ready to load games from disk
|
||||||
if not shared.store:
|
if not shared.store:
|
||||||
shared.store = Store()
|
shared.store = Store()
|
||||||
|
shared.store.add_manager(FileManager(), False)
|
||||||
shared.store.add_manager(DisplayManager())
|
shared.store.add_manager(DisplayManager())
|
||||||
|
|
||||||
self.load_games_from_disk()
|
self.load_games_from_disk()
|
||||||
@@ -91,7 +92,8 @@ class CartridgesApplication(Adw.Application):
|
|||||||
shared.store.add_manager(SteamAPIManager())
|
shared.store.add_manager(SteamAPIManager())
|
||||||
shared.store.add_manager(OnlineCoverManager())
|
shared.store.add_manager(OnlineCoverManager())
|
||||||
shared.store.add_manager(SGDBManager())
|
shared.store.add_manager(SGDBManager())
|
||||||
shared.store.add_manager(FileManager())
|
|
||||||
|
shared.store.manager_to_pipeline(FileManager)
|
||||||
|
|
||||||
# Create actions
|
# Create actions
|
||||||
self.create_actions(
|
self.create_actions(
|
||||||
@@ -137,7 +139,7 @@ class CartridgesApplication(Adw.Application):
|
|||||||
for game_file in shared.games_dir.iterdir():
|
for game_file in shared.games_dir.iterdir():
|
||||||
data = json.load(game_file.open())
|
data = json.load(game_file.open())
|
||||||
game = Game(data, allow_side_effects=False)
|
game = Game(data, allow_side_effects=False)
|
||||||
shared.store.add_game(game, tuple())
|
shared.store.add_game(game, {"skip_save": True})
|
||||||
|
|
||||||
def on_about_action(self, *_args):
|
def on_about_action(self, *_args):
|
||||||
about = Adw.AboutWindow(
|
about = Adw.AboutWindow(
|
||||||
@@ -148,9 +150,9 @@ class CartridgesApplication(Adw.Application):
|
|||||||
version=shared.VERSION,
|
version=shared.VERSION,
|
||||||
developers=[
|
developers=[
|
||||||
"kramo https://kramo.hu",
|
"kramo https://kramo.hu",
|
||||||
|
"Geoffrey Coulaud https://geoffrey-coulaud.fr",
|
||||||
"Arcitec https://github.com/Arcitec",
|
"Arcitec https://github.com/Arcitec",
|
||||||
"Domenico https://github.com/Domefemia",
|
"Domenico https://github.com/Domefemia",
|
||||||
"Geoffrey Coulaud https://geoffrey-coulaud.fr",
|
|
||||||
"Paweł Lidwin https://github.com/imLinguin",
|
"Paweł Lidwin https://github.com/imLinguin",
|
||||||
"Rafael Mardojai CM https://mardojai.com",
|
"Rafael Mardojai CM https://mardojai.com",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,16 +1,55 @@
|
|||||||
from src import shared # pylint: disable=no-name-in-module
|
from src import shared # pylint: disable=no-name-in-module
|
||||||
from src.game import Game
|
from src.game import Game
|
||||||
|
from src.game_cover import GameCover
|
||||||
|
from src.store.managers.manager import Manager
|
||||||
from src.store.managers.sgdb_manager import SGDBManager
|
from src.store.managers.sgdb_manager import SGDBManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||||
from src.store.managers.manager import Manager
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayManager(Manager):
|
class DisplayManager(Manager):
|
||||||
"""Manager in charge of adding a game to the UI"""
|
"""Manager in charge of adding a game to the UI"""
|
||||||
|
|
||||||
run_after = (SteamAPIManager, SGDBManager)
|
run_after = (SteamAPIManager, SGDBManager)
|
||||||
|
signals = {"update-ready"}
|
||||||
|
|
||||||
def manager_logic(self, game: Game, _additional_data: dict) -> None:
|
def manager_logic(self, game: Game, _additional_data: dict) -> None:
|
||||||
# TODO decouple a game from its widget
|
|
||||||
shared.win.games[game.game_id] = game
|
shared.win.games[game.game_id] = game
|
||||||
game.update()
|
if game.get_parent():
|
||||||
|
game.get_parent().get_parent().remove(game)
|
||||||
|
if game.get_parent():
|
||||||
|
game.get_parent().set_child()
|
||||||
|
|
||||||
|
game.menu_button.set_menu_model(
|
||||||
|
game.hidden_game_options if game.hidden else game.game_options
|
||||||
|
)
|
||||||
|
|
||||||
|
game.title.set_label(game.name)
|
||||||
|
|
||||||
|
game.menu_button.get_popover().connect(
|
||||||
|
"notify::visible", game.toggle_play, None
|
||||||
|
)
|
||||||
|
game.menu_button.get_popover().connect(
|
||||||
|
"notify::visible", game.win.set_active_game, game
|
||||||
|
)
|
||||||
|
|
||||||
|
if game.game_id in game.win.game_covers:
|
||||||
|
game.game_cover = game.win.game_covers[game.game_id]
|
||||||
|
game.game_cover.add_picture(game.cover)
|
||||||
|
else:
|
||||||
|
game.game_cover = GameCover({game.cover}, game.get_cover_path())
|
||||||
|
game.win.game_covers[game.game_id] = game.game_cover
|
||||||
|
|
||||||
|
if (
|
||||||
|
game.win.stack.get_visible_child() == game.win.details_view
|
||||||
|
and game.win.active_game == game
|
||||||
|
):
|
||||||
|
game.win.show_details_view(game)
|
||||||
|
|
||||||
|
if not game.removed and not game.blacklisted:
|
||||||
|
if game.hidden:
|
||||||
|
game.win.hidden_library.append(game)
|
||||||
|
else:
|
||||||
|
game.win.library.append(game)
|
||||||
|
game.get_parent().set_focusable(False)
|
||||||
|
|
||||||
|
game.win.set_library_child()
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from src import shared # pylint: disable=no-name-in-module
|
||||||
from src.game import Game
|
from src.game import Game
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from src.store.managers.async_manager import AsyncManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||||
@@ -7,6 +10,31 @@ class FileManager(AsyncManager):
|
|||||||
"""Manager in charge of saving a game to a file"""
|
"""Manager in charge of saving a game to a file"""
|
||||||
|
|
||||||
run_after = (SteamAPIManager,)
|
run_after = (SteamAPIManager,)
|
||||||
|
signals = {"save-ready"}
|
||||||
|
|
||||||
def manager_logic(self, game: Game, _additional_data: dict) -> None:
|
def manager_logic(self, game: Game, additional_data: dict) -> None:
|
||||||
game.save()
|
if additional_data.get("skip_save"): # Skip saving when loading games from disk
|
||||||
|
return
|
||||||
|
|
||||||
|
shared.games_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
attrs = (
|
||||||
|
"added",
|
||||||
|
"executable",
|
||||||
|
"game_id",
|
||||||
|
"source",
|
||||||
|
"hidden",
|
||||||
|
"last_played",
|
||||||
|
"name",
|
||||||
|
"developer",
|
||||||
|
"removed",
|
||||||
|
"blacklisted",
|
||||||
|
"version",
|
||||||
|
)
|
||||||
|
|
||||||
|
json.dump(
|
||||||
|
{attr: getattr(game, attr) for attr in attrs if attr},
|
||||||
|
(shared.games_dir / f"{game.game_id}.json").open("w"),
|
||||||
|
indent=4,
|
||||||
|
sort_keys=True,
|
||||||
|
)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Manager:
|
|||||||
|
|
||||||
retryable_on: Container[type[Exception]] = tuple()
|
retryable_on: Container[type[Exception]] = tuple()
|
||||||
continue_on: Container[type[Exception]] = tuple()
|
continue_on: Container[type[Exception]] = tuple()
|
||||||
|
signals: Container[type[str]] = set()
|
||||||
retry_delay: int = 3
|
retry_delay: int = 3
|
||||||
max_tries: int = 3
|
max_tries: int = 3
|
||||||
|
|
||||||
@@ -110,6 +111,5 @@ class Manager:
|
|||||||
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
|
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Pass the game through the manager"""
|
"""Pass the game through the manager"""
|
||||||
# TODO: connect to signals here
|
|
||||||
self.execute_resilient_manager_logic(game, additional_data)
|
self.execute_resilient_manager_logic(game, additional_data)
|
||||||
callback(self)
|
callback(self)
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ class Store:
|
|||||||
self.games = {}
|
self.games = {}
|
||||||
self.pipelines = {}
|
self.pipelines = {}
|
||||||
|
|
||||||
def add_manager(self, manager: Manager):
|
def add_manager(self, manager: Manager, in_pipeline=True):
|
||||||
"""Add a manager that will run when games are added"""
|
"""Add a manager that will run when games are added"""
|
||||||
self.managers[type(manager)] = manager
|
self.managers[type(manager)] = [manager, in_pipeline]
|
||||||
|
|
||||||
|
def manager_to_pipeline(self, manager_type: type[Manager]):
|
||||||
|
self.managers[manager_type][1] = True
|
||||||
|
|
||||||
def add_game(
|
def add_game(
|
||||||
self, game: Game, additional_data: dict, replace=False
|
self, game: Game, additional_data: dict, replace=False
|
||||||
@@ -50,8 +53,17 @@ class Store:
|
|||||||
path.unlink(missing_ok=True)
|
path.unlink(missing_ok=True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Connect signals
|
||||||
|
for manager, _in_pipeline in self.managers.values():
|
||||||
|
for signal in manager.signals:
|
||||||
|
game.connect(signal, manager.execute_resilient_manager_logic)
|
||||||
|
|
||||||
# Run the pipeline for the game
|
# Run the pipeline for the game
|
||||||
pipeline = Pipeline(game, additional_data, self.managers.values())
|
pipeline = Pipeline(
|
||||||
|
game,
|
||||||
|
additional_data,
|
||||||
|
(manager[0] for manager in self.managers.values() if manager[1]),
|
||||||
|
)
|
||||||
self.games[game.game_id] = game
|
self.games[game.game_id] = game
|
||||||
self.pipelines[game.game_id] = pipeline
|
self.pipelines[game.game_id] = pipeline
|
||||||
pipeline.advance()
|
pipeline.advance()
|
||||||
|
|||||||
Reference in New Issue
Block a user