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.update()
|
||||
|
||||
# TODO: this is fucked up
|
||||
# Get a cover from SGDB if none is present
|
||||
if not self.game_cover.get_pixbuf():
|
||||
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
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
@@ -25,10 +24,9 @@ import subprocess
|
||||
from pathlib import Path
|
||||
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.game_cover import GameCover
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@@ -86,74 +84,15 @@ class Game(Gtk.Box):
|
||||
|
||||
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):
|
||||
for key, value in data.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def update(self):
|
||||
self.emit("update-ready", {})
|
||||
|
||||
def save(self):
|
||||
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(self, attr) for attr in attrs if attr},
|
||||
(shared.games_dir / f"{self.game_id}.json").open("w"),
|
||||
indent=4,
|
||||
sort_keys=True,
|
||||
)
|
||||
self.emit("save-ready", {})
|
||||
|
||||
def create_toast(self, title, action=None):
|
||||
toast = Adw.Toast.new(title.format(self.name))
|
||||
@@ -270,3 +209,11 @@ class Game(Gtk.Box):
|
||||
def schema_changed(self, _settings, key):
|
||||
if key == "cover-launches-game":
|
||||
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
|
||||
if not shared.store:
|
||||
shared.store = Store()
|
||||
shared.store.add_manager(FileManager(), False)
|
||||
shared.store.add_manager(DisplayManager())
|
||||
|
||||
self.load_games_from_disk()
|
||||
@@ -91,7 +92,8 @@ class CartridgesApplication(Adw.Application):
|
||||
shared.store.add_manager(SteamAPIManager())
|
||||
shared.store.add_manager(OnlineCoverManager())
|
||||
shared.store.add_manager(SGDBManager())
|
||||
shared.store.add_manager(FileManager())
|
||||
|
||||
shared.store.manager_to_pipeline(FileManager)
|
||||
|
||||
# Create actions
|
||||
self.create_actions(
|
||||
@@ -137,7 +139,7 @@ class CartridgesApplication(Adw.Application):
|
||||
for game_file in shared.games_dir.iterdir():
|
||||
data = json.load(game_file.open())
|
||||
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):
|
||||
about = Adw.AboutWindow(
|
||||
@@ -148,9 +150,9 @@ class CartridgesApplication(Adw.Application):
|
||||
version=shared.VERSION,
|
||||
developers=[
|
||||
"kramo https://kramo.hu",
|
||||
"Geoffrey Coulaud https://geoffrey-coulaud.fr",
|
||||
"Arcitec https://github.com/Arcitec",
|
||||
"Domenico https://github.com/Domefemia",
|
||||
"Geoffrey Coulaud https://geoffrey-coulaud.fr",
|
||||
"Paweł Lidwin https://github.com/imLinguin",
|
||||
"Rafael Mardojai CM https://mardojai.com",
|
||||
],
|
||||
|
||||
@@ -1,16 +1,55 @@
|
||||
from src import shared # pylint: disable=no-name-in-module
|
||||
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.steam_api_manager import SteamAPIManager
|
||||
from src.store.managers.manager import Manager
|
||||
|
||||
|
||||
class DisplayManager(Manager):
|
||||
"""Manager in charge of adding a game to the UI"""
|
||||
|
||||
run_after = (SteamAPIManager, SGDBManager)
|
||||
signals = {"update-ready"}
|
||||
|
||||
def manager_logic(self, game: Game, _additional_data: dict) -> None:
|
||||
# TODO decouple a game from its widget
|
||||
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.store.managers.async_manager import AsyncManager
|
||||
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"""
|
||||
|
||||
run_after = (SteamAPIManager,)
|
||||
signals = {"save-ready"}
|
||||
|
||||
def manager_logic(self, game: Game, _additional_data: dict) -> None:
|
||||
game.save()
|
||||
def manager_logic(self, game: Game, additional_data: dict) -> None:
|
||||
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()
|
||||
continue_on: Container[type[Exception]] = tuple()
|
||||
signals: Container[type[str]] = set()
|
||||
retry_delay: int = 3
|
||||
max_tries: int = 3
|
||||
|
||||
@@ -110,6 +111,5 @@ class Manager:
|
||||
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
|
||||
) -> None:
|
||||
"""Pass the game through the manager"""
|
||||
# TODO: connect to signals here
|
||||
self.execute_resilient_manager_logic(game, additional_data)
|
||||
callback(self)
|
||||
|
||||
@@ -16,9 +16,12 @@ class Store:
|
||||
self.games = {}
|
||||
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"""
|
||||
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(
|
||||
self, game: Game, additional_data: dict, replace=False
|
||||
@@ -50,8 +53,17 @@ class Store:
|
||||
path.unlink(missing_ok=True)
|
||||
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
|
||||
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.pipelines[game.game_id] = pipeline
|
||||
pipeline.advance()
|
||||
|
||||
Reference in New Issue
Block a user