[game-data-packager] 04/05: Add support for expansions that are only conditionally downloaded

Simon McVittie smcv at debian.org
Mon Dec 4 10:02:19 UTC 2017


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to branch master
in repository game-data-packager.

commit bbc1f7eabd902d0c22e0e413b7bf44031609450f
Author: Simon McVittie <smcv at debian.org>
Date:   Sun Dec 3 18:07:17 2017 +0000

    Add support for expansions that are only conditionally downloaded
    
    If an addon is freely downloadable, but not part of the official game,
    then we might not always want to download it. Only download these
    addons if they are "activated" by requesting the package specifically,
    or by --everything, or by finding a key file from that addon.
    
    Closes: #775080
    Signed-off-by: Simon McVittie <smcv at debian.org>
---
 data/quake.yaml                    |  3 ++
 debian/changelog                   |  3 ++
 doc/adding_a_game.mdwn             |  4 ++
 doc/tags.txt                       |  1 +
 game_data_packager/__init__.py     |  1 +
 game_data_packager/build.py        | 76 ++++++++++++++++++++++++++++++++++----
 game_data_packager/command_line.py |  3 ++
 game_data_packager/data.py         | 15 ++++++++
 8 files changed, 98 insertions(+), 8 deletions(-)

diff --git a/data/quake.yaml b/data/quake.yaml
index 8dbdf5e..d5c54ca 100644
--- a/data/quake.yaml
+++ b/data/quake.yaml
@@ -128,6 +128,9 @@ packages:
     install_to: $assets/quake/aopfm_v2
     install:
       - AoPFM content
+    activated_by:
+      - AoPFM content
+      - aopfm_v2.zip
     doc:
       - AoPFM docs
     symlinks:
