mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-20 20:01:31 -06:00
Core: MultiData typing (#5071)
This commit is contained in:
23
Main.py
23
Main.py
@@ -1,9 +1,11 @@
|
|||||||
import collections
|
import collections
|
||||||
|
from collections.abc import Mapping
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
from typing import Any
|
||||||
import zipfile
|
import zipfile
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
@@ -239,11 +241,13 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
|||||||
def write_multidata():
|
def write_multidata():
|
||||||
import NetUtils
|
import NetUtils
|
||||||
from NetUtils import HintStatus
|
from NetUtils import HintStatus
|
||||||
slot_data = {}
|
slot_data: dict[int, Mapping[str, Any]] = {}
|
||||||
client_versions = {}
|
client_versions: dict[int, tuple[int, int, int]] = {}
|
||||||
games = {}
|
games: dict[int, str] = {}
|
||||||
minimum_versions = {"server": AutoWorld.World.required_server_version, "clients": client_versions}
|
minimum_versions: NetUtils.MinimumVersions = {
|
||||||
slot_info = {}
|
"server": AutoWorld.World.required_server_version, "clients": client_versions
|
||||||
|
}
|
||||||
|
slot_info: dict[int, NetUtils.NetworkSlot] = {}
|
||||||
names = [[name for player, name in sorted(multiworld.player_name.items())]]
|
names = [[name for player, name in sorted(multiworld.player_name.items())]]
|
||||||
for slot in multiworld.player_ids:
|
for slot in multiworld.player_ids:
|
||||||
player_world: AutoWorld.World = multiworld.worlds[slot]
|
player_world: AutoWorld.World = multiworld.worlds[slot]
|
||||||
@@ -258,7 +262,9 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
|||||||
group_members=sorted(group["players"]))
|
group_members=sorted(group["players"]))
|
||||||
precollected_items = {player: [item.code for item in world_precollected if type(item.code) == int]
|
precollected_items = {player: [item.code for item in world_precollected if type(item.code) == int]
|
||||||
for player, world_precollected in multiworld.precollected_items.items()}
|
for player, world_precollected in multiworld.precollected_items.items()}
|
||||||
precollected_hints = {player: set() for player in range(1, multiworld.players + 1 + len(multiworld.groups))}
|
precollected_hints: dict[int, set[NetUtils.Hint]] = {
|
||||||
|
player: set() for player in range(1, multiworld.players + 1 + len(multiworld.groups))
|
||||||
|
}
|
||||||
|
|
||||||
for slot in multiworld.player_ids:
|
for slot in multiworld.player_ids:
|
||||||
slot_data[slot] = multiworld.worlds[slot].fill_slot_data()
|
slot_data[slot] = multiworld.worlds[slot].fill_slot_data()
|
||||||
@@ -315,7 +321,7 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
|||||||
if current_sphere:
|
if current_sphere:
|
||||||
spheres.append(dict(current_sphere))
|
spheres.append(dict(current_sphere))
|
||||||
|
|
||||||
multidata = {
|
multidata: NetUtils.MultiData | bytes = {
|
||||||
"slot_data": slot_data,
|
"slot_data": slot_data,
|
||||||
"slot_info": slot_info,
|
"slot_info": slot_info,
|
||||||
"connect_names": {name: (0, player) for player, name in multiworld.player_name.items()},
|
"connect_names": {name: (0, player) for player, name in multiworld.player_name.items()},
|
||||||
@@ -325,7 +331,7 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
|||||||
"er_hint_data": er_hint_data,
|
"er_hint_data": er_hint_data,
|
||||||
"precollected_items": precollected_items,
|
"precollected_items": precollected_items,
|
||||||
"precollected_hints": precollected_hints,
|
"precollected_hints": precollected_hints,
|
||||||
"version": tuple(version_tuple),
|
"version": (version_tuple.major, version_tuple.minor, version_tuple.build),
|
||||||
"tags": ["AP"],
|
"tags": ["AP"],
|
||||||
"minimum_versions": minimum_versions,
|
"minimum_versions": minimum_versions,
|
||||||
"seed_name": multiworld.seed_name,
|
"seed_name": multiworld.seed_name,
|
||||||
@@ -333,6 +339,7 @@ def main(args, seed=None, baked_server_options: dict[str, object] | None = None)
|
|||||||
"datapackage": data_package,
|
"datapackage": data_package,
|
||||||
"race_mode": int(multiworld.is_race),
|
"race_mode": int(multiworld.is_race),
|
||||||
}
|
}
|
||||||
|
# TODO: change to `"version": version_tuple` after getting better serialization
|
||||||
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
|
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
|
||||||
|
|
||||||
for key in ("slot_data", "er_hint_data"):
|
for key in ("slot_data", "er_hint_data"):
|
||||||
|
@@ -43,7 +43,7 @@ import NetUtils
|
|||||||
import Utils
|
import Utils
|
||||||
from Utils import version_tuple, restricted_loads, Version, async_start, get_intended_text
|
from Utils import version_tuple, restricted_loads, Version, async_start, get_intended_text
|
||||||
from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, NetworkPlayer, Permission, NetworkSlot, \
|
from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, NetworkPlayer, Permission, NetworkSlot, \
|
||||||
SlotType, LocationStore, Hint, HintStatus
|
SlotType, LocationStore, MultiData, Hint, HintStatus
|
||||||
from BaseClasses import ItemClassification
|
from BaseClasses import ItemClassification
|
||||||
|
|
||||||
|
|
||||||
@@ -445,7 +445,7 @@ class Context:
|
|||||||
raise Utils.VersionException("Incompatible multidata.")
|
raise Utils.VersionException("Incompatible multidata.")
|
||||||
return restricted_loads(zlib.decompress(data[1:]))
|
return restricted_loads(zlib.decompress(data[1:]))
|
||||||
|
|
||||||
def _load(self, decoded_obj: dict, game_data_packages: typing.Dict[str, typing.Any],
|
def _load(self, decoded_obj: MultiData, game_data_packages: typing.Dict[str, typing.Any],
|
||||||
use_embedded_server_options: bool):
|
use_embedded_server_options: bool):
|
||||||
|
|
||||||
self.read_data = {}
|
self.read_data = {}
|
||||||
|
39
NetUtils.py
39
NetUtils.py
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Mapping, Sequence
|
||||||
import typing
|
import typing
|
||||||
import enum
|
import enum
|
||||||
import warnings
|
import warnings
|
||||||
@@ -83,7 +84,7 @@ class NetworkSlot(typing.NamedTuple):
|
|||||||
name: str
|
name: str
|
||||||
game: str
|
game: str
|
||||||
type: SlotType
|
type: SlotType
|
||||||
group_members: typing.Union[typing.List[int], typing.Tuple] = () # only populated if type == group
|
group_members: Sequence[int] = () # only populated if type == group
|
||||||
|
|
||||||
|
|
||||||
class NetworkItem(typing.NamedTuple):
|
class NetworkItem(typing.NamedTuple):
|
||||||
@@ -471,6 +472,42 @@ class _LocationStore(dict, typing.MutableMapping[int, typing.Dict[int, typing.Tu
|
|||||||
location_id not in checked])
|
location_id not in checked])
|
||||||
|
|
||||||
|
|
||||||
|
class MinimumVersions(typing.TypedDict):
|
||||||
|
server: tuple[int, int, int]
|
||||||
|
clients: dict[int, tuple[int, int, int]]
|
||||||
|
|
||||||
|
|
||||||
|
class GamesPackage(typing.TypedDict, total=False):
|
||||||
|
item_name_groups: dict[str, list[str]]
|
||||||
|
item_name_to_id: dict[str, int]
|
||||||
|
location_name_groups: dict[str, list[str]]
|
||||||
|
location_name_to_id: dict[str, int]
|
||||||
|
checksum: str
|
||||||
|
|
||||||
|
|
||||||
|
class DataPackage(typing.TypedDict):
|
||||||
|
games: dict[str, GamesPackage]
|
||||||
|
|
||||||
|
|
||||||
|
class MultiData(typing.TypedDict):
|
||||||
|
slot_data: dict[int, Mapping[str, typing.Any]]
|
||||||
|
slot_info: dict[int, NetworkSlot]
|
||||||
|
connect_names: dict[str, tuple[int, int]]
|
||||||
|
locations: dict[int, dict[int, tuple[int, int, int]]]
|
||||||
|
checks_in_area: dict[int, dict[str, int | list[int]]]
|
||||||
|
server_options: dict[str, object]
|
||||||
|
er_hint_data: dict[int, dict[int, str]]
|
||||||
|
precollected_items: dict[int, list[int]]
|
||||||
|
precollected_hints: dict[int, set[Hint]]
|
||||||
|
version: tuple[int, int, int]
|
||||||
|
tags: list[str]
|
||||||
|
minimum_versions: MinimumVersions
|
||||||
|
seed_name: str
|
||||||
|
spheres: list[dict[int, set[int]]]
|
||||||
|
datapackage: dict[str, GamesPackage]
|
||||||
|
race_mode: int
|
||||||
|
|
||||||
|
|
||||||
if typing.TYPE_CHECKING: # type-check with pure python implementation until we have a typing stub
|
if typing.TYPE_CHECKING: # type-check with pure python implementation until we have a typing stub
|
||||||
LocationStore = _LocationStore
|
LocationStore = _LocationStore
|
||||||
else:
|
else:
|
||||||
|
@@ -13,9 +13,8 @@ from pony.orm.core import TransactionIntegrityError
|
|||||||
import schema
|
import schema
|
||||||
|
|
||||||
import MultiServer
|
import MultiServer
|
||||||
from NetUtils import SlotType
|
from NetUtils import GamesPackage, SlotType
|
||||||
from Utils import VersionException, __version__
|
from Utils import VersionException, __version__
|
||||||
from worlds import GamesPackage
|
|
||||||
from worlds.Files import AutoPatchRegister
|
from worlds.Files import AutoPatchRegister
|
||||||
from worlds.AutoWorld import data_package_checksum
|
from worlds.AutoWorld import data_package_checksum
|
||||||
from . import app
|
from . import app
|
||||||
|
@@ -539,7 +539,7 @@ In addition, the following methods can be implemented and are called in this ord
|
|||||||
creates the output files if there is output to be generated. When this is called,
|
creates the output files if there is output to be generated. When this is called,
|
||||||
`self.multiworld.get_locations(self.player)` has all locations for the player, with attribute `item` pointing to the
|
`self.multiworld.get_locations(self.player)` has all locations for the player, with attribute `item` pointing to the
|
||||||
item. `location.item.player` can be used to see if it's a local item.
|
item. `location.item.player` can be used to see if it's a local item.
|
||||||
* `fill_slot_data(self)` and `modify_multidata(self, multidata: Dict[str, Any])` can be used to modify the data that
|
* `fill_slot_data(self)` and `modify_multidata(self, multidata: MultiData)` can be used to modify the data that
|
||||||
will be used by the server to host the MultiWorld.
|
will be used by the server to host the MultiWorld.
|
||||||
|
|
||||||
All instance methods can, optionally, have a class method defined which will be called after all instance methods are
|
All instance methods can, optionally, have a class method defined which will be called after all instance methods are
|
||||||
|
@@ -16,7 +16,7 @@ from Utils import deprecate
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from BaseClasses import MultiWorld, Item, Location, Tutorial, Region, Entrance
|
from BaseClasses import MultiWorld, Item, Location, Tutorial, Region, Entrance
|
||||||
from . import GamesPackage
|
from NetUtils import GamesPackage, MultiData
|
||||||
from settings import Group
|
from settings import Group
|
||||||
|
|
||||||
perf_logger = logging.getLogger("performance")
|
perf_logger = logging.getLogger("performance")
|
||||||
@@ -450,7 +450,7 @@ class World(metaclass=AutoWorldRegister):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def modify_multidata(self, multidata: Dict[str, Any]) -> None: # TODO: TypedDict for multidata?
|
def modify_multidata(self, multidata: "MultiData") -> None:
|
||||||
"""For deeper modification of server multidata."""
|
"""For deeper modification of server multidata."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -7,8 +7,9 @@ import warnings
|
|||||||
import zipimport
|
import zipimport
|
||||||
import time
|
import time
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Dict, List, TypedDict
|
from typing import List
|
||||||
|
|
||||||
|
from NetUtils import DataPackage
|
||||||
from Utils import local_path, user_path
|
from Utils import local_path, user_path
|
||||||
|
|
||||||
local_folder = os.path.dirname(__file__)
|
local_folder = os.path.dirname(__file__)
|
||||||
@@ -24,8 +25,6 @@ __all__ = {
|
|||||||
"world_sources",
|
"world_sources",
|
||||||
"local_folder",
|
"local_folder",
|
||||||
"user_folder",
|
"user_folder",
|
||||||
"GamesPackage",
|
|
||||||
"DataPackage",
|
|
||||||
"failed_world_loads",
|
"failed_world_loads",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,18 +32,6 @@ __all__ = {
|
|||||||
failed_world_loads: List[str] = []
|
failed_world_loads: List[str] = []
|
||||||
|
|
||||||
|
|
||||||
class GamesPackage(TypedDict, total=False):
|
|
||||||
item_name_groups: Dict[str, List[str]]
|
|
||||||
item_name_to_id: Dict[str, int]
|
|
||||||
location_name_groups: Dict[str, List[str]]
|
|
||||||
location_name_to_id: Dict[str, int]
|
|
||||||
checksum: str
|
|
||||||
|
|
||||||
|
|
||||||
class DataPackage(TypedDict):
|
|
||||||
games: Dict[str, GamesPackage]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(order=True)
|
@dataclasses.dataclass(order=True)
|
||||||
class WorldSource:
|
class WorldSource:
|
||||||
path: str # typically relative path from this module
|
path: str # typically relative path from this module
|
||||||
|
Reference in New Issue
Block a user