From e87ba125168a0d4a06ae3f3b403e24061eccf547 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Fri, 24 Sep 2021 22:04:03 +0100 Subject: [PATCH] Various documentation updates --- docs/api/introduction.rst | 5 +-- docs/api/miscellaneous.rst | 66 +++++++++++++++++++++++++------------- docs/api/sorting.rst | 9 ++++-- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/docs/api/introduction.rst b/docs/api/introduction.rst index 94d4b414..287a325a 100644 --- a/docs/api/introduction.rst +++ b/docs/api/introduction.rst @@ -11,5 +11,6 @@ thousands of Bash Tag suggestions. This metadata that LOOT supplies is stored in its masterlist, which is maintained by the LOOT team using information provided by mod authors and users. Users can also add to and modify the metadata used by LOOT through the use of -userlist files. libloot provides a way for third-party developers to access -this metadata for use in their own programs. +userlist files. libloot provides all of LOOT's non-UI-related functionality, and +can be used by third-party developers to access this metadata for use in their +own programs. diff --git a/docs/api/miscellaneous.rst b/docs/api/miscellaneous.rst index 5cb1a76b..d78666c3 100644 --- a/docs/api/miscellaneous.rst +++ b/docs/api/miscellaneous.rst @@ -15,7 +15,10 @@ String Encoding Language Codes ============== -All language strings in the API are codes of the form ``ll`` or ``ll_CC``, where ``ll`` is an ISO 639-1 language code and ``CC`` is an ISO 3166 country code. For example, the default language for metadata message content is English, identified by the code ``en``, and Brazilian Portuguese is ``pt_BR``. +All language strings in the API are codes of the form ``ll`` or ``ll_CC``, where +``ll`` is an ISO 639-1 language code and ``CC`` is an ISO 3166 country code. For +example, the default language for metadata message content is English, +identified by the code ``en``, and Brazilian Portuguese is ``pt_BR``. Errors ====== @@ -26,45 +29,64 @@ All errors encountered are thrown as exceptions that inherit from Metadata Files ============== -LOOT stores plugin metadata in YAML files. It distinguishes between *masterlist* -and *userlist* files: each game has a single masterlist, which is a public, -curated metadata store, and each LOOT user has a private userlist, which can -contain metadata added by the user. The two files use the same syntax, but -metadata in the userlist extends or replaces metadata sourced from the -masterlist. +LOOT stores plugin metadata in YAML files. It distinguishes between three types +of metadata file: + +- *masterlist* files: each game has a single masterlist, which is a public, + curated metadata store +- *masterlist prelude* files: there is a single masterlist prelude, which is a + public store of common metadata templates that can be shared across all + masterlists +- *userlist* files: each game has a userlist, which is a private user-specific + metadata store containing metadata added by the LOOT user. + +All three files use the same syntax, but the masterlist prelude file is used to +replace part of a masterlist file before it is parsed, and metadata in the +userlist extends or replaces metadata sourced from the masterlist. LOOT's plugin metadata can be conditional, eg. a plugin may require a patch only if another plugin is also present. The API's :cpp:func:`LoadLists` method parses metadata files into memory, but does not evaluate these conditions, so the loaded metadata may contain metadata that is invalid for the installed game that -the :cpp:class:`loot::DatabaseInterface` object being operated on was created for. +the :cpp:class:`loot::DatabaseInterface` object being operated on was created +for. Caching ======= All unevaluated metadata is cached between calls to :cpp:func:`LoadLists`. +The results of evaluating metadata conditions are cached between calls to :cpp:func:`LoadPlugins`, :cpp:func:`SortPlugins` and +:cpp:func:`GetGeneralMessages`. + Plugin content is cached between calls to :cpp:func:`LoadPlugins` and :cpp:func:`SortPlugins`. -Load order is cached between calls to :cpp:func:`LoadPlugins`, -:cpp:func:`SortPlugins` and :cpp:func:`LoadCurrentLoadOrderState`. +Load order is cached between calls to :cpp:func:`LoadCurrentLoadOrderState`. Performance =========== -Loading metadata lists is a relatively costly operation, as is updating the -masterlist (which involves loading it). +The following may involve filesystem access and reading/parsing or writing of +data from the filesystem: -Sorting plugins is expensive, as it involves loading all the content of all -the plugins, apart from the game's main master file, which is skipped as an -optimisation (it doesn't depend on anything else and is much bigger than any -other plugin, so is unnecessary and slow to load). +- Any function that takes a ``std::filesystem::path`` +- :cpp:any:`GameInterface::IsValidPlugin()` +- :cpp:any:`GameInterface::LoadPlugins()` +- :cpp:any:`GameInterface::LoadCurrentLoadOrderState()` +- :cpp:any:`GameInterface::SetLoadOrder()` -Getting plugin metadata once loaded is cheap, as is getting a masterlist's -revision. +Evaluating conditions may also involve filesystem read access. -Loading the current load order state is relatively cheap and can take < 1 ms -depending on hardware and the size of the load order, but involves filesystem -access and should not be done more often than necessary to avoid a performance -impact. +:cpp:any:`UpdateFile()` may involve network communication if given the URL of a +remote server. + +:cpp:any:`GameInterface::SortPlugins()` is expensive, as it involves loading +all the content of all the plugins, apart from the game's main master file, which is skipped as an optimisation (it doesn't depend on anything else and is much bigger than any other plugin, so is unnecessary and slow to load). + +:cpp:any:`DatabaseInterface::GetGroupsPath()` involves building a graph of all defined groups and +then using it to search for the shortest path between the two given groups, +which may be relatively slow given a sufficiently large and/or complex set of +group definitions. + +All other API functions should be relatively fast. diff --git a/docs/api/sorting.rst b/docs/api/sorting.rst index 1c612433..7be5710e 100644 --- a/docs/api/sorting.rst +++ b/docs/api/sorting.rst @@ -53,6 +53,10 @@ the group-derived plugin to the plugin would cause a cycle, and if not the edge is recorded. Once all potential edges have been checked, the recorded edges are added to the graph. +At this point the plugin graph is checked for cycles, and an error is thrown if +any are encountered, so that metadata (or indeed plugin data) that cause them +can be corrected. + Plugin overlap edges are then added. Two plugins overlap if they contain the same record, i.e. if they both edit the same record or if one edits a record the other plugin adds. @@ -89,9 +93,8 @@ Topologically sort the plugin graph =================================== Note that edges for explicit interdependencies are the only edges allowed to -create cycles: this is because the first step of this stage is to check the -plugin graph for cycles, and throw an error if any are encountered, so that -metadata (or indeed plugin data) that cause them can be corrected. +create cycles. However, the graph is again checked for cycles to guard against +potential logic bugs, and if a cycle is encountered an error is thrown. Once the graph is confirmed to be cycle-free, a topological sort is performed on the graph, outputting a list of plugins in their newly-sorted load order.