diff --git a/debian/changelog b/debian/changelog
index c5b566d..ca76dc2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,9 @@ game-data-packager (55) UNRELEASED; urgency=medium
     - quake: add Abyss of Pandemonium, a formerly-commercial third party
       expansion (Closes: #800505) [smcv]
   * Enhancements and bug fixes:
+    - Add support for expansions that are only downloaded if specifically
+      requested, or if an important file from that expansion is found
+      (Closes: #775080) [smcv]
     - Review of the French manpage [nyav]
     - unreal, ut99: adjust to web.archive.org behaviour changes
       (Closes: #882712) [smcv]
diff --git a/doc/adding_a_game.mdwn b/doc/adding_a_game.mdwn
index bf705a0..d99ced4 100644
--- a/doc/adding_a_game.mdwn
+++ b/doc/adding_a_game.mdwn
@@ -117,6 +117,10 @@ to mapping:
 * `install`: list of strings: unique names of known files or alternative
   sets to install
 * `engine`: let you overide the engine in special cases
+* `activated_by`: Some expansions are free-to-download, but not all users
+  will want them. List their most important/distinctive/large files here,
+  and the expansion will only be downloaded if game-data-packager finds
+  those files or if the user specifically asks for it.
 
 #### Steam games
 
diff --git a/doc/tags.txt b/doc/tags.txt
index 1989e05..86fe3e4 100644
--- a/doc/tags.txt
+++ b/doc/tags.txt
@@ -71,6 +71,7 @@ packages:
     doc: list
     license: list
     lintian_overrides: list of string
+    activated_by: list of string
 
 files:
   <file>:
diff --git a/game_data_packager/__init__.py b/game_data_packager/__init__.py
index f802586..b3123f8 100644
--- a/game_data_packager/__init__.py
+++ b/game_data_packager/__init__.py
@@ -750,6 +750,7 @@ class GameData(object):
 
             package.install_files = set(self._iter_expand_groups(package.install))
             package.optional_files = set(self._iter_expand_groups(package.optional))
+            package.activated_by_files = set(self._iter_expand_groups(package.activated_by))
 
         # _iter_expand_groups could change the contents of self.files
         for filename, f in list(self.files.items()):
diff --git a/game_data_packager/build.py b/game_data_packager/build.py
index 881eaf7..a18b259 100644
--- a/game_data_packager/build.py
+++ b/game_data_packager/build.py
@@ -60,6 +60,7 @@ class FillResult(Enum):
     DOWNLOAD_NEEDED = 2
     COMPLETE = 3
     UPGRADE_NEEDED = 4
+    DEACTIVATED = 5
 
     def __and__(self, other):
         if other is FillResult.UNDETERMINED:
@@ -71,6 +72,9 @@ class FillResult(Enum):
         if other is FillResult.IMPOSSIBLE or self is FillResult.IMPOSSIBLE:
             return FillResult.IMPOSSIBLE
 
+        if other is FillResult.DEACTIVATED or self is FillResult.DEACTIVATED:
+            return FillResult.DEACTIVATED
+
         if other is FillResult.UPGRADE_NEEDED or self is FillResult.UPGRADE_NEEDED:
             return FillResult.UPGRADE_NEEDED
 
@@ -95,6 +99,9 @@ class FillResult(Enum):
         if other is FillResult.UPGRADE_NEEDED or self is FillResult.UPGRADE_NEEDED:
             return FillResult.UPGRADE_NEEDED
 
+        if other is FillResult.DEACTIVATED or self is FillResult.DEACTIVATED:
+            return FillResult.DEACTIVATED
+
         return FillResult.IMPOSSIBLE
 
 class BinaryExecutablesNotAllowed(Exception):
@@ -467,11 +474,46 @@ class PackagingTask(object):
                 logger.error('%s should have provided %s but did not',
                         self.found[provider.name], missing)
 
-    def fill_gaps(self, package, download=False, log=True, recheck=False):
+    def fill_gaps(self, package, download=False, log=True, recheck=False,
+            requested=False):
         """Return a FillResult.
         """
         assert package is not None
 
+        extraneous = False
+
+        if requested:
+            logger.debug('Package %s was specifically requested', package.name)
+        elif package.activated_by_files:
+            logger.debug(
+                'Checking whether we have any interest in %s', package.name)
+            # If the package has activated_by, we don't build it unless
+            # either: we found one of the distinctive files by which it
+            # is activated, or the user specifically asked for it.
+            extraneous = True
+
+            for wanted in package.activated_by_files:
+                logger.debug('Checking for %s', wanted.name)
+
+                if wanted.name in self.found:
+                    logger.debug('... yes')
+                    extraneous = False
+                    break
+                else:
+                    for alt in wanted.alternatives:
+                        if alt in self.found:
+                            logger.debug('... yes (%s)', alt.name)
+                            extraneous = False
+                            break
+
+                    if not extraneous:
+                        break
+
+            if extraneous:
+                logger.debug(
+                    'No reason found to be interested in %s', package.name)
+                return FillResult.DEACTIVATED
+
         logger.debug('trying to fill any gaps for %s', package.name)
 
         # this is redundant, it's only done to get the debug messages first
@@ -1508,6 +1550,7 @@ class PackagingTask(object):
             args.demo = True
             args.packages = [args.shortname]
             packages = set([self.game.packages[args.shortname]])
+            requested_packages = packages
         elif args.packages:
             args.demo = True
             packages = set()
@@ -1517,10 +1560,12 @@ class PackagingTask(object):
                             '"%s"', p, args.shortname)
                     raise SystemExit(1)
                 packages.add(self.game.packages[p])
+            requested_packages = packages
         else:
             # if no packages were specified, we require --demo to build
             # a demo if we have its corresponding full game
             packages = set(self.game.packages.values())
+            requested_packages = set()
 
         self.look_for_engines(packages, force=not args.install)
 
@@ -1536,7 +1581,9 @@ class PackagingTask(object):
         try:
             ready = self.prepare_packages(packages,
                     build_demos=args.demo, download=args.download,
-                    search=args.search, log_immediately=bool(args.packages))
+                    search=args.search, log_immediately=bool(args.packages),
+                    everything=args.everything,
+                    requested_packages=requested_packages)
         except NoPackagesPossible:
             logger.error('Unable to complete any packages.')
             if self.missing_tools:
@@ -1645,7 +1692,8 @@ class PackagingTask(object):
             raise CDRipFailed()
 
     def prepare_packages(self, packages=None, build_demos=False, download=True,
-            search=True, log_immediately=True):
+            search=True, log_immediately=True, everything=False,
+            requested_packages=()):
         if packages is None:
             packages = self.game.packages.values()
 
@@ -1671,7 +1719,9 @@ class PackagingTask(object):
             if package.rip_cd and not self.cd_tracks.get(package.name):
                 logger.debug('no CD tracks found for %s', package.name)
             elif self.fill_gaps(package,
-                    log=log_immediately) is not FillResult.IMPOSSIBLE:
+                    requested=(everything or package in requested_packages),
+                    log=log_immediately) not in (FillResult.IMPOSSIBLE,
+                        FillResult.DEACTIVATED):
                 logger.debug('%s is possible', package.name)
                 possible.add(package)
             # download game if it is already owned by user's GOG.com account
@@ -1712,7 +1762,8 @@ class PackagingTask(object):
             for package in self.game.packages.values():
                 if package.demo_for:
                     if self.fill_gaps(package=package,
-                            log=True) is not FillResult.IMPOSSIBLE:
+                            log=True) not in (FillResult.IMPOSSIBLE,
+                                FillResult.DEACTIVATED):
                         logger.error('%s unexpectedly succeeded on second ' +
                                 'attempt. Please report this as a bug',
                                 package.name)
