Zillion: fix invalid slot data from race condition

This commit is contained in:
beauxq 2022-12-09 17:54:29 -08:00 committed by Fabian Dill
parent c3fe341736
commit b7d46004e2
3 changed files with 14 additions and 1 deletions

View File

@ -258,6 +258,10 @@ class ZillionContext(CommonContext):
assert id_ in id_to_loc assert id_ in id_to_loc
self.loc_mem_to_id[mem] = id_ self.loc_mem_to_id[mem] = id_
if len(self.loc_mem_to_id) != 394:
logger.warn("invalid Zillion `Connected` packet, "
f"`slot_data` missing locations in `loc_mem_to_id` - len {len(self.loc_mem_to_id)}")
self.got_slot_data.set() self.got_slot_data.set()
payload = { payload = {

View File

@ -251,7 +251,10 @@ class World(metaclass=AutoWorldRegister):
def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot
"""Fill in the `slot_data` field in the `Connected` network package. """Fill in the `slot_data` field in the `Connected` network package.
This is a way the generator can give custom data to the client. This is a way the generator can give custom data to the client.
The client will receive this as JSON in the `Connected` response.""" The client will receive this as JSON in the `Connected` response.
The generation does not wait for `generate_output` to complete before calling this.
`threading.Event` can be used if you need to wait for something from `generate_output`."""
return {} return {}
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]): def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):

View File

@ -1,6 +1,7 @@
from collections import deque, Counter from collections import deque, Counter
from contextlib import redirect_stdout from contextlib import redirect_stdout
import functools import functools
import threading
from typing import Any, Dict, List, Set, Tuple, Optional, cast from typing import Any, Dict, List, Set, Tuple, Optional, cast
import os import os
import logging import logging
@ -101,12 +102,15 @@ 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
""" This event is set in `generate_output` when the data is ready for `fill_slot_data` """
def __init__(self, world: MultiWorld, player: int): def __init__(self, world: MultiWorld, player: int):
super().__init__(world, player) super().__init__(world, player)
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()
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)
@ -338,6 +342,7 @@ class ZillionWorld(World):
zz_patcher.write_locations(self.zz_system.randomizer.regions, zz_patcher.write_locations(self.zz_system.randomizer.regions,
zz_options.start_char, zz_options.start_char,
self.zz_system.randomizer.loc_name_2_pretty) self.zz_system.randomizer.loc_name_2_pretty)
self.slot_data_ready.set()
zz_patcher.all_fixes_and_options(zz_options) zz_patcher.all_fixes_and_options(zz_options)
zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level) zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level)
zz_patcher.set_multiworld_items(multi_items) zz_patcher.set_multiworld_items(multi_items)
@ -385,6 +390,7 @@ class ZillionWorld(World):
assert self.zz_system.randomizer, "didn't get randomizer from generate_early" assert self.zz_system.randomizer, "didn't get randomizer from generate_early"
rescues: Dict[str, Any] = {} rescues: Dict[str, Any] = {}
self.slot_data_ready.wait()
for i in (0, 1): for i in (0, 1):
if i in zz_patcher.rescue_locations: if i in zz_patcher.rescue_locations:
ri = zz_patcher.rescue_locations[i] ri = zz_patcher.rescue_locations[i]