1
0
mirror of https://github.com/lutris/lutris synced 2024-07-01 06:34:26 +00:00

Launch games from local services directly

This commit is contained in:
Mathieu Comandon 2021-03-16 00:41:07 -07:00
parent 6f89bbe0fd
commit b4234878f0
10 changed files with 93 additions and 52 deletions

View File

@ -1,10 +1,10 @@
"""Handle the game, runner and global system configurations."""
# Standard Library
import os
import time
# Lutris Modules
import yaml
from lutris import settings, sysoptions
from lutris.runners import InvalidRunner, import_runner
from lutris.util.log import logger
@ -17,6 +17,17 @@ def make_game_config_id(game_slug):
return "{}-{}".format(game_slug, int(time.time()))
def write_game_config(game_slug, config):
"""Writes a game config to disk"""
configpath = make_game_config_id(game_slug)
config_filename = os.path.join(settings.CONFIG_DIR, "games/%s.yml" % configpath)
yaml_config = yaml.safe_dump(config, default_flow_style=False)
with open(config_filename, "w") as config_file:
logger.debug("Writing game config to %s", config_filename)
config_file.write(yaml_config)
return configpath
class LutrisConfig:
"""Class where all the configuration handling happens.

View File

@ -130,12 +130,11 @@ def get_games_by_slug(slug):
return sql.db_select(settings.PGA_DB, "games", condition=("slug", slug))
def add_game(name, **game_data):
def add_game(**game_data):
"""Add a game to the PGA database."""
game_data["name"] = name
game_data["installed_at"] = int(time.time())
if "slug" not in game_data:
game_data["slug"] = slugify(name)
game_data["slug"] = slugify(game_data["name"])
return sql.db_insert(settings.PGA_DB, "games", game_data)

View File

@ -477,7 +477,10 @@ class Application(Gtk.Application):
if game.service:
service = get_services()[game.service]()
db_game = ServiceGameCollection.get_game(service.id, game.appid)
service.install(db_game)
game_id = service.install(db_game)
if game_id:
game = Game(game_id)
game.launch()
return True
installers = get_installers(game_slug=game.slug)

View File

@ -792,26 +792,26 @@ class LutrisWindow(Gtk.ApplicationWindow): # pylint: disable=too-many-public-me
def on_game_activated(self, view, game_id):
"""Handles view activations (double click, enter press)"""
if self.service and self.service.id != "lutris":
db_game = games_db.get_game_for_service(self.service.id, game_id)
if db_game:
game_id = db_game["id"]
else:
db_game = ServiceGameCollection.get_game(self.service.id, game_id)
if self.service:
if self.service.id != "lutris":
db_game = games_db.get_game_for_service(self.service.id, game_id)
if db_game:
self.service.install(db_game)
game_id = db_game["id"]
else:
db_game = ServiceGameCollection.get_game(self.service.id, game_id)
if db_game:
game_id = self.service.install(db_game)
else:
game_id = self.service.install(game_id)
else:
db_game = games_db.get_game_by_field(game_id)
if not db_game:
self.service.install(game_id)
return
if self.service and self.service.id == "lutris":
db_game = games_db.get_game_by_field(game_id)
if not db_game:
self.service.install(game_id)
return
if db_game["installed"] != 1:
self.service.install(game_id)
return
game_id = db_game["id"]
game = Game(game_id)
game.emit("game-launch")
return
if db_game["installed"] != 1:
self.service.install(game_id)
return
game_id = db_game["id"]
if game_id:
game = Game(game_id)
game.emit("game-launch")

View File

@ -182,6 +182,9 @@ class GameBar(Gtk.Fixed):
button.set_label(_("Install"))
button.connect("clicked", self.game_actions.on_install_clicked)
if self.service:
if self.service.local:
# Local services don't show an install dialog, they can be launched directly
button.set_label(_("Play"))
button.set_size_request(120, 32)
return button
button.set_size_request(84, 32)

View File

@ -2,10 +2,7 @@
import json
import os
import yaml
from lutris import settings
from lutris.config import LutrisConfig, make_game_config_id
from lutris.config import LutrisConfig, write_game_config
from lutris.database.games import add_or_update, get_game_by_field
from lutris.game import Game
from lutris.installer import AUTO_ELF_EXE, AUTO_WIN32_EXE
@ -215,16 +212,6 @@ class LutrisInstaller: # pylint: disable=too-many-instance-attributes
return config
def write_game_config(self):
configpath = make_game_config_id(self.slug)
config_filename = os.path.join(settings.CONFIG_DIR, "games/%s.yml" % configpath)
config = self.get_game_config()
yaml_config = yaml.safe_dump(config, default_flow_style=False)
with open(config_filename, "w") as config_file:
logger.debug("Writing game config to %s", config_filename)
config_file.write(yaml_config)
return configpath
def save(self):
"""Write the game configuration in the DB and config file"""
if self.extends:
@ -233,7 +220,7 @@ class LutrisInstaller: # pylint: disable=too-many-instance-attributes
self.extends,
)
return
configpath = self.write_game_config()
configpath = write_game_config(self.slug, self.get_game_config())
runner_inst = import_runner(self.runner)()
if self.service:
service_id = self.service.id

View File

@ -5,8 +5,9 @@ import shutil
from gi.repository import Gio, GObject
from lutris import api, settings
from lutris.config import write_game_config
from lutris.database import sql
from lutris.database.games import get_games
from lutris.database.games import add_game, get_games
from lutris.database.services import ServiceGameCollection
from lutris.game import Game
from lutris.installer import fetch_script
@ -117,6 +118,8 @@ class BaseService(GObject.Object):
"""Install a service game"""
appid = db_game["appid"]
logger.debug("Installing %s from service %s", appid, self.id)
if self.local:
return self.simple_install(db_game)
service_installers = self.get_installers_from_api(appid)
# Check if the game is not already installed
for service_installer in service_installers:
@ -137,6 +140,27 @@ class BaseService(GObject.Object):
application = Gio.Application.get_default()
application.show_installer_window(service_installers, service=self, appid=appid)
def simple_install(self, db_game):
"""A simplified version of the install method for local services"""
installer = self.generate_installer(db_game)
configpath = write_game_config(db_game["slug"], installer["script"])
game_id = add_game(
name=installer["name"],
runner=installer["runner"],
slug=installer["game_slug"],
directory=self.get_game_directory(installer),
installed=1,
installer_slug=installer["slug"],
configpath=configpath,
service=self.id,
service_id=db_game["appid"],
)
return game_id
def get_game_directory(self, _installer):
"""Specific services should implement this"""
return ""
class OnlineService(BaseService):
"""Base class for online gaming services"""

View File

@ -1,5 +1,5 @@
import os
import json
import os
from gettext import gettext as _
from lutris import settings
@ -50,10 +50,15 @@ class DolphinService(BaseService):
"script": {
"game": {
"main_file": details["path"],
"platform": details["platform"]
},
}
}
def get_game_directory(self, installer):
"""Pull install location from installer"""
return os.path.dirname(installer["script"]["game"]["main_file"])
class DolphinGame(ServiceGame):
"""Game for the Dolphin emulator"""
@ -66,11 +71,14 @@ class DolphinGame(ServiceGame):
def new_from_cache(cls, cache_entry):
"""Create a service game from an entry from the Dolphin cache"""
service_game = cls()
service_game.name = cls.get_game_name(cache_entry)
service_game.appid = str(cache_entry["code_1"])
service_game.slug = slugify(cls.get_game_name(cache_entry))
service_game.name = cache_entry["real_name"]
service_game.appid = str(cache_entry["game_id"])
service_game.slug = slugify(cache_entry["real_name"])
service_game.icon = ""
service_game.details = json.dumps({"path": cache_entry["path"]})
service_game.details = json.dumps({
"path": cache_entry["path"],
"platform": cache_entry["platform"][:-1]
})
return service_game
@staticmethod

View File

@ -118,6 +118,10 @@ class XDGService(BaseService):
}
}
def get_game_directory(self, installer):
"""Pull install location from installer"""
return os.path.dirname(installer["script"]["game"]["exe"])
class XDGGame(ServiceGame):
"""XDG game (Linux game with a desktop launcher)"""

View File

@ -27,11 +27,12 @@ class DolphinCacheReader:
'maker_short': 'a',
'maker_long': 'a',
'description': 'a',
'some_other_name': 's',
'code_1': 's',
'code_2': 's',
'field_c': 32,
'field_d': 1,
'real_name': 's',
'game_id': 's',
'game_tdbid': 's',
'field_c': 6,
'platform': 1,
'field_d': 26,
'rel_date': 's',
'field_e': 8,
'banner': 'i',
@ -83,6 +84,7 @@ class DolphinCacheReader:
if has_image:
image = self.cache_content[self.offset:self.offset + 12288]
self.offset += 12288
image = ''
return image
def get_raw(self, word_len):