Core: ensure slot_data and er_hint_info are only base data types (#5144)

---------

Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
This commit is contained in:
Fabian Dill
2025-07-07 15:51:39 +02:00
committed by GitHub
parent e68b1ad428
commit 4623d59206
3 changed files with 27 additions and 2 deletions

View File

@@ -12,6 +12,7 @@ import worlds
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld
from Fill import FillError, balance_multiworld_progression, distribute_items_restrictive, flood_items, \ from Fill import FillError, balance_multiworld_progression, distribute_items_restrictive, flood_items, \
parse_planned_blocks, distribute_planned_blocks, resolve_early_locations_for_planned parse_planned_blocks, distribute_planned_blocks, resolve_early_locations_for_planned
from NetUtils import convert_to_base_types
from Options import StartInventoryPool from Options import StartInventoryPool
from Utils import __version__, output_path, version_tuple from Utils import __version__, output_path, version_tuple
from settings import get_settings from settings import get_settings
@@ -334,6 +335,9 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
} }
AutoWorld.call_all(multiworld, "modify_multidata", multidata) AutoWorld.call_all(multiworld, "modify_multidata", multidata)
for key in ("slot_data", "er_hint_data"):
multidata[key] = convert_to_base_types(multidata[key])
multidata = zlib.compress(pickle.dumps(multidata), 9) multidata = zlib.compress(pickle.dumps(multidata), 9)
with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f: with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:

View File

@@ -106,6 +106,27 @@ def _scan_for_TypedTuples(obj: typing.Any) -> typing.Any:
return obj return obj
_base_types = str | int | bool | float | None | tuple["_base_types", ...] | dict["_base_types", "base_types"]
def convert_to_base_types(obj: typing.Any) -> _base_types:
if isinstance(obj, (tuple, list, set, frozenset)):
return tuple(convert_to_base_types(o) for o in obj)
elif isinstance(obj, dict):
return {convert_to_base_types(key): convert_to_base_types(value) for key, value in obj.items()}
elif obj is None or type(obj) in (str, int, float, bool):
return obj
# unwrap simple types to their base, such as StrEnum
elif isinstance(obj, str):
return str(obj)
elif isinstance(obj, int):
return int(obj)
elif isinstance(obj, float):
return float(obj)
else:
raise Exception(f"Cannot handle {type(obj)}")
_encode = JSONEncoder( _encode = JSONEncoder(
ensure_ascii=False, ensure_ascii=False,
check_circular=False, check_circular=False,

View File

@@ -1,7 +1,7 @@
import unittest import unittest
from Fill import distribute_items_restrictive from Fill import distribute_items_restrictive
from NetUtils import encode from NetUtils import convert_to_base_types
from worlds.AutoWorld import AutoWorldRegister, call_all from worlds.AutoWorld import AutoWorldRegister, call_all
from worlds import failed_world_loads from worlds import failed_world_loads
from . import setup_solo_multiworld from . import setup_solo_multiworld
@@ -47,7 +47,7 @@ class TestImplemented(unittest.TestCase):
call_all(multiworld, "post_fill") call_all(multiworld, "post_fill")
for key, data in multiworld.worlds[1].fill_slot_data().items(): for key, data in multiworld.worlds[1].fill_slot_data().items():
self.assertIsInstance(key, str, "keys in slot data must be a string") self.assertIsInstance(key, str, "keys in slot data must be a string")
self.assertIsInstance(encode(data), str, f"object {type(data).__name__} not serializable.") convert_to_base_types(data) # only put base data types into slot data
def test_no_failed_world_loads(self): def test_no_failed_world_loads(self):
if failed_world_loads: if failed_world_loads: