We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Can't interact with existing winesteam games in the library
```ERROR    2021-12-29 15:17:54,343 [game._get_runner:195]:Unable to import runner winesteam for steven-universe-save-the-light
Traceback (most recent call last):
  File "/usr/lib/python3.10/site-packages/lutris/gui/lutriswindow.py", line 436, in update_revealer
    self.game_bar = GameBar(game, self.game_actions, self.application)
  File "/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py", line 60, in __init__
    self.update_view()
  File "/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py", line 76, in update_view
    self.play_button = self.get_play_button()
  File "/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py", line 216, in get_play_button
    popover = self.get_popover(self.get_game_buttons(), popover_button)
  File "/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py", line 223, in get_game_buttons
    displayed = self.game_actions.get_displayed_entries()
  File "/usr/lib/python3.10/site-packages/lutris/game_actions.py", line 106, in get_displayed_entries
    "execute-script": bool(self.game.is_installed and self.game.runner.system_config.get("manual_command")),
AttributeError: 'NoneType' object has no attribute 'system_config'```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/game_actions.py`
Content:
```
1 """Handle game specific actions"""
2 
3 # Standard Library
4 # pylint: disable=too-many-public-methods
5 import os
6 from gettext import gettext as _
7 
8 from gi.repository import Gio
9 
10 from lutris.command import MonitoredCommand
11 from lutris.game import Game
12 from lutris.gui import dialogs
13 from lutris.gui.config.add_game import AddGameDialog
14 from lutris.gui.config.edit_game import EditGameConfigDialog
15 from lutris.gui.dialogs.log import LogWindow
16 from lutris.gui.dialogs.uninstall_game import RemoveGameDialog, UninstallGameDialog
17 from lutris.gui.widgets.utils import open_uri
18 from lutris.util import xdgshortcuts
19 from lutris.util.log import logger
20 from lutris.util.system import path_exists
21 
22 
23 class GameActions:
24     """Regroup a list of callbacks for a game"""
25 
26     def __init__(self, application=None, window=None):
27         self.application = application or Gio.Application.get_default()
28         self.window = window
29         self.game_id = None
30         self._game = None
31 
32     @property
33     def game(self):
34         if not self._game:
35             self._game = self.application.get_game_by_id(self.game_id)
36             if not self._game:
37                 self._game = Game(self.game_id)
38             self._game.connect("game-error", self.window.on_game_error)
39         return self._game
40 
41     @property
42     def is_game_running(self):
43         return bool(self.application.get_game_by_id(self.game_id))
44 
45     def set_game(self, game=None, game_id=None):
46         if game:
47             self._game = game
48             self.game_id = game.id
49         else:
50             self._game = None
51             self.game_id = game_id
52 
53     def get_game_actions(self):
54         """Return a list of game actions and their callbacks"""
55         return [
56             ("play", _("Play"), self.on_game_launch),
57             ("stop", _("Stop"), self.on_game_stop),
58             ("show_logs", _("Show logs"), self.on_show_logs),
59             ("install", _("Install"), self.on_install_clicked),
60             ("add", _("Add installed game"), self.on_add_manually),
61             ("configure", _("Configure"), self.on_edit_game_configuration),
62             ("favorite", _("Add to favorites"), self.on_add_favorite_game),
63             ("deletefavorite", _("Remove from favorites"), self.on_delete_favorite_game),
64             ("execute-script", _("Execute script"), self.on_execute_script_clicked),
65             ("browse", _("Browse files"), self.on_browse_files),
66             (
67                 "desktop-shortcut",
68                 _("Create desktop shortcut"),
69                 self.on_create_desktop_shortcut,
70             ),
71             (
72                 "rm-desktop-shortcut",
73                 _("Delete desktop shortcut"),
74                 self.on_remove_desktop_shortcut,
75             ),
76             (
77                 "menu-shortcut",
78                 _("Create application menu shortcut"),
79                 self.on_create_menu_shortcut,
80             ),
81             (
82                 "rm-menu-shortcut",
83                 _("Delete application menu shortcut"),
84                 self.on_remove_menu_shortcut,
85             ),
86             ("install_more", _("Install another version"), self.on_install_clicked),
87             ("remove", _("Remove"), self.on_remove_game),
88             ("view", _("View on Lutris.net"), self.on_view_game),
89             ("hide", _("Hide game from library"), self.on_hide_game),
90             ("unhide", _("Unhide game from library"), self.on_unhide_game),
91         ]
92 
93     def get_displayed_entries(self):
94         """Return a dictionary of actions that should be shown for a game"""
95         return {
96             "add": not self.game.is_installed,
97             "install": not self.game.is_installed,
98             "play": self.game.is_installed and not self.is_game_running,
99             "stop": self.is_game_running,
100             "configure": bool(self.game.is_installed),
101             "browse": self.game.is_installed and self.game.runner_name != "browser",
102             "show_logs": self.game.is_installed,
103             "favorite": not self.game.is_favorite,
104             "deletefavorite": self.game.is_favorite,
105             "install_more": not self.game.service and self.game.is_installed,
106             "execute-script": bool(self.game.is_installed and self.game.runner.system_config.get("manual_command")),
107             "desktop-shortcut": (
108                 self.game.is_installed
109                 and not xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)
110             ),
111             "menu-shortcut": (
112                 self.game.is_installed
113                 and not xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)
114             ),
115             "rm-desktop-shortcut": bool(
116                 self.game.is_installed
117                 and xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)
118             ),
119             "rm-menu-shortcut": bool(
120                 self.game.is_installed
121                 and xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)
122             ),
123             "remove": True,
124             "view": True,
125             "hide": self.game.is_installed and not self.game.is_hidden,
126             "unhide": self.game.is_hidden,
127         }
128 
129     def on_game_launch(self, *_args):
130         """Launch a game"""
131         self.game.launch()
132 
133     def get_running_game(self):
134         ids = self.application.get_running_game_ids()
135         for game_id in ids:
136             if str(game_id) == str(self.game.id):
137                 return self.game
138         logger.warning("Game %s not in %s", self.game_id, ids)
139 
140     def on_game_stop(self, _caller):
141         """Stops the game"""
142         game = self.get_running_game()
143         if game:
144             game.force_stop()
145 
146     def on_show_logs(self, _widget):
147         """Display game log"""
148         _buffer = self.game.log_buffer
149         if not _buffer:
150             logger.info("No log for game %s", self.game)
151         return LogWindow(
152             title=_("Log for {}").format(self.game),
153             buffer=_buffer,
154             application=self.application
155         )
156 
157     def on_install_clicked(self, *_args):
158         """Install a game"""
159         # Install the currently selected game in the UI
160         if not self.game.slug:
161             raise RuntimeError("No game to install: %s" % self.game.id)
162         self.game.emit("game-install")
163 
164     def on_locate_installed_game(self, _button, game):
165         """Show the user a dialog to import an existing install to a DRM free service
166 
167         Params:
168             game (Game): Game instance without a database ID, populated with a fields the service can provides
169         """
170         AddGameDialog(self.window, game=game)
171 
172     def on_add_manually(self, _widget, *_args):
173         """Callback that presents the Add game dialog"""
174         return AddGameDialog(self.window, game=self.game, runner=self.game.runner_name)
175 
176     def on_edit_game_configuration(self, _widget):
177         """Edit game preferences"""
178         EditGameConfigDialog(self.window, self.game)
179 
180     def on_add_favorite_game(self, _widget):
181         """Add to favorite Games list"""
182         self.game.add_to_favorites()
183 
184     def on_delete_favorite_game(self, _widget):
185         """delete from favorites"""
186         self.game.remove_from_favorites()
187 
188     def on_hide_game(self, _widget):
189         """Add a game to the list of hidden games"""
190         self.game.set_hidden(True)
191 
192     def on_unhide_game(self, _widget):
193         """Removes a game from the list of hidden games"""
194         self.game.set_hidden(False)
195 
196     def on_execute_script_clicked(self, _widget):
197         """Execute the game's associated script"""
198         manual_command = self.game.runner.system_config.get("manual_command")
199         if path_exists(manual_command):
200             MonitoredCommand(
201                 [manual_command],
202                 include_processes=[os.path.basename(manual_command)],
203                 cwd=self.game.directory,
204             ).start()
205             logger.info("Running %s in the background", manual_command)
206 
207     def on_browse_files(self, _widget):
208         """Callback to open a game folder in the file browser"""
209         path = self.game.get_browse_dir()
210         if not path:
211             dialogs.NoticeDialog(_("This game has no installation directory"))
212         elif path_exists(path):
213             open_uri("file://%s" % path)
214         else:
215             dialogs.NoticeDialog(_("Can't open %s \nThe folder doesn't exist.") % path)
216 
217     def on_create_menu_shortcut(self, *_args):
218         """Add the selected game to the system's Games menu."""
219         xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, menu=True)
220 
221     def on_create_desktop_shortcut(self, *_args):
222         """Create a desktop launcher for the selected game."""
223         xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, desktop=True)
224 
225     def on_remove_menu_shortcut(self, *_args):
226         """Remove an XDG menu shortcut"""
227         xdgshortcuts.remove_launcher(self.game.slug, self.game.id, menu=True)
228 
229     def on_remove_desktop_shortcut(self, *_args):
230         """Remove a .desktop shortcut"""
231         xdgshortcuts.remove_launcher(self.game.slug, self.game.id, desktop=True)
232 
233     def on_view_game(self, _widget):
234         """Callback to open a game on lutris.net"""
235         open_uri("https://lutris.net/games/%s" % self.game.slug)
236 
237     def on_remove_game(self, *_args):
238         """Callback that present the uninstall dialog to the user"""
239         if self.game.is_installed:
240             UninstallGameDialog(game_id=self.game.id, parent=self.window)
241         else:
242             RemoveGameDialog(game_id=self.game.id, parent=self.window)
243
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/game_actions.py b/lutris/game_actions.py
--- a/lutris/game_actions.py
+++ b/lutris/game_actions.py
@@ -103,7 +103,10 @@
             "favorite": not self.game.is_favorite,
             "deletefavorite": self.game.is_favorite,
             "install_more": not self.game.service and self.game.is_installed,
