Zillion: Finalize item locations in either generate_output or fill_slot_data (#4121)
Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
This commit is contained in:
parent
cbf4bbbca8
commit
9443861849
|
@ -39,7 +39,7 @@ class TestImplemented(unittest.TestCase):
|
|||
"""Tests that if a world creates slot data, it's json serializable."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
# has an await for generate_output which isn't being called
|
||||
if game_name in {"Ocarina of Time", "Zillion"}:
|
||||
if game_name in {"Ocarina of Time"}:
|
||||
continue
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
with self.subTest(game=game_name, seed=multiworld.seed):
|
||||
|
|
|
@ -119,8 +119,13 @@ class ZillionWorld(World):
|
|||
"""
|
||||
my_locations: list[ZillionLocation] = []
|
||||
""" This is kind of a cache to avoid iterating through all the multiworld locations in logic. """
|
||||
slot_data_ready: threading.Event
|
||||
""" This event is set in `generate_output` when the data is ready for `fill_slot_data` """
|
||||
finalized_gen_data: GenData | None
|
||||
""" Finalized generation data needed by `generate_output` and by `fill_slot_data`. """
|
||||
item_locations_finalization_lock: threading.Lock
|
||||
"""
|
||||
This lock is used in `generate_output` and `fill_slot_data` to ensure synchronized access to `finalized_gen_data`,
|
||||
so that whichever is run first can finalize the item locations while the other waits.
|
||||
"""
|
||||
logic_cache: ZillionLogicCache | None = None
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int) -> None:
|
||||
|
@ -128,7 +133,8 @@ class ZillionWorld(World):
|
|||
self.logger = logging.getLogger("Zillion")
|
||||
self.lsi = ZillionWorld.LogStreamInterface(self.logger)
|
||||
self.zz_system = System()
|
||||
self.slot_data_ready = threading.Event()
|
||||
self.finalized_gen_data = None
|
||||
self.item_locations_finalization_lock = threading.Lock()
|
||||
|
||||
def _make_item_maps(self, start_char: Chars) -> None:
|
||||
_id_to_name, _id_to_zz_id, id_to_zz_item = make_id_to_others(start_char)
|
||||
|
@ -305,6 +311,19 @@ class ZillionWorld(World):
|
|||
|
||||
self.zz_system.post_fill()
|
||||
|
||||
def finalize_item_locations_thread_safe(self) -> GenData:
|
||||
"""
|
||||
Call self.finalize_item_locations() and cache the result in a thread-safe manner so that either
|
||||
`generate_output` or `fill_slot_data` can finalize item locations without concern for which of the two functions
|
||||
is called first.
|
||||
"""
|
||||
# The lock is acquired when entering the context manager and released when exiting the context manager.
|
||||
with self.item_locations_finalization_lock:
|
||||
# If generation data has yet to be finalized, finalize it.
|
||||
if self.finalized_gen_data is None:
|
||||
self.finalized_gen_data = self.finalize_item_locations()
|
||||
return self.finalized_gen_data
|
||||
|
||||
def finalize_item_locations(self) -> GenData:
|
||||
"""
|
||||
sync zilliandomizer item locations with AP item locations
|
||||
|
@ -363,12 +382,7 @@ class ZillionWorld(World):
|
|||
def generate_output(self, output_directory: str) -> None:
|
||||
"""This method gets called from a threadpool, do not use multiworld.random here.
|
||||
If you need any last-second randomization, use self.random instead."""
|
||||
try:
|
||||
gen_data = self.finalize_item_locations()
|
||||
except BaseException:
|
||||
raise
|
||||
finally:
|
||||
self.slot_data_ready.set()
|
||||
gen_data = self.finalize_item_locations_thread_safe()
|
||||
|
||||
out_file_base = self.multiworld.get_out_file_name_base(self.player)
|
||||
|
||||
|
@ -392,9 +406,7 @@ class ZillionWorld(World):
|
|||
# TODO: tell client which canisters are keywords
|
||||
# so it can open and get those when restoring doors
|
||||
|
||||
self.slot_data_ready.wait()
|
||||
assert self.zz_system.randomizer, "didn't get randomizer from generate_early"
|
||||
game = self.zz_system.get_game()
|
||||
game = self.finalize_item_locations_thread_safe().zz_game
|
||||
return get_slot_info(game.regions, game.char_order[0], game.loc_name_2_pretty)
|
||||
|
||||
# end of ordered Main.py calls
|
||||
|
|
Loading…
Reference in New Issue