From f3b490b10aeac32ba859b929ff13ff882d818a17 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 1 Nov 2025 14:32:04 -0400 Subject: Store stable IDs in multiworld state --- apworld/context.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'apworld/context.py') diff --git a/apworld/context.py b/apworld/context.py index e2d80cd..52b04ae 100644 --- a/apworld/context.py +++ b/apworld/context.py @@ -30,6 +30,16 @@ KEY_STORAGE_MAPPING = { REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} +# There is a distinction between an object's ID and its AP ID. The latter is stable between releases, whereas the former +# can change and is also namespaced based on the object type. We should only store AP IDs in multiworld state (such as +# slot data and data storage) to increase compatability between releases. The data we currently store is: +# - Port pairings for worldport shuffle (slot data) +# - Checked worldports for worldport shuffle (data storage) +# - Latched doors (data storage) +# The client generally deals in the actual object IDs rather than the stable IDs, although it does have to convert the +# port pairing IDs when reading them from slot data. The context (this file here) does the work of converting back and +# forth between the values. AP IDs are converted to IDs after reading them from data storage, and IDs are converted to +# AP IDs before sending them to data storage. class Lingo2Manager: game_ctx: "Lingo2GameContext" client_ctx: "Lingo2ClientContext" @@ -74,6 +84,7 @@ class Lingo2Manager: return ret + # Input should be real IDs, not AP IDs def update_worldports(self, new_worldports: set[int]) -> set[int]: ret = new_worldports.difference(self.worldports) self.worldports.update(new_worldports) @@ -227,6 +238,7 @@ class Lingo2GameContext: async_start(self.send_msgs([msg]), name="update keyboard") + # Input should be real IDs, not AP IDs def send_update_worldports(self, worldports): if self.server is None: return @@ -441,13 +453,15 @@ class Lingo2ClientContext(CommonContext): elif args["key"] == self.get_datastorage_key("keyboard2"): self.handle_keyboard_update(2, args) elif args["key"] == self.get_datastorage_key("worldports"): - updates = self.manager.update_worldports(set(args["value"])) + port_ids = set(Lingo2World.static_logic.port_id_by_ap_id[ap_id] for ap_id in args["value"]) + updates = self.manager.update_worldports(port_ids) if len(updates) > 0: self.manager.game_ctx.send_update_worldports(updates) elif args["key"] == self.victory_data_storage_key: self.handle_status_update(args["value"]) elif args["key"] == self.get_datastorage_key("latches"): - updates = self.manager.update_latches(set(args["value"])) + door_ids = set(Lingo2World.static_logic.door_id_by_ap_id[ap_id] for ap_id in args["value"]) + updates = self.manager.update_latches(door_ids) if len(updates) > 0: self.manager.game_ctx.send_update_latches(updates) @@ -515,14 +529,16 @@ class Lingo2ClientContext(CommonContext): if len(updates) > 0: self.manager.game_ctx.send_update_keyboard(updates) + # Input should be real IDs, not AP IDs async def update_worldports(self, updates: set[int]): + port_ap_ids = [Lingo2World.static_logic.objects.ports[port_id].ap_id for port_id in updates] await self.send_msgs([{ "cmd": "Set", "key": self.get_datastorage_key("worldports"), "want_reply": True, "operations": [{ "operation": "update", - "value": updates + "value": port_ap_ids }] }]) @@ -532,13 +548,14 @@ class Lingo2ClientContext(CommonContext): self.manager.game_ctx.send_accessible_locations() async def update_latches(self, updates: set[int]): + door_ap_ids = [Lingo2World.static_logic.objects.doors[door_id].ap_id for door_id in updates] await self.send_msgs([{ "cmd": "Set", "key": self.get_datastorage_key("latches"), "want_reply": True, "operations": [{ "operation": "update", - "value": updates + "value": door_ap_ids }] }]) @@ -590,12 +607,14 @@ async def process_game_cmd(manager: Lingo2Manager, args: dict): async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") elif cmd == "CheckWorldport": port_id = args["port_id"] + port_ap_id = Lingo2World.static_logic.objects.ports[port_id].ap_id worldports = {port_id} # Also check the reverse port if it's a two-way connection. port_pairings = manager.client_ctx.slot_data["port_pairings"] - if str(port_id) in port_pairings and port_pairings.get(str(port_pairings[str(port_id)]), None) == port_id: - worldports.add(port_pairings[str(port_id)]) + if str(port_ap_id) in port_pairings and\ + port_pairings.get(str(port_pairings[str(port_ap_id)]), None) == port_ap_id: + worldports.add(Lingo2World.static_logic.port_id_by_ap_id[port_pairings[str(port_ap_id)]]) updates = manager.update_worldports(worldports) if len(updates) > 0: -- cgit 1.4.1