-            "execute-script": bool(self.game.is_installed and self.game.runner.system_config.get("manual_command")),
+            "execute-script": bool(
+                self.game.is_installed and self.game.runner
+                and self.game.runner.system_config.get("manual_command")
+            ),
             "desktop-shortcut": (
                 self.game.is_installed
                 and not xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)

{"golden_diff": "diff --git a/lutris/game_actions.py b/lutris/game_actions.py\n--- a/lutris/game_actions.py\n+++ b/lutris/game_actions.py\n@@ -103,7 +103,10 @@\n             \"favorite\": not self.game.is_favorite,\n             \"deletefavorite\": self.game.is_favorite,\n             \"install_more\": not self.game.service and self.game.is_installed,\n-            \"execute-script\": bool(self.game.is_installed and self.game.runner.system_config.get(\"manual_command\")),\n+            \"execute-script\": bool(\n+                self.game.is_installed and self.game.runner\n+                and self.game.runner.system_config.get(\"manual_command\")\n+            ),\n             \"desktop-shortcut\": (\n                 self.game.is_installed\n                 and not xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)\n", "issue": "Can't interact with existing winesteam games in the library\n```ERROR    2021-12-29 15:17:54,343 [game._get_runner:195]:Unable to import runner winesteam for steven-universe-save-the-light\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.10/site-packages/lutris/gui/lutriswindow.py\", line 436, in update_revealer\r\n    self.game_bar = GameBar(game, self.game_actions, self.application)\r\n  File \"/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py\", line 60, in __init__\r\n    self.update_view()\r\n  File \"/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py\", line 76, in update_view\r\n    self.play_button = self.get_play_button()\r\n  File \"/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py\", line 216, in get_play_button\r\n    popover = self.get_popover(self.get_game_buttons(), popover_button)\r\n  File \"/usr/lib/python3.10/site-packages/lutris/gui/widgets/game_bar.py\", line 223, in get_game_buttons\r\n    displayed = self.game_actions.get_displayed_entries()\r\n  File \"/usr/lib/python3.10/site-packages/lutris/game_actions.py\", line 106, in get_displayed_entries\r\n    \"execute-script\": bool(self.game.is_installed and self.game.runner.system_config.get(\"manual_command\")),\r\nAttributeError: 'NoneType' object has no attribute 'system_config'```\n", "before_files": [{"content": "\"\"\"Handle game specific actions\"\"\"\n\n# Standard Library\n# pylint: disable=too-many-public-methods\nimport os\nfrom gettext import gettext as _\n\nfrom gi.repository import Gio\n\nfrom lutris.command import MonitoredCommand\nfrom lutris.game import Game\nfrom lutris.gui import dialogs\nfrom lutris.gui.config.add_game import AddGameDialog\nfrom lutris.gui.config.edit_game import EditGameConfigDialog\nfrom lutris.gui.dialogs.log import LogWindow\nfrom lutris.gui.dialogs.uninstall_game import RemoveGameDialog, UninstallGameDialog\nfrom lutris.gui.widgets.utils import open_uri\nfrom lutris.util import xdgshortcuts\nfrom lutris.util.log import logger\nfrom lutris.util.system import path_exists\n\n\nclass GameActions:\n    \"\"\"Regroup a list of callbacks for a game\"\"\"\n\n    def __init__(self, application=None, window=None):\n        self.application = application or Gio.Application.get_default()\n        self.window = window\n        self.game_id = None\n        self._game = None\n\n    @property\n    def game(self):\n        if not self._game:\n            self._game = self.application.get_game_by_id(self.game_id)\n            if not self._game:\n                self._game = Game(self.game_id)\n            self._game.connect(\"game-error\", self.window.on_game_error)\n        return self._game\n\n    @property\n    def is_game_running(self):\n        return bool(self.application.get_game_by_id(self.game_id))\n\n    def set_game(self, game=None, game_id=None):\n        if game:\n            self._game = game\n            self.game_id = game.id\n        else:\n            self._game = None\n            self.game_id = game_id\n\n    def get_game_actions(self):\n        \"\"\"Return a list of game actions and their callbacks\"\"\"\n        return [\n            (\"play\", _(\"Play\"), self.on_game_launch),\n            (\"stop\", _(\"Stop\"), self.on_game_stop),\n            (\"show_logs\", _(\"Show logs\"), self.on_show_logs),\n            (\"install\", _(\"Install\"), self.on_install_clicked),\n            (\"add\", _(\"Add installed game\"), self.on_add_manually),\n            (\"configure\", _(\"Configure\"), self.on_edit_game_configuration),\n            (\"favorite\", _(\"Add to favorites\"), self.on_add_favorite_game),\n            (\"deletefavorite\", _(\"Remove from favorites\"), self.on_delete_favorite_game),\n            (\"execute-script\", _(\"Execute script\"), self.on_execute_script_clicked),\n            (\"browse\", _(\"Browse files\"), self.on_browse_files),\n            (\n                \"desktop-shortcut\",\n                _(\"Create desktop shortcut\"),\n                self.on_create_desktop_shortcut,\n            ),\n            (\n                \"rm-desktop-shortcut\",\n                _(\"Delete desktop shortcut\"),\n                self.on_remove_desktop_shortcut,\n            ),\n            (\n                \"menu-shortcut\",\n                _(\"Create application menu shortcut\"),\n                self.on_create_menu_shortcut,\n            ),\n            (\n                \"rm-menu-shortcut\",\n                _(\"Delete application menu shortcut\"),\n                self.on_remove_menu_shortcut,\n            ),\n            (\"install_more\", _(\"Install another version\"), self.on_install_clicked),\n            (\"remove\", _(\"Remove\"), self.on_remove_game),\n            (\"view\", _(\"View on Lutris.net\"), self.on_view_game),\n            (\"hide\", _(\"Hide game from library\"), self.on_hide_game),\n            (\"unhide\", _(\"Unhide game from library\"), self.on_unhide_game),\n        ]\n\n    def get_displayed_entries(self):\n        \"\"\"Return a dictionary of actions that should be shown for a game\"\"\"\n        return {\n            \"add\": not self.game.is_installed,\n            \"install\": not self.game.is_installed,\n            \"play\": self.game.is_installed and not self.is_game_running,\n            \"stop\": self.is_game_running,\n            \"configure\": bool(self.game.is_installed),\n            \"browse\": self.game.is_installed and self.game.runner_name != \"browser\",\n            \"show_logs\": self.game.is_installed,\n            \"favorite\": not self.game.is_favorite,\n            \"deletefavorite\": self.game.is_favorite,\n            \"install_more\": not self.game.service and self.game.is_installed,\n            \"execute-script\": bool(self.game.is_installed and self.game.runner.system_config.get(\"manual_command\")),\n            \"desktop-shortcut\": (\n                self.game.is_installed\n                and not xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"menu-shortcut\": (\n                self.game.is_installed\n                and not xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"rm-desktop-shortcut\": bool(\n                self.game.is_installed\n                and xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"rm-menu-shortcut\": bool(\n                self.game.is_installed\n                and xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"remove\": True,\n            \"view\": True,\n            \"hide\": self.game.is_installed and not self.game.is_hidden,\n            \"unhide\": self.game.is_hidden,\n        }\n\n    def on_game_launch(self, *_args):\n        \"\"\"Launch a game\"\"\"\n        self.game.launch()\n\n    def get_running_game(self):\n        ids = self.application.get_running_game_ids()\n        for game_id in ids:\n            if str(game_id) == str(self.game.id):\n                return self.game\n        logger.warning(\"Game %s not in %s\", self.game_id, ids)\n\n    def on_game_stop(self, _caller):\n        \"\"\"Stops the game\"\"\"\n        game = self.get_running_game()\n        if game:\n            game.force_stop()\n\n    def on_show_logs(self, _widget):\n        \"\"\"Display game log\"\"\"\n        _buffer = self.game.log_buffer\n        if not _buffer:\n            logger.info(\"No log for game %s\", self.game)\n        return LogWindow(\n            title=_(\"Log for {}\").format(self.game),\n            buffer=_buffer,\n            application=self.application\n        )\n\n    def on_install_clicked(self, *_args):\n        \"\"\"Install a game\"\"\"\n        # Install the currently selected game in the UI\n        if not self.game.slug:\n            raise RuntimeError(\"No game to install: %s\" % self.game.id)\n        self.game.emit(\"game-install\")\n\n    def on_locate_installed_game(self, _button, game):\n        \"\"\"Show the user a dialog to import an existing install to a DRM free service\n\n        Params:\n            game (Game): Game instance without a database ID, populated with a fields the service can provides\n        \"\"\"\n        AddGameDialog(self.window, game=game)\n\n    def on_add_manually(self, _widget, *_args):\n        \"\"\"Callback that presents the Add game dialog\"\"\"\n        return AddGameDialog(self.window, game=self.game, runner=self.game.runner_name)\n\n    def on_edit_game_configuration(self, _widget):\n        \"\"\"Edit game preferences\"\"\"\n        EditGameConfigDialog(self.window, self.game)\n\n    def on_add_favorite_game(self, _widget):\n        \"\"\"Add to favorite Games list\"\"\"\n        self.game.add_to_favorites()\n\n    def on_delete_favorite_game(self, _widget):\n        \"\"\"delete from favorites\"\"\"\n        self.game.remove_from_favorites()\n\n    def on_hide_game(self, _widget):\n        \"\"\"Add a game to the list of hidden games\"\"\"\n        self.game.set_hidden(True)\n\n    def on_unhide_game(self, _widget):\n        \"\"\"Removes a game from the list of hidden games\"\"\"\n        self.game.set_hidden(False)\n\n    def on_execute_script_clicked(self, _widget):\n        \"\"\"Execute the game's associated script\"\"\"\n        manual_command = self.game.runner.system_config.get(\"manual_command\")\n        if path_exists(manual_command):\n            MonitoredCommand(\n                [manual_command],\n                include_processes=[os.path.basename(manual_command)],\n                cwd=self.game.directory,\n            ).start()\n            logger.info(\"Running %s in the background\", manual_command)\n\n    def on_browse_files(self, _widget):\n        \"\"\"Callback to open a game folder in the file browser\"\"\"\n        path = self.game.get_browse_dir()\n        if not path:\n            dialogs.NoticeDialog(_(\"This game has no installation directory\"))\n        elif path_exists(path):\n            open_uri(\"file://%s\" % path)\n        else:\n            dialogs.NoticeDialog(_(\"Can't open %s \\nThe folder doesn't exist.\") % path)\n\n    def on_create_menu_shortcut(self, *_args):\n        \"\"\"Add the selected game to the system's Games menu.\"\"\"\n        xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, menu=True)\n\n    def on_create_desktop_shortcut(self, *_args):\n        \"\"\"Create a desktop launcher for the selected game.\"\"\"\n        xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, desktop=True)\n\n    def on_remove_menu_shortcut(self, *_args):\n        \"\"\"Remove an XDG menu shortcut\"\"\"\n        xdgshortcuts.remove_launcher(self.game.slug, self.game.id, menu=True)\n\n    def on_remove_desktop_shortcut(self, *_args):\n        \"\"\"Remove a .desktop shortcut\"\"\"\n        xdgshortcuts.remove_launcher(self.game.slug, self.game.id, desktop=True)\n\n    def on_view_game(self, _widget):\n        \"\"\"Callback to open a game on lutris.net\"\"\"\n        open_uri(\"https://lutris.net/games/%s\" % self.game.slug)\n\n    def on_remove_game(self, *_args):\n        \"\"\"Callback that present the uninstall dialog to the user\"\"\"\n        if self.game.is_installed:\n            UninstallGameDialog(game_id=self.game.id, parent=self.window)\n        else:\n            RemoveGameDialog(game_id=self.game.id, parent=self.window)\n", "path": "lutris/game_actions.py"}], "after_files": [{"content": "\"\"\"Handle game specific actions\"\"\"\n\n# Standard Library\n# pylint: disable=too-many-public-methods\nimport os\nfrom gettext import gettext as _\n\nfrom gi.repository import Gio\n\nfrom lutris.command import MonitoredCommand\nfrom lutris.game import Game\nfrom lutris.gui import dialogs\nfrom lutris.gui.config.add_game import AddGameDialog\nfrom lutris.gui.config.edit_game import EditGameConfigDialog\nfrom lutris.gui.dialogs.log import LogWindow\nfrom lutris.gui.dialogs.uninstall_game import RemoveGameDialog, UninstallGameDialog\nfrom lutris.gui.widgets.utils import open_uri\nfrom lutris.util import xdgshortcuts\nfrom lutris.util.log import logger\nfrom lutris.util.system import path_exists\n\n\nclass GameActions:\n    \"\"\"Regroup a list of callbacks for a game\"\"\"\n\n    def __init__(self, application=None, window=None):\n        self.application = application or Gio.Application.get_default()\n        self.window = window\n        self.game_id = None\n        self._game = None\n\n    @property\n    def game(self):\n        if not self._game:\n            self._game = self.application.get_game_by_id(self.game_id)\n            if not self._game:\n                self._game = Game(self.game_id)\n            self._game.connect(\"game-error\", self.window.on_game_error)\n        return self._game\n\n    @property\n    def is_game_running(self):\n        return bool(self.application.get_game_by_id(self.game_id))\n\n    def set_game(self, game=None, game_id=None):\n        if game:\n            self._game = game\n            self.game_id = game.id\n        else:\n            self._game = None\n            self.game_id = game_id\n\n    def get_game_actions(self):\n        \"\"\"Return a list of game actions and their callbacks\"\"\"\n        return [\n            (\"play\", _(\"Play\"), self.on_game_launch),\n            (\"stop\", _(\"Stop\"), self.on_game_stop),\n            (\"show_logs\", _(\"Show logs\"), self.on_show_logs),\n            (\"install\", _(\"Install\"), self.on_install_clicked),\n            (\"add\", _(\"Add installed game\"), self.on_add_manually),\n            (\"configure\", _(\"Configure\"), self.on_edit_game_configuration),\n            (\"favorite\", _(\"Add to favorites\"), self.on_add_favorite_game),\n            (\"deletefavorite\", _(\"Remove from favorites\"), self.on_delete_favorite_game),\n            (\"execute-script\", _(\"Execute script\"), self.on_execute_script_clicked),\n            (\"browse\", _(\"Browse files\"), self.on_browse_files),\n            (\n                \"desktop-shortcut\",\n                _(\"Create desktop shortcut\"),\n                self.on_create_desktop_shortcut,\n            ),\n            (\n                \"rm-desktop-shortcut\",\n                _(\"Delete desktop shortcut\"),\n                self.on_remove_desktop_shortcut,\n            ),\n            (\n                \"menu-shortcut\",\n                _(\"Create application menu shortcut\"),\n                self.on_create_menu_shortcut,\n            ),\n            (\n                \"rm-menu-shortcut\",\n                _(\"Delete application menu shortcut\"),\n                self.on_remove_menu_shortcut,\n            ),\n            (\"install_more\", _(\"Install another version\"), self.on_install_clicked),\n            (\"remove\", _(\"Remove\"), self.on_remove_game),\n            (\"view\", _(\"View on Lutris.net\"), self.on_view_game),\n            (\"hide\", _(\"Hide game from library\"), self.on_hide_game),\n            (\"unhide\", _(\"Unhide game from library\"), self.on_unhide_game),\n        ]\n\n    def get_displayed_entries(self):\n        \"\"\"Return a dictionary of actions that should be shown for a game\"\"\"\n        return {\n            \"add\": not self.game.is_installed,\n            \"install\": not self.game.is_installed,\n            \"play\": self.game.is_installed and not self.is_game_running,\n            \"stop\": self.is_game_running,\n            \"configure\": bool(self.game.is_installed),\n            \"browse\": self.game.is_installed and self.game.runner_name != \"browser\",\n            \"show_logs\": self.game.is_installed,\n            \"favorite\": not self.game.is_favorite,\n            \"deletefavorite\": self.game.is_favorite,\n            \"install_more\": not self.game.service and self.game.is_installed,\n            \"execute-script\": bool(\n                self.game.is_installed and self.game.runner\n                and self.game.runner.system_config.get(\"manual_command\")\n            ),\n            \"desktop-shortcut\": (\n                self.game.is_installed\n                and not xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"menu-shortcut\": (\n                self.game.is_installed\n                and not xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"rm-desktop-shortcut\": bool(\n                self.game.is_installed\n                and xdgshortcuts.desktop_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"rm-menu-shortcut\": bool(\n                self.game.is_installed\n                and xdgshortcuts.menu_launcher_exists(self.game.slug, self.game.id)\n            ),\n            \"remove\": True,\n            \"view\": True,\n            \"hide\": self.game.is_installed and not self.game.is_hidden,\n            \"unhide\": self.game.is_hidden,\n        }\n\n    def on_game_launch(self, *_args):\n        \"\"\"Launch a game\"\"\"\n        self.game.launch()\n\n    def get_running_game(self):\n        ids = self.application.get_running_game_ids()\n        for game_id in ids:\n            if str(game_id) == str(self.game.id):\n                return self.game\n        logger.warning(\"Game %s not in %s\", self.game_id, ids)\n\n    def on_game_stop(self, _caller):\n        \"\"\"Stops the game\"\"\"\n        game = self.get_running_game()\n        if game:\n            game.force_stop()\n\n    def on_show_logs(self, _widget):\n        \"\"\"Display game log\"\"\"\n        _buffer = self.game.log_buffer\n        if not _buffer:\n            logger.info(\"No log for game %s\", self.game)\n        return LogWindow(\n            title=_(\"Log for {}\").format(self.game),\n            buffer=_buffer,\n            application=self.application\n        )\n\n    def on_install_clicked(self, *_args):\n        \"\"\"Install a game\"\"\"\n        # Install the currently selected game in the UI\n        if not self.game.slug:\n            raise RuntimeError(\"No game to install: %s\" % self.game.id)\n        self.game.emit(\"game-install\")\n\n    def on_locate_installed_game(self, _button, game):\n        \"\"\"Show the user a dialog to import an existing install to a DRM free service\n\n        Params:\n            game (Game): Game instance without a database ID, populated with a fields the service can provides\n        \"\"\"\n        AddGameDialog(self.window, game=game)\n\n    def on_add_manually(self, _widget, *_args):\n        \"\"\"Callback that presents the Add game dialog\"\"\"\n        return AddGameDialog(self.window, game=self.game, runner=self.game.runner_name)\n\n    def on_edit_game_configuration(self, _widget):\n        \"\"\"Edit game preferences\"\"\"\n        EditGameConfigDialog(self.window, self.game)\n\n    def on_add_favorite_game(self, _widget):\n        \"\"\"Add to favorite Games list\"\"\"\n        self.game.add_to_favorites()\n\n    def on_delete_favorite_game(self, _widget):\n        \"\"\"delete from favorites\"\"\"\n        self.game.remove_from_favorites()\n\n    def on_hide_game(self, _widget):\n        \"\"\"Add a game to the list of hidden games\"\"\"\n        self.game.set_hidden(True)\n\n    def on_unhide_game(self, _widget):\n        \"\"\"Removes a game from the list of hidden games\"\"\"\n        self.game.set_hidden(False)\n\n    def on_execute_script_clicked(self, _widget):\n        \"\"\"Execute the game's associated script\"\"\"\n        manual_command = self.game.runner.system_config.get(\"manual_command\")\n        if path_exists(manual_command):\n            MonitoredCommand(\n                [manual_command],\n                include_processes=[os.path.basename(manual_command)],\n                cwd=self.game.directory,\n            ).start()\n            logger.info(\"Running %s in the background\", manual_command)\n\n    def on_browse_files(self, _widget):\n        \"\"\"Callback to open a game folder in the file browser\"\"\"\n        path = self.game.get_browse_dir()\n        if not path:\n            dialogs.NoticeDialog(_(\"This game has no installation directory\"))\n        elif path_exists(path):\n            open_uri(\"file://%s\" % path)\n        else:\n            dialogs.NoticeDialog(_(\"Can't open %s \\nThe folder doesn't exist.\") % path)\n\n    def on_create_menu_shortcut(self, *_args):\n        \"\"\"Add the selected game to the system's Games menu.\"\"\"\n        xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, menu=True)\n\n    def on_create_desktop_shortcut(self, *_args):\n        \"\"\"Create a desktop launcher for the selected game.\"\"\"\n        xdgshortcuts.create_launcher(self.game.slug, self.game.id, self.game.name, desktop=True)\n\n    def on_remove_menu_shortcut(self, *_args):\n        \"\"\"Remove an XDG menu shortcut\"\"\"\n        xdgshortcuts.remove_launcher(self.game.slug, self.game.id, menu=True)\n\n    def on_remove_desktop_shortcut(self, *_args):\n        \"\"\"Remove a .desktop shortcut\"\"\"\n        xdgshortcuts.remove_launcher(self.game.slug, self.game.id, desktop=True)\n\n    def on_view_game(self, _widget):\n        \"\"\"Callback to open a game on lutris.net\"\"\"\n        open_uri(\"https://lutris.net/games/%s\" % self.game.slug)\n\n    def on_remove_game(self, *_args):\n        \"\"\"Callback that present the uninstall dialog to the user\"\"\"\n        if self.game.is_installed:\n            UninstallGameDialog(game_id=self.game.id, parent=self.window)\n        else:\n            RemoveGameDialog(game_id=self.game.id, parent=self.window)\n", "path": "lutris/game_actions.py"}]}