mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-04-05 16:18:12 -07:00
Core: change how required versions work, deprecate IgnoreGame (#426)
`AutoWorld.World`s can set required_server_version and required_client_version properties. Drop `get_required_client_version()`. `MultiServer` will set an absolute minimum client version based on its capability (protocol level). `IgnoreVersion` tag is replaced by using `Tracker` or `TextOnly` with empty or null `game`. Ignoring game will also ignore game's required_client_version (and fall back to server capability).
This commit is contained in:
@@ -27,6 +27,15 @@ class AutoWorldRegister(type):
|
||||
dct["location_names"] = frozenset(dct["location_name_to_id"])
|
||||
dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {})))
|
||||
|
||||
# move away from get_required_client_version function
|
||||
if "game" in dct:
|
||||
assert "get_required_client_version" not in dct, f"{name}: required_client_version is an attribute now"
|
||||
# set minimum required_client_version from bases
|
||||
if "required_client_version" in dct and bases:
|
||||
for base in bases:
|
||||
if "required_client_version" in base.__dict__:
|
||||
dct["required_client_version"] = max(dct["required_client_version"], base.required_client_version)
|
||||
|
||||
# construct class
|
||||
new_class = super().__new__(cls, name, bases, dct)
|
||||
if "game" in dct:
|
||||
@@ -106,6 +115,14 @@ class World(metaclass=AutoWorldRegister):
|
||||
# retrieved by clients on every connection.
|
||||
data_version: int = 1
|
||||
|
||||
# override this if changes to a world break forward-compatibility of the client
|
||||
# The base version of (0, 1, 6) is provided for backwards compatibility and does *not* need to be updated in the
|
||||
# future. Protocol level compatibility check moved to MultiServer.min_client_version.
|
||||
required_client_version: Tuple[int, int, int] = (0, 1, 6)
|
||||
|
||||
# update this if the resulting multidata breaks forward-compatibility of the server
|
||||
required_server_version: Tuple[int, int, int] = (0, 2, 4)
|
||||
|
||||
hint_blacklist: Set[str] = frozenset() # any names that should not be hintable
|
||||
|
||||
# NOTE: remote_items and remote_start_inventory are now available in the network protocol for the client to set.
|
||||
@@ -191,9 +208,6 @@ class World(metaclass=AutoWorldRegister):
|
||||
"""For deeper modification of server multidata."""
|
||||
pass
|
||||
|
||||
def get_required_client_version(self) -> Tuple[int, int, int]:
|
||||
return 0, 1, 6
|
||||
|
||||
# Spoiler writing is optional, these may not get called.
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO):
|
||||
"""Write to the spoiler header. If individual it's right at the end of that player's options,
|
||||
|
||||
@@ -43,6 +43,7 @@ class ALTTPWorld(World):
|
||||
data_version = 8
|
||||
remote_items: bool = False
|
||||
remote_start_inventory: bool = False
|
||||
required_client_version = (0, 3, 2)
|
||||
|
||||
set_rules = set_rules
|
||||
|
||||
@@ -324,9 +325,6 @@ class ALTTPWorld(World):
|
||||
new_name = base64.b64encode(bytes(self.rom_name)).decode()
|
||||
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]]
|
||||
|
||||
def get_required_client_version(self) -> tuple:
|
||||
return max((0, 3, 2), super(ALTTPWorld, self).get_required_client_version())
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return ALttPItem(name, self.player, **as_dict_item_table[name])
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class Factorio(World):
|
||||
"Progressive": set(progressive_tech_table.values()),
|
||||
}
|
||||
data_version = 5
|
||||
required_client_version = (0, 3, 0)
|
||||
|
||||
def __init__(self, world, player: int):
|
||||
super(Factorio, self).__init__(world, player)
|
||||
@@ -179,9 +180,6 @@ class Factorio(World):
|
||||
|
||||
return super(Factorio, self).collect_item(state, item, remove)
|
||||
|
||||
def get_required_client_version(self) -> tuple:
|
||||
return max((0, 2, 6), super(Factorio, self).get_required_client_version())
|
||||
|
||||
options = factorio_options
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -32,6 +32,9 @@ class MeritousWorld(World):
|
||||
data_version = 2
|
||||
forced_auto_forfeit = False
|
||||
|
||||
# NOTE: Remember to change this before this game goes live
|
||||
required_client_version = (0, 2, 4)
|
||||
|
||||
options = meritous_options
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
@@ -150,10 +153,6 @@ class MeritousWorld(World):
|
||||
self.world.completion_condition[self.player] = lambda state: state.has(
|
||||
"Full Victory", self.player)
|
||||
|
||||
def get_required_client_version(self) -> tuple:
|
||||
# NOTE: Remember to change this before this game goes live
|
||||
return max((0, 2, 4), super(MeritousWorld, self).get_required_client_version())
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
return {
|
||||
"goal": self.goal,
|
||||
|
||||
@@ -27,6 +27,7 @@ class RaftWorld(World):
|
||||
options = raft_options
|
||||
|
||||
data_version = 1
|
||||
required_client_version = (0, 2, 0)
|
||||
|
||||
def generate_basic(self):
|
||||
minRPSpecified = self.world.minimum_resource_pack_amount[self.player].value
|
||||
@@ -111,9 +112,6 @@ class RaftWorld(World):
|
||||
|
||||
return super(RaftWorld, self).collect_item(state, item, remove)
|
||||
|
||||
def get_required_client_version(self) -> typing.Tuple[int, int, int]:
|
||||
return max((0, 2, 0), super(RaftWorld, self).get_required_client_version())
|
||||
|
||||
def pre_fill(self):
|
||||
if self.world.island_frequency_locations[self.player] == 0:
|
||||
self.setLocationItem("Radio Tower Frequency to Vasagatan", "Vasagatan Frequency")
|
||||
|
||||
@@ -21,6 +21,7 @@ class LegacyWorld(World):
|
||||
options = legacy_options
|
||||
topology_present = False
|
||||
data_version = 3
|
||||
required_client_version = (0, 2, 3)
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = location_table
|
||||
@@ -54,9 +55,6 @@ class LegacyWorld(World):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
|
||||
def get_required_client_version(self) -> typing.Tuple[int, int, int]:
|
||||
return max((0, 2, 3), super(LegacyWorld, self).get_required_client_version())
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
for option_name in legacy_options:
|
||||
|
||||
@@ -69,6 +69,10 @@ class SMWorld(World):
|
||||
remote_items: bool = False
|
||||
remote_start_inventory: bool = False
|
||||
|
||||
# changes to client DeathLink handling for 0.2.1
|
||||
# changes to client Remote Item handling for 0.2.6
|
||||
required_client_version = (0, 2, 6)
|
||||
|
||||
itemManager: ItemManager
|
||||
|
||||
locations = {}
|
||||
@@ -167,11 +171,6 @@ class SMWorld(World):
|
||||
create_locations(self, self.player)
|
||||
create_regions(self, self.world, self.player)
|
||||
|
||||
def get_required_client_version(self):
|
||||
# changes to client DeathLink handling for 0.2.1
|
||||
# changes to client Remote Item handling for 0.2.6
|
||||
return max(super(SMWorld, self).get_required_client_version(), (0, 2, 6))
|
||||
|
||||
def getWord(self, w):
|
||||
return (w & 0x00FF, (w & 0xFF00) >> 8)
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ class SMZ3World(World):
|
||||
remote_items: bool = False
|
||||
remote_start_inventory: bool = False
|
||||
|
||||
# first added for 0.2.6
|
||||
required_client_version = (0, 2, 6)
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
self.rom_name_available_event = threading.Event()
|
||||
self.locations = {}
|
||||
@@ -135,10 +138,6 @@ class SMZ3World(World):
|
||||
startRegion.exits.append(exit)
|
||||
exit.connect(currentRegion)
|
||||
|
||||
def get_required_client_version(self):
|
||||
# first added for 0.2.6
|
||||
return max(super(SMZ3World, self).get_required_client_version(), (0, 2, 6))
|
||||
|
||||
def apply_sm_custom_sprite(self):
|
||||
itemSprites = ["off_world_prog_item.bin", "off_world_item.bin"]
|
||||
itemSpritesAddress = [0xF800, 0xF900]
|
||||
|
||||
@@ -147,6 +147,7 @@ class SoEWorld(World):
|
||||
remote_items = False
|
||||
data_version = 2
|
||||
web = SoEWebWorld()
|
||||
required_client_version = (0, 2, 6)
|
||||
|
||||
item_name_to_id, item_id_to_raw = _get_item_mapping()
|
||||
location_name_to_id, location_id_to_raw = _get_location_mapping()
|
||||
@@ -321,10 +322,6 @@ class SoEWorld(World):
|
||||
payload = multidata["connect_names"][self.world.player_name[self.player]]
|
||||
multidata["connect_names"][self.connect_name] = payload
|
||||
|
||||
def get_required_client_version(self):
|
||||
return max((0, 2, 6), super(SoEWorld, self).get_required_client_version())
|
||||
|
||||
|
||||
class SoEItem(Item):
|
||||
game: str = "Secret of Evermore"
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ class SubnauticaWorld(World):
|
||||
options = options
|
||||
|
||||
data_version = 2
|
||||
required_client_version = (0, 1, 9)
|
||||
|
||||
def generate_basic(self):
|
||||
# Link regions
|
||||
@@ -77,10 +78,6 @@ class SubnauticaWorld(World):
|
||||
item = lookup_name_to_item[name]
|
||||
return SubnauticaItem(name, item["progression"], item["id"], player=self.player)
|
||||
|
||||
def get_required_client_version(self) -> typing.Tuple[int, int, int]:
|
||||
return max((0, 1, 9), super(SubnauticaWorld, self).get_required_client_version())
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, None, name, player)
|
||||
ret.world = world
|
||||
|
||||
Reference in New Issue
Block a user