Skip to content

Commit 2cdd03f

Browse files
authored
Network: implement 0.4 marked compatibility removals (ArchipelagoMW#757)
* world remote items handling * players list when connecting
1 parent ce42fda commit 2cdd03f

20 files changed

+27
-108
lines changed

Main.py

-4
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,6 @@ def precollect_hint(location):
381381
"names": names, # TODO: remove around 0.2.5 in favor of slot_info
382382
"games": games, # TODO: remove around 0.2.5 in favor of slot_info
383383
"connect_names": {name: (0, player) for player, name in world.player_name.items()},
384-
"remote_items": {player for player in world.player_ids if
385-
world.worlds[player].remote_items},
386-
"remote_start_inventory": {player for player in world.player_ids if
387-
world.worlds[player].remote_start_inventory},
388384
"locations": locations_data,
389385
"checks_in_area": checks_in_area,
390386
"server_options": baked_server_options,

MultiServer.py

+6-25
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,6 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo
148148
self.player_name_lookup: typing.Dict[str, team_slot] = {}
149149
self.connect_names = {} # names of slots clients can connect to
150150
self.allow_forfeits = {}
151-
self.remote_items = set()
152-
self.remote_start_inventory = set()
153151
# player location_id item_id target_player_id
154152
self.locations = {}
155153
self.host = host
@@ -366,8 +364,6 @@ def _load(self, decoded_obj: dict, use_embedded_server_options: bool):
366364
self.seed_name = decoded_obj["seed_name"]
367365
self.random.seed(self.seed_name)
368366
self.connect_names = decoded_obj['connect_names']
369-
self.remote_items = decoded_obj['remote_items']
370-
self.remote_start_inventory = decoded_obj.get('remote_start_inventory', decoded_obj['remote_items'])
371367
self.locations = decoded_obj['locations']
372368
self.slot_data = decoded_obj['slot_data']
373369
for slot, data in self.slot_data.items():
@@ -548,7 +544,7 @@ def set_save(self, savedata: dict):
548544

549545
if "stored_data" in savedata:
550546
self.stored_data = savedata["stored_data"]
551-
# count items and slots from lists for item_handling = remote
547+
# count items and slots from lists for items_handling = remote
552548
logging.info(
553549
f'Loaded save file with {sum([len(v) for k, v in self.received_items.items() if k[2]])} received items '
554550
f'for {sum(k[2] for k in self.received_items)} players')
@@ -708,19 +704,14 @@ async def on_client_connected(ctx: Context, client: Client):
708704
await ctx.send_msgs(client, [{
709705
'cmd': 'RoomInfo',
710706
'password': bool(ctx.password),
711-
# TODO remove around 0.4
712-
'players': players,
713-
# TODO convert to list of games present in 0.4
714-
'games': [ctx.games[x] for x in range(1, len(ctx.games) + 1)],
707+
'games': {ctx.games[x] for x in range(1, len(ctx.games) + 1)},
715708
# tags are for additional features in the communication.
716709
# Name them by feature or fork, as you feel is appropriate.
717710
'tags': ctx.tags,
718711
'version': Utils.version_tuple,
719712
'permissions': get_permissions(ctx),
720713
'hint_cost': ctx.hint_cost,
721714
'location_check_points': ctx.location_check_points,
722-
'datapackage_version': sum(game_data["version"] for game_data in ctx.gamespackage.values())
723-
if all(game_data["version"] for game_data in ctx.gamespackage.values()) else 0,
724715
'datapackage_versions': {game: game_data["version"] for game, game_data
725716
in ctx.gamespackage.items()},
726717
'seed_name': ctx.seed_name,
@@ -1557,20 +1548,10 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
15571548
minver = min_client_version if ignore_game else ctx.minimum_client_versions[slot]
15581549
if minver > args['version']:
15591550
errors.add('IncompatibleVersion')
1560-
if args.get('items_handling', None) is None:
1561-
# fall back to load from multidata
1562-
client.no_items = False
1563-
client.remote_items = slot in ctx.remote_items
1564-
client.remote_start_inventory = slot in ctx.remote_start_inventory
1565-
await ctx.send_msgs(client, [{
1566-
"cmd": "Print", "text":
1567-
"Warning: Client is not sending items_handling flags, "
1568-
"which will not be supported in the future."}])
1569-
else:
1570-
try:
1571-
client.items_handling = args['items_handling']
1572-
except (ValueError, TypeError):
1573-
errors.add('InvalidItemsHandling')
1551+
try:
1552+
client.items_handling = args['items_handling']
1553+
except (ValueError, TypeError):
1554+
errors.add('InvalidItemsHandling')
15741555

15751556
# only exact version match allowed
15761557
if ctx.compatibility == 0 and args['version'] != version_tuple:

WebHostLib/api/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ def get_datapackage():
3939

4040
@api_endpoints.route('/datapackage_version')
4141
@cache.cached()
42+
4243
def get_datapackage_versions():
4344
from worlds import network_data_package, AutoWorldRegister
45+
4446
version_package = {game: world.data_version for game, world in AutoWorldRegister.world_types.items()}
45-
version_package["version"] = network_data_package["version"]
4647
return version_package
4748

4849

docs/network protocol.md

-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ Sent to clients when they connect to an Archipelago server.
7474
| hint_cost | int | The amount of points it costs to receive a hint from the server. |
7575
| location_check_points | int | The amount of hint points you receive per item/location check completed. ||
7676
| games | list\[str\] | List of games present in this multiworld. |
77-
| datapackage_version | int | Sum of individual games' datapackage version. Deprecated. Use `datapackage_versions` instead. |
7877
| datapackage_versions | dict\[str, int\] | Data versions of the individual games' data packages the server will send. Used to decide which games' caches are outdated. See [Data Package Contents](#Data-Package-Contents). |
7978
| seed_name | str | uniquely identifying name of this generation |
8079
| time | float | Unix time stamp of "now". Send for time synchronization if wanted for things like the DeathLink Bounce. |

docs/world api.md

+9-26
Original file line numberDiff line numberDiff line change
@@ -343,18 +343,6 @@ class MyGameWorld(World):
343343
option_definitions = mygame_options # assign the options dict to the world
344344
#...
345345
```
346-
347-
### Local or Remote
348-
349-
A world with `remote_items` set to `True` gets all items items from the server
350-
and no item from the local game. So for an RPG opening a chest would not add
351-
any item to your inventory, instead the server will send you what was in that
352-
chest. The advantage is that a generic mod can be used that does not need to
353-
know anything about the seed.
354-
355-
A world with `remote_items` set to `False` will locally reward its local items.
356-
For console games this can remove delay and make script/animation/dialog flow
357-
more natural. These games typically have been edited to 'bake in' the items.
358346

359347
### A World Class Skeleton
360348

@@ -379,8 +367,6 @@ class MyGameWorld(World):
379367
game: str = "My Game" # name of the game/world
380368
option_definitions = mygame_options # options the player can set
381369
topology_present: bool = True # show path to required location checks in spoiler
382-
remote_items: bool = False # True if all items come from the server
383-
remote_start_inventory: bool = False # True if start inventory comes from the server
384370

385371
# data_version is used to signal that items, locations or their names
386372
# changed. Set this to 0 during development so other games' clients do not
@@ -415,30 +401,27 @@ The world has to provide the following things for generation
415401
* additions to the item pool
416402
* additions to the regions list: at least one called "Menu"
417403
* locations placed inside those regions
418-
* a `def create_item(self, item: str) -> MyGameItem` for plando/manual placing
419-
* applying `self.world.precollected_items` for plando/start inventory
420-
if not using a `remote_start_inventory`
404+
* a `def create_item(self, item: str) -> MyGameItem` to create any item on demand
405+
* applying `self.world.push_precollected` for start inventory
421406
* a `def generate_output(self, output_directory: str)` that creates the output
422-
if there is output to be generated. If only items are randomized and
423-
`remote_items = True` it is possible to have a generic mod and output
424-
generation can be skipped. In all other cases this is required. When this is
425-
called, `self.world.get_locations()` has all locations for all players, with
426-
properties `item` pointing to the item and `player` identifying the player.
427-
`self.world.get_filled_locations(self.player)` will filter for this world.
428-
`item.player` can be used to see if it's a local item.
407+
files if there is output to be generated. When this is
408+
called, `self.world.get_locations(self.player)` has all locations for the player, with
409+
attribute `item` pointing to the item.
410+
`location.item.player` can be used to see if it's a local item.
429411

430412
In addition, the following methods can be implemented and attributes can be set
431413

432414
* `def generate_early(self)`
433415
called per player before any items or locations are created. You can set
434416
properties on your world here. Already has access to player options and RNG.
435417
* `def create_regions(self)`
436-
called to place player's regions into the MultiWorld's regions list. If it's
418+
called to place player's regions and their locations into the MultiWorld's regions list. If it's
437419
hard to separate, this can be done during `generate_early` or `basic` as well.
438420
* `def create_items(self)`
439421
called to place player's items into the MultiWorld's itempool.
440422
* `def set_rules(self)`
441-
called to set access and item rules on locations and entrances.
423+
called to set access and item rules on locations and entrances.
424+
Locations have to be defined before this, or rule application can miss them.
442425
* `def generate_basic(self)`
443426
called after the previous steps. Some placement and player specific
444427
randomizations can be done here. After this step all regions and items have

worlds/AutoWorld.py

-12
Original file line numberDiff line numberDiff line change
@@ -160,18 +160,6 @@ class World(metaclass=AutoWorldRegister):
160160

161161
hint_blacklist: ClassVar[FrozenSet[str]] = frozenset() # any names that should not be hintable
162162

163-
# NOTE: remote_items and remote_start_inventory are now available in the network protocol for the client to set.
164-
# These values will be removed.
165-
# if a world is set to remote_items, then it just needs to send location checks to the server and the server
166-
# sends back the items
167-
# if a world is set to remote_items = False, then the server never sends an item where receiver == finder,
168-
# the client finds its own items in its own world.
169-
remote_items: bool = True
170-
171-
# If remote_start_inventory is true, the start_inventory/world.precollected_items is sent on connection,
172-
# otherwise the world implementation is in charge of writing the items to their output data.
173-
remote_start_inventory: bool = True
174-
175163
# For games where after a victory it is impossible to go back in and get additional/remaining Locations checked.
176164
# this forces forfeit: auto for those games.
177165
forced_auto_forfeit: bool = False

worlds/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,11 @@ class WorldSource(typing.NamedTuple):
8181
lookup_any_location_id_to_name.update(world.location_id_to_name)
8282

8383
network_data_package: DataPackage = {
84-
"version": sum(world.data_version for world in AutoWorldRegister.world_types.values()),
8584
"games": games,
8685
}
8786

8887
# Set entire datapackage to version 0 if any of them are set to 0
8988
if any(not world.data_version for world in AutoWorldRegister.world_types.values()):
90-
network_data_package["version"] = 0
9189
import logging
9290

9391
logging.warning(f"Datapackage is in custom mode. Custom Worlds: "

worlds/alttp/Rom.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -795,11 +795,11 @@ def patch_rom(world, rom, player, enemized):
795795
itemid = 0x33
796796
elif location.item.compass:
797797
itemid = 0x25
798-
if world.worlds[player].remote_items: # remote items does not currently work
799-
itemid = list(location_table.keys()).index(location.name) + 1
800-
assert itemid < 0x100
801-
rom.write_byte(location.player_address, 0xFF)
802-
elif location.item.player != player:
798+
# if world.worlds[player].remote_items: # remote items does not currently work
799+
# itemid = list(location_table.keys()).index(location.name) + 1
800+
# assert itemid < 0x100
801+
# rom.write_byte(location.player_address, 0xFF)
802+
if location.item.player != player:
803803
if location.player_address is not None:
804804
rom.write_byte(location.player_address, min(location.item.player, ROM_PLAYER_LIMIT))
805805
else:
@@ -1654,7 +1654,7 @@ def get_reveal_bytes(itemName):
16541654
write_strings(rom, world, player)
16551655

16561656
# remote items flag, does not currently work
1657-
rom.write_byte(0x18637C, int(world.worlds[player].remote_items))
1657+
rom.write_byte(0x18637C, 0)
16581658

16591659
# set rom name
16601660
# 21 bytes

worlds/alttp/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ class ALTTPWorld(World):
121121
location_name_to_id = lookup_name_to_id
122122

123123
data_version = 8
124-
remote_items: bool = False
125-
remote_start_inventory: bool = False
126124
required_client_version = (0, 3, 2)
127125
web = ALTTPWeb()
128126

worlds/dark_souls_3/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ class DarkSouls3World(World):
5151
game: str = "Dark Souls III"
5252
option_definitions = dark_souls_options
5353
topology_present: bool = True
54-
remote_items: bool = False
55-
remote_start_inventory: bool = False
5654
web = DarkSouls3Web()
5755
data_version = 4
5856
base_id = 100000

worlds/ff1/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ class FF1World(World):
3030
option_definitions = ff1_options
3131
game = "Final Fantasy"
3232
topology_present = False
33-
remote_items = True
3433
data_version = 2
35-
remote_start_inventory = True
3634

3735
ff1_items = FF1Items()
3836
ff1_locations = FF1Locations()

worlds/hylics2/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ class Hylics2World(World):
3636
option_definitions = Options.hylics2_options
3737

3838
topology_present: bool = True
39-
remote_items: bool = True
40-
remote_start_inventory: bool = True
4139

4240
data_version: 1
4341

worlds/oot/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@ class OOTWorld(World):
102102
item_name_to_id = {item_name: oot_data_to_ap_id(data, False) for item_name, data in item_table.items() if
103103
data[2] is not None}
104104
location_name_to_id = location_name_to_id
105-
remote_items: bool = False
106-
remote_start_inventory: bool = False
107105
web = OOTWeb()
108106

109107
data_version = 2

worlds/overcooked2/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ class Overcooked2World(World):
4949
required_client_version = (0, 3, 4)
5050
option_definitions = overcooked_options
5151
topology_present: bool = False
52-
remote_items: bool = True
53-
remote_start_inventory: bool = False
5452
data_version = 2
5553

5654
item_name_to_id = item_name_to_id

worlds/pokemon_rb/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ class PokemonRedBlueWorld(World):
3838
# -MuffinJets#4559
3939
game = "Pokemon Red and Blue"
4040
option_definitions = pokemon_rb_options
41-
remote_items = False
41+
4242
data_version = 3
4343
required_client_version = (0, 3, 7)
44+
4445
topology_present = False
4546

4647

worlds/sm/__init__.py

-3
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ class SMWorld(World):
9393
location_name_to_id = {key: locations_start_id + value.Id for key, value in locationsDict.items() if value.Id != None}
9494
web = SMWeb()
9595

96-
remote_items: bool = False
97-
remote_start_inventory: bool = False
98-
9996
# changes to client DeathLink handling for 0.2.1
10097
# changes to client Remote Item handling for 0.2.6
10198
required_client_version = (0, 2, 6)

worlds/smz3/__init__.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ class SMZ3World(World):
7676
for key, value in TotalSMZ3World(Config(), "", 0, "").locationLookup.items()}
7777
web = SMZ3Web()
7878

79-
remote_items: bool = False
80-
remote_start_inventory: bool = False
81-
8279
locationNamesGT: Set[str] = {loc.Name for loc in GanonsTower(None, None).Locations}
8380

8481
# first added for 0.2.6
@@ -485,9 +482,9 @@ def remove(self, state: CollectionState, item: Item) -> bool:
485482
return False
486483

487484
def create_item(self, name: str) -> Item:
488-
return SMZ3Item(name,
485+
return SMZ3Item(name,
489486
ItemClassification.progression if SMZ3World.isProgression(TotalSMZ3Item.ItemType[name]) else ItemClassification.filler,
490-
TotalSMZ3Item.ItemType[name], self.item_name_to_id[name],
487+
TotalSMZ3Item.ItemType[name], self.item_name_to_id[name],
491488
self.player,
492489
TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[name], self))
493490

worlds/soe/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ class SoEWorld(World):
153153
game: str = "Secret of Evermore"
154154
option_definitions = soe_options
155155
topology_present = False
156-
remote_items = False
157156
data_version = 4
158157
web = SoEWebWorld()
159158
required_client_version = (0, 3, 5)

worlds/timespinner/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ class TimespinnerWorld(World):
4343
option_definitions = timespinner_options
4444
game = "Timespinner"
4545
topology_present = True
46-
remote_items = False
4746
data_version = 10
4847
web = TimespinnerWebWorld()
4948

worlds/zillion/__init__.py

-8
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,6 @@ class ZillionWorld(World):
6161
# retrieved by clients on every connection.
6262
data_version: int = 1
6363

64-
# NOTE: remote_items and remote_start_inventory are now available in the network protocol for the client to set.
65-
# These values will be removed.
66-
# if a world is set to remote_items, then it just needs to send location checks to the server and the server
67-
# sends back the items
68-
# if a world is set to remote_items = False, then the server never sends an item where receiver == finder,
69-
# the client finds its own items in its own world.
70-
remote_items: bool = False
71-
7264
logger: logging.Logger
7365

7466
class LogStreamInterface:

0 commit comments

Comments
 (0)