-
Notifications
You must be signed in to change notification settings - Fork 816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core: Lazy loading experiment #4573
base: main
Are you sure you want to change the base?
Core: Lazy loading experiment #4573
Conversation
No more hacks for setting module/package of worlds loaded from zip files. Though unsure how this is working. Now handles loading of non-zip worlds too. World load times now include only the duration of exec_module in the module loader, which should be fairer on modules that have to be loaded from a .zip.
Look for the archipelago.json in the patch file zip and load the game specified in the archipelago.json. I don't know if APPatchExtension (AutoPatchExtensionRegister) without a game specified will work.
There could still be some missing or some of these might not be necessary.
The Temp World was copying the ap_info.json used by clique, so was thinking it was also Clique.
Trying to load all worlds here would load all worlds even when trying to generate with only a few worlds. The late loading of these worlds would result in worlds adding copy/init mixins after CollectionState instances have already been created, meaning that copying any existing CollectionState would fail by trying to copy attributes the new copy mixins were expecting to exist, but did not because the CollectionState was created before the init mixin was added. This probably means it would be a good idea for there to be a function that can be called to prevent further world loading. Possibly raising an error if such loading is attempted.
Only load the worlds for the games present in the embedded data package.
The "Archipelago" world (worlds.generic) always needs to be loaded so that its items and locations can be added to each data package.
WebHostContext, unlike Context, needed to call self._load_game_data() immediately. This has been moved to its __init__() right after the super().__init__() and its _load_game_data now does nothing. get_static_server_data() now ensures all the worlds get loaded before caching the data. Not sure how I missed this usage.
In the original code, the ZipImporter is told to find the spec, but it does not know that "<world name>" in the zip should really be providing "world.<world name>", so the spec it creates is for importing a "<world name>" module, so after importing AP had to mess with the module and package name to correct it. It's probably possible to fix this separately on the main branch.
The set could be constructed programmatically based on knowing which games need to be loaded and subtracting games that have already been loaded, resulting in an empty set when all required games have already been loaded. In this case, no worlds would need to be loaded.
Removes warnings about shadowing global variables.
Loading worlds late into generation can result in confusing errors from CollectionState copy_mixins failing due to expected init_mixins not being run because the CollectionState was created before the world was loaded.
typing.override was only added in Python 3.12
# Prevent additional world loading as a safeguard. | ||
# Additional worlds must not be loaded after a CollectionState has been created because worlds may add init_mixins | ||
# and copy_mixins to the CollectionState class. | ||
worlds.world_loading_enabled = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in the PR description, for WebHost, Main.main
(generation) and worlds.ensure_worlds_loaded
probably need to use a lock to properly prevent world loading while a generation is ongoing on the same process.
worlds.world_loading_enabled
is also currently not set back to True
anywhere.
This was not reverted correctly and ended up above LogicMixin when it should have been below LogicMixin
What is this fixing or adding?
Implements lazy loading by reading the game for each world from an
ap_info.json
(similar to thearchipelago.json
suggested in #4516), interceptingworlds.<world>
module loading, and allowing worlds to be loaded by game name.There is currently no intention to merge this PR, it has been created to aid with discussion surrounding the problem of lazy loading. I think there is more planning and design discussion required before lazy loading can be properly implemented into Archipelago.
worlds.ensure_worlds_loaded()
: Load worlds by game name or load all worldsworlds.ZipWorldLoader
,worlds.SourceWorldLoader
andworlds.BytecodeWorldLoader
implement the module loadingworlds.WorldFinder
is animportlib.abc.MetaPathFinder
, it is responsible for handling attempts to importworlds.<world>
modules that have not been loaded yet, and picking the correctLoader
for the world.WorldFinder
functions by being prepended tosys.meta_path
:sys.meta_path.insert(0, WorldFinder())
. This is necessary to intercept loading of non-zipped worlds when running from source, but it can be appended tosys.meta_path
instead to only handle loading of zipped worlds, which might be better for frozen builds (assuming things actually work on frozen builds)For how module importing/loading works in Python, I would recommend reading through most of https://docs.python.org/3/library/importlib.html. I did not know much about module importing/loading before I started working on this PR and learned pretty much everything necessary to implement this PR from that documentation.
Known broken things
CollectionState.additional_copy_functions
andCollectionState.additional_init_functions
, so loading a world while generation is ongoing can add a copy function and then attempting to copymultiworld.state
will fail because the attributes the copy function was expecting to be created by the init function do not exist.Main.main
) andworlds.ensure_worlds_loaded()
most likely need to use locks to prevent concurrent generations on the same process, as well as anything else on the same process that might try to load worlds. Care will be needed with implementing this to ensure that deadlocks cannot happen.General information
ap_info.json
are attempted to be loaded to try to find a matching world.archipelago.json
within the patch for patches that usePatch.create_rom_file()
, and then load the world for that gameAPPatchExtension
/AutoPatchExtensionRegister
already provide their game, so it is easy to then ensure the world for that game is loadedworlds.ensure_all_worlds_loaded()
at the start and pray"Potential improvements
worlds.scan_for_worlds_on_module_load()
) should be deferred until the first attempt to load a worldap_info.json
it is probably possible to determine the package of a loaded world class and create theap_info.json
for that worldHow was this tested?
Unit tests have been run (note that if I missed updating some tests that iterate through all worlds, those tests would now be iterating no worlds, so would probably succeed and test nothing).
Seeds have been generated locally.
Solo seeds have been generated on webhost and a solo seed has been played a small amount while connected to a webhost hosted room. This solo seed also used a game with a patch file, so that was tested on the client side too.
worlds.BytecodeWorldLoader
was tested by replacing the Clique source files with a copy of the Clique world from a frozen build of AP 0.5.1.Frozen builds have not been tested at all.