|
1 | 1 | import importlib |
| 2 | +import importlib.abc |
| 3 | +import importlib.machinery |
2 | 4 | import importlib.util |
3 | 5 | import logging |
4 | 6 | import os |
5 | 7 | import sys |
6 | | -import warnings |
7 | 8 | import zipimport |
8 | 9 | import time |
9 | 10 | import dataclasses |
10 | 11 | import json |
11 | | -from typing import List |
| 12 | +from pathlib import Path |
| 13 | +from types import ModuleType |
| 14 | +from typing import List, Sequence |
12 | 15 |
|
13 | 16 | from NetUtils import DataPackage |
14 | 17 | from Utils import local_path, user_path, Version, version_tuple, tuplize_version |
@@ -53,21 +56,7 @@ def resolved_path(self) -> str: |
53 | 56 | def load(self) -> bool: |
54 | 57 | try: |
55 | 58 | start = time.perf_counter() |
56 | | - if self.is_zip: |
57 | | - importer = zipimport.zipimporter(self.resolved_path) |
58 | | - spec = importer.find_spec(os.path.basename(self.path).rsplit(".", 1)[0]) |
59 | | - assert spec, f"{self.path} is not a loadable module" |
60 | | - mod = importlib.util.module_from_spec(spec) |
61 | | - |
62 | | - mod.__package__ = f"worlds.{mod.__package__}" |
63 | | - |
64 | | - mod.__name__ = f"worlds.{mod.__name__}" |
65 | | - sys.modules[mod.__name__] = mod |
66 | | - with warnings.catch_warnings(): |
67 | | - warnings.filterwarnings("ignore", message="__package__ != __spec__.parent") |
68 | | - importer.exec_module(mod) |
69 | | - else: |
70 | | - importlib.import_module(f".{self.path}", "worlds") |
| 59 | + importlib.import_module(f".{Path(self.path).stem}", "worlds") |
71 | 60 | self.time_taken = time.perf_counter()-start |
72 | 61 | return True |
73 | 62 |
|
@@ -112,7 +101,6 @@ def load(self) -> bool: |
112 | 101 | else: |
113 | 102 | world_source.load() |
114 | 103 |
|
115 | | - |
116 | 104 | from .AutoWorld import AutoWorldRegister |
117 | 105 |
|
118 | 106 | for world_source in world_sources: |
@@ -174,13 +162,29 @@ def fail_world(game_name: str, reason: str, add_as_failed_to_load: bool = True) |
174 | 162 | core_compatible.sort( |
175 | 163 | key=lambda element: element[1].world_version if element[1].world_version else Version(0, 0, 0), |
176 | 164 | reverse=True) |
| 165 | + |
| 166 | + apworld_module_specs = {} |
| 167 | + class APWorldModuleFinder(importlib.abc.MetaPathFinder): |
| 168 | + def find_spec( |
| 169 | + self, fullname: str, _path: Sequence[str] | None, _target: ModuleType = None |
| 170 | + ) -> importlib.machinery.ModuleSpec | None: |
| 171 | + return apworld_module_specs.get(fullname) |
| 172 | + |
| 173 | + sys.meta_path.insert(0, APWorldModuleFinder()) |
| 174 | + |
177 | 175 | for apworld_source, apworld in core_compatible: |
178 | 176 | if apworld.game and apworld.game in AutoWorldRegister.world_types: |
179 | 177 | fail_world(apworld.game, |
180 | 178 | f"Did not load {apworld_source.path} " |
181 | 179 | f"as its game {apworld.game} is already loaded.", |
182 | 180 | add_as_failed_to_load=False) |
183 | 181 | else: |
| 182 | + importer = zipimport.zipimporter(apworld_source.resolved_path) |
| 183 | + world_name = Path(apworld.path).stem |
| 184 | + |
| 185 | + spec = importer.find_spec(f"worlds.{world_name}") |
| 186 | + apworld_module_specs[f"worlds.{world_name}"] = spec |
| 187 | + |
184 | 188 | apworld_source.load() |
185 | 189 | if apworld.game in AutoWorldRegister.world_types: |
186 | 190 | # world could fail to load at this point |
|
0 commit comments