@@ -1725,7 +1776,11 @@ class PackagingTask(object):
                 for package in self.game.packages.values():
                     if package.type == 'full':
                         if self.fill_gaps(package=package,
-                                log=True) is not FillResult.IMPOSSIBLE:
+                                requested=(
+                                    everything or
+                                    package in requested_packages),
+                                log=True) not in (FillResult.IMPOSSIBLE,
+                                    FillResult.DEACTIVATED):
                             logger.error('%s unexpectedly succeeded on ' +
                                     'second attempt. Please report this as '
                                     'a bug', package.name)
@@ -1845,6 +1900,7 @@ class PackagingTask(object):
         for package in possible:
             logger.debug('will produce %s', package.name)
             result = self.fill_gaps(package=package, download=download,
+              requested=(everything or package in requested_packages),
               log=package.name not in external_download,
               recheck=package.name in external_download)
             if result is FillResult.COMPLETE:
@@ -1882,7 +1938,9 @@ class PackagingTask(object):
 
                     # recheck file status
                     if self.fill_gaps(package, log=True, download=True,
-                       recheck=True) is not FillResult.IMPOSSIBLE:
+                       requested=(everything or package in requested_packages),
+                       recheck=True) not in (FillResult.IMPOSSIBLE,
+                           FillResult.DEACTIVATED):
                        ready.add(package)
                     if self.save_downloads:
                         for archive in archives:
@@ -1918,7 +1976,9 @@ class PackagingTask(object):
                 for path in set(self.iter_steam_paths(packages=(package,))):
                     self.consider_file_or_dir(path)
                 if self.fill_gaps(package, log=True, download=True,
-                    recheck=True) is not FillResult.IMPOSSIBLE:
+                    requested=(everything or package in requested_packages),
+                    recheck=True) not in (FillResult.IMPOSSIBLE,
+                        FillResult.DEACTIVATED):
                     ready.add(package)
             elif (result is FillResult.DOWNLOAD_NEEDED or
                   package.name in possible_with_lgogdownloader) and not download:
diff --git a/game_data_packager/command_line.py b/game_data_packager/command_line.py
index 3c7edbf..fe29fe2 100644
--- a/game_data_packager/command_line.py
+++ b/game_data_packager/command_line.py
@@ -88,6 +88,8 @@ def run_command_line():
             add_help=False,
             argument_default=argparse.SUPPRESS)
 
+    base_parser.add_argument('--everything', action='store_true',
+            help='Download all possible expansions')
     base_parser.add_argument('--package', '-p', action='append',
             dest='packages', metavar='PACKAGE',
             help='Produce this data package (may be repeated)')
@@ -233,6 +235,7 @@ def run_command_line():
             compress=None,
             destination=None,
             download=True,
+            everything=False,
             verbose=False,
             install=False,
             install_method='',
diff --git a/game_data_packager/data.py b/game_data_packager/data.py
index 42bab22..5a733d5 100644
--- a/game_data_packager/data.py
+++ b/game_data_packager/data.py
@@ -513,11 +513,16 @@ class Package(object):
         # set of names of WantedFile instances to be optionally installed
         self._optional = set()
 
+        # set of names of WantedFile instances that indicate that we want this
+        self.activated_by = set()
+
         # set of WantedFile instances for install, with groups expanded
         # only available after load_file_data()
         self.install_files = None
         # set of WantedFile instances for optional, with groups expanded
         self.optional_files = None
+        # set of names of WantedFile instances that indicate that we want this
+        self.activated_by_files = None
 
         self.version = GAME_PACKAGE_VERSION
 
@@ -673,6 +678,11 @@ class Package(object):
             for filename in data['optional']:
                 self.optional.add(filename)
 
+        if 'activated_by' in data:
+            assert isinstance(data['activated_by'], list), self.name
+            for filename in data['activated_by']:
+                self.activated_by.add(filename)
+
         if 'doc' in data:
             assert isinstance(data['doc'], list), self.name
             for filename in data['doc']:
@@ -799,11 +809,16 @@ class Package(object):
                 ret['install'] = sorted(f.name for f in self.install_files)
             if self.optional_files:
                 ret['optional'] = sorted(f.name for f in self.optional_files)
+            if self.activated_by_files:
+                ret['activated_by'] = sorted(
+                    f.name for f in self.activated_by_files)
         else:
             if self.install:
                 ret['install'] = sorted(self.install)
             if self.optional:
                 ret['optional'] = sorted(self.optional)
+            if self.activated_by:
+                ret['activated_by'] = sorted(self.activated_by)
 
         for k in (
                 'copyright',

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/game-data-packager.git



More information about the Pkg-games-commits mailing list