From eb029a9689c7c915589881b2a7acacaff7c5717d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Tue, 5 Jul 2016 14:34:34 +0300 Subject: [PATCH] Refactor|FS|Resources: PackageLoader handles optional package contents Rather than doing it in libdoomsday, optional package contents (recommends, extras) are now handled at the lowest level in PackageLoader. This allows using the same mechanism for Doomsday 2 packages. --- .../modules/appconfig.de | 3 +- .../widgets/packagecontentoptionswidget.cpp | 4 +- .../apps/libdoomsday/src/resource/bundles.cpp | 48 +------------- doomsday/sdk/libcore/include/de/core/app.h | 2 + .../sdk/libcore/include/de/filesys/package.h | 2 + .../net.dengine.stdlib.pack/modules/Config.de | 6 +- doomsday/sdk/libcore/src/core/app.cpp | 5 ++ doomsday/sdk/libcore/src/filesys/package.cpp | 5 ++ .../sdk/libcore/src/filesys/packageloader.cpp | 62 +++++++++++++++++-- 9 files changed, 82 insertions(+), 55 deletions(-) diff --git a/doomsday/apps/client/net.dengine.client.pack/modules/appconfig.de b/doomsday/apps/client/net.dengine.client.pack/modules/appconfig.de index 6e5d1a7164..40262b5d09 100644 --- a/doomsday/apps/client/net.dengine.client.pack/modules/appconfig.de +++ b/doomsday/apps/client/net.dengine.client.pack/modules/appconfig.de @@ -1,6 +1,6 @@ # The Doomsday Engine Project -- Doomsday Client # -# Copyright (c) 2013 Jaakko Keränen +# Copyright (c) 2013-2016 Jaakko Keränen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -97,7 +97,6 @@ def setDefaults(d) record d.resource d.resource.iwadFolder = '' d.resource.packageFolder = '' - d.resource.selectedPackages = {} # Renderer settings. record d.render diff --git a/doomsday/apps/client/src/ui/widgets/packagecontentoptionswidget.cpp b/doomsday/apps/client/src/ui/widgets/packagecontentoptionswidget.cpp index bf3c27a320..ee60a0ea45 100644 --- a/doomsday/apps/client/src/ui/widgets/packagecontentoptionswidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/packagecontentoptionswidget.cpp @@ -71,12 +71,12 @@ DENG_GUI_PIMPL(PackageContentOptionsWidget) DictionaryValue &conf() { - return Config::get()["resource.selectedPackages"].value(); + return Config::get()["fs.selectedPackages"].value(); } DictionaryValue const &conf() const { - return Config::get()["resource.selectedPackages"].value(); + return Config::get()["fs.selectedPackages"].value(); } bool isSelected() const diff --git a/doomsday/apps/libdoomsday/src/resource/bundles.cpp b/doomsday/apps/libdoomsday/src/resource/bundles.cpp index 48a030d61e..c50d60bfd9 100644 --- a/doomsday/apps/libdoomsday/src/resource/bundles.cpp +++ b/doomsday/apps/libdoomsday/src/resource/bundles.cpp @@ -355,57 +355,13 @@ QList Bundles::loaded() const auto &loader = PackageLoader::get(); QList loadedBundles; - // Collection contents enabled/disabled for use. - DictionaryValue const &selPkgs = Config::get()["resource.selectedPackages"].value(); - // Check all the loaded packages to see which ones are data bundles. for (auto *f : loader.loadedPackagesAsFilesInPackageOrder()) { if (DataBundle const *bundle = f->maybeAs()) { - if (bundle->format() == DataBundle::Collection) - { - // Instead of adding the collection, check which contained packages are - // selected for loading. - - auto isSelected = [bundle, &selPkgs] (TextValue const &id, bool byDefault) -> bool - { - TextValue const key(bundle->packageId()); - if (selPkgs.contains(key)) { - auto const &sels = selPkgs.element(key).as(); - if (sels.contains(id)) { - return sels.element(id).isTrue(); - } - } - return byDefault; - }; - - auto addToLoaded = [&loadedBundles] (String const &id) - { - if (DataBundle const *b = DataBundle::bundleForPackage(id)) { - loadedBundles << b; - } - }; - - Record const &meta = bundle->packageMetadata(); - for (auto const *id : meta.geta("requires").elements()) - { - addToLoaded(id->asText()); - } - for (auto const *id : meta.geta("recommends").elements()) - { - if (isSelected(id->asText(), true)) addToLoaded(id->asText()); - } - for (auto const *id : meta.geta("extras").elements()) - { - if (isSelected(id->asText(), false)) addToLoaded(id->asText()); - } - } - else - { - // Non-collection data files are loaded as-is. - loadedBundles << bundle; - } + // Non-collection data files are loaded as-is. + loadedBundles << bundle; } } diff --git a/doomsday/sdk/libcore/include/de/core/app.h b/doomsday/sdk/libcore/include/de/core/app.h index b59f2fa6d6..b1aa99e722 100644 --- a/doomsday/sdk/libcore/include/de/core/app.h +++ b/doomsday/sdk/libcore/include/de/core/app.h @@ -318,6 +318,8 @@ class DENG2_PUBLIC App : DENG2_OBSERVES(Clock, TimeChange) */ static ScriptSystem &scriptSystem(); + static bool configExists(); + /** * Returns the configuration. */ diff --git a/doomsday/sdk/libcore/include/de/filesys/package.h b/doomsday/sdk/libcore/include/de/filesys/package.h index abd7bafa2d..c57a4576a3 100644 --- a/doomsday/sdk/libcore/include/de/filesys/package.h +++ b/doomsday/sdk/libcore/include/de/filesys/package.h @@ -169,6 +169,8 @@ class DENG2_PUBLIC Package : public IObject static Record &initializeMetadata(File &packageFile, String const &id = String()); + static Record const &metadata(File const &packageFile); + static QStringList tags(File const &packageFile); static bool matchTags(File const &packageFile, String const &tagRegExp); diff --git a/doomsday/sdk/libcore/net.dengine.stdlib.pack/modules/Config.de b/doomsday/sdk/libcore/net.dengine.stdlib.pack/modules/Config.de index e6a3ebc6d8..bab7c59209 100644 --- a/doomsday/sdk/libcore/net.dengine.stdlib.pack/modules/Config.de +++ b/doomsday/sdk/libcore/net.dengine.stdlib.pack/modules/Config.de @@ -1,6 +1,6 @@ # The Doomsday Engine Project -- libcore # -# Copyright (c) 2012-2014 Jaakko Keränen +# Copyright (c) 2012-2016 Jaakko Keränen # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by @@ -65,6 +65,10 @@ def setDefaults(d = None) initFilter(record d.log.filter.input, Log.MESSAGE, False) initFilter(record d.log.filter.network, Log.MESSAGE, False) + # File system settings. + record d.fs + d.fs.selectedPackages = {} + try # Get the application's default configuration, if we can. import appconfig diff --git a/doomsday/sdk/libcore/src/core/app.cpp b/doomsday/sdk/libcore/src/core/app.cpp index 7e2e1cba2a..0635b75190 100644 --- a/doomsday/sdk/libcore/src/core/app.cpp +++ b/doomsday/sdk/libcore/src/core/app.cpp @@ -771,6 +771,11 @@ ScriptSystem &App::scriptSystem() return DENG2_APP->d->scriptSys; } +bool App::configExists() +{ + return DENG2_APP->d->config != nullptr; +} + Folder &App::rootFolder() { return fileSystem().root(); diff --git a/doomsday/sdk/libcore/src/filesys/package.cpp b/doomsday/sdk/libcore/src/filesys/package.cpp index 888eecf95d..26f68d5d0f 100644 --- a/doomsday/sdk/libcore/src/filesys/package.cpp +++ b/doomsday/sdk/libcore/src/filesys/package.cpp @@ -332,6 +332,11 @@ Record &Package::initializeMetadata(File &packageFile, String const &id) return metadata; } +Record const &Package::metadata(File const &packageFile) +{ + return packageFile.objectNamespace().subrecord(VAR_PACKAGE); +} + QStringList Package::tags(File const &packageFile) { return tags(packageFile.objectNamespace().gets(PACKAGE_TAGS)); diff --git a/doomsday/sdk/libcore/src/filesys/packageloader.cpp b/doomsday/sdk/libcore/src/filesys/packageloader.cpp index 587d54bd36..43ce13c58f 100644 --- a/doomsday/sdk/libcore/src/filesys/packageloader.cpp +++ b/doomsday/sdk/libcore/src/filesys/packageloader.cpp @@ -17,15 +17,18 @@ */ #include "de/PackageLoader" -#include "de/FS" + #include "de/App" #include "de/CommandLine" -#include "de/Version" +#include "de/Config" +#include "de/DictionaryValue" +#include "de/FS" #include "de/Info" -#include "de/Log" -#include "de/Parser" #include "de/LinkFile" +#include "de/Log" #include "de/PackageFeed" +#include "de/Parser" +#include "de/Version" #include #include @@ -208,6 +211,13 @@ DENG2_PIMPL(PackageLoader) // exception is any are missing. loadRequirements(source); + // Optional content can be loaded once Config is available so the user's + // preferences are known. + if (App::configExists()) + { + loadOptionalContent(source); + } + Package *pkg = new Package(source); loaded.insert(packageId, pkg); pkg->setOrder(loadCounter++); @@ -288,6 +298,50 @@ DENG2_PIMPL(PackageLoader) } } + void loadOptionalContent(File const &packageFile) + { + // Packages enabled/disabled for use. + DictionaryValue const &selPkgs = Config::get()["fs.selectedPackages"] + .value(); + + Record const &meta = Package::metadata(packageFile); + + // Helper function to determine if a particular pacakge is marked for loading. + auto isPackageSelected = [meta, &selPkgs] (TextValue const &id, bool byDefault) -> bool { + TextValue const key(meta.gets("ID")); + if (selPkgs.contains(key)) { + auto const &sels = selPkgs.element(key).as(); + if (sels.contains(id)) { + return sels.element(id).isTrue(); + } + } + return byDefault; + }; + + if (meta.has("recommends")) + { + for (auto const *id : meta.geta("recommends").elements()) + { + String const pkgId = id->asText(); + if (isPackageSelected(pkgId, true) && !self.isLoaded(pkgId)) + { + self.load(pkgId); + } + } + } + if (meta.has("extras")) + { + for (auto const *id : meta.geta("extras").elements()) + { + String const pkgId = id->asText(); + if (isPackageSelected(pkgId, false) && !self.isLoaded(pkgId)) + { + self.load(pkgId); + } + } + } + } + QList loadedInOrder() const { QList pkgs = loaded.values();