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."""
 | 
					        """Tests that if a world creates slot data, it's json serializable."""
 | 
				
			||||||
        for game_name, world_type in AutoWorldRegister.world_types.items():
 | 
					        for game_name, world_type in AutoWorldRegister.world_types.items():
 | 
				
			||||||
            # has an await for generate_output which isn't being called
 | 
					            # 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
 | 
					                continue
 | 
				
			||||||
            multiworld = setup_solo_multiworld(world_type)
 | 
					            multiworld = setup_solo_multiworld(world_type)
 | 
				
			||||||
            with self.subTest(game=game_name, seed=multiworld.seed):
 | 
					            with self.subTest(game=game_name, seed=multiworld.seed):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -119,8 +119,13 @@ class ZillionWorld(World):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    my_locations: list[ZillionLocation] = []
 | 
					    my_locations: list[ZillionLocation] = []
 | 
				
			||||||
    """ This is kind of a cache to avoid iterating through all the multiworld locations in logic. """
 | 
					    """ This is kind of a cache to avoid iterating through all the multiworld locations in logic. """
 | 
				
			||||||
    slot_data_ready: threading.Event
 | 
					    finalized_gen_data: GenData | None
 | 
				
			||||||
    """ This event is set in `generate_output` when the data is ready for `fill_slot_data` """
 | 
					    """ 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
 | 
					    logic_cache: ZillionLogicCache | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, world: MultiWorld, player: int) -> None:
 | 
					    def __init__(self, world: MultiWorld, player: int) -> None:
 | 
				
			||||||
| 
						 | 
					@ -128,7 +133,8 @@ class ZillionWorld(World):
 | 
				
			||||||
        self.logger = logging.getLogger("Zillion")
 | 
					        self.logger = logging.getLogger("Zillion")
 | 
				
			||||||
        self.lsi = ZillionWorld.LogStreamInterface(self.logger)
 | 
					        self.lsi = ZillionWorld.LogStreamInterface(self.logger)
 | 
				
			||||||
        self.zz_system = System()
 | 
					        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:
 | 
					    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)
 | 
					        _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()
 | 
					        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:
 | 
					    def finalize_item_locations(self) -> GenData:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        sync zilliandomizer item locations with AP item locations
 | 
					        sync zilliandomizer item locations with AP item locations
 | 
				
			||||||
| 
						 | 
					@ -363,12 +382,7 @@ class ZillionWorld(World):
 | 
				
			||||||
    def generate_output(self, output_directory: str) -> None:
 | 
					    def generate_output(self, output_directory: str) -> None:
 | 
				
			||||||
        """This method gets called from a threadpool, do not use multiworld.random here.
 | 
					        """This method gets called from a threadpool, do not use multiworld.random here.
 | 
				
			||||||
        If you need any last-second randomization, use self.random instead."""
 | 
					        If you need any last-second randomization, use self.random instead."""
 | 
				
			||||||
        try:
 | 
					        gen_data = self.finalize_item_locations_thread_safe()
 | 
				
			||||||
            gen_data = self.finalize_item_locations()
 | 
					 | 
				
			||||||
        except BaseException:
 | 
					 | 
				
			||||||
            raise
 | 
					 | 
				
			||||||
        finally:
 | 
					 | 
				
			||||||
            self.slot_data_ready.set()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        out_file_base = self.multiworld.get_out_file_name_base(self.player)
 | 
					        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
 | 
					        # TODO: tell client which canisters are keywords
 | 
				
			||||||
        # so it can open and get those when restoring doors
 | 
					        # so it can open and get those when restoring doors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.slot_data_ready.wait()
 | 
					        game = self.finalize_item_locations_thread_safe().zz_game
 | 
				
			||||||
        assert self.zz_system.randomizer, "didn't get randomizer from generate_early"
 | 
					 | 
				
			||||||
        game = self.zz_system.get_game()
 | 
					 | 
				
			||||||
        return get_slot_info(game.regions, game.char_order[0], game.loc_name_2_pretty)
 | 
					        return get_slot_info(game.regions, game.char_order[0], game.loc_name_2_pretty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # end of ordered Main.py calls
 | 
					    # end of ordered Main.py calls